diff --git a/bin/patch b/bin/patch index 067901b90..5223e320b 100755 --- a/bin/patch +++ b/bin/patch @@ -1,5 +1,6 @@ #!/usr/bin/env perl -# patch - Apply a patch to Webmin core or its modules from GitHub or a local file +# patch - Apply one or more patches to Webmin core or its modules from +# GitHub or a local file use strict; use warnings; @@ -49,20 +50,55 @@ if (!has_command('patch')) { } } -# Get patch URL or file -my $patch = $ARGV[0]; +# Get patch URLs or files +my @patches = @ARGV; # Params check -if (!$patch) { +if (!@patches) { pod2usage(0); exit 1; } +# Apply all patches in the given order, stopping at the first failure +my $orig_cwd = cwd(); +my $applied = 0; +my $failed = 0; +foreach my $patch (@patches) { + # Show which patch is being applied if there are multiple + print "Applying $patch ..\n" if (@patches > 1); + # Always start from the original directory, as applying a patch + # may change it + chdir($orig_cwd); + if (!apply_patch($patch)) { + $failed = 1; + last; + } + $applied++; + } + +# Reload Webmin once, if any patches were applied +reload_miniserv() if ($applied); + +# Report if some patches were not applied +if ($failed) { + print "Stopped: $applied of ".scalar(@patches)." patches applied\n" + if (@patches > 1); + exit 1; + } +exit 0; + +# apply_patch(patch) +# Apply a single patch given as URL or local file, printing the outcome +# and returning 1 on success or 0 on failure +sub apply_patch +{ +my ($patch) = @_; + # Patch check if ($patch !~ /^https?:\/\//) { if (!-r $patch) { print "Patch file $patch doesn't exist\n"; - exit 1; + return 0; } } elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ && @@ -101,9 +137,9 @@ if ($patch =~ m{ # Check if module exists if (!-d "$path/$module") { - print "Module '$module' doesn't exist\n"; - exit 1; -} + print "Module '$module' doesn't exist\n"; + return 0; + } # Prepare patch command my $cmd; @@ -128,7 +164,7 @@ if ($patch =~ m{^https?://raw\.githubusercontent\.com/} || } else { print "Patch failed: Can't parse file name from URL\n"; - exit 1; + return 0; } my $cd = "$path/$module/$dir"; $cd =~ s|/+|/|g; @@ -149,9 +185,9 @@ else { if ($direct) { $output = `$cmd 2>&1`; if ($output != 200) { - print "Patch failed: Cannot download '$filename'. HTTP status code: $output\n"; - exit 1; - } + print "Patch failed: Cannot download '$filename'. HTTP status code: $output\n"; + return 0; + } } # Apply patch using patch command elsif (has_command('patch')) { @@ -159,7 +195,7 @@ elsif (has_command('patch')) { $output = `$cmd 2>&1 | patch -p1 --verbose --no-backup-if-mismatch 2>&1`; if ($output !~ /succeeded/i) { print "Patch failed: $output\n"; - exit 1; + return 0; } } # Apply patch using git command @@ -174,9 +210,9 @@ else { $output = `$cmd 2>&1 | git apply --reject --verbose -C1 --whitespace=fix 2>&1`; if ($output !~ /applied patch.*?cleanly/i) { print "Patch failed: $output\n"; - exit 1; + return 0; } -} + } # Print results if ($direct) { @@ -191,11 +227,10 @@ if ($direct) { else { print "Patch applied successfully to:\n"; print " $1\n" while $output =~ /^(?|Applied patch\s+(\S+)|patching file\s+(\S+))/mg; + } +return 1; } -# Reload Webmin -reload_miniserv(); - =pod =head1 NAME @@ -207,9 +242,13 @@ patch Apply a patch to Webmin core or its modules from GitHub/GitLab, a local file, or by downloading and replacing the entire file from a raw URL. +Multiple patches can be given at once and are applied in the given order, +stopping at the first failure. Webmin is reloaded only once after all +patches are applied. + =head1 SYNOPSIS -webmin patch patch-url/file +webmin patch patch-url/file [patch-url/file ...] =head1 OPTIONS @@ -237,6 +276,11 @@ Examples of usage: - cd /usr/libexec/webmin/virtual-server/pro && webmin patch /root/virtualmin-pro/patches/patch-1.patch + Apply multiple patches at once. + + - webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0 \ + https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d + =back =head1 LICENSE AND COPYRIGHT