# -*- perl -*- package Debian::Dgit; use strict; use warnings; use POSIX; use IO::Handle; use Config; BEGIN { use Exporter (); our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS); $VERSION = 1.00; @ISA = qw(Exporter); @EXPORT = qw(debiantag server_branch server_ref stat_exists fail waitstatusmsg failedcmd cmdoutput cmdoutput_errok git_for_each_ref git_for_each_tag_referring $package_re $component_re $deliberately_re $branchprefix initdebug enabledebug enabledebuglevel printdebug debugcmd $debugprefix *debuglevel *DEBUG shellquote printcmd); # implicitly uses $main::us %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO)] ); @EXPORT_OK = @{ $EXPORT_TAGS{policyflags} }; } our @EXPORT_OK; our $package_re = '[0-9a-z][-+.0-9a-z]*'; our $component_re = '[0-9a-zA-Z][-+.0-9a-zA-Z]*'; our $deliberately_re = "(?:TEST-)?$package_re"; our $branchprefix = 'dgit'; # policy hook exit status bits # see dgit-repos-server head comment for documentation # 1 is reserved in case something fails with `exit 1' and to spot # dynamic loader, runtime, etc., failures, which report 127 or 255 sub NOFFCHECK () { return 0x2; } sub FRESHREPO () { return 0x4; } our $debugprefix; our $debuglevel = 0; sub initdebug ($) { ($debugprefix) = @_; open DEBUG, ">/dev/null" or die $!; } sub enabledebug () { open DEBUG, ">&STDERR" or die $!; DEBUG->autoflush(1); $debuglevel ||= 1; } sub enabledebuglevel ($) { my ($newlevel) = @_; # may be undef (eg from env var) die if $debuglevel; $newlevel //= 0; $newlevel += 0; return unless $newlevel; $debuglevel = $newlevel; enabledebug(); } sub printdebug { print DEBUG $debugprefix, @_ or die $! if $debuglevel>0; } sub shellquote { my @out; local $_; foreach my $a (@_) { $_ = $a; if (!length || m{[^-=_./0-9a-z]}i) { s{['\\]}{'\\$&'}g; push @out, "'$_'"; } else { push @out, $_; } } return join ' ', @out; } sub printcmd { my $fh = shift @_; my $intro = shift @_; print $fh $intro," " or die $!; print $fh shellquote @_ or die $!; print $fh "\n" or die $!; } sub debugcmd { my $extraprefix = shift @_; printcmd(\*DEBUG,$debugprefix.$extraprefix,@_) if $debuglevel>0; } sub debiantag ($) { my ($v) = @_; $v =~ y/~:/_%/; return "debian/$v"; } sub server_branch ($) { return "$branchprefix/$_[0]"; } sub server_ref ($) { return "refs/".server_branch($_[0]); } sub stat_exists ($) { my ($f) = @_; return 1 if stat $f; return 0 if $!==&ENOENT; die "stat $f: $!"; } sub _us () { $::us // ($0 =~ m#[^/]*$#, $&); } sub fail { my $s = "@_\n"; my $prefix = _us().": "; $s =~ s/^/$prefix/gm; die $s; } our @signames = split / /, $Config{sig_name}; sub waitstatusmsg () { if (!$?) { return "terminated, reporting successful completion"; } elsif (!($? & 255)) { return "failed with error exit status ".WEXITSTATUS($?); } elsif (WIFSIGNALED($?)) { my $signum=WTERMSIG($?); return "died due to fatal signal ". ($signames[$signum] // "number $signum"). ($? & 128 ? " (core dumped)" : ""); # POSIX(3pm) has no WCOREDUMP } else { return "failed with unknown wait status ".$?; } } sub failedcmd { { local ($!); printcmd \*STDERR, _us().": failed command:", @_ or die $!; }; if ($!) { fail "failed to fork/exec: $!"; } elsif ($?) { fail "subprocess ".waitstatusmsg(); } else { fail "subprocess produced invalid output"; } } sub cmdoutput_errok { die Dumper(\@_)." ?" if grep { !defined } @_; debugcmd "|",@_; open P, "-|", @_ or die $!; my $d; $!=0; $?=0; { local $/ = undef; $d =

; } die $! if P->error; if (!close P) { printdebug "=>!$?\n"; return undef; } chomp $d; $d =~ m/^.*/; printdebug "=> \`$&'",(length $' ? '...' : ''),"\n" if $debuglevel>0; #'; return $d; } sub cmdoutput { my $d = cmdoutput_errok @_; defined $d or failedcmd @_; return $d; } sub git_for_each_ref ($$) { my ($pattern,$func) = @_; # calls $func->($objid,$objtype,$fullrefname,$reftail); # $reftail is RHS of ref after refs/\w+/ # breaks if $pattern matches any ref `refs/blah' where blah has no `/' my @cmd = (qw(git for-each-ref), $pattern); open GFER, "-|", @cmd or die $!; debugcmd "|", @cmd; while () { chomp or die "$_ ?"; printdebug "|> ", $_, "\n"; m#^(\w+)\s+(\w+)\s+(refs/\w+/(\S+))$# or die "$_ ?"; $func->($1,$2,$3,$4); } $!=0; $?=0; close GFER or die "$pattern $? $!"; } sub git_for_each_tag_referring ($$) { my ($objreferring, $func) = @_; # calls $func->($objid,$fullrefname,$tagname); printdebug "git_for_each_tag_referring ", ($objreferring // 'UNDEF'),"\n"; git_for_each_ref('refs/tags', sub { my ($objid,$objtype,$fullrefname,$tagname) = @_; return unless $objtype eq 'tag'; return if defined $objreferring and $objid ne $objreferring; $func->($objid,$fullrefname,$tagname); }); } 1;