X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=infra%2Fdgit-repos-policy-debian;h=b21f797643a76ea27e19c93e6202c103ec644c21;hp=526db03db8660f252e73355b33d28749e5d6d2cf;hb=76cbbc78a07e44b4c917bbaa93bf7cd602387082;hpb=a90a93dc51850eef80e50f95b44f3e3aa09a613c diff --git a/infra/dgit-repos-policy-debian b/infra/dgit-repos-policy-debian index 526db03d..b21f7976 100755 --- a/infra/dgit-repos-policy-debian +++ b/infra/dgit-repos-policy-debian @@ -2,21 +2,31 @@ # dgit repos policy hook script for Debian use strict; +$SIG{__WARN__} = sub { die $_[0]; }; + use POSIX; use JSON; use File::Temp qw(tempfile); use DBI; use IPC::Open2; +use Data::Dumper; use Debian::Dgit qw(:DEFAULT :policyflags); use Debian::Dgit::Policy::Debian; +initdebug('%'); +enabledebuglevel $ENV{'DGIT_DRS_DEBUG'}; + +END { $? = 127; } # deliberate exit uses _exit + our $distro = shift @ARGV // die "need DISTRO"; our $repos = shift @ARGV // die "need DGIT-REPOS-DIR"; +our $dgitlive = shift @ARGV // die "need DGIT-LIVE-DIR"; +our $distrodir = shift @ARGV // die "need DISTRO-DIR"; our $action = shift @ARGV // die "need ACTION"; our $publicmode = 02775; -our $new_upload_propagation_slop = 3600*4 + 100; +our $new_upload_propagation_slop = 3600*4 + 100;# fixme config; our $poldbh; our $pkg; @@ -76,36 +86,42 @@ our %deliberately; sub apiquery ($) { my ($subpath) = @_; local $/=undef; - $!=0; $?=0; my $json = `dgit -d $distro archive-api-query $subpath`; - defined $json or die "$subpath $! $?"; - return decode_json $json; + my $dgit = "$dgitlive/dgit"; + $dgit = "dgit" if !stat_exists $dgit; + my $cmd = "$dgit -d$distro \$DGIT_TEST_OPTS"; + $cmd .= " -".("D" x $debuglevel) if $debuglevel; + $cmd .= " archive-api-query $subpath"; + printdebug "apiquery $cmd\n"; + $!=0; $?=0; my $json = `$cmd`; + defined $json && !$? or die "$subpath $! $?"; + my $r = decode_json $json; + my $d = new Data::Dumper([$r], [qw(r)]); + printdebug "apiquery $subpath | ", $d->Dump() if $debuglevel>=2; + return $r; } sub specific_suite_has_vsn_in_our_history ($) { my ($suite) = @_; - my $in_suite = apiquery "/dsc_in_suite/$suite/$pkg"; + my $in_suite = apiquery "dsc_in_suite/$suite/$pkg"; foreach my $entry (@$in_suite) { my $vsn = $entry->{version}; die "$pkg ?" unless defined $vsn; - my $tag = debiantag $vsn; - $?=0; my $r = system qw(git show-ref --verify --quiet), $tag; + my $tagref = "refs/tags/".debiantag $vsn; + printdebug " checking history suite=$suite vsn=$vsn tagref=$tagref\n"; + $?=0; my $r = system qw(git show-ref --verify --quiet), $tagref; return 1 if !$r; next if $r==256; - die "$pkg tag $tag $? $!"; + die "$pkg tagref $tagref $? $!"; } return 0; } sub new_has_vsn_in_our_history () { - stat $pkgdir or die "$pkgdir $!"; - my $mtime = ((stat _)[9]); - my $age = time - $mtime; - return 1 if $age < $new_upload_propagation_slop; return specific_suite_has_vsn_in_our_history('new'); } sub good_suite_has_vsn_in_our_history () { - my $suites = apiquery "/suites"; + my $suites = apiquery "suites"; foreach my $suitei (@$suites) { my $suite = $suitei->{name}; die unless defined $suite; @@ -115,36 +131,49 @@ sub good_suite_has_vsn_in_our_history () { return 0; } -sub getpackage () { - die unless @ARGV >= 1; - $pkg = shift @ARGV; - die unless $pkg =~ m/^$package_re$/; - - $pkgdir = "$repos/$pkg"; +sub statpackage () { + $pkgdir = "$repos/$pkg.git"; if (!stat_exists $pkgdir) { + printdebug "statpackage $pkg => ENOENT\n"; $pkg_exists = 0; } else { $pkg_exists = 1; $pkg_secret = !!(~(stat _)[2] & 05); + printdebug "statpackage $pkg => exists, secret=$pkg_secret.\n"; } } +sub getpackage () { + die unless @ARGV >= 1; + $pkg = shift @ARGV; + die unless $pkg =~ m/^$package_re$/; + + statpackage(); +} + sub add_taint ($$) { - my ($refobj, $reason); + my ($refobj, $reason) = @_; + + printdebug "TAINTING $refobj\n", + (map { "\%| $_" } split "\n", $reason), + "\n"; my $tf = new File::Temp or die $!; print $tf "$refobj^0\n" or die $!; + flush $tf or die $!; + seek $tf,0,0 or die $!; my $gcfpid = open GCF, "-|"; defined $gcfpid or die $!; if (!$gcfpid) { open STDIN, "<&", $tf or die $!; - exec 'git', 'cat-file'; + exec 'git', 'cat-file', '--batch'; die $!; } close $tf or die $!; $_ = ; + defined $_ or die; m/^(\w+) (\w+) (\d+)\n/ or die "$_ ?"; my $gitobjid = $1; my $gitobjtype = $2; @@ -158,7 +187,7 @@ sub add_taint ($$) { close GCF; $poldbh->do("INSERT INTO taints". - " (package, gitobjid, gitobjtype, gitobjdata, time, comment)", + " (package, gitobjid, gitobjtype, gitobjdata, time, comment)". " VALUES (?,?,?,?,?,?)", {}, $pkg, $gitobjid, $gitobjtype, $gitobjdata, time, $reason); @@ -166,9 +195,9 @@ sub add_taint ($$) { die unless defined $taint_id; $poldbh->do("INSERT INTO taintoverrides". - " (taint_id, deliberately)", - " VALUES (?, 'include-questionable-history')", {}, - $taint_id); + " (taint_id, deliberately)". + " VALUES (?, '--deliberately-include-questionable-history')", + {}, $taint_id); } sub add_taint_by_tag ($$) { @@ -179,19 +208,31 @@ sub add_taint_by_tag ($$) { " removed from NEW (ie, rejected) (or never arrived)"); } -sub action__check_package () { - getpackage(); +sub check_package () { return 0 unless $pkg_exists; return 0 unless $pkg_secret; + printdebug "check_package\n"; + chdir $pkgdir or die "$pkgdir $!"; - return if new_has_vsn_in_our_history(); + + stat '.' or die "$pkgdir $!"; + my $mtime = ((stat _)[9]); + my $age = time - $mtime; + printdebug "check_package age=$age\n"; if (good_suite_has_vsn_in_our_history) { chmod $publicmode, "." or die $!; + $pkg_secret = 0; return 0; } + return 0 if $age < $new_upload_propagation_slop; + + return 0 if new_has_vsn_in_our_history(); + + printdebug "check_package secret, deleted, tainting\n"; + git_for_each_ref('refs/tags', sub { my ($objid,$objtype,$fullrefname,$tagname) = @_; add_taint_by_tag($tagname,$objid); @@ -200,6 +241,11 @@ sub action__check_package () { return FRESHREPO; } +sub action_check_package () { + getpackage(); + return check_package(); +} + sub getpushinfo () { die unless @ARGV >= 4; $version = shift @ARGV; @@ -211,10 +257,14 @@ sub getpushinfo () { } } -sub deliberately ($) { return $deliberately{$_[0]}; } +sub deliberately ($) { return $deliberately{"--deliberately-$_[0]"}; } sub action_push () { getpackage(); + getpushinfo(); + + check_package(); # might make package public, or might add taints + return 0 unless $pkg_exists; return 0 unless $pkg_secret; @@ -223,9 +273,9 @@ sub action_push () { if (deliberately('not-fast-forward')) { add_taint(server_ref($suite), - "suite $suite when --deliberately-not-fast-forward". + "rewound suite $suite; --deliberately-not-fast-forward". " specified in signed tag $tagname for upload of". - " version $version into suite $suite"); + " version $version"); return NOFFCHECK|FRESHREPO; } if (deliberately('include-questionable-history')) { @@ -238,17 +288,28 @@ sub action_push () { } sub action_push_confirm () { + getpackage(); + getpushinfo(); + die unless @ARGV >= 1; + my $freshrepo = shift @ARGV; + my $initq = $poldbh->prepare(<execute($pkg); + my @objscatcmd = qw(git); + push @objscatcmd, qw(--git-dir), $freshrepo if length $freshrepo; + push @objscatcmd, qw(cat-file --batch); + debugcmd '|',@objscatcmd if $debuglevel>=2; + my @taintids; my $chkinput = tempfile(); while (my $taint = $initq->fetchrow_hashref()) { push @taintids, $taint->{taint_id}; print $chkinput $taint->{gitobjid}, "\n" or die $!; + printdebug '|> ', $taint->{gitobjid}, "\n" if $debuglevel>=2; } flush $chkinput or die $!; seek $chkinput,0,0 or die $!; @@ -256,16 +317,17 @@ END my $checkpid = open CHKOUT, "-|" // die $!; if (!$checkpid) { open STDIN, "<&", $chkinput or die $!; - exec qw(git cat-file --batch) or die $!; + exec @objscatcmd or die $!; } my ($taintinfoq,$overridesanyq,$untaintq,$overridesq); my $overridesstmt = <; die "$? $!" unless defined $_; + printdebug "|< ", $_ if $debuglevel>=2; next if m/^\w+ missing$/; die unless m/^(\w+) (\w+) (\d+)\s/; @@ -311,6 +374,8 @@ Taint recorded$timeshow for $pkgshow Reason: $ti->{comment} END + printdebug "SQL overrides: @overridesv $taintid /\n$overridesstmt\n"; + $overridesq ||= $poldbh->prepare($overridesstmt); $overridesq->execute(@overridesv, $taintid); my ($ovwhy) = $overridesq->fetchrow_array(); @@ -346,34 +411,84 @@ END return 1; } + if (length $freshrepo) { + if (!good_suite_has_vsn_in_our_history()) { + stat $freshrepo or die "$freshrepo $!"; + my $oldmode = ((stat _)[2]); + my $oldwrites = $oldmode & 0222; + # remove r and x bits which have corresponding w bits clear + my $newmode = $oldmode & + (~0555 | ($oldwrites << 1) | ($oldwrites >> 1)); + printdebug sprintf "chmod %#o (was %#o) %s\n", + $newmode, $oldmode, $freshrepo; + chmod $newmode, $freshrepo or die $!; + utime undef, undef, $freshrepo or die $!; + } + } + + return 0; +} + +sub action_check_list () { + opendir L, "$repos" or die "$repos $!"; + while (defined (my $dent = readdir L)) { + next unless $dent =~ m/^($package_re)\.git$/; + $pkg = $1; + statpackage(); + next unless $pkg_exists; + next unless $pkg_secret; + print "$pkg\n" or die $!; + } + closedir L or die $!; + close STDOUT or die $!; return 0; } $action =~ y/-/_/; my $fn = ${*::}{"action_$action"}; if (!$fn) { + printdebug "dgit-repos-policy-debian: unknown action $action\n"; exit 0; } my $sleepy=0; -our $rcode = 127; +my $rcode; -for (;;) { - poldb_setup(poldb_path($repos)); - $stderr = ''; +my $db_busy_exception= 'Debian::Dgit::Policy::Debian::DB_BUSY'; - $rcode = $fn->(); - die unless defined $rcode; +my @orgargv = @ARGV; - eval { $poldbh->commit; }; +for (;;) { + @ARGV = @orgargv; + eval { + poldb_setup(poldb_path($repos), sub { + $poldbh->{HandleError} = sub { + return 0 unless $poldbh->err == 5; # SQLITE_BUSY, not in .pm :-( + die bless { }, $db_busy_exception; + }; + + eval ($ENV{'DGIT_RPD_TEST_DBLOOP_HOOK'}//''); + die $@ if length $@; + # used by tests/tests/debpolicy-dbretry + }); + + $stderr = ''; + + $rcode = $fn->(); + die unless defined $rcode; + + $poldbh->commit; + }; last unless length $@; + die $@ unless ref $@ eq $db_busy_exception; die if $sleepy >= 20; - print STDERR "[policy database busy, retrying]\n"; - sleep ++$sleepy; + $sleepy++; + print STDERR "[policy database busy, retrying (${sleepy}s)]\n"; - $poldbh->rollback; + eval { $poldbh->rollback; }; } -print STDERR $stderr; -exit $rcode; +print STDERR $stderr or die $!; +flush STDERR or die $!; +_exit $rcode;