chiark / gitweb /
rpush: Support empty files in protocol_read_bytes
[dgit.git] / dgit
diff --git a/dgit b/dgit
index f77d11f4d25ff58a0ec25132bc6f960997b9b621..d5aa38718c551c7c46b32ecf6c046bb502a50b96 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -18,7 +18,9 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 use strict;
-$SIG{__WARN__} = sub { die $_[0]; };
+
+use Debian::Dgit;
+setup_sigwarn();
 
 use IO::Handle;
 use Data::Dumper;
@@ -37,7 +39,8 @@ use Debian::Dgit;
 
 our $our_version = 'UNRELEASED'; ###substituted###
 
-our $rpushprotovsn = 2;
+our @rpushprotovsn_support = qw(2);
+our $protovsn;
 
 our $isuite = 'unstable';
 our $idistro;
@@ -173,7 +176,9 @@ sub deliberately_not_fast_forward () {
 #---------- remote protocol support, common ----------
 
 # remote push initiator/responder protocol:
-#  < dgit-remote-push-ready [optional extra info ignored by old initiators]
+#  $ dgit remote-push-build-host <n-rargs> <rargs>... <push-args>...
+#  where <rargs> is <push-host-dir> <supported-proto-vsn>,... ...
+#  < dgit-remote-push-ready <actual-proto-vsn>
 #
 #  > file parsed-changelog
 #  [indicates that output of dpkg-parsechangelog follows]
@@ -268,7 +273,7 @@ sub protocol_send_file ($$) {
 
 sub protocol_read_bytes ($$) {
     my ($fh, $nbytes) = @_;
-    $nbytes =~ m/^[1-9]\d{0,5}$/ or badproto \*RO, "bad byte count";
+    $nbytes =~ m/^[1-9]\d{0,5}$|^0$/ or badproto \*RO, "bad byte count";
     my $d;
     my $got = read $fh, $d, $nbytes;
     $got==$nbytes or badproto_badread $fh, "data block";
@@ -446,8 +451,9 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               'dgit-distro.debian.archive-query' => 'ftpmasterapi:',
               'dgit-distro.debian.git-check' => 'url',
               'dgit-distro.debian.git-check-suffix' => '/info/refs',
+              'dgit-distro.debian.new-private-pushers' => 't',
               'dgit-distro.debian/push.git-url' => '',
-              'dgit-distro.debian/push.git-host' => 'dgit-git.debian.net',
+              'dgit-distro.debian/push.git-host' => 'push.dgit.debian.org',
               'dgit-distro.debian/push.git-user-force' => 'dgit',
               'dgit-distro.debian/push.git-proto' => 'git+ssh://',
               'dgit-distro.debian/push.git-path' => '/dgit/debian/repos',
@@ -547,10 +553,45 @@ sub access_quirk () {
     return ('none',undef);
 }
 
-our $access_pushing = 0;
+our $access_forpush;
+
+sub parse_cfg_bool ($$$) {
+    my ($what,$def,$v) = @_;
+    $v //= $def;
+    return
+       $v =~ m/^[ty1]/ ? 1 :
+       $v =~ m/^[fn0]/ ? 0 :
+       badcfg "$what needs t (true, y, 1) or f (false, n, 0) not \`$v'";
+}      
+
+sub access_forpush_config () {
+    my $d = access_basedistro();
+
+    return 1 if
+       $new_package &&
+       parse_cfg_bool('new-private-pushers', 0,
+                      cfg("dgit-distro.$d.new-private-pushers",
+                          'RETURN-UNDEF'));
+
+    my $v = cfg("dgit-distro.$d.readonly", 'RETURN-UNDEF');
+    $v //= 'a';
+    return
+       $v =~ m/^[ty1]/ ? 0 : # force readonly,    forpush = 0
+       $v =~ m/^[fn0]/ ? 1 : # force nonreadonly, forpush = 1
+       $v =~ m/^[a]/  ? '' : # auto,              forpush = ''
+       badcfg "readonly needs t (true, y, 1) or f (false, n, 0) or a (auto)";
+}
+
+sub access_forpush () {
+    $access_forpush //= access_forpush_config();
+    return $access_forpush;
+}
 
 sub pushing () {
-    $access_pushing = 1;
+    die "$access_forpush ?" if ($access_forpush // 1) ne 1;
+    badcfg "pushing but distro is configured readonly"
+       if access_forpush_config() eq '0';
+    $access_forpush = 1;
 }
 
 sub access_distros () {
@@ -568,7 +609,7 @@ sub access_distros () {
     unshift @l, $instead_distro;
     @l = grep { defined } @l;
 
-    if ($access_pushing) {
+    if (access_forpush()) {
        @l = map { ("$_/push", $_) } @l;
     }
     @l;
@@ -645,7 +686,7 @@ sub access_giturl (;$) {
     my ($optional) = @_;
     my $url = access_cfg('git-url','RETURN-UNDEF');
     my $suffix;
-    if (!defined $url) {
+    if (!length $url) {
        my $proto = access_cfg('git-proto', 'RETURN-UNDEF');
        return undef unless defined $proto;
        $url =
@@ -1127,7 +1168,21 @@ sub mktree_in_ud_from_only_subdir () {
     $dirs[0] =~ m#^([^/]+)/\.$# or die;
     my $dir = $1;
     changedir $dir;
-    fail "source package contains .git directory" if stat_exists '.git';
+
+    my @gitscmd = qw(find -name .git -prune -print0);
+    debugcmd "|",@gitscmd;
+    open GITS, "-|", @gitscmd or failedcmd @gitscmd;
+    {
+       local $/="\0";
+       while (<GITS>) {
+           chomp or die;
+           print STDERR "$us: warning: removing from source package: ",
+               (messagequote $_), "\n";
+           rmtree $_;
+       }
+    }
+    $!=0; $?=0; close GITS or failedcmd @gitscmd;
+
     mktree_in_ud_here();
     my $format=get_source_format();
     if (madformat($format)) {
@@ -2052,11 +2107,16 @@ sub cmd_remote_push_build_host {
     autoflush STDOUT 1;
 
     $vsnwant //= 1;
-    fail "build host has dgit rpush protocol version".
-       " $rpushprotovsn but invocation host has $vsnwant"
-       unless grep { $rpushprotovsn eq $_ } split /,/, $vsnwant;
+    ($protovsn) = grep {
+       $vsnwant =~ m{^(?:.*,)?$_(?:,.*)?$}
+    } @rpushprotovsn_support;
 
-    responder_send_command("dgit-remote-push-ready $rpushprotovsn");
+    fail "build host has dgit rpush protocol versions ".
+       (join ",", @rpushprotovsn_support).
+        " but invocation host has $vsnwant"
+       unless defined $protovsn;
+
+    responder_send_command("dgit-remote-push-ready $protovsn");
 
     changedir $dir;
     &cmd_push;
@@ -2102,7 +2162,8 @@ sub cmd_rpush {
        $dir = nextarg;
     }
     $dir =~ s{^-}{./-};
-    my @rargs = ($dir,$rpushprotovsn);
+    my @rargs = ($dir);
+    push @rargs, join ",", @rpushprotovsn_support;
     my @rdgit;
     push @rdgit, @dgit;
     push @rdgit, @ropts;
@@ -2120,7 +2181,8 @@ sub cmd_rpush {
     }
     $i_child_pid = open2(\*RO, \*RI, @cmd);
     changedir $i_tmp;
-    initiator_expect { m/^dgit-remote-push-ready/ };
+    ($protovsn) = initiator_expect { m/^dgit-remote-push-ready (\S+)/ };
+    die "$protovsn ?" unless grep { $_ eq $protovsn } @rpushprotovsn_support;
     for (;;) {
        my ($icmd,$iargs) = initiator_expect {
            m/^(\S+)(?: (.*))?$/;
@@ -3026,6 +3088,7 @@ if ($ENV{$fakeeditorenv}) {
 }
 
 parseopts();
+
 print STDERR "DRY RUN ONLY\n" if $dryrun_level > 1;
 print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
     if $dryrun_level == 1;
@@ -3037,6 +3100,7 @@ my $cmd = shift @ARGV;
 $cmd =~ y/-/_/;
 
 if (!defined $quilt_mode) {
+    local $access_forpush;
     $quilt_mode = cfg('dgit.force.quilt-mode', 'RETURN-UNDEF')
        // access_cfg('quilt-mode', 'RETURN-UNDEF')
        // 'linear';