2 # dgit-repos-push-receiver
5 # .../dgit-repos-push-receiver DGIT-REPOS-DIR --ssh
6 # .../dgit-repos-push-receiver DGIT-REPOS-DIR PACKAGE
8 # .../dgit-repos-push-receiver --pre-receive-hook PACKAGE
10 # Invoked as the ssh restricted command
12 # Works like git-receive-pack
17 # - extract the destination repo name somehow
18 # - make a hardlink clone of the destination repo
19 # - provide the destination with a stunt pre-receive hook
20 # - run actual git-receive-pack with that new destination
21 # as a result of this the stunt pre-receive hook runs; it does this
22 # find the keyring(s) to use for verification
23 # verify the signed tag
24 # check that the signed tag has a suitable name
25 # parse the signed tag body to extract the intended
27 # check that the distro is right
28 # check that the suite is the same as the branch we are
30 # check that the signed tag refers to the same commit
32 # check that the signer was correct
33 # push the signed tag to the actual repo
34 # push the new dgit branch head to the actual repo
39 our $package_re = '[0-9a-z][-+.0-9a-z]+';
46 sub acquirelock ($$) {
47 my ($lock, $must) = @_;
49 my $fh = new IO::File, ">", $lock or die "open $lock: $!";
50 my $ok = flock $fh, $must ? LOCK_EX : (LOCK_EX|LOCK_NB);
53 die "flock $lock: $!";
59 my $want = (stat _)[1];
61 my $got = (stat _)[1];
62 return $fh if $got == $want;
66 sub makeworkingclone () {
67 $workrepo = "$dgitrepos/_tmp/$package_incoming$$";
68 my $lock = "$workrepo.lock";
69 my $lockfh = acquirelock($lock, 1);
70 if (!stat $destrepo) {
71 $! == ENOENT or die "stat dest repo $destrepo: $!";
72 mkdir $workrepo or die "create work repo $workrepo: $!";
73 runcmd qw(git init --bare), $workrepo;
75 runcmd qw(git clone -l -q --mirror), $destrepo, $workrepo;
79 sub setupstunthook () {
80 my $prerecv = "$workrepo/hooks/pre-receive";
81 my $fh = new IO::File, $prerecv, O_WRONLY|O_CREAT|O_TRUNC, 0777
82 or die "$prerecv: $!";
83 print $fh <<END or die "$prerecv: $!";
86 exec $0 --pre-receive-hook $package
88 close $fh or die "$prerecv: $!";
89 $ENV{'DGIT_RPR_WORK'}= $workrepo;
90 $ENV{'DGIT_RPR_DEST'}= $destrepo;
93 #----- stunt post-receive hook -----
95 our ($tagname, $tagval, $suite, $commit);
96 our ($version, %tagh);
100 m/^\S+ (\S+) (\S+)$/ or die "$_ ?";
101 my ($sha1, $refname) = ($1, $2);
102 if ($refname =~ m{^refs/tags/(?=debian/)}) {
103 die if defined $tagname;
106 } elsif ($refname =~ m{^refs/dgit/}) {
107 die if defined $suite;
114 STDIN->error and die $!;
116 die unless defined $refname;
117 die unless defined $branchname;
121 open PT, ">dgit-tmp/plaintext" or die $!;
122 open DS, ">dgit-tmp/plaintext.asc" or die $!;
123 open T, "-|", qw(git cat-file tag), $tagval or die $!;
126 $!=0; $_=<T>; defined or die $!;
128 if (m/^(\S+) (.*)/) {
129 push @{ $tagh{$1} }, $2;
136 $!=0; $_=<T>; defined or die $!;
137 m/^($package_re) release (\S+) for (\S+) \[dgit\]$/ or die;
139 die unless $1 eq $package;
141 die unless $3 eq $suite;
145 $!=0; $_=<T>; defined or die $!;
146 last if m/^-----BEGIN PGP/;
159 tagh1('object') eq $branchval or die;
160 tagh1('type') eq 'commit' or die;
161 tagh1('tag') eq $tagname or die;
165 $tagname eq "debian/$v" or die;
170 chdir $workrepo or die "chdir $workrepo: $!";
171 mkdir "dgit-tmp" or $!==EEXIST or die $!;
179 #----- arg parsing and main program -----
184 if ($ARGV[0] eq '--pre-receive-hook') {
187 $package = shift @ARGV;
188 defined($workrepo = $ENV{'DGIT_RPR_WORK'}) or die;
189 defined($destrepo = $ENV{'DGIT_RPR_DEST'}) or die;
190 open STDOUT, ">&STDERR" or die $!;
195 die if $ARGV[0] =~ m/^-/;
199 if ($ARGV[0] != m/^-/) {
201 $package = shift @ARGV;
202 } elsif ($ARGV[0] eq '--ssh') {
205 my $cmd = $ENV{'SSH_ORIGINAL_COMMAND'};
209 (?:dgit-repos-push-receiver|git-receive-pack)
215 or die "requested command $cmd not understood";
221 $destrepo = "$dgitrepos/$package.git";
228 runcmd qw(git receive-pack), $destdir;