Make flavor push and handle multiple dependencies correctly
This commit is contained in:
parent
f6120a85d0
commit
b4971ff45a
1 changed files with 65 additions and 23 deletions
80
flavor
80
flavor
|
@ -4,9 +4,10 @@ use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
use autodie;
|
use autodie;
|
||||||
use Cwd qw(abs_path);
|
use Cwd qw(abs_path);
|
||||||
use List::Util qw(none any first);
|
use List::Util qw(none any first all);
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
use Getopt::Std;
|
use Getopt::Std;
|
||||||
|
use Data::Dumper;
|
||||||
|
|
||||||
my $dir = dirname(abs_path($0));
|
my $dir = dirname(abs_path($0));
|
||||||
|
|
||||||
|
@ -26,6 +27,7 @@ sub build {
|
||||||
my $graph = create_graph(@images, $options);
|
my $graph = create_graph(@images, $options);
|
||||||
|
|
||||||
my @queue = keys %{$graph->{top}};
|
my @queue = keys %{$graph->{top}};
|
||||||
|
my @done = ();
|
||||||
my %running = ();
|
my %running = ();
|
||||||
|
|
||||||
while (scalar(@queue) > 0 or scalar(keys(%running)) > 0) {
|
while (scalar(@queue) > 0 or scalar(keys(%running)) > 0) {
|
||||||
|
@ -39,9 +41,15 @@ sub build {
|
||||||
print 'ERROR: Failed running build for '.$running{$pid}." with exit code ".($? >> 8)."\n";
|
print 'ERROR: Failed running build for '.$running{$pid}." with exit code ".($? >> 8)."\n";
|
||||||
} else {
|
} else {
|
||||||
print "INFO: Finished build for $running{$pid}\n";
|
print "INFO: Finished build for $running{$pid}\n";
|
||||||
if (defined($graph->{dict}{$running{$pid}})) {
|
my $curr = $running{$pid};
|
||||||
my @add_queue = @{$graph->{dict}{$running{$pid}}};
|
push @done, ($curr);
|
||||||
@queue = (@queue, @add_queue);
|
if (defined($graph->{parents}{$curr})) {
|
||||||
|
for my $dependee (@{$graph->{parents}{$curr}}) {
|
||||||
|
my $inp = 0;
|
||||||
|
if (all {$inp=$_; any {$inp eq $_} @done} @{$graph->{dict}{$dependee}}) {
|
||||||
|
@queue = (@queue, ($dependee));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,23 +59,28 @@ sub build {
|
||||||
|
|
||||||
if (my $job = shift @queue) {
|
if (my $job = shift @queue) {
|
||||||
print "INFO: Starting build for $job\n";
|
print "INFO: Starting build for $job\n";
|
||||||
|
|
||||||
my $pid = fork;
|
my $pid = fork;
|
||||||
if ($pid) {
|
if ($pid) {
|
||||||
$running{$pid} = $job;
|
$running{$pid} = $job;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
build_dockerfile("$dir/$job", $options->{repo} . "/${job}");
|
if ($options->{noop}) {
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
build_dockerfile("$dir/$job", $options->{repo} . "/${job}", $job);
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
print "INFO: Done building!\n";
|
print "INFO: Done building!\n";
|
||||||
|
return @done;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub build_dockerfile {
|
sub build_dockerfile {
|
||||||
my ($path, $tag) = @_;
|
my ($path, $tag, $job) = @_;
|
||||||
exec "docker build -t $tag $path >/dev/null 2>/dev/null" or exit 1;
|
exec "docker build -t $tag $path > logs/$job.log 2>&1" or exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_dependencies {
|
sub get_dependencies {
|
||||||
|
@ -109,13 +122,14 @@ sub get_from {
|
||||||
my $dockerfile = "$dir/$image/Dockerfile";
|
my $dockerfile = "$dir/$image/Dockerfile";
|
||||||
if (!-f $dockerfile) {return undef;}
|
if (!-f $dockerfile) {return undef;}
|
||||||
open(my $df, '<', $dockerfile) or return undef;
|
open(my $df, '<', $dockerfile) or return undef;
|
||||||
$cache{$image} = first {$_} map {
|
my @res = grep {$_} map {
|
||||||
/^\s*FROM\s+(.*)\s*/;
|
/^\s*FROM\s+([A-Za-z0-9\.\-_\/]+)(\s*AS.*)?\s*/;
|
||||||
$1
|
$1
|
||||||
} <$df>;
|
} <$df>;
|
||||||
|
$cache{$image} = \@res
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cache{$image}
|
return @{$cache{$image}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub create_graph {
|
sub create_graph {
|
||||||
|
@ -124,24 +138,40 @@ sub create_graph {
|
||||||
|
|
||||||
my %top = ();
|
my %top = ();
|
||||||
my %dict = ();
|
my %dict = ();
|
||||||
|
my %parents = ();
|
||||||
|
|
||||||
for my $image (@images) {
|
for my $image (@images) {
|
||||||
my $parent = get_from($image);
|
my @curr_parents = get_from($image);
|
||||||
|
print "DEBUG: $image depends on: " . join(" ", @curr_parents). "\n";
|
||||||
|
my $is_top = 1;
|
||||||
|
for my $parent (@curr_parents) {
|
||||||
my ($repo, $name) = split('/', $parent, 2) if $parent;
|
my ($repo, $name) = split('/', $parent, 2) if $parent;
|
||||||
|
|
||||||
if (!$parent or $repo ne $options->{repo} or $options->{exclude}{$name} or !-f "$dir/$name/Dockerfile") {
|
if (!$parent or $repo ne $options->{repo} or $options->{exclude}{$name} or !-f "$dir/$name/Dockerfile") {
|
||||||
$top{$image} = 1;
|
;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!$dict{$name}) {
|
$is_top = 0;
|
||||||
$dict{$name} = ();
|
if (!$dict{$image}) {
|
||||||
|
$dict{$image} = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
push @{$dict{$name}}, ($image);
|
push @{$dict{$image}}, ($name);
|
||||||
|
|
||||||
|
if (!$parents{$name}) {
|
||||||
|
$parents{$name} = ()
|
||||||
|
}
|
||||||
|
|
||||||
|
push @{$parents{$name}}, ($image);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {top => {%top}, dict => {%dict}};
|
if ($is_top) {
|
||||||
|
$top{$image} = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {top => {%top}, dict => {%dict}, parents => {%parents}};
|
||||||
}
|
}
|
||||||
|
|
||||||
sub usage() {
|
sub usage() {
|
||||||
|
@ -152,6 +182,7 @@ Build Docker images with dependency graphing
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-x Comma separated list of images not to rebuild in chain
|
-x Comma separated list of images not to rebuild in chain
|
||||||
|
-n Noop, don't actually build.
|
||||||
-o Only build given images, don't build parents
|
-o Only build given images, don't build parents
|
||||||
-r Which repo or prefix to use, default: d.xr.to
|
-r Which repo or prefix to use, default: d.xr.to
|
||||||
-p Push image after building
|
-p Push image after building
|
||||||
|
@ -165,11 +196,12 @@ sub MAIN() {
|
||||||
my $push = 0;
|
my $push = 0;
|
||||||
my $jobs = 4;
|
my $jobs = 4;
|
||||||
my $only = 0;
|
my $only = 0;
|
||||||
|
my $noop = 0;
|
||||||
my %exclude = ();
|
my %exclude = ();
|
||||||
my @images = ();
|
my @images = ();
|
||||||
my %opts;
|
my %opts;
|
||||||
|
|
||||||
getopts('hr:x:opj:', \%opts);
|
getopts('hr:x:opj:n', \%opts);
|
||||||
|
|
||||||
if (scalar(@ARGV) < 1 || $opts{'h'}) {
|
if (scalar(@ARGV) < 1 || $opts{'h'}) {
|
||||||
usage;
|
usage;
|
||||||
|
@ -180,6 +212,10 @@ sub MAIN() {
|
||||||
$push = 1;
|
$push = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($opts{'n'}) {
|
||||||
|
$noop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if ($opts{'r'}) {
|
if ($opts{'r'}) {
|
||||||
$repo = $opts{'r'};
|
$repo = $opts{'r'};
|
||||||
}
|
}
|
||||||
|
@ -212,7 +248,7 @@ sub MAIN() {
|
||||||
@images = map {
|
@images = map {
|
||||||
/\/([^\/]+)\/Dockerfile/;
|
/\/([^\/]+)\/Dockerfile/;
|
||||||
$1
|
$1
|
||||||
} <$dir/*/Dockerfile>;
|
} <$dir/*"/Dockerfile">;
|
||||||
|
|
||||||
@images = grep {not $exclude{$_}} @images;
|
@images = grep {not $exclude{$_}} @images;
|
||||||
}
|
}
|
||||||
|
@ -227,7 +263,13 @@ sub MAIN() {
|
||||||
|
|
||||||
print "INFO: building: " . join(', ', @images) . "\n";
|
print "INFO: building: " . join(', ', @images) . "\n";
|
||||||
|
|
||||||
build @images, { only => $only, exclude => { %exclude }, repo => $repo, jobs => $jobs };
|
my @succ = build @images, { noop => $noop, only => $only, exclude => { %exclude }, repo => $repo, jobs => $jobs };
|
||||||
|
|
||||||
|
if ($push) {
|
||||||
|
for my $item (@succ) {
|
||||||
|
system "docker push $repo/$item"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MAIN;
|
MAIN;
|
||||||
|
|
Loading…
Reference in a new issue