X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=infra%2Fdgit-repos-server;h=98ec16d9b4e382540ffd6ec59a07f307be5d5446;hb=8a2c3aa997480f715f703b01be7568038eb82fd8;hp=9c4859db4eba73f068e29d66c12d222388e95ae8;hpb=48f18b95adcd0f9c1d5a9a204126e845d116d44f;p=dgit.git diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index 9c4859db..98ec16d9 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -2,8 +2,13 @@ # dgit-repos-server # # usages: -# .../dgit-repos-server DISTRO SUITES KEYRING-AUTH-SPEC \ -# DGIT-REPOS-DIR POLICY-HOOK-SCRIPT --ssh +# dgit-repos-server DISTRO DISTRO-DIR AUTH-SPEC [] --ssh +# dgit-repos-server DISTRO DISTRO-DIR AUTH-SPEC [] --cron +# settings +# --repos=GIT-REPOS-DIR default DISTRO-DIR/repos/ +# --suites=SUITES-FILE default DISTRO-DIR/suites +# --policy-hook=POLICY-HOOK default DISTRO-DIR/policy-hook +# (DISTRO-DIR is not used other than as default) # internal usage: # .../dgit-repos-server --pre-receive-hook PACKAGE # @@ -14,7 +19,7 @@ # SUITES is the name of a file which lists the permissible suites # one per line (#-comments and blank lines ignored) # -# KEYRING-AUTH-SPEC is a :-separated list of +# AUTH-SPEC is a :-separated list of # KEYRING.GPG,AUTH-SPEC # where AUTH-SPEC is one of # a @@ -34,6 +39,8 @@ use strict; # PACKAGE_garbage } (also covers executions of # PACKAGE_garbage-old } policy hook script for PACKAGE) # PACKAGE_garbage-tmp } +# policy* } (for policy hook script, covered by +# } lock only when invoked for a package) # # leaf locks, held during brief operaton only: # @@ -105,6 +112,40 @@ use strict; # the corresponding temporary tree, as the lockfile is also # a stampfile whose presence indicates that there may be # cleanup to do +# +# Policy hook script is invoked like this: +# POLICY-HOOK-SCRIPT DISTRO DGIT-REPOS-DIR ACTION... +# ie. +# POLICY-HOOK-SCRIPT ... check-list [...] +# POLICY-HOOK-SCRIPT ... check-package PACKAGE [...] +# POLICY-HOOK-SCRIPT ... push|push-confirm PACKAGE \ +# VERSION SUITE TAGNAME DELIBERATELIES [...] +# +# Exit status 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) +# blow away repo right away (ie, as if before push or fetch) +# ("check-package" and "push" only) +# any unexpected bits mean failure, and then known set bits are ignored +# if no unexpected bits set, operation continues (subject to meaning +# of any expected bits set). So, eg, exit 0 means "continue normally" +# and would be appropriate for an unknown action. +# +# cwd for push and push-confirm is a temporary repo where the +# to-be-pushed objects have been received; TAGNAME is the +# version-based tag +# +# if push requested FRESHREPO, push-confirm happens in said fresh repo +# +# policy hook for a particular package will be invoked only once at +# a time - (see comments about DGIT-REPOS-DIR, above) +# +# check-list and check-package are invoked via the --cron option. +# First, without any locking, check-list is called. It should produce +# a list of package names. Then check-package will be invoked for +# each named package, in each case after taking an appropriate lock. + use POSIX; use Fcntl qw(:flock); @@ -117,6 +158,7 @@ open DEBUG, ">/dev/null" or die $!; our $func; our $dgitrepos; our $package; +our $distro; our $suitesfile; our $policyhook; our $realdestrepo; @@ -222,9 +264,9 @@ sub policyhook { my ($policyallowbits, @polargs) = @_; # => ($exitstatuspolicybitmap); die if $policyallowbits & ~0x3e; - my @cmd = ($policyhook,$distro,$repos,@polargs); - debugcmd @_; - my $r = system @_; + my @cmd = ($policyhook,$distro,$dgitrepos,@polargs); + debugcmd @cmd; + my $r = system @cmd; die "system: $!" if $r < 0; die "hook (@cmd) failed ($?)" if $r & ~($policyallowbits << 8); return $r >> 8; @@ -264,17 +306,6 @@ sub movetogarbage () { or die "$garbagerepo $!"; } -sub onwardpush () { - my @cmd = (qw(git send-pack), $destrepo); - push @cmd, qw(--force) if $policy & NOFFCHECK; - 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 $!"; -} - #----- git-receive-pack ----- sub fixmissing__git_receive_pack () { @@ -432,12 +463,14 @@ sub parsetag () { $version = $2; die "$3 != $suite " unless $3 eq $suite; + my $copyl = $_; for (;;) { - print PT or die $!; + print PT $copyl or die $!; $!=0; $_=; defined or die "missing signature? $!"; + $copyl = $_; if (m/^\[dgit ([^"].*)\]$/) { # [dgit "something"] is for future $_ = $1." "; - for (;;) { + while (length) { if (s/^distro\=(\S+) //) { die "$1 != $distro" unless $1 eq $distro; } elsif (s/^(--deliberately-$package_re) //) { @@ -447,13 +480,14 @@ sub parsetag () { $supersedes{$1} = $2; } elsif (s/^[-+.=0-9a-z]\S* //) { } else { - die "unknown dgit info in tag"; + die "unknown dgit info in tag ($_)"; } } next; } last if m/^-----BEGIN PGP/; } + $_ = $copyl; for (;;) { print DS or die $!; $!=0; $_=; @@ -619,7 +653,7 @@ sub checktagnoreplay () { my @problems; - git_for_each_tag_referring($objreferring, sub { + git_for_each_tag_referring($onlyreferring, sub { my ($objid,$fullrefname,$tagname) = @_; debug "checktagnoreplay - overwriting $fullrefname=$objid"; my $supers = $supersedes{$fullrefname}; @@ -666,7 +700,7 @@ sub checks () { lockrealtree(); my @policy_args = ($package,$version,$suite,$tagname, - join(",",@delberatelies)); + join(",",@deliberatelies)); $policy = policyhook(NOFFCHECK|FRESHREPO, 'push', @policy_args); checktagnoreplay(); @@ -700,6 +734,17 @@ sub checks () { policyhook(0, 'push-confirm', @policy_args); } +sub onwardpush () { + my @cmd = (qw(git send-pack), $destrepo); + push @cmd, qw(--force) if $policy & NOFFCHECK; + 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 $!"; +} + sub stunthook () { debug "stunthook"; chdir $workrepo or die "chdir $workrepo: $!"; @@ -741,46 +786,19 @@ sub argval () { return $v; } -sub parseargsdispatch () { - die unless @ARGV; - - delete $ENV{'GIT_DIR'}; # if not run via ssh, our parent git process - delete $ENV{'GIT_PREFIX'}; # sets these and they mess things up - - if ($ENV{'DGIT_DRS_DEBUG'}) { - $debug='='; - open DEBUG, ">&STDERR" or die $!; - } +our %indistrodir = ( + # keys are used for DGIT_DRS_XXX too + 'repos' => \$dgitrepos, + 'suites' => \$suitesfile, + 'policy-hook' => \$policyhook, + ); - if ($ARGV[0] eq '--pre-receive-hook') { - if ($debug) { $debug.="="; } - shift @ARGV; - @ARGV == 1 or die; - $package = shift @ARGV; - defined($distro = $ENV{'DGIT_DRS_DISTRO'}) or die; - defined($suitesfile = $ENV{'DGIT_DRS_SUITES'}) or die; - defined($workrepo = $ENV{'DGIT_DRS_WORK'}) or die; - defined($destrepo = $ENV{'DGIT_DRS_DEST'}) or die; - defined($keyrings = $ENV{'DGIT_DRS_KEYRINGS'}) or die $!; - defined($policyhook = $ENV{'DGIT_DRS_POLICYHOOK'}) or die $!; - open STDOUT, ">&STDERR" or die $!; - eval { - stunthook(); - }; - if ($@) { - recorderror "$@" or die; - die $@; - } - exit 0; - } +our @hookenvs = qw(distro suitesfile policyhook keyrings dgitrepos); - $ENV{'DGIT_DRS_DISTRO'} = argval(); - $ENV{'DGIT_DRS_SUITES'} = argval(); - $ENV{'DGIT_DRS_KEYRINGS'} = argval(); - $dgitrepos = argval(); - $ENV{'DGIT_DRS_POLICYHOOK'} = $policyhook = argval(); +# workrepo and destrepo handled ad-hoc - die unless @ARGV==1 && $ARGV[0] eq '--ssh'; +sub mode_ssh () { + die if @ARGV; my $cmd = $ENV{'SSH_ORIGINAL_COMMAND'}; $cmd =~ m{ @@ -824,6 +842,60 @@ sub parseargsdispatch () { &$mainfunc; } +sub parseargsdispatch () { + die unless @ARGV; + + delete $ENV{'GIT_DIR'}; # if not run via ssh, our parent git process + delete $ENV{'GIT_PREFIX'}; # sets these and they mess things up + + if ($ENV{'DGIT_DRS_DEBUG'}) { + $debug='='; + open DEBUG, ">&STDERR" or die $!; + } + + if ($ARGV[0] eq '--pre-receive-hook') { + if ($debug) { $debug.="="; } + shift @ARGV; + @ARGV == 1 or die; + $package = shift @ARGV; + ${ $main::{$_} } = $ENV{"DGIT_DRS_\U$_"} foreach @hookenvs; + defined($workrepo = $ENV{'DGIT_DRS_WORK'}) or die; + defined($destrepo = $ENV{'DGIT_DRS_DEST'}) or die; + open STDOUT, ">&STDERR" or die $!; + eval { + stunthook(); + }; + if ($@) { + recorderror "$@" or die; + die $@; + } + exit 0; + } + + $distro = $ENV{'DGIT_DRS_DISTRO'} = argval(); + my $distrodir = argval(); + $keyrings = $ENV{'DGIT_DRS_KEYRINGS'} = argval(); + + foreach my $dk (keys %indistrodir) { + ${ $indistrodir{$dk} } = "$distrodir/$dk"; + } + + while (@ARGV && $ARGV[0] =~ m/^--([-0-9a-z]+)=/ && $indistrodir{$1}) { + ${ $indistrodir{$1} } = $'; #'; + shift @ARGV; + } + + $ENV{"DGIT_DRS_\U$_"} = ${ $main::{$_} } foreach @hookenvs; + + die unless @ARGV==1; + + my $mode = shift @ARGV; + die unless $mode =~ m/^--(\w+)$/; + my $fn = ${*::}{"mode_$1"}; + die unless $fn; + $fn->(); +} + sub unlockall () { while (my $fh = pop @lockfhs) { close $fh; } }