chiark / gitweb /
dgit-repos-server: New prototype tag2upload mode for git-debpush
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Fri, 5 Jul 2019 13:41:29 +0000 (14:41 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sat, 6 Jul 2019 20:12:44 +0000 (21:12 +0100)
Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
infra/dgit-repos-server

index eea5d94fc7a465c3ffa4f43c8813a00aed403ae4..68429eb73a13881614e33510f844648ab6fedd48 100755 (executable)
@@ -21,6 +21,8 @@
 # usages:
 #   dgit-repos-server DISTRO DISTRO-DIR AUTH-SPEC [<settings>] --ssh
 #   dgit-repos-server DISTRO DISTRO-DIR AUTH-SPEC [<settings>] --cron
+#   dgit-repos-server DISTRO DISTRO-DIR AUTH-SPEC [<settings>] \
+#      --tag2upload URL TAGNAME
 # settings
 #   --repos=GIT-REPOS-DIR      default DISTRO-DIR/repos/
 #   --suites=SUITES-FILE       default DISTRO-DIR/suites
@@ -50,6 +52,8 @@
 # (With --cron AUTH-SPEC is not used and may be the empty string.)
 
 use strict;
+use Carp;
+use IO::Handle;
 
 use Debian::Dgit::Infra; # must precede Debian::Dgit; - can change @INC!
 use Debian::Dgit qw(:DEFAULT :policyflags);
@@ -1068,6 +1072,209 @@ our @hookenvs = qw(distro suitesfile suitesformasterfile policyhook
 
 # workrepo and destrepo handled ad-hoc
 
+sub mode_tag2upload () {
+    # PROTOTYPE
+    # CALLER MUST PREVENT MULTIPLE CONCURRENT RUNS IN SAME CWD
+    # If we fail (exit nonzero), caller should capture our stderr,
+    #  and retry some bounded number of times in some appropriate way
+    @ARGV==2 or die;
+
+    my $url;
+    ($url,$tagval) = @ARGV;
+
+    $ENV{DGIT_DRS_EMAIL_NOREPLY} // die;
+
+    my $start = time // die;
+    my @t = gmtime $start;
+
+    die if $url =~ m/[^[:graph:]]/;
+    die if $tagval =~ m/[^[:graph:]]/;
+
+    open OL, ">>overall.log" or die $!;
+    autoflush OL 1;
+    my $quit = sub {
+       printf OL "%04d-%02d-%02d %02d:%02d:%02d (%04ds): %s %s: %s\n",
+           $t[5] + 1900, @t[4,3,2,1,0], (time-$start), $url, $tagval, $_[0];
+       exit 0;
+    };
+
+    $ENV{DGIT_DRS_ANY_URL} or $url =~ m{^https://}s
+       or $quit->("url scheme not as expected");
+
+    $tagval =~ m{^$distro/($versiontag_re)$}s
+       or $quit->("tag name not for us");
+
+    $version = $1;
+    $version =~ y/_\%\#/:~/d;
+
+    my $work = 'work';
+
+    my $tagref = "refs/tags/$tagval";
+
+    rmtree $work;
+    mkdir $work or die $!;
+    changedir $work;
+    runcmd qw(git init -q);
+    runcmd qw(git remote add origin), $url;
+    runcmd qw(git fetch --depth=1 origin), "$tagref:$tagref";
+    changedir ".git";
+    mkdir 'dgit-tmp' or die $!;
+
+    my $tagger;
+    open T, "-|", qw(git cat-file tag), $tagval or die $!;
+    {
+       local $/ = undef;
+       $!=0; $_=<T>; defined or die $!;
+
+       # quick and dirty check, will check properly later
+       m/^\[dgit[^"]* please-upload(?:\]| )/m or
+           $quit->("tag missing please-upload request $_");
+
+       m/^tagger (.*) \d+ [-+]\d+$/m or
+           $quit->("failed to fish tagger out of tag");
+       $tagger = $1;
+    };
+
+    readtag();
+    m/^($package_re) release (\S+) for ($suite_re)$/ or
+       $quit->("tag headline not for us");
+    $package = $1;
+    my $tagmversion = $2;
+    $suite = $3;
+
+
+    # This is for us.  From now on, we will capture errors to
+    # be emailed to the tagger.
+    # TODO: failures to git fetch from salsa will burn a version
+
+    open H, ">>dgit-tmp/tagupl.email" or die $!;
+    print H <<END or die $!;
+Subject: push-to-upload failed, $package $version ($distro)
+X-Debian-Push-Distro: $distro
+X-Debian-Push-Package: $package
+END
+    printf H "To: %s", $tagger or die $!; # no newline
+    flush H or die $!;
+
+    open L, ">>dgit-tmp/tagupl.log" or die $!;
+
+    my $child = fork() // die $!;
+    if ($child) {
+       # we are the parent
+       # if child exits 0, it has called $quit->()
+       $!=0; waitpid $child, 0 == $child or die $!;
+       printdebug "child $child ?=$?\n";
+       exit 0 unless $?;
+       print L "execution child: ", waitstatusmsg(), "\n" or die $!;
+       close L or die $!;
+       print H <<END or die $!;
+
+
+Processing of tag $tagval
+From url $url
+Was not successful:
+
+END
+       $ENV{DGIT_DRS_SENDMAIL} //= '/usr/lib/sendmail';
+
+       close H or die $!;
+       runcmd qw(sh -ec), <<"END";
+            cd dgit-tmp
+            cat tagupl.log >>tagupl.email
+            $ENV{DGIT_DRS_SENDMAIL} -oee -odb -oi -t  \\
+                -f$ENV{DGIT_DRS_EMAIL_NOREPLY}        \\
+                <tagupl.email
+END
+       exit 0;
+    }
+
+    open STDERR, ">&L" or die $!;
+    open STDOUT, ">&STDERR" or die $!;
+    open DEBUG, ">&STDERR" if $debuglevel;
+
+    die "$tagmversion != $version " unless $tagmversion eq $version;
+
+    my %need = map { $_ => 1 } qw(please-upload split);
+    my ($upstreamc, $upstreamt);
+    my $quilt;
+    my $distro_ok;
+
+    parsetag_general sub {
+       if (m/^(\S+) / && exists $need{$1}) {
+           $_ = $';
+           delete $need{$1};
+       } elsif (s/^upstream=(\w+) //) {
+           $upstreamc = $1;
+       } elsif (s/^upstream-tag=(\S+) //) {
+           $upstreamt = $1;
+       } elsif (s/^quilt=([-+0-9a-z]+) //) {
+           $quilt = $1;
+       } else {
+           return 0;
+       }
+       return 1;
+    }, sub {
+       my ($gotdistro) = @_;
+       $distro_ok ||= $gotdistro eq $distro;
+    };
+
+    $quit->("other distro") unless $distro_ok;
+
+    reject "missing \"$_\"" foreach keys %need;
+
+    reject "upstream tag and not commitish, or v-v"
+       unless defined $upstreamt == defined $upstreamc;
+
+    verifytag();
+
+    my @dgit;
+    push @dgit, $ENV{DGIT_DRS_DGIT} // 'dgit';
+    push @dgit, '-wn';
+    push @dgit, "-p$package";
+
+    changedir "..";
+    runcmd (@dgit, qw(setup-gitattributes));
+
+    my @fetch = qw(git fetch origin --unshallow);
+    if (defined $upstreamt) {
+       runcmd qw(git check-ref-format), "refs/tags/$upstreamt";
+       runcmd qw(git check-ref-format), "refs/tags/$upstreamt";
+       my $utagref = "refs/tags/$upstreamt";
+       push @fetch, "$utagref:$utagref";
+    }
+    runcmd @fetch;
+
+    $upstreamc eq git_rev_parse "refs/tags/$upstreamt" or die;
+
+    runcmd qw(git checkout -q), "refs/tags/$tagval";
+
+    @fetch = (@dgit, qw(fetch), $suite);
+    debugcmd "+",@_;
+    $!=0; $?=-1;
+    if (system @fetch) {
+       failedcmd @fetch unless $? == 4*256;
+    }
+    # this is just to get the orig, so we don't really care about the ref
+    runcmd qw(git deborig), "$upstreamc";
+
+    my @dgitcmd;
+    push @dgitcmd, @dgit;
+    push @dgitcmd, qw(--force-uploading-source-only);
+    if (defined $quilt) {
+       push @dgitcmd, "--quilt=$quilt";
+       if ($quilt =~ m/baredebian/) {
+           die "needed upstream commmitish with --quilt=baredebian";
+           push @dgitcmd, "--upstream-commitish=$upstreamc";
+       }
+    }
+    push @dgitcmd, qw(push-source --new --overwrite), $suite;
+    # xxx what about the key to use?
+    
+    runcmd @dgitcmd;
+
+    $quit->('done');
+}
+
 sub mode_ssh () {
     die if @ARGV;