From: Eric Wong <e@80x24.org>
To: meta@public-inbox.org
Subject: [PATCH 1/5] fetch: support --exit-code switch
Date: Wed, 15 Sep 2021 21:35:55 +0000 [thread overview]
Message-ID: <20210915213559.21757-2-e@80x24.org> (raw)
In-Reply-To: <20210915213559.21757-1-e@80x24.org>
As noted in the new manpage entry, this is useful for avoiding
public-inbox-index invocations when there's nothing to update.
We use 127 to match "grok-pull", and also because it doesn't
conflict with any of the current curl(1) exit codes.
---
Documentation/public-inbox-fetch.pod | 30 ++++++++++++++++++++++++++--
lib/PublicInbox/Fetch.pm | 21 ++++++++++++++++++-
script/public-inbox-fetch | 4 +++-
t/lei-mirror.t | 12 +++++++----
t/v2mirror.t | 3 ++-
5 files changed, 61 insertions(+), 9 deletions(-)
diff --git a/Documentation/public-inbox-fetch.pod b/Documentation/public-inbox-fetch.pod
index 7944fdcd..28d5638d 100644
--- a/Documentation/public-inbox-fetch.pod
+++ b/Documentation/public-inbox-fetch.pod
@@ -4,7 +4,7 @@ public-inbox-fetch - "git fetch" wrapper for v2 inbox mirrors
=head1 SYNOPSIS
-public-inbox-fetch -C INBOX_DIR
+public-inbox-fetch [--exit-code] -C INBOX_DIR
=head1 DESCRIPTION
@@ -31,6 +31,15 @@ file to speed up future invocations.
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
@@ -45,6 +54,23 @@ Whether to wrap L<git(1)> and L<curl(1)> commands with torsocks.
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>
@@ -60,4 +86,4 @@ License: AGPL-3.0+ L<https://www.gnu.org/licenses/agpl-3.0.txt>
=head1 SEE ALSO
-L<public-inbox-index(1)>
+L<public-inbox-index(1)>, L<curl(1)>
diff --git a/lib/PublicInbox/Fetch.pm b/lib/PublicInbox/Fetch.pm
index 9ea55e9d..0539fe50 100644
--- a/lib/PublicInbox/Fetch.pm
+++ b/lib/PublicInbox/Fetch.pm
@@ -75,12 +75,22 @@ sub do_manifest ($$$) {
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;
@@ -136,11 +146,14 @@ EOM
}
# 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;
@@ -152,6 +165,7 @@ EOM
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
@@ -159,6 +173,10 @@ EOM
$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) {
@@ -166,6 +184,7 @@ EOM
rename($fn, $mf) or die "E: rename($fn, $mf): $!\n";
$ft->unlink_on_destroy(0);
}
+ $lei->child_error($xit << 8) if $fp2 && $xit;
}
1;
diff --git a/script/public-inbox-fetch b/script/public-inbox-fetch
index 5d303574..d7d4ba47 100755
--- a/script/public-inbox-fetch
+++ b/script/public-inbox-fetch
@@ -16,12 +16,13 @@ options:
--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});
@@ -33,3 +34,4 @@ my $lei = bless {
0 => *STDIN{GLOB}, 1 => *STDOUT{GLOB}, 2 => *STDERR{GLOB},
}, 'PublicInbox::LEI';
PublicInbox::Fetch->do_fetch($lei, '.');
+exit(($lei->{child_error} // 0) >> 8);
diff --git a/t/lei-mirror.t b/t/lei-mirror.t
index 5238b67c..9fdda5aa 100644
--- a/t/lei-mirror.t
+++ b/t/lei-mirror.t
@@ -111,12 +111,14 @@ SKIP: {
'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),
@@ -124,13 +126,15 @@ SKIP: {
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');
diff --git a/t/v2mirror.t b/t/v2mirror.t
index 54ad6945..3df5d053 100644
--- a/t/v2mirror.t
+++ b/t/v2mirror.t
@@ -98,8 +98,9 @@ $ibx->cleanup;
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;
};
next prev parent reply other threads:[~2021-09-15 21:35 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-09-15 21:35 [PATCH 0/5] make(1) inspired things Eric Wong
2021-09-15 21:35 ` Eric Wong [this message]
2021-09-15 21:35 ` [PATCH 2/5] TODO: update some items Eric Wong
2021-09-15 21:35 ` [PATCH 3/5] clone|fetch|--mirror: add convenience $INBOX_DIR/Makefile Eric Wong
2021-09-15 22:45 ` Kyle Meyer
2021-09-15 23:15 ` Eric Wong
2021-09-15 21:35 ` [PATCH 4/5] fetch|clone|--mirror: shorten paths for progress output Eric Wong
2021-09-15 21:35 ` [PATCH 5/5] support -C (chdir) for most non-daemon commands Eric Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://public-inbox.org/README
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210915213559.21757-2-e@80x24.org \
--to=e@80x24.org \
--cc=meta@public-inbox.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/public-inbox.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).