From c76fda35ce5a5496b3f3688b871ee8fc6afc4987 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Mon, 17 Aug 2026 21:43:28 +0200 Subject: [PATCH] Add support for applying multiple patches with patch command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⓘ Allow passing several patch URLs or files at once, e.g. webmin patch url1 url2 url3. Patches are applied in the given order, stopping at the first failure, and Webmin is reloaded only once after all of them are applied. --- bin/patch | 82 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 19 deletions(-) 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