=head1 SYNOPSIS
-public-inbox-fetch -C INBOX_DIR
+public-inbox-fetch [--exit-code] -C INBOX_DIR
=head1 DESCRIPTION
Quiets down progress messages, also passed to L<git-fetch(1)>.
+=item --exit-code
+
+Exit with C<127> if no updates are done. This can be used in
+shell scripts to avoid invoking L<public-inbox-index(1)> when
+there are no updates:
+
+ public-inbox-fetch -q --exit-code && public-inbox-index
+ test $? -eq 0 || exit $?
+
=item -v
=item --verbose
Default: C<auto>
+=back
+
+=head1 EXIT CODES
+
+=over
+
+=item 127
+
+no updates when L</--exit-code> is used above
+
+=back
+
+public-inbox-fetch will also exit with curl L<curl(1)/EXIT CODES>
+as documented in the L<curl(1)> manpage (e.g. C<7> when curl cannot
+reach a host). Likewise, L<git-fetch(1)> failures are also
+propagated to the user.
+
=head1 CONTACT
Feedback welcome via plain-text mail to L<mailto:meta@public-inbox.org>
=head1 SEE ALSO
-L<public-inbox-index(1)>
+L<public-inbox-index(1)>, L<curl(1)>
my $t1 = $cur->{modified} // next;
delete($mdiff->{$k}) if $f0 eq $f1 && $t0 == $t1;
}
- return unless keys %$mdiff;
+ unless (keys %$mdiff) {
+ $lei->child_error(127 << 8) if $lei->{opt}->{'exit-code'};
+ return;
+ }
my (undef, $v1_path, @v2_epochs) =
PublicInbox::LeiMirror::deduce_epochs($mdiff, $ibx_uri->path);
[ 200, $v1_path, \@v2_epochs, $muri, $ft, $mf ];
}
+sub get_fingerprint2 {
+ my ($git_dir) = @_;
+ require Digest::SHA;
+ my $rd = popen_rd([qw(git show-ref)], undef, { -C => $git_dir });
+ Digest::SHA::sha256(do { local $/; <$rd> });
+}
+
sub do_fetch {
my ($cls, $lei, $cd) = @_;
my $ibx_ver;
}
# n.b. this expects all epochs are from the same host
my $torsocks = $lei->{curl}->torsocks($lei, $muri);
+ my $fp2 = $lei->{opt}->{'exit-code'} ? [] : undef;
+ my $xit = 127;
for my $d (@git_dir) {
my $cmd;
my $opt = {}; # for spawn
if (-d $d) {
$opt->{-C} = $d;
+ $fp2->[0] = get_fingerprint2($d) if $fp2;
$cmd = [ @$torsocks, fetch_cmd($lei, $opt) ];
} else {
my $e_uri = $ibx_uri->clone;
PublicInbox::LeiMirror::clone_cmd($lei, $opt),
$$e_uri, $d];
push @new_epoch, substr($epath, 5, -4) + 0;
+ $xit = 0;
}
my $cerr = PublicInbox::LeiMirror::run_reap($lei, $cmd, $opt);
# do not bail on clone failure if we didn't have a manifest
$lei->child_error($cerr, "@$cmd failed");
return;
}
+ if ($fp2 && $xit) {
+ $fp2->[1] = get_fingerprint2($d);
+ $xit = 0 if $fp2->[0] ne $fp2->[1];
+ }
}
for my $i (@new_epoch) { $mg->epoch_cfg_set($i) }
if ($ft) {
rename($fn, $mf) or die "E: rename($fn, $mf): $!\n";
$ft->unlink_on_destroy(0);
}
+ $lei->child_error($xit << 8) if $fp2 && $xit;
}
1;
--torsocks VAL whether or not to wrap git and curl commands with
torsocks (default: `auto')
Must be one of: `auto', `no' or `yes'
+ --exit-code exit with 127 if no updates
--verbose | -v increase verbosity (may be repeated)
--quiet | -q increase verbosity (may be repeated)
-C DIR chdir to specified directory
EOF
GetOptions($opt, qw(help|h quiet|q verbose|v+ C=s@ c=s@
- no-torsocks torsocks=s)) or die $help;
+ no-torsocks torsocks=s exit-code)) or die $help;
if ($opt->{help}) { print $help; exit };
require PublicInbox::Fetch; # loads Admin
PublicInbox::Admin::do_chdir(delete $opt->{C});
0 => *STDIN{GLOB}, 1 => *STDOUT{GLOB}, 2 => *STDERR{GLOB},
}, 'PublicInbox::LEI';
PublicInbox::Fetch->do_fetch($lei, '.');
+exit(($lei->{child_error} // 0) >> 8);
'all.git alternates created');
ok(-f "$d/t2/manifest.js.gz", 'manifest saved');
ok(!-e "$d/t2/mirror.done", 'no leftover mirror.done');
- ok(run_script([qw(-fetch -C), "$d/t2"], undef, $opt),
+ ok(!run_script([qw(-fetch --exit-code -C), "$d/t2"], undef, $opt),
'-fetch succeeds w/ manifest.js.gz');
+ is($? >> 8, 127, '--exit-code gave 127');
unlike($err, qr/git fetch/, 'no fetch done w/ manifest');
unlink("$d/t2/manifest.js.gz") or xbail "unlink $!";
- ok(run_script([qw(-fetch -C), "$d/t2"], undef, $opt),
+ ok(!run_script([qw(-fetch --exit-code -C), "$d/t2"], undef, $opt),
'-fetch succeeds w/o manifest.js.gz');
+ is($? >> 8, 127, '--exit-code gave 127');
like($err, qr/git fetch/, 'fetch forced w/o manifest');
ok(run_script([qw(-clone -q -C), $d, "$http/t1"], undef, $opt),
ok(-d "$d/t1", 'v1 cloned');
ok(!-e "$d/t1/mirror.done", 'no leftover file');
ok(-f "$d/t1/manifest.js.gz", 'manifest saved');
- ok(run_script([qw(-fetch -C), "$d/t1"], undef, $opt),
+ ok(!run_script([qw(-fetch --exit-code -C), "$d/t1"], undef, $opt),
'fetching v1 works');
+ is($? >> 8, 127, '--exit-code gave 127');
unlike($err, qr/git fetch/, 'no fetch done w/ manifest');
unlink("$d/t1/manifest.js.gz") or xbail "unlink $!";
my $before = [ glob("$d/t1/*") ];
- ok(run_script([qw(-fetch -C), "$d/t1"], undef, $opt),
+ ok(!run_script([qw(-fetch --exit-code -C), "$d/t1"], undef, $opt),
'fetching v1 works w/o manifest.js.gz');
+ is($? >> 8, 127, '--exit-code gave 127');
unlink("$d/t1/FETCH_HEAD"); # git internal
like($err, qr/git fetch/, 'no fetch done w/ manifest');
ok(unlink("$d/t1/manifest.js.gz"), 'manifest created');
my @new_epochs;
my $fetch_each_epoch = sub {
my %before = map { $_ => 1 } glob("$tmpdir/m/git/*");
- run_script([qw(-fetch -q)], undef, {-C => "$tmpdir/m"}) or
+ run_script([qw(-fetch --exit-code -q)], undef, {-C => "$tmpdir/m"}) or
xbail '-fetch fail';
+ is($?, 0, '--exit-code 0 after fetch updated');
my @after = grep { !$before{$_} } glob("$tmpdir/m/git/*");
push @new_epochs, @after;
};