mirror of
https://github.com/containers/podman
synced 2024-10-20 01:03:51 +00:00
a4aa07a07d
Buildah got vendored into podman last week, and the script went kablooie because of ever-so-slight conflicts between what was in the treadmill PR (#13808) and what ultimately got merged (#14127) which was obviously better (hey, I tried). After a buildah vendor, there really isn't any point to keeping the treadmill commits - we're much better off just restarting with two fresh empty placeholder commits. Do so. Also, mild cleanup. Signed-off-by: Ed Santiago <santiago@redhat.com>
864 lines
28 KiB
Perl
Executable file
864 lines
28 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
#
|
|
# buildah-vendor-treadmill - daily vendor of latest-buildah onto latest-podman
|
|
#
|
|
package Podman::BuildahVendorTreadmill;
|
|
|
|
use v5.14;
|
|
use utf8;
|
|
use open qw( :encoding(UTF-8) :std );
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Temp qw(tempfile);
|
|
use JSON;
|
|
use LWP::UserAgent;
|
|
use POSIX qw(strftime);
|
|
|
|
(our $ME = $0) =~ s|.*/||;
|
|
our $VERSION = '0.3';
|
|
|
|
# For debugging, show data structures using DumpTree($var)
|
|
#use Data::TreeDumper; $Data::TreeDumper::Displayaddress = 0;
|
|
|
|
###############################################################################
|
|
# BEGIN user-customizable section
|
|
|
|
# Page describing this process in much more detail
|
|
our $Docs_URL =
|
|
'https://github.com/containers/podman/wiki/Buildah-Vendor-Treadmill';
|
|
|
|
# github path to buildah
|
|
our $Buildah = 'github.com/containers/buildah';
|
|
|
|
# FIXME FIXME FIXME: add 'main'? I hope we never need this script for branches.
|
|
our $Treadmill_PR_Title = 'DO NOT MERGE: buildah vendor treadmill';
|
|
|
|
# Github API; this is where we query to find out the active treadmill PR
|
|
our $API_URL = 'https://api.github.com/graphql';
|
|
|
|
# Temporary file used to preserve current treadmill patches. This file
|
|
# should only exist very briefly while we perform branch operations.
|
|
our $Patch_File = "0000-$ME.patch";
|
|
|
|
# Use colors if available and if stdout is a tty
|
|
our $C_Highlight = '';
|
|
our $C_Warning = '';
|
|
our $C_Reset = '';
|
|
eval '
|
|
use Term::ANSIColor;
|
|
if (-t 1) {
|
|
$C_Highlight = color("green");
|
|
$C_Warning = color("bold red");
|
|
$C_Reset = color("reset");
|
|
|
|
}
|
|
$SIG{__WARN__} = sub { print STDERR $C_Warning, "@_", $C_Reset; };
|
|
|
|
';
|
|
|
|
# END user-customizable section
|
|
###############################################################################
|
|
|
|
###############################################################################
|
|
# BEGIN boilerplate args checking, usage messages
|
|
|
|
sub usage {
|
|
print <<"END_USAGE";
|
|
Usage: $ME [OPTIONS] [--sync | --pick | --reset ]
|
|
|
|
$ME is (2022-04-20) **EXPERIMENTAL**
|
|
|
|
$ME is intended to solve the problem of vendoring
|
|
buildah into podman.
|
|
|
|
Call me with one of two options:
|
|
|
|
--sync The usual case. Mostly used by Ed. Called from a
|
|
development branch, this just updates everything so
|
|
we vendor in latest-buildah (main) on top of
|
|
latest-podman (main). With a few sanity checks.
|
|
|
|
--pick Used for really-truly vendoring in a new buildah; will
|
|
cherry-pick a commit on your buildah-vendor working branch
|
|
|
|
--reset Used after vendoring buildah into main, when there
|
|
really aren't any buildah patches to keep rolling.
|
|
|
|
For latest documentation and best practices, please see:
|
|
|
|
$Docs_URL
|
|
|
|
OPTIONS:
|
|
|
|
--help display this message
|
|
--version display program name and version
|
|
END_USAGE
|
|
|
|
exit;
|
|
}
|
|
|
|
# Command-line options. Note that this operates directly on @ARGV !
|
|
our %action;
|
|
our $debug = 0;
|
|
our $force_old_main = 0; # in --pick, proceeds even if main is old
|
|
our $force_testing = 0; # in --sync, test even no podman/buildah changes
|
|
our $verbose = 0;
|
|
our $NOT = ''; # print "blahing the blah$NOT\n" if $debug
|
|
sub handle_opts {
|
|
use Getopt::Long;
|
|
GetOptions(
|
|
'sync' => sub { $action{sync}++ },
|
|
'pick' => sub { $action{pick}++ },
|
|
'reset' => sub { $action{reset}++ },
|
|
|
|
'force-old-main' => \$force_old_main,
|
|
'force-testing' => \$force_testing,
|
|
|
|
'debug!' => \$debug,
|
|
'dry-run|n!' => sub { $NOT = ' [NOT]' },
|
|
'verbose|v' => \$verbose,
|
|
|
|
help => \&usage,
|
|
version => sub { print "$ME version $VERSION\n"; exit 0 },
|
|
) or die "Try `$ME --help' for help\n";
|
|
}
|
|
|
|
# END boilerplate args checking, usage messages
|
|
###############################################################################
|
|
|
|
############################## CODE BEGINS HERE ###############################
|
|
|
|
# The term is "modulino".
|
|
__PACKAGE__->main() unless caller();
|
|
|
|
# Main code.
|
|
sub main {
|
|
# Note that we operate directly on @ARGV, not on function parameters.
|
|
# This is deliberate: it's because Getopt::Long only operates on @ARGV
|
|
# and there's no clean way to make it use @_.
|
|
handle_opts(); # will set package globals
|
|
|
|
# Fetch command-line arguments. Barf if too many.
|
|
# FIXME: if called with arg, that's the --sync branch?
|
|
# FIXME: if called with --pick + arg, that's the PR?
|
|
die "$ME: Too many arguments; try $ME --help\n" if @ARGV;
|
|
|
|
my @action = keys(%action);
|
|
die "$ME: Please invoke me with one of --sync or --pick\n"
|
|
if ! @action;
|
|
die "$ME: Please invoke me with ONLY one of --sync or --pick\n"
|
|
if @action > 1;
|
|
|
|
my $handler = __PACKAGE__->can("do_@action")
|
|
or die "$ME: No handler available for --@action\n";
|
|
|
|
# We've validated the command-line args. Before running action, check
|
|
# that repo is clean. None of our actions can be run on a dirty repo.
|
|
assert_clean_repo();
|
|
|
|
$handler->();
|
|
}
|
|
|
|
###############################################################################
|
|
# BEGIN sync and its helpers
|
|
|
|
sub do_sync {
|
|
# Preserve current branch name, so we can come back after switching to main
|
|
my $current_branch = git_current_branch();
|
|
|
|
# Branch HEAD must be the treadmill commit.
|
|
my $commit_message = git('log', '-1', '--format=%s', 'HEAD');
|
|
print "[$commit_message]\n" if $verbose;
|
|
$commit_message =~ /buildah.*treadmill/
|
|
or die "$ME: HEAD must be a 'buildah treadmill' commit.\n";
|
|
|
|
# ...and previous commit must be a scratch buildah vendor
|
|
$commit_message = git('log', '-1', '--format=%B', 'HEAD^');
|
|
$commit_message =~ /DO NOT MERGE.* vendor in buildah.*JUNK COMMIT/s
|
|
or die "$ME: HEAD^ must be a DO NOT MERGE / JUNK COMMIT commit\n";
|
|
assert_buildah_vendor_commit('HEAD^');
|
|
|
|
# Looks good so far.
|
|
my $buildah_old = vendored_buildah();
|
|
print "-> buildah old = $buildah_old\n";
|
|
|
|
# Pull main, and pivot back to this branch
|
|
pull_main();
|
|
git('checkout', '-q', $current_branch);
|
|
|
|
# Preserve local patches. --always will generate empty patches (e.g.,
|
|
# after a buildah vendor when everything is copacetic); --no-signature
|
|
# prevents a buildup of "-- 2.35" (git version) lines at the end.
|
|
git('format-patch', '--always', '--no-signature', "--output=$Patch_File", 'HEAD^');
|
|
progress("Treadmill patches saved to $Patch_File");
|
|
|
|
#
|
|
# Danger Will Robinson! This is where it gets scary: a failure here
|
|
# can leave us in a state where we could lose the treadmill patches.
|
|
# Proceed with extreme caution.
|
|
#
|
|
local $SIG{__DIE__} = sub {
|
|
print STDERR $C_Warning, "@_", <<"END_FAIL_INSTRUCTIONS";
|
|
|
|
This is not something I can recover from. Your human judgment is needed.
|
|
|
|
You will need to recover from this manually. Your best option is to
|
|
look at the source code for this script.
|
|
|
|
Your treadmill patches are here: $Patch_File
|
|
END_FAIL_INSTRUCTIONS
|
|
|
|
exit 1;
|
|
};
|
|
|
|
my $forkpoint = git_forkpoint();
|
|
my $rebased;
|
|
|
|
# Unlikely to fail
|
|
git('reset', '--hard', 'HEAD^^');
|
|
|
|
# Rebase branch. Also unlikely to fail
|
|
my $main_commit = git('rev-parse', 'main');
|
|
if ($forkpoint eq $main_commit) {
|
|
progress("[Already rebased on podman main]");
|
|
}
|
|
else {
|
|
progress("Rebasing on podman main...");
|
|
git('rebase', '--empty=keep', 'main');
|
|
$rebased = 1;
|
|
}
|
|
|
|
# This does have a high possibility of failing.
|
|
progress("Vendoring in buildah...");
|
|
system('go', 'mod', 'edit', '--require' => "${Buildah}\@main") == 0
|
|
or die "$ME: go mod edit failed";
|
|
system('make', 'vendor') == 0
|
|
or die "$ME: make vendor failed";
|
|
my $buildah_new = vendored_buildah();
|
|
print "-> buildah new = $buildah_new\n";
|
|
|
|
# Tweak .cirrus.yml so we run bud tests first in CI (to fail fast).
|
|
tweak_cirrus_test_order();
|
|
|
|
# 'make vendor' seems to git-add files under buildah itself, but not
|
|
# under other changed modules. Add those now, otherwise we fail
|
|
# the dirty-tree test in CI.
|
|
if (my @v = git('status', '--porcelain', '--untracked=all', 'vendor')) {
|
|
if (my @untracked = grep { /^\?\?\s/ } @v) {
|
|
my %repos = map {
|
|
s!^.*?vendor/[^/]+/([^/]+/[^/]+)/.*$!$1!; $_ => 1;
|
|
} @untracked;
|
|
my $repos = join(', ', sort keys %repos);
|
|
progress("Adding untracked files under $repos");
|
|
git('add', 'vendor');
|
|
}
|
|
}
|
|
|
|
# Commit everything.
|
|
git_commit_buildah($buildah_new);
|
|
|
|
# And, finally, this has the highest possibility of failing
|
|
progress('Reapplying preserved patches');
|
|
git('am', '--empty=keep', $Patch_File);
|
|
|
|
# It worked! Clean up: remove our local die() handler and the patch file
|
|
undef $SIG{__DIE__};
|
|
unlink $Patch_File;
|
|
|
|
# if buildah is unchanged, and we did not pull main, exit cleanly
|
|
my $change_message = '';
|
|
if ($buildah_new eq $buildah_old) {
|
|
if (! $rebased) {
|
|
$change_message = "Nothing has changed (same buildah, same podman).";
|
|
if ($force_testing) {
|
|
$change_message .= " Testing anyway due to --force-testing.";
|
|
}
|
|
else {
|
|
progress($change_message);
|
|
progress("Not much point to testing this, but use --force-testing to continue.");
|
|
exit 0;
|
|
}
|
|
}
|
|
else {
|
|
$change_message = "Podman has bumped, but Buildah is unchanged. There's probably not much point to testing this.";
|
|
}
|
|
}
|
|
else {
|
|
my $samenew = ($rebased ? 'new' : 'same');
|
|
$change_message = "New buildah, $samenew podman. Good candidate for pushing.";
|
|
}
|
|
progress($change_message);
|
|
|
|
build_and_check_podman();
|
|
|
|
progress("All OK. It's now up to you to 'git push --force'");
|
|
progress(" --- Reminder: $change_message");
|
|
}
|
|
|
|
###############
|
|
# pull_main # Switch to main, and pull latest from github
|
|
###############
|
|
sub pull_main {
|
|
progress("Pulling podman main...");
|
|
git('checkout', '-q', 'main');
|
|
git('pull', '-r', git_upstream(), 'main');
|
|
}
|
|
|
|
#############################
|
|
# tweak_cirrus_test_order # Run bud tests first, to fail fast & early
|
|
#############################
|
|
sub tweak_cirrus_test_order {
|
|
my $cirrus_yml = '.cirrus.yml';
|
|
my $tmpfile = "$cirrus_yml.tmp.$$";
|
|
unlink $tmpfile;
|
|
|
|
progress("Tweaking test order in $cirrus_yml to run bud tests early");
|
|
open my $in, '<', $cirrus_yml
|
|
or do {
|
|
warn "$ME: Cannot read $cirrus_yml: $!\n";
|
|
warn "$ME: Will continue anyway\n";
|
|
return;
|
|
};
|
|
open my $out, '>'. $tmpfile
|
|
or die "$ME: Cannot create $tmpfile: $!\n";
|
|
my $current_task = '';
|
|
my $in_depend;
|
|
while (my $line = <$in>) {
|
|
chomp $line;
|
|
if ($line =~ /^(\S+)_task:$/) {
|
|
$current_task = $1;
|
|
undef $in_depend;
|
|
}
|
|
elsif ($line =~ /^(\s+)depends_on:$/) {
|
|
$in_depend = $1;
|
|
}
|
|
elsif ($in_depend && $line =~ /^($in_depend\s+-\s+)(\S+)/) {
|
|
if ($current_task eq 'buildah_bud_test') {
|
|
# Buildah bud test now depends on validate, so it runs early
|
|
$line = "${1}validate";
|
|
}
|
|
elsif ($2 eq 'validate' && $current_task ne 'success') {
|
|
# Other tests that relied on validate, now rely on bud instead
|
|
$line = "${1}buildah_bud_test";
|
|
}
|
|
}
|
|
else {
|
|
undef $in_depend;
|
|
}
|
|
|
|
print { $out } $line, "\n";
|
|
}
|
|
close $in;
|
|
close $out
|
|
or die "$ME: Error writing $tmpfile: $!\n";
|
|
chmod 0644 => $tmpfile;
|
|
rename $tmpfile => $cirrus_yml
|
|
or die "$ME: Could not rename $tmpfile: $!\n";
|
|
}
|
|
|
|
############################
|
|
# build_and_check_podman # Run quick (local) sanity checks before pushing
|
|
############################
|
|
sub build_and_check_podman {
|
|
my $errs = 0;
|
|
|
|
# Confirm that we can still build podman
|
|
progress("Running 'make' to confirm that podman builds cleanly...");
|
|
system('make') == 0
|
|
or die "$ME: 'make' failed with new buildah. Cannot continue.\n";
|
|
|
|
# See if any new options need man pages. (C_Warning will highlight errs)
|
|
progress('Cross-checking man pages...');
|
|
print $C_Warning;
|
|
$errs += system('hack/xref-helpmsgs-manpages');
|
|
print $C_Reset;
|
|
|
|
# Confirm that buildah-bud patches still apply. This requires knowing
|
|
# the name of the directory created by the bud-tests script.
|
|
progress("Confirming that buildah-bud-tests patches still apply...");
|
|
system('rm -rf test-buildah-*');
|
|
if (system('test/buildah-bud/run-buildah-bud-tests', '--no-test')) {
|
|
# Error
|
|
++$errs;
|
|
warn "$ME: Leaving test-buildah- directory for you to investigate\n";
|
|
}
|
|
else {
|
|
# Patches apply cleanly. Clean up
|
|
system('rm -rf test-buildah-*');
|
|
}
|
|
|
|
return if !$errs;
|
|
warn <<"END_WARN";
|
|
$ME: Errors found. I have to stop now for you to fix them.
|
|
Your best bet now is:
|
|
1) Find and fix whatever needs to be fixed; then
|
|
2) git commit -am'fixme-fixme'; then
|
|
3) git rebase -i main:
|
|
a) you are now in an editor window
|
|
b) move the new fixme-fixme commit up a line, to between the
|
|
'buildah vendor treadmill' and 'vendor in buildah @ ...' lines
|
|
c) change 'pick' to 'squash' (or just 's')
|
|
d) save & quit to continue the rebase
|
|
e) back to a new editor window
|
|
f) change the commit message: remove fixme-fixme, add a description
|
|
of what you actually fixed. If possible, reference the PR (buildah
|
|
or podman) that introduced the failure
|
|
g) save & quit to continue the rebase
|
|
|
|
Now, for good measure, rerun this script.
|
|
|
|
For full documentation, refer to
|
|
|
|
$Docs_URL
|
|
END_WARN
|
|
exit 1;
|
|
}
|
|
|
|
# END sync and its helpers
|
|
###############################################################################
|
|
# BEGIN pick and its helpers
|
|
#
|
|
# This is what gets used on a real vendor-new-buildah PR
|
|
|
|
sub do_pick {
|
|
my $current_branch = git_current_branch();
|
|
|
|
# Confirm that current branch is a buildah-vendor one
|
|
assert_buildah_vendor_commit('HEAD');
|
|
progress("HEAD is a buildah vendor commit. Good.");
|
|
|
|
# Identify and pull the treadmill PR
|
|
my $treadmill_pr = treadmill_pr();
|
|
my $treadmill_branch = "$ME/pr$treadmill_pr/tmp$$";
|
|
progress("Fetching treadmill PR $treadmill_pr into $treadmill_branch");
|
|
git('fetch', '-q', git_upstream(), "pull/$treadmill_pr/head:$treadmill_branch");
|
|
|
|
# Compare merge bases of our branch and the treadmill one
|
|
progress("Checking merge bases");
|
|
check_merge_bases($treadmill_pr, $treadmill_branch);
|
|
|
|
# read buildah go.mod from it, and from current tree, and compare
|
|
my $buildah_on_treadmill = vendored_buildah($treadmill_branch);
|
|
my $buildah_here = vendored_buildah();
|
|
if ($buildah_on_treadmill ne $buildah_here) {
|
|
warn "$ME: Warning: buildah version mismatch:\n";
|
|
warn "$ME: on treadmill: $buildah_on_treadmill\n";
|
|
warn "$ME: on this branch: $buildah_here\n";
|
|
# FIXME: should this require --force? A yes/no prompt?
|
|
# FIXME: I think not, because usual case will be a true tagged version
|
|
warn "$ME: Continuing anyway\n";
|
|
}
|
|
|
|
cherry_pick($treadmill_pr, $treadmill_branch);
|
|
|
|
# Clean up
|
|
git('branch', '-D', $treadmill_branch);
|
|
|
|
build_and_check_podman();
|
|
|
|
progress("Looks good! Please 'git commit --amend' and edit commit message before pushing.");
|
|
}
|
|
|
|
##################
|
|
# treadmill_pr # Returns ID of open podman PR with the desired subject
|
|
##################
|
|
sub treadmill_pr {
|
|
my $query = <<'END_QUERY';
|
|
{
|
|
search(
|
|
query: "buildah vendor treadmill repo:containers/podman",
|
|
type: ISSUE,
|
|
first: 10
|
|
) {
|
|
edges { node { ... on PullRequest { number state title } } }
|
|
}
|
|
}
|
|
END_QUERY
|
|
|
|
my $ua = LWP::UserAgent->new;
|
|
$ua->agent("$ME " . $ua->agent); # Identify ourself
|
|
|
|
my %headers = (
|
|
'Accept' => "application/vnd.github.antiope-preview+json",
|
|
'Content-Type' => "application/json",
|
|
);
|
|
|
|
# Use github token if available, but don't require it. (All it does is
|
|
# bump up our throttling limit, which shouldn't be an issue) (unless
|
|
# someone invokes this script hundreds of times per minute).
|
|
if (my $token = $ENV{GITHUB_TOKEN}) {
|
|
$headers{Authorization} = "bearer $token";
|
|
}
|
|
$ua->default_header($_ => $headers{$_}) for keys %headers;
|
|
|
|
# Massage the query: escape quotes, put it all in one line, collapse spaces
|
|
$query =~ s/\"/\\"/g;
|
|
$query =~ s/\n/\\n/g;
|
|
$query =~ s/\s+/ /g;
|
|
# ...and now one more massage
|
|
my $postquery = qq/{ "query": \"$query\" }/;
|
|
|
|
print $postquery, "\n" if $debug;
|
|
my $res = $ua->post($API_URL, Content => $postquery);
|
|
if ((my $code = $res->code) != 200) {
|
|
print $code, " ", $res->message, "\n";
|
|
exit 1;
|
|
}
|
|
|
|
# Got something. Confirm that it has all our required fields
|
|
my $content = decode_json($res->content);
|
|
use Data::Dump; dd $content if $debug;
|
|
exists $content->{data}
|
|
or die "$ME: No '{data}' section in response\n";
|
|
exists $content->{data}{search}
|
|
or die "$ME: No '{data}{search}' section in response\n";
|
|
exists $content->{data}{search}{edges}
|
|
or die "$ME: No '{data}{search}{edges}' section in response\n";
|
|
|
|
# Confirm that there is exactly one such PR
|
|
my @prs = @{ $content->{data}{search}{edges} };
|
|
@prs > 0
|
|
or die "$ME: WEIRD! No 'buildah vendor treadmill' PRs found!\n";
|
|
@prs = grep { $_->{node}{title} eq $Treadmill_PR_Title } @prs
|
|
or die "$ME: No PRs found with title '$Treadmill_PR_Title'\n";
|
|
@prs = grep { $_->{node}{state} eq 'OPEN' } @prs
|
|
or die "$ME: Found '$Treadmill_PR_Title' PRs, but none are OPEN\n";
|
|
@prs == 1
|
|
or die "$ME: Multiple OPEN '$Treadmill_PR_Title' PRs found!\n";
|
|
|
|
# Yay. Found exactly one.
|
|
return $prs[0]{node}{number};
|
|
}
|
|
|
|
#######################
|
|
# check_merge_bases # It's OK if our branch is newer than treadmill
|
|
#######################
|
|
sub check_merge_bases {
|
|
my $treadmill_pr = shift; # e.g., 12345
|
|
my $treadmill_branch = shift; # e.g., b-v-p/pr12345/tmpNNN
|
|
|
|
# Fetch latest main, for accurate comparison
|
|
git('fetch', '-q', git_upstream(), 'main');
|
|
|
|
my $forkpoint_cur = git_forkpoint();
|
|
my $forkpoint_treadmill = git_forkpoint($treadmill_branch);
|
|
|
|
print "fork cur: $forkpoint_cur\nfork tm: $forkpoint_treadmill\n"
|
|
if $debug;
|
|
if ($forkpoint_cur eq $forkpoint_treadmill) {
|
|
progress("Nice. This branch is up-to-date wrt treadmill PR $treadmill_pr");
|
|
return;
|
|
}
|
|
|
|
# They differ.
|
|
if (git_is_ancestor($forkpoint_cur, $forkpoint_treadmill)) {
|
|
warn <<"END_WARN";
|
|
$ME: treadmill PR $treadmill_pr is based on
|
|
a newer main than this branch. This means it might have
|
|
more up-to-date patches.
|
|
|
|
END_WARN
|
|
|
|
if ($force_old_main) {
|
|
warn "$ME: Proceeding due to --force-old-main\n";
|
|
return;
|
|
}
|
|
|
|
# Cannot continue. Clean up side branch, and bail.
|
|
git('branch', '-D', $treadmill_branch);
|
|
warn "$ME: You might want to consider rebasing on latest main.\n";
|
|
warn "$ME: Aborting. Use --force-old-main to continue without rebasing.\n";
|
|
exit 1;
|
|
}
|
|
else {
|
|
progress("Your branch is based on a newer main than treadmill PR $treadmill_pr. This is usually OK.");
|
|
}
|
|
}
|
|
|
|
#################
|
|
# cherry_pick # cherry-pick a commit, updating its commit message
|
|
#################
|
|
sub cherry_pick {
|
|
my $treadmill_pr = shift; # e.g., 12345
|
|
my $treadmill_branch = shift; # e.g., b-v-p/pr12345/tmpNNN
|
|
|
|
progress("Cherry-picking from $treadmill_pr");
|
|
|
|
# Create a temp script. Do so in /var/tmp because sometimes $TMPDIR
|
|
# (e.g. /tmp) has noexec.
|
|
my ($fh, $editor) = tempfile( "$ME.edit-commit-message.XXXXXXXX", DIR => "/var/tmp" );
|
|
printf { $fh } <<'END_EDIT_SCRIPT', $ME, $VERSION, $treadmill_pr;
|
|
#!/bin/bash
|
|
|
|
if [[ -z "$1" ]]; then
|
|
echo "FATAL: Did not get called with an arg" >&2
|
|
exit 1
|
|
fi
|
|
|
|
msgfile=$1
|
|
if [[ ! -e $msgfile ]]; then
|
|
echo "FATAL: git-commit file does not exist: $msgfile" >&2
|
|
exit 1
|
|
fi
|
|
|
|
tmpfile=$msgfile.tmp
|
|
rm -f $tmpfile
|
|
|
|
cat >$tmpfile <<EOF
|
|
WIP: Fixes for vendoring Buildah
|
|
|
|
This commit was automatically cherry-picked
|
|
by %s v%s
|
|
from the buildah vendor treadmill PR, #%s
|
|
|
|
/vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
|
> The git commit message from that PR is below. Please review it,
|
|
> edit as necessary, then remove this comment block.
|
|
\^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
EOF
|
|
|
|
# Strip the "DO NOT MERGE" header from the treadmill PR, print only
|
|
# the "Changes as of YYYY-MM-DD" and subsequent lines
|
|
sed -ne '/^Changes as of/,$ p' <$msgfile >>$tmpfile
|
|
mv $tmpfile $msgfile
|
|
|
|
END_EDIT_SCRIPT
|
|
close $fh
|
|
or die "$ME: Error writing $editor: $!\n";
|
|
chmod 0755 => $editor;
|
|
local $ENV{EDITOR} = $editor;
|
|
git('cherry-pick', '--allow-empty', '--edit', $treadmill_branch);
|
|
unlink $editor;
|
|
}
|
|
|
|
# END pick and its helpers
|
|
###############################################################################
|
|
# BEGIN reset and its helpers
|
|
|
|
sub do_reset {
|
|
my $current_branch = git_current_branch();
|
|
|
|
# Make sure side branch == main (i.e., there are no commits on the branch)
|
|
if (git('rev-parse', $current_branch) ne git('rev-parse', 'main')) {
|
|
die "$ME: for --reset, $current_branch must == main\n";
|
|
}
|
|
|
|
# Pull main, and pivot back to this branch
|
|
pull_main();
|
|
git('checkout', '-q', $current_branch);
|
|
|
|
git('rebase', '--empty=keep', 'main');
|
|
git_commit_buildah('[none]');
|
|
|
|
my $ymd = strftime("%Y-%m-%d", localtime);
|
|
git('commit', '--allow-empty', '-s', '-m' => <<"END_COMMIT_MESSAGE");
|
|
$Treadmill_PR_Title
|
|
|
|
As you run --sync, please update this commit message with your
|
|
actual changes.
|
|
|
|
Changes since $ymd:
|
|
END_COMMIT_MESSAGE
|
|
|
|
progress("Done. You may now run --sync.\n");
|
|
}
|
|
|
|
# END reset and its helpers
|
|
###############################################################################
|
|
# BEGIN general-purpose helpers
|
|
|
|
##############
|
|
# progress # Progris riport Dr Strauss says I shud rite down what I think
|
|
##############
|
|
sub progress {
|
|
print $C_Highlight, "|\n+---> @_\n", $C_Reset;
|
|
}
|
|
|
|
#######################
|
|
# assert_clean_repo # Don't even think of running with local changes
|
|
#######################
|
|
sub assert_clean_repo {
|
|
# Our patch file should only exist for brief moments during a sync run.
|
|
# If it exists at any other time, something has gone very wrong.
|
|
if (-e $Patch_File) {
|
|
warn <<"END_WARN";
|
|
$ME: File exists: $Patch_File
|
|
|
|
This means that something went very wrong during an earlier sync run.
|
|
Your git branch may be in an inconsistent state. Your work to date
|
|
may be lost. This file may be your only hope of recovering it.
|
|
|
|
This is not something a script can resolve. You need to look at this
|
|
file, compare to your git HEAD, and manually reconcile any differences.
|
|
END_WARN
|
|
exit 1;
|
|
}
|
|
|
|
# OK so far. Now check for modified files.
|
|
if (my @changed = git('status', '--porcelain', '--untracked=no')) {
|
|
warn "$ME: Modified files in repo:\n";
|
|
warn " $_\n" for @changed;
|
|
exit 1;
|
|
}
|
|
|
|
# ...and for untracked files under vendor/
|
|
if (my @v = git('status', '--porcelain', '--untracked=all', 'vendor')) {
|
|
warn "$ME: Untracked vendor files:\n";
|
|
warn " $_\n" for @v;
|
|
exit 1;
|
|
}
|
|
}
|
|
|
|
########################
|
|
# git_current_branch # e.g., 'vendor_buildah'
|
|
########################
|
|
sub git_current_branch() {
|
|
my $b = git('rev-parse', '--abbrev-ref=strict', 'HEAD');
|
|
|
|
# There is no circumstance in which we can ever be called from main
|
|
die "$ME: must run from side branch, not main\n" if $b eq 'main';
|
|
return $b;
|
|
}
|
|
|
|
###################
|
|
# git_forkpoint # Hash at which branch (default: cur) branched from main
|
|
###################
|
|
sub git_forkpoint {
|
|
return git('merge-base', '--fork-point', 'main', @_);
|
|
}
|
|
|
|
#####################
|
|
# git_is_ancestor # Is hash1 an ancestor of hash2?
|
|
#####################
|
|
sub git_is_ancestor {
|
|
# Use system(), not git(), because we don't want to abort on exit status
|
|
my $rc = system('git', 'merge-base', '--is-ancestor', @_);
|
|
die "$ME: Cannot continue\n" if $? > 256; # e.g., Not a valid object
|
|
|
|
# Translate shell 0/256 status to logical 1/0
|
|
return !$rc;
|
|
}
|
|
|
|
##################
|
|
# git_upstream # Name of true github upstream
|
|
##################
|
|
sub git_upstream {
|
|
for my $line (git('remote', '-v')) {
|
|
my ($remote, $url, $type) = split(' ', $line);
|
|
if ($url =~ m!github\.com.*containers/(podman|libpod)!) {
|
|
if ($type =~ /fetch/) {
|
|
return $remote;
|
|
}
|
|
}
|
|
}
|
|
|
|
die "$ME: did not find a remote with 'github.com/containers/podman'\n";
|
|
}
|
|
|
|
########################
|
|
# git_commit_buildah # Do the buildah commit
|
|
########################
|
|
sub git_commit_buildah {
|
|
my $buildah_version = shift;
|
|
|
|
# When called by --reset, this can be empty
|
|
git('commit', '-as', '--allow-empty', '-m', <<"END_COMMIT_MESSAGE");
|
|
DO NOT MERGE: vendor in buildah \@ $buildah_version
|
|
|
|
This is a JUNK COMMIT from $ME v$VERSION.
|
|
|
|
DO NOT MERGE! This is just a way to keep the buildah-podman
|
|
vendoring in sync. Refer to:
|
|
|
|
$Docs_URL
|
|
END_COMMIT_MESSAGE
|
|
}
|
|
|
|
#########
|
|
# git # Run a git command
|
|
#########
|
|
sub git {
|
|
my @cmd = ('git', @_);
|
|
print "\$ @cmd\n" if $verbose || $debug;
|
|
open my $fh, '-|', @cmd
|
|
or die "$ME: Cannot fork: $!\n";
|
|
my @results;
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
push @results, $line;
|
|
}
|
|
close $fh
|
|
or die "$ME: command failed: @cmd\n";
|
|
|
|
return wantarray ? @results : join("\n", @results);
|
|
}
|
|
|
|
##################################
|
|
# assert_buildah_vendor_commit # Fails if input arg is not a buildah vendor
|
|
##################################
|
|
sub assert_buildah_vendor_commit {
|
|
my $ref = shift; # in: probably HEAD or HEAD^
|
|
|
|
my @deltas = git('diff', '--name-only', "$ref^", $ref);
|
|
|
|
# It's OK if there are no deltas, e.g. immediately after a buildah vendor PR
|
|
return if !@deltas;
|
|
|
|
# It's OK if there are more modified files than just these.
|
|
# It's not OK if any of these are missing.
|
|
my @expect = qw(go.mod go.sum vendor/modules.txt);
|
|
my @missing;
|
|
for my $expect (@expect) {
|
|
if (! grep { $_ eq $expect } @deltas) {
|
|
push @missing, "$expect is unchanged";
|
|
}
|
|
}
|
|
|
|
if (! grep { m!^vendor/\Q$Buildah\E/! } @deltas) {
|
|
push @missing, "no changes under $Buildah";
|
|
}
|
|
|
|
return if !@missing;
|
|
|
|
warn "$ME: $ref does not look like a buildah vendor commit:\n";
|
|
warn "$ME: - $_\n" for @missing;
|
|
die "$ME: Cannot continue\n";
|
|
}
|
|
|
|
######################
|
|
# vendored_buildah # Returns currently-vendored buildah
|
|
######################
|
|
sub vendored_buildah {
|
|
my $gomod_file = 'go.mod';
|
|
my @gomod;
|
|
if (@_) {
|
|
# Called with a branch argument; fetch that version of go.mod
|
|
$gomod_file = "@_:$gomod_file";
|
|
@gomod = git('show', $gomod_file);
|
|
}
|
|
else {
|
|
# No branch argument, read file
|
|
open my $fh, '<', $gomod_file
|
|
or die "$ME: Cannot read $gomod_file: $!\n";
|
|
while (my $line = <$fh>) {
|
|
chomp $line;
|
|
push @gomod, $line;
|
|
}
|
|
close $fh;
|
|
}
|
|
|
|
for my $line (@gomod) {
|
|
if ($line =~ m!^\s+\Q$Buildah\E\s+(\S+)!) {
|
|
return $1;
|
|
}
|
|
}
|
|
|
|
die "$ME: Could not find buildah in $gomod_file!\n";
|
|
}
|
|
|
|
# END general-purpose helpers
|
|
###############################################################################
|