X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=infra%2Fdgit-repos-server;h=92f197b0b001b31bc5dd11907ab7ab1c1359a00f;hp=50cba57082ddc385d22afd1f3fba23b8853eb27d;hb=7184a61e0bf4b097711965eee629541f54ebf4ec;hpb=68ace2786dc26749014ea70a6ebb2b896b1f25d1 diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index 50cba570..92f197b0 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -7,9 +7,12 @@ # settings # --repos=GIT-REPOS-DIR default DISTRO-DIR/repos/ # --suites=SUITES-FILE default DISTRO-DIR/suites +# --suites-master=SUITES-FILE default DISTRO-DIR/suites-master # --policy-hook=POLICY-HOOK default DISTRO-DIR/policy-hook +# --mirror-hook=MIRROR-HOOK default DISTRO-DIR/mirror-hook # --dgit-live=DGIT-LIVE-DIR default DISTRO-DIR/dgit-live -# (DISTRO-DIR is not used other than as default and to pass to policy hook) +# (DISTRO-DIR is not used other than as default and to pass to policy +# and mirror hooks) # internal usage: # .../dgit-repos-server --pre-receive-hook PACKAGE # @@ -17,8 +20,10 @@ # # Works like git-receive-pack # -# SUITES is the name of a file which lists the permissible suites -# one per line (#-comments and blank lines ignored) +# SUITES-FILE is the name of a file which lists the permissible suites +# one per line (#-comments and blank lines ignored). For --suites-master +# it is a list of the suite(s) which should, when pushed to, update +# `master' on the server (if fast forward). # # AUTH-SPEC is a :-separated list of # KEYRING.GPG,AUTH-SPEC @@ -62,7 +67,7 @@ $SIG{__WARN__} = sub { die $_[0]; }; # as a result of this the stunt pre-receive hook runs; it does this: # + understand what refs we are allegedly updating and # check some correspondences: -# * we are updating only refs/tags/debian/* and refs/dgit/* +# * we are updating only refs/tags/DISTRO/* and refs/dgit/* # * and only one of each # * and the tag does not already exist # and @@ -116,7 +121,7 @@ $SIG{__WARN__} = sub { die $_[0]; }; # a stampfile whose presence indicates that there may be # cleanup to do # -# Policy hook script is invoked like this: +# Policy hook scripts are invoked like this: # POLICY-HOOK-SCRIPT DISTRO DGIT-REPOS-DIR DGIT-LIVE-DIR DISTRO-DIR ACTION... # ie. # POLICY-HOOK-SCRIPT ... check-list [...] @@ -128,7 +133,8 @@ $SIG{__WARN__} = sub { die $_[0]; }; # # DELIBERATELIES is like this: --deliberately-foo,--deliberately-bar,... # -# Exit status is a bitmask. Bit weight constants are defined in Dgit.pm. +# Exit status of policy hook is a bitmask. +# Bit weight constants are defined in Dgit.pm. # NOFFCHECK (2) # suppress dgit-repos-server's fast-forward check ("push" only) # FRESHREPO (4) @@ -165,7 +171,18 @@ $SIG{__WARN__} = sub { die $_[0]; }; # If policy hook wants to run dgit (or something else in the dgit # package), it should use DGIT-LIVE-DIR/dgit (etc.), or if that is # ENOENT, use the installed version. - +# +# Mirror hook scripts are invoked like this: +# MIRROR-HOOK-SCRIPT DISTRO-DIR ACTION... +# and currently there is only one action invoked by dgit-repos-server: +# MIRROR-HOOK-SCRIPT DISTRO-DIR updated-hook PACKAGE [...] +# +# Exit status of the mirror hook is advisory only. The mirror hook +# runs too late to do anything useful about a problem, so the only +# effect of a mirror hook exiting nonzero is a warning message to +# stderr (which the pushing user should end up seeing). +# +# If the mirror hook does not exist, it is silently skipped. use POSIX; use Fcntl qw(:flock); @@ -181,7 +198,9 @@ our $dgitrepos; our $package; our $distro; our $suitesfile; +our $suitesformasterfile; our $policyhook; +our $mirrorhook; our $dgitlive; our $distrodir; our $destrepo; @@ -259,7 +278,7 @@ sub recorderror ($) { sub reject ($) { my ($why) = @_; recorderror "reject: $why"; - die "dgit-repos-server: reject: $why\n"; + die "\ndgit-repos-server: reject: $why\n\n"; } sub runcmd { @@ -274,7 +293,7 @@ sub policyhook { # => ($exitstatuspolicybitmap); die if $policyallowbits & ~0x3e; my @cmd = ($policyhook,$distro,$dgitrepos,$dgitlive,$distrodir,@polargs); - debugcmd '+',@cmd; + debugcmd '+M',@cmd; my $r = system @cmd; die "system: $!" if $r < 0; die "dgit-repos-server: policy hook failed (or rejected) ($?)\n" @@ -291,10 +310,11 @@ sub mkemptyrepo ($$) { sub mkrepo_fromtemplate ($) { my ($dir) = @_; my $template = "$dgitrepos/_template"; - locksometree($template); + my $templatelock = locksometree($template); printdebug "copy template $template -> $dir\n"; my $r = system qw(cp -a --), $template, $dir; !$r or die "create new repo $dir failed: $r $!"; + close $templatelock; } sub movetogarbage () { @@ -322,7 +342,7 @@ sub movetogarbage () { ensuredir "$dgitrepos/_removed-tags"; open PREVIOUS, ">>", removedtagsfile or die removedtagsfile." $!"; - git_for_each_ref('refs/tags/'.debiantag('*'), sub { + git_for_each_ref('refs/tags/'.debiantag('*',$distro), sub { my ($objid,$objtype,$fullrefname,$reftail) = @_; print PREVIOUS "\n$objid $reftail .\n" or die $!; }, $real); @@ -385,6 +405,23 @@ sub dealwithfreshrepo () { $destrepo = $freshrepo; } +sub mirrorhook { + my @cmd = ($mirrorhook,$distrodir,@_); + debugcmd '+',@cmd; + return unless stat_exists $mirrorhook; + my $r = system @cmd; + if ($r) { + printf STDERR <> 8) : + "wait status $?"); + } +} + sub maybeinstallprospective () { return if $destrepo eq realdestrepo; @@ -412,6 +449,7 @@ sub maybeinstallprospective () { chomp or die; printdebug " show-refs| $_\n"; s/^\S*[1-9a-f]\S* (\S+)$/$1/ or die; + next if m{^refs/heads/master$}; my $wh = m{^refs/tags/} ? 'tag' : m{^refs/dgit/} ? 'head' : @@ -441,6 +479,7 @@ sub main__git_receive_pack () { runcmd qw(git receive-pack), $workrepo; dealwithfreshrepo(); maybeinstallprospective(); + mirrorhook('updated-hook', $package); } #----- stunt post-receive hook ----- @@ -457,7 +496,7 @@ sub readupdates () { printdebug " upd.| $_\n"; m/^(\S+) (\S+) (\S+)$/ or die "$_ ?"; my ($old, $sha1, $refname) = ($1, $2, $3); - if ($refname =~ m{^refs/tags/(?=debian/)}) { + if ($refname =~ m{^refs/tags/(?=$distro/)}) { reject "pushing multiple tags!" if defined $tagname; $tagname = $'; #'; $tagval = $sha1; @@ -635,17 +674,27 @@ sub verifytag () { reject "key not found in keyrings"; } -sub checksuite () { - printdebug "checksuite ($suitesfile)\n"; - open SUITES, "<", $suitesfile or die $!; +sub suite_is_in ($) { + my ($sf) = @_; + printdebug "suite_is_in ($sf)\n"; + if (!open SUITES, "<", $sf) { + $!==ENOENT or die $!; + return 0; + } while () { chomp; next unless m/\S/; next if m/^\#/; s/\s+$//; - return if $_ eq $suite; + return 1 if $_ eq $suite; } die $! if SUITES->error; + return 0; +} + +sub checksuite () { + printdebug "checksuite ($suitesfile)\n"; + return if suite_is_in $suitesfile; reject "unknown suite"; } @@ -772,11 +821,9 @@ sub checks () { tagh1('object') eq $commit or reject "tag refers to wrong commit"; tagh1('tag') eq $tagname or reject "tag name in tag is wrong"; - my $v = $version; - $v =~ y/~:/_%/; - - printdebug "translated version $v\n"; - $tagname eq "debian/$v" or die; + my $expecttagname = debiantag $version, $distro; + printdebug "expected tag $expecttagname\n"; + $tagname eq $expecttagname or die; lockrealtree(); @@ -822,14 +869,26 @@ sub checks () { } sub onwardpush () { - my @cmd = (qw(git send-pack), $destrepo); - push @cmd, qw(--force) if $policy & NOFFCHECK; + my @cmdbase = (qw(git send-pack), $destrepo); + push @cmdbase, qw(--force) if $policy & NOFFCHECK; + + my @cmd = @cmdbase; push @cmd, "$commit:refs/dgit/$suite", "$tagval:refs/tags/$tagname"; debugcmd '+',@cmd; $!=0; my $r = system @cmd; !$r or die "onward push to $destrepo failed: $r $!"; + + if (suite_is_in $suitesformasterfile) { + @cmd = @cmdbase; + push @cmd, "$commit:refs/heads/master"; + debugcmd '+', @cmd; + $!=0; my $r = system @cmd; + # tolerate errors (might be not ff) + !($r & ~0xff00) or die + "onward push to $destrepo#master failed: $r $!"; + } } sub finalisepush () { @@ -900,12 +959,14 @@ our %indistrodir = ( # keys are used for DGIT_DRS_XXX too 'repos' => \$dgitrepos, 'suites' => \$suitesfile, + 'suites-master' => \$suitesformasterfile, 'policy-hook' => \$policyhook, + 'mirror-hook' => \$mirrorhook, 'dgit-live' => \$dgitlive, ); -our @hookenvs = qw(distro suitesfile policyhook - dgitlive keyrings dgitrepos distrodir); +our @hookenvs = qw(distro suitesfile suitesformasterfile policyhook + mirrorhook dgitlive keyrings dgitrepos distrodir); # workrepo and destrepo handled ad-hoc