3
# buildah-vendor-treadmill - daily vendor of latest-buildah onto latest-podman
5
package Podman::BuildahVendorTreadmill;
9
use open qw( :encoding(UTF-8) :std );
14
use File::Temp qw(tempfile);
17
use POSIX qw(strftime);
19
(our $ME = $0) =~ s|.*/||;
22
# For debugging, show data structures using DumpTree($var)
23
#use Data::TreeDumper; $Data::TreeDumper::Displayaddress = 0;
25
###############################################################################
26
# BEGIN user-customizable section
28
# Page describing this process in much more detail
30
'https://github.com/containers/podman/wiki/Buildah-Vendor-Treadmill';
32
# github path to buildah
33
our $Buildah = 'github.com/containers/buildah';
35
# FIXME FIXME FIXME: add 'main'? I hope we never need this script for branches.
36
our $Treadmill_PR_Title = 'DO NOT MERGE: buildah vendor treadmill';
38
# Github API; this is where we query to find out the active treadmill PR
39
our $API_URL = 'https://api.github.com/graphql';
41
# Use colors if available and if stdout is a tty
48
$C_Highlight = color("green");
49
$C_Warning = color("bold red");
50
$C_Reset = color("reset");
53
$SIG{__WARN__} = sub { print STDERR $C_Warning, "@_", $C_Reset; };
57
# END user-customizable section
58
###############################################################################
60
###############################################################################
61
# BEGIN boilerplate args checking, usage messages
65
Usage: $ME [OPTIONS] [--sync | --pick [PR] | --reset ]
67
$ME is (2022-04-20) **EXPERIMENTAL**
69
$ME is intended to solve the problem of vendoring
72
Call me with one of three options:
74
--sync The usual case. Mostly used by Ed. Called from a
75
development branch, this just updates everything so
76
we vendor in latest-buildah (main) on top of
77
latest-podman (main). With a few sanity checks.
79
--pick Used for really-truly vendoring in a new buildah; will
80
cherry-pick a commit on your buildah-vendor working branch.
81
Optional PR arg is the ID of the treadmill PR on github.
83
--reset Used after vendoring buildah into main, when there
84
really aren't any buildah patches to keep rolling.
86
For latest documentation and best practices, please see:
92
--help display this message
93
--version display program name and version
99
# Command-line options. Note that this operates directly on @ARGV !
102
our $force_old_main = 0; # in --pick, proceeds even if main is old
103
our $force_retry = 0; # in --sync, continue despite saved checkpoint
104
our $force_testing = 0; # in --sync, test even no podman/buildah changes
106
our $NOT = ''; # print "blahing the blah$NOT\n" if $debug
110
'sync' => sub { $action{sync}++ },
111
'pick' => sub { $action{pick}++ },
112
'reset' => sub { $action{reset}++ },
114
'force-old-main' => \$force_old_main,
115
'force-retry' => \$force_retry,
116
'force-testing' => \$force_testing,
119
'dry-run|n!' => sub { $NOT = ' [NOT]' },
120
'verbose|v' => \$verbose,
123
version => sub { print "$ME version $VERSION\n"; exit 0 },
124
) or die "Try `$ME --help' for help\n";
127
# END boilerplate args checking, usage messages
128
###############################################################################
130
############################## CODE BEGINS HERE ###############################
132
# The term is "modulino".
133
__PACKAGE__->main() unless caller();
137
# Note that we operate directly on @ARGV, not on function parameters.
138
# This is deliberate: it's because Getopt::Long only operates on @ARGV
139
# and there's no clean way to make it use @_.
140
handle_opts(); # will set package globals
142
my @action = keys(%action);
143
die "$ME: Please invoke me with one of --sync or --pick\n"
145
die "$ME: Please invoke me with ONLY one of --sync or --pick\n"
148
my $handler = __PACKAGE__->can("do_@action")
149
or die "$ME: No handler available for --@action\n";
151
# We've validated the command-line args. Before running action, check
152
# that repo is clean. None of our actions can be run on a dirty repo.
158
###############################################################################
159
# BEGIN sync and its helpers
162
die "$ME: --sync takes no arguments; try $ME --help\n" if @_;
164
# Preserve current branch name, so we can come back after switching to main
165
my $current_branch = git_current_branch();
167
# Branch HEAD must be the treadmill commit.
168
my $commit_message = git('log', '-1', '--format=%s', 'HEAD');
169
print "[$commit_message]\n" if $verbose;
170
$commit_message =~ /buildah.*treadmill/
171
or die "$ME: HEAD must be a 'buildah treadmill' commit.\n";
173
# ...and previous commit must be a scratch buildah vendor
174
$commit_message = git('log', '-1', '--format=%B', 'HEAD^');
175
$commit_message =~ /DO NOT MERGE.* vendor in buildah.*JUNK COMMIT/s
176
or die "$ME: HEAD^ must be a DO NOT MERGE / JUNK COMMIT commit\n";
177
assert_buildah_vendor_commit('HEAD^');
180
my $buildah_old = vendored_buildah();
181
print "-> buildah old = $buildah_old\n";
183
# Pull main, and pivot back to this branch
185
git('checkout', '-q', $current_branch);
187
# Make a temporary copy of this branch
188
my $temp_branch = strftime("__buildah-treadmill-checkpoint/%Y%m%d-%H%M%S", localtime);
189
git('branch', $temp_branch, $current_branch);
190
progress("Current branch preserved as $temp_branch");
192
# Get the hash of the top (treadmill) commit, to cherry-pick later
193
my $treadmill_commit = git('rev-parse', 'HEAD');
196
# Danger Will Robinson! This is where it gets scary: a failure here
197
# can leave us in a state where we could lose the treadmill patches.
198
# Proceed with extreme caution.
200
local $SIG{__DIE__} = sub {
201
print STDERR $C_Warning, "@_", <<"END_FAIL_INSTRUCTIONS";
203
This is not something I can recover from. Your human judgment is needed.
205
You will need to recover from this manually. Your best option is to
206
look at the source code for this script.
208
Treadmill branch copy is preserved in $temp_branch
210
To restore state to where you were before this sync:
212
\$ git branch -f $current_branch $treadmill_commit
218
my $forkpoint = git_forkpoint();
222
git('reset', '--hard', 'HEAD^^');
224
# Rebase branch. Also unlikely to fail
225
my $main_commit = git('rev-parse', 'main');
226
if ($forkpoint eq $main_commit) {
227
progress("[Already rebased on podman main]");
230
progress("Rebasing on podman main...");
231
git('rebase', '--empty=keep', 'main');
235
# This does have a high possibility of failing.
236
progress("Vendoring in buildah...");
237
system('go', 'mod', 'edit', '--require' => "${Buildah}\@main") == 0
238
or die "$ME: go mod edit failed";
239
system('make', 'vendor') == 0
240
or die "$ME: make vendor failed";
241
my $buildah_new = vendored_buildah();
242
print "-> buildah new = $buildah_new\n";
244
# Tweak .cirrus.yml so we run bud tests first in CI (to fail fast).
245
tweak_cirrus_test_order();
247
# 'make vendor' seems to git-add files under buildah itself, but not
248
# under other changed modules. Add those now, otherwise we fail
249
# the dirty-tree test in CI.
250
if (my @v = git('status', '--porcelain', '--untracked=all', 'vendor')) {
251
if (my @untracked = grep { /^\?\?\s/ } @v) {
253
s!^.*?vendor/[^/]+/([^/]+/[^/]+)/.*$!$1!; $_ => 1;
255
my $repos = join(', ', sort keys %repos);
256
progress("Adding untracked files under $repos");
257
git('add', 'vendor');
262
git_commit_buildah($buildah_new);
264
# And, finally, this has the highest possibility of failing
265
local $SIG{__DIE__} = sub {
266
print STDERR $C_Warning, "@_", <<"END_FAIL_INSTRUCTIONS";
268
This is not something I can recover from. Your human judgment is needed.
270
Chances are, you might be able to run 'git status', look for
271
merge conflicts, manually resolve those, 'git add', then
272
'git cherry-pick --continue'. If that works, run this script
273
again (you will probably need the --force-retry option).
275
If that DOES NOT work, your only option is to look at the source code
276
for this script. Sorry. There's only so much that can be done automatically.
278
Treadmill branch copy is preserved in $temp_branch
280
To restore state to where you were before this sync:
282
\$ git branch -f $current_branch $treadmill_commit
287
progress('Reapplying treadmill patches');
288
git('cherry-pick', '--allow-empty', $treadmill_commit);
290
# It worked! Clean up: remove our local die() handler and the saved branch
292
git('branch', '-D', $temp_branch);
294
# if buildah is unchanged, and we did not pull main, exit cleanly
295
my $change_message = '';
296
if ($buildah_new eq $buildah_old) {
298
$change_message = "Nothing has changed (same buildah, same podman).";
299
if ($force_testing) {
300
$change_message .= " Testing anyway due to --force-testing.";
303
progress($change_message);
304
progress("Not much point to testing this, but use --force-testing to continue.");
309
$change_message = "Podman has bumped, but Buildah is unchanged. There's probably not much point to testing this.";
313
my $samenew = ($rebased ? 'new' : 'same');
314
$change_message = "New buildah, $samenew podman. Good candidate for pushing.";
316
progress($change_message);
318
build_and_check_podman();
320
progress("All OK. It's now up to you to 'git push --force'");
321
progress(" --- Reminder: $change_message");
323
# Kind of kludgy. If user had to retry a prior failed attempt, and
324
# things are now successful, remind them to delete old checkpoints.
325
# ($force_retry is a 'git branch -D' command string at this point.)
327
progress(" --- Retry worked! You may now $force_retry");
332
# pull_main # Switch to main, and pull latest from github
335
progress("Pulling podman main...");
336
git('checkout', '-q', 'main');
337
git('pull', '-r', git_upstream(), 'main');
340
#############################
341
# tweak_cirrus_test_order # Run bud tests first, to fail fast & early
342
#############################
343
sub tweak_cirrus_test_order {
344
my $cirrus_yml = '.cirrus.yml';
345
my $tmpfile = "$cirrus_yml.tmp.$$";
348
progress("Tweaking test order in $cirrus_yml to run bud tests early");
349
open my $in, '<', $cirrus_yml
351
warn "$ME: Cannot read $cirrus_yml: $!\n";
352
warn "$ME: Will continue anyway\n";
355
open my $out, '>'. $tmpfile
356
or die "$ME: Cannot create $tmpfile: $!\n";
357
my $current_task = '';
359
while (my $line = <$in>) {
361
if ($line =~ /^(\S+)_task:$/) {
365
elsif ($line =~ /^(\s+)depends_on:$/) {
368
elsif ($in_depend && $line =~ /^($in_depend\s+-\s+)(\S+)/) {
371
# Run the buildah-bud tests early: that's the entire point
372
# of the treadmill PR. Here we switch Cirrus task dependencies
373
# such that bud tests run as early as possible.
374
if ($current_task =~ /buildah_bud_test/) {
375
# Buildah bud now depends only on validate...
376
$line = "${indent}validate";
378
elsif ($2 eq 'validate' && $current_task ne 'success') {
379
# ...and all other tests that relied on validate now rely on
380
# bud tests instead. The point of the treadmill PR is to
381
# run the bud tests and only then, if everything passes,
382
# run normal tests. (Reason: bud tests are the only ones
383
# likely to fail on a buildah revendor, and we want to see
385
$line = "${indent}buildah_bud_test";
391
# FIXME THIS IS HORRIBLE!
392
# Add rootless jobs to the buildah bud test matrix.
393
# This is incredibly fragile; it relies on the fact
394
# (true as of 2023-12-07) that the "matrix" yaml lines
395
# are formatted just so and are followed immediately
396
# by a "gce_instance" line.
398
# Since Ed is the only one who ever runs this script,
399
# he is expected to notice if this ever changes, and
401
if ($current_task eq 'buildah_bud_test') {
402
if ($line =~ /^(\s+)gce_instance:/) {
403
print { $out } <<'END_ROOTLESS_BUD';
415
print { $out } $line, "\n";
419
or die "$ME: Error writing $tmpfile: $!\n";
420
chmod 0644 => $tmpfile;
421
rename $tmpfile => $cirrus_yml
422
or die "$ME: Could not rename $tmpfile: $!\n";
425
############################
426
# build_and_check_podman # Run quick (local) sanity checks before pushing
427
############################
428
sub build_and_check_podman {
431
# Confirm that we can still build podman
432
progress("Running 'make' to confirm that podman builds cleanly...");
434
or die "$ME: 'make' failed with new buildah. Cannot continue.\n";
436
# See if any new options need man pages. (C_Warning will highlight errs)
437
progress('Cross-checking man pages...');
439
$errs += system('hack/xref-helpmsgs-manpages');
442
# Confirm that buildah-bud patches still apply. This requires knowing
443
# the name of the directory created by the bud-tests script.
444
progress("Confirming that buildah-bud-tests patches still apply...");
445
system('rm -rf test-buildah-*');
446
if (system('test/buildah-bud/run-buildah-bud-tests', '--no-test')) {
449
warn "$ME: Leaving test-buildah- directory for you to investigate\n";
452
# Patches apply cleanly. Clean up
453
system('rm -rf test-buildah-*');
458
$ME: Errors found. I have to stop now for you to fix them.
459
Your best bet now is:
460
1) Find and fix whatever needs to be fixed; then
461
2) git commit -am'fixme-fixme'; then
462
3) git rebase -i main:
463
a) you are now in an editor window
464
b) move the new fixme-fixme commit up a line, to between the
465
'buildah vendor treadmill' and 'vendor in buildah @ ...' lines
466
c) change 'pick' to 'squash' (or just 's')
467
d) save & quit to continue the rebase
468
e) back to a new editor window
469
f) change the commit message: remove fixme-fixme, add a description
470
of what you actually fixed. If possible, reference the PR (buildah
471
or podman) that introduced the failure
472
g) save & quit to continue the rebase
474
Now, for good measure, rerun this script.
476
For full documentation, refer to
483
# END sync and its helpers
484
###############################################################################
485
# BEGIN pick and its helpers
487
# This is what gets used on a real vendor-new-buildah PR
490
my $current_branch = git_current_branch();
492
# Confirm that current branch is a buildah-vendor one
493
assert_buildah_vendor_commit('HEAD');
494
progress("HEAD is a buildah vendor commit. Good.");
496
# Identify and pull the treadmill PR.
497
my $treadmill_pr = shift || treadmill_pr();
499
my $treadmill_branch = "$ME/pr$treadmill_pr/tmp$$";
500
progress("Fetching treadmill PR $treadmill_pr into $treadmill_branch");
501
git('fetch', '-q', git_upstream(), "pull/$treadmill_pr/head:$treadmill_branch");
503
# Compare merge bases of our branch and the treadmill one
504
progress("Checking merge bases");
505
check_merge_bases($treadmill_pr, $treadmill_branch);
507
# read buildah go.mod from it, and from current tree, and compare
508
my $buildah_on_treadmill = vendored_buildah($treadmill_branch);
509
my $buildah_here = vendored_buildah();
510
if ($buildah_on_treadmill ne $buildah_here) {
511
warn "$ME: Warning: buildah version mismatch:\n";
512
warn "$ME: on treadmill: $buildah_on_treadmill\n";
513
warn "$ME: on this branch: $buildah_here\n";
514
# FIXME: should this require --force? A yes/no prompt?
515
# FIXME: I think not, because usual case will be a true tagged version
516
warn "$ME: Continuing anyway\n";
519
cherry_pick($treadmill_pr, $treadmill_branch);
522
git('branch', '-D', $treadmill_branch);
524
build_and_check_podman();
526
progress("Looks good! Please 'git commit --amend' and edit commit message before pushing.");
530
# treadmill_pr # Returns ID of open podman PR with the desired subject
533
# Github API (or maybe just the search endpoint???) is restricted.
534
my $token = $ENV{GITHUB_TOKEN}
536
warn <<"END_NEED_PR";
537
$ME: Cannot proceed without PR ID.
539
If you have a github API token, please: export GITHUB_TOKEN=.......
542
If you do not have a github API token, please go here:
544
https://github.com/containers/podman/pulls?q=is%3Apr+is%3Aopen+%22buildah+vendor+treadmill%22
546
...then reinvoke me, adding that PR ID to the command line args.
548
As of 2022-09-12 the treadmill PR is 13808, but that may change over time.
553
my $query = <<'END_QUERY';
556
query: "buildah vendor treadmill repo:containers/podman",
560
edges { node { ... on PullRequest { number state title } } }
565
my $ua = LWP::UserAgent->new;
566
$ua->agent("$ME " . $ua->agent); # Identify ourself
569
'Authorization' => "bearer $token",
570
'Accept' => "application/vnd.github.antiope-preview+json",
571
'Content-Type' => "application/json",
573
$ua->default_header($_ => $headers{$_}) for keys %headers;
575
# Massage the query: escape quotes, put it all in one line, collapse spaces
576
$query =~ s/\"/\\"/g;
577
$query =~ s/\n/\\n/g;
579
# ...and now one more massage
580
my $postquery = qq/{ "query": \"$query\" }/;
582
print $postquery, "\n" if $debug;
583
my $res = $ua->post($API_URL, Content => $postquery);
584
if ((my $code = $res->code) != 200) {
585
warn "$ME: GraphQL request failed on $API_URL:\n";
586
print STDERR " ", $code, " ", $res->message, "\n";
587
warn "Cannot continue.\n";
591
# Got something. Confirm that it has all our required fields
592
my $content = decode_json($res->content);
593
use Data::Dump; dd $content if $debug;
594
exists $content->{data}
595
or die "$ME: No '{data}' section in response\n";
596
exists $content->{data}{search}
597
or die "$ME: No '{data}{search}' section in response\n";
598
exists $content->{data}{search}{edges}
599
or die "$ME: No '{data}{search}{edges}' section in response\n";
601
# Confirm that there is exactly one such PR
602
my @prs = @{ $content->{data}{search}{edges} };
604
or die "$ME: WEIRD! No 'buildah vendor treadmill' PRs found!\n";
605
@prs = grep { $_->{node}{title} eq $Treadmill_PR_Title } @prs
606
or die "$ME: No PRs found with title '$Treadmill_PR_Title'\n";
607
@prs = grep { $_->{node}{state} eq 'OPEN' } @prs
608
or die "$ME: Found '$Treadmill_PR_Title' PRs, but none are OPEN\n";
610
or die "$ME: Multiple OPEN '$Treadmill_PR_Title' PRs found!\n";
612
# Yay. Found exactly one.
613
return $prs[0]{node}{number};
616
#######################
617
# check_merge_bases # It's OK if our branch is newer than treadmill
618
#######################
619
sub check_merge_bases {
620
my $treadmill_pr = shift; # e.g., 12345
621
my $treadmill_branch = shift; # e.g., b-v-p/pr12345/tmpNNN
623
# Fetch latest main, for accurate comparison
624
git('fetch', '-q', git_upstream(), 'main');
626
my $forkpoint_cur = git_forkpoint();
627
my $forkpoint_treadmill = git_forkpoint($treadmill_branch);
629
print "fork cur: $forkpoint_cur\nfork tm: $forkpoint_treadmill\n"
631
if ($forkpoint_cur eq $forkpoint_treadmill) {
632
progress("Nice. This branch is up-to-date wrt treadmill PR $treadmill_pr");
637
if (git_is_ancestor($forkpoint_cur, $forkpoint_treadmill)) {
639
$ME: treadmill PR $treadmill_pr is based on
640
a newer main than this branch. This means it might have
641
more up-to-date patches.
645
if ($force_old_main) {
646
warn "$ME: Proceeding due to --force-old-main\n";
650
# Cannot continue. Clean up side branch, and bail.
651
git('branch', '-D', $treadmill_branch);
652
warn "$ME: You might want to consider rebasing on latest main.\n";
653
warn "$ME: Aborting. Use --force-old-main to continue without rebasing.\n";
657
progress("Your branch is based on a newer main than treadmill PR $treadmill_pr. This is usually OK.");
662
# cherry_pick # cherry-pick a commit, updating its commit message
665
my $treadmill_pr = shift; # e.g., 12345
666
my $treadmill_branch = shift; # e.g., b-v-p/pr12345/tmpNNN
668
progress("Cherry-picking from $treadmill_pr");
670
# Create a temp script. Do so in /var/tmp because sometimes $TMPDIR
671
# (e.g. /tmp) has noexec.
672
my ($fh, $editor) = tempfile( "$ME.edit-commit-message.XXXXXXXX", DIR => "/var/tmp" );
673
printf { $fh } <<'END_EDIT_SCRIPT', $ME, $VERSION, $treadmill_pr;
676
if [[ -z "$1" ]]; then
677
echo "FATAL: Did not get called with an arg" >&2
682
if [[ ! -e $msgfile ]]; then
683
echo "FATAL: git-commit file does not exist: $msgfile" >&2
691
WIP: Fixes for vendoring Buildah
693
This commit was automatically cherry-picked
695
from the buildah vendor treadmill PR, #%s
697
/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
698
> The git commit message from that PR is below. Please review it,
699
> edit as necessary, then remove this comment block.
700
\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
704
# Strip the "DO NOT MERGE" header from the treadmill PR, print only
705
# the "Changes since YYYY-MM-DD" and subsequent lines
706
sed -ne '/^Changes since /,$ p' <$msgfile >>$tmpfile
711
or die "$ME: Error writing $editor: $!\n";
712
chmod 0755 => $editor;
713
local $ENV{EDITOR} = $editor;
714
git('cherry-pick', '--allow-empty', '--edit', $treadmill_branch);
718
# END pick and its helpers
719
###############################################################################
720
# BEGIN reset and its helpers
723
die "$ME: --sync takes no arguments; try $ME --help\n" if @_;
725
my $current_branch = git_current_branch();
727
# Make sure side branch == main (i.e., there are no commits on the branch)
728
if (git('rev-parse', $current_branch) ne git('rev-parse', 'main')) {
729
die "$ME: for --reset, $current_branch must == main\n";
732
# Pull main, and pivot back to this branch
734
git('checkout', '-q', $current_branch);
736
git('rebase', '--empty=keep', 'main');
737
git_commit_buildah('[none]');
739
my $ymd = strftime("%Y-%m-%d", localtime);
740
git('commit', '--allow-empty', '-s', '-m' => <<"END_COMMIT_MESSAGE");
743
As you run --sync, please update this commit message with your
749
progress("Done. You may now run --sync.\n");
752
# END reset and its helpers
753
###############################################################################
754
# BEGIN general-purpose helpers
757
# progress # Progris riport Dr Strauss says I shud rite down what I think
760
print $C_Highlight, "|\n+---> @_\n", $C_Reset;
763
#######################
764
# assert_clean_repo # Don't even think of running with local changes
765
#######################
766
sub assert_clean_repo {
767
# During --sync we create a temporary copy of the treadmill branch,
768
# in case something goes wrong. The branch is deleted on success.
769
# If one exists, it means we may have lost work.
771
m!^__buildah-treadmill-checkpoint/\d+-\d+$!
772
} git('branch', '--list', '--format=%(refname:lstrip=2)');
776
$ME: WARNING: leftover checkpoint(s): @relics
778
...continuing due to --force-retry.
780
If things work out, you can 'git branch -D @relics'
783
# OK, ugly override of a binary flag, but it's OK because
784
# it helps with user-friendliness: offer a reminder upon
785
# successful completion of the script.
786
$force_retry = "git branch -D @relics";
790
$ME: FATAL: leftover checkpoint: @relics
792
This means that something went very wrong during an earlier sync run.
793
Your git branch may be in an inconsistent state. Your work to date
794
may be lost. This branch may be your only hope of recovering it.
796
This is not something a script can resolve. You need to look at this
797
branch, compare to your git HEAD, and manually reconcile any differences.
799
If you really know what you're doing, i.e., if you've reconciled
800
merge conflicts and have a pretty secure branch structure, try
801
rerunning me with --force-retry. Or, if that checkpoint is a
802
remnant from a past run, and you're ultra-certain that you don't
803
need it, you can git branch -D @relics
809
# OK so far. Now check for modified files.
810
if (my @changed = git('status', '--porcelain', '--untracked=no')) {
811
warn "$ME: Modified files in repo:\n";
812
warn " $_\n" for @changed;
816
# ...and for untracked files under vendor/
817
if (my @v = git('status', '--porcelain', '--untracked=all', 'vendor')) {
818
warn "$ME: Untracked vendor files:\n";
824
########################
825
# git_current_branch # e.g., 'vendor_buildah'
826
########################
827
sub git_current_branch() {
828
my $b = git('rev-parse', '--abbrev-ref=strict', 'HEAD');
830
# There is no circumstance in which we can ever be called from main
831
die "$ME: must run from side branch, not main\n" if $b eq 'main';
836
# git_forkpoint # Hash at which branch (default: cur) branched from main
839
# '--fork-point vendor-branch' fails silently on Paul's git tree,
840
# but plain merge-base works fine. My head hurts from trying to
841
# understand the docs, so I give up. Just try fork-point first,
842
# and if it fails, try without. #cargocult #gitishard
843
my $forkpoint = eval { git('merge-base', '--fork-point', 'main', @_) };
845
$forkpoint = git('merge-base', 'main', @_);
851
# git_is_ancestor # Is hash1 an ancestor of hash2?
854
# Use system(), not git(), because we don't want to abort on exit status
855
my $rc = system('git', 'merge-base', '--is-ancestor', @_);
856
die "$ME: Cannot continue\n" if $? > 256; # e.g., Not a valid object
858
# Translate shell 0/256 status to logical 1/0
863
# git_upstream # Name of true github upstream
866
for my $line (git('remote', '-v')) {
867
my ($remote, $url, $type) = split(' ', $line);
868
if ($url =~ m!github\.com.*containers/(podman|libpod)!) {
869
if ($type =~ /fetch/) {
875
die "$ME: did not find a remote with 'github.com/containers/podman'\n";
878
########################
879
# git_commit_buildah # Do the buildah commit
880
########################
881
sub git_commit_buildah {
882
my $buildah_version = shift;
884
# When called by --reset, this can be empty
885
git('commit', '-as', '--allow-empty', '-m', <<"END_COMMIT_MESSAGE");
886
DO NOT MERGE: vendor in buildah \@ $buildah_version
888
This is a JUNK COMMIT from $ME v$VERSION.
890
DO NOT MERGE! This is just a way to keep the buildah-podman
891
vendoring in sync. Refer to:
898
# git # Run a git command
901
my @cmd = ('git', @_);
902
print "\$ @cmd\n" if $verbose || $debug;
903
open my $fh, '-|', @cmd
904
or die "$ME: Cannot fork: $!\n";
906
while (my $line = <$fh>) {
908
push @results, $line;
911
or die "$ME: command failed: @cmd\n";
913
return wantarray ? @results : join("\n", @results);
916
##################################
917
# assert_buildah_vendor_commit # Fails if input arg is not a buildah vendor
918
##################################
919
sub assert_buildah_vendor_commit {
920
my $ref = shift; # in: probably HEAD or HEAD^
922
my @deltas = git('diff', '--name-only', "$ref^", $ref);
924
# It's OK if there are no deltas, e.g. immediately after a buildah vendor PR
927
# It's OK if there are more modified files than just these.
928
# It's not OK if any of these are missing.
929
my @expect = qw(go.mod go.sum vendor/modules.txt);
931
for my $expect (@expect) {
932
if (! grep { $_ eq $expect } @deltas) {
933
push @missing, "$expect is unchanged";
937
if (! grep { m!^vendor/\Q$Buildah\E/! } @deltas) {
938
push @missing, "no changes under $Buildah";
943
warn "$ME: $ref does not look like a buildah vendor commit:\n";
944
warn "$ME: - $_\n" for @missing;
945
die "$ME: Cannot continue\n";
948
######################
949
# vendored_buildah # Returns currently-vendored buildah
950
######################
951
sub vendored_buildah {
952
my $gomod_file = 'go.mod';
955
# Called with a branch argument; fetch that version of go.mod
956
$gomod_file = "@_:$gomod_file";
957
@gomod = git('show', $gomod_file);
960
# No branch argument, read file
961
open my $fh, '<', $gomod_file
962
or die "$ME: Cannot read $gomod_file: $!\n";
963
while (my $line = <$fh>) {
970
for my $line (@gomod) {
971
if ($line =~ m!^\s+\Q$Buildah\E\s+(\S+)!) {
976
die "$ME: Could not find buildah in $gomod_file!\n";
979
# END general-purpose helpers
980
###############################################################################