From e41037388c7440c04a557420d15b4e49ef3a9535 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Fri, 28 Jun 2024 01:35:10 +0300 Subject: [PATCH 1/8] Apply a patch to Webmin core or its modules from GitHub or a local file --- bin/patch | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100755 bin/patch diff --git a/bin/patch b/bin/patch new file mode 100755 index 000000000..71a8f1ff2 --- /dev/null +++ b/bin/patch @@ -0,0 +1,142 @@ +#!/usr/bin/env perl +# patch - Apply a patch to Webmin core or its modules from GitHub or a local file + +use strict; +use warnings; + +use 5.010; + +use Getopt::Long qw(:config permute pass_through); +use Pod::Usage; +use Term::ANSIColor qw(:constants); +use File::Basename; +use Cwd qw(cwd); + +my %opt; +GetOptions('help|h' => \$opt{'help'}); +pod2usage(0) if ($opt{'help'}); + +# Get Webmin path +my $path = cwd; +my $lib = "web-lib-funcs.pl"; +if (!-r "$path/$lib") { + $path = dirname(dirname($0)); + if (!-r "$path/$lib") { + $path = $path = Cwd::realpath('..'); + } +} + +# Get module and patch +my $module = $ARGV[2]; +my $patch = $ARGV[3]; + +# Params check +if (!$module || !$patch) { + pod2usage(0); + exit 1; +} + +# Special modules handling +if ($module =~ /^virtualmin-(gpl|pro)$/) { + if ($module =~ /^virtualmin-pro/) { + $module = 'virtual-server/pro'; + } + else { + $module = 'virtual-server'; + } + } +if (!-d "$path/$module") { + print RED, "Module $module doesn't exist\n", RESET; + exit 1; +} + +# Patch check +if ($patch !~ /^https?:\/\//) { + if (!-r $patch) { + print RED, "Patch file $patch doesn't exist\n", RESET; + exit 1; + } + } +elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ && + $patch !~ /\.patch$/ && $patch !~ /\.diff$/) { + $patch .= '.patch'; + } + +# Check if curl is installed +if (!`which curl`) { + print RED, "curl is not installed\n", RESET; + exit 1; + } + +# Check if git is installed +if (!`which git`) { + print RED, "git is not installed\n", RESET; + exit 1; + } + +# Download command or cat patch file +my $cmd; +if ($patch =~ /^https?:\/\//) { + $cmd = "curl -s $patch"; + } +else { + $cmd = "cat $patch"; + } + +# Apply patch using Git +chdir "$path/$module"; +my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`; +if ($output !~ /applied patch.*?cleanly/i) { + print YELLOW, "Patch failed: $output\n", RESET; + exit 1; + } +print "Patch applied successfully to:\n"; +print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg; + +=pod + +=head1 NAME + +patch + +=head1 DESCRIPTION + +Apply a patch to Webmin core or its modules from GitHub or a local file. + +=head1 SYNOPSIS + +patch module-name patch-url/file + +=head1 OPTIONS + +=over + +=item --help, -h + +Give this help list. + +Examples of usage: + + Apply a patch to the Package Updates module from GitHub using the commit ID. + + - webmin patch package-updates https://github.com/webmin/webmin/commit/fbee8 + + Apply a patch to Webmin root files (e.g., `web-lib-funcs.pl`) from + GitHub using the commit ID. + + - webmin patch . https://github.com/webmin/webmin/commit/e6a2bb15b0.patch + + Apply a patch to Virtualmin GPL module from GitHub using commit ID. + + - webmin patch virtualmin-gpl \ + https://github.com/virtualmin/virtualmin-gpl/commit/f4433153dbbb017932b9 + + Apply a patch to Virtualmin Pro module from local file. + + - webmin patch virtualmin-pro /root/virtualmin-pro/patches/patch-1.patch + +=back + +=head1 LICENSE AND COPYRIGHT + + Copyright 2024 Ilia Ross From a6832450d106c1733fa9364b4d3e36b0cfb0f2bd Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Fri, 28 Jun 2024 01:38:28 +0300 Subject: [PATCH 2/8] Fix to restart Webmin when done --- bin/patch | 1 + 1 file changed, 1 insertion(+) diff --git a/bin/patch b/bin/patch index 71a8f1ff2..69bef33c3 100755 --- a/bin/patch +++ b/bin/patch @@ -92,6 +92,7 @@ if ($output !~ /applied patch.*?cleanly/i) { } print "Patch applied successfully to:\n"; print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg; +system('/etc/webmin/restart'); =pod From 3d9497ff458516a74cd11ac306bd7ba75397f873 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Fri, 28 Jun 2024 01:51:02 +0300 Subject: [PATCH 3/8] Fix command example --- bin/patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/patch b/bin/patch index 69bef33c3..72ae62b68 100755 --- a/bin/patch +++ b/bin/patch @@ -106,7 +106,7 @@ Apply a patch to Webmin core or its modules from GitHub or a local file. =head1 SYNOPSIS -patch module-name patch-url/file +webmin patch module-name patch-url/file =head1 OPTIONS From 01d08a36055b9e03baebd76ff0e867814d108203 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Fri, 28 Jun 2024 19:48:51 +0300 Subject: [PATCH 4/8] Fix to make patch API work directly from URL --- bin/patch | 82 ++++++++++++++++++++++++++----------------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/bin/patch b/bin/patch index 72ae62b68..a86617d81 100755 --- a/bin/patch +++ b/bin/patch @@ -26,30 +26,27 @@ if (!-r "$path/$lib") { } } -# Get module and patch -my $module = $ARGV[2]; -my $patch = $ARGV[3]; +# Check if curl is installed +if (!`which curl`) { + print RED, "curl is not installed\n", RESET; + exit 1; + } + +# Check if git is installed +if (!`which git`) { + print RED, "git is not installed\n", RESET; + exit 1; + } + +# Get patch URL or file +my $patch = $ARGV[2]; # Params check -if (!$module || !$patch) { +if (!$patch) { pod2usage(0); exit 1; } -# Special modules handling -if ($module =~ /^virtualmin-(gpl|pro)$/) { - if ($module =~ /^virtualmin-pro/) { - $module = 'virtual-server/pro'; - } - else { - $module = 'virtual-server'; - } - } -if (!-d "$path/$module") { - print RED, "Module $module doesn't exist\n", RESET; - exit 1; -} - # Patch check if ($patch !~ /^https?:\/\//) { if (!-r $patch) { @@ -62,29 +59,35 @@ elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ && $patch .= '.patch'; } -# Check if curl is installed -if (!`which curl`) { - print RED, "curl is not installed\n", RESET; - exit 1; +# Parse module name from URL +my $module = ""; +if ($patch =~ m{https://(github|gitlab)\.com/[^/]+/([^/]+)/commit/[^/]+}) { + $module = $2; + $module = "" if ($2 eq 'webmin'); + # Special handling for some modules + $module = $module =~ /^virtualmin-pro$/ ? + 'virtual-server/pro' : + 'virtual-server' + if $module =~ /^virtualmin-(gpl|pro)$/; } -# Check if git is installed -if (!`which git`) { - print RED, "git is not installed\n", RESET; - exit 1; - } +# Check if module exists +if (!-d "$path/$module") { + print RED, "Module $module doesn't exist\n", RESET; + exit 1; +} # Download command or cat patch file my $cmd; if ($patch =~ /^https?:\/\//) { $cmd = "curl -s $patch"; + chdir "$path/$module"; } else { $cmd = "cat $patch"; } # Apply patch using Git -chdir "$path/$module"; my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`; if ($output !~ /applied patch.*?cleanly/i) { print YELLOW, "Patch failed: $output\n", RESET; @@ -106,7 +109,7 @@ Apply a patch to Webmin core or its modules from GitHub or a local file. =head1 SYNOPSIS -webmin patch module-name patch-url/file +webmin patch patch-url/file =head1 OPTIONS @@ -118,23 +121,16 @@ Give this help list. Examples of usage: - Apply a patch to the Package Updates module from GitHub using the commit ID. - - - webmin patch package-updates https://github.com/webmin/webmin/commit/fbee8 - - Apply a patch to Webmin root files (e.g., `web-lib-funcs.pl`) from - GitHub using the commit ID. - - - webmin patch . https://github.com/webmin/webmin/commit/e6a2bb15b0.patch + Apply a patch from a URL. - Apply a patch to Virtualmin GPL module from GitHub using commit ID. + - webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0.patch + + - webmin patch https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d - - webmin patch virtualmin-gpl \ - https://github.com/virtualmin/virtualmin-gpl/commit/f4433153dbbb017932b9 + Apply a patch from local file. - Apply a patch to Virtualmin Pro module from local file. - - - webmin patch virtualmin-pro /root/virtualmin-pro/patches/patch-1.patch + - cd /usr/libexec/webmin/virtual-server/pro && + webmin patch /root/virtualmin-pro/patches/patch-1.patch =back From d89f6411b62ae55e10e7bb48c87be64b1241aa78 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sat, 29 Jun 2024 20:34:29 +0300 Subject: [PATCH 5/8] Fix to use has_command API --- bin/patch | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/bin/patch b/bin/patch index a86617d81..10f61a3fe 100755 --- a/bin/patch +++ b/bin/patch @@ -13,7 +13,10 @@ use File::Basename; use Cwd qw(cwd); my %opt; -GetOptions('help|h' => \$opt{'help'}); +GetOptions( + 'help|h' => \$opt{'help'}, + 'config|c=s' => \$opt{'config'}, + ); pod2usage(0) if ($opt{'help'}); # Get Webmin path @@ -24,35 +27,41 @@ if (!-r "$path/$lib") { if (!-r "$path/$lib") { $path = $path = Cwd::realpath('..'); } -} + } + +# Init core +$ENV{'WEBMIN_CONFIG'} = $opt{'config'} || "/etc/webmin"; +push(@INC, $path); +eval 'use WebminCore'; +init_config(); # Check if curl is installed -if (!`which curl`) { +if (!has_command('curl')) { print RED, "curl is not installed\n", RESET; exit 1; } # Check if git is installed -if (!`which git`) { +if (!has_command('git')) { print RED, "git is not installed\n", RESET; exit 1; } # Get patch URL or file -my $patch = $ARGV[2]; +my $patch = $ARGV[0]; # Params check if (!$patch) { - pod2usage(0); - exit 1; -} + pod2usage(0); + exit 1; + } # Patch check if ($patch !~ /^https?:\/\//) { if (!-r $patch) { print RED, "Patch file $patch doesn't exist\n", RESET; exit 1; - } + } } elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ && $patch !~ /\.patch$/ && $patch !~ /\.diff$/) { @@ -119,6 +128,11 @@ webmin patch patch-url/file Give this help list. +=item --config, -c + +Specify the full path to the Webmin configuration directory. Defaults to +C + Examples of usage: Apply a patch from a URL. From 94b7fdf0ec54045fc39f45c8314194f8a5553996 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sat, 29 Jun 2024 23:29:55 +0300 Subject: [PATCH 6/8] Fix to escape params --- bin/patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/patch b/bin/patch index 10f61a3fe..ccf932df5 100755 --- a/bin/patch +++ b/bin/patch @@ -89,11 +89,11 @@ if (!-d "$path/$module") { # Download command or cat patch file my $cmd; if ($patch =~ /^https?:\/\//) { - $cmd = "curl -s $patch"; + $cmd = "curl -s @{[quotemeta($patch)]}"; chdir "$path/$module"; } else { - $cmd = "cat $patch"; + $cmd = "cat @{[quotemeta($patch)]}"; } # Apply patch using Git From 248cb719c020c7171c6b6deed2e52d2a82771f3c Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sat, 29 Jun 2024 23:32:15 +0300 Subject: [PATCH 7/8] Fix restart command depend on config dir --- bin/patch | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bin/patch b/bin/patch index ccf932df5..ebafa5409 100755 --- a/bin/patch +++ b/bin/patch @@ -30,7 +30,8 @@ if (!-r "$path/$lib") { } # Init core -$ENV{'WEBMIN_CONFIG'} = $opt{'config'} || "/etc/webmin"; +my $config_dir = $opt{'config'} || '/etc/webmin'; +$ENV{'WEBMIN_CONFIG'} = $config_dir; push(@INC, $path); eval 'use WebminCore'; init_config(); @@ -104,7 +105,7 @@ if ($output !~ /applied patch.*?cleanly/i) { } print "Patch applied successfully to:\n"; print " $1\n" while $output =~ /^Applied patch\s+(\S+)/mg; -system('/etc/webmin/restart'); +system("$config_dir/restart"); =pod From 7507433bf1c333a2fafe0f84639cb3e0dededa12 Mon Sep 17 00:00:00 2001 From: Ilia Ross Date: Sun, 30 Jun 2024 11:41:41 +0300 Subject: [PATCH 8/8] Fix to drop dependency from Term::ANSIColor --- bin/patch | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/bin/patch b/bin/patch index ebafa5409..2173ecec5 100755 --- a/bin/patch +++ b/bin/patch @@ -8,7 +8,6 @@ use 5.010; use Getopt::Long qw(:config permute pass_through); use Pod::Usage; -use Term::ANSIColor qw(:constants); use File::Basename; use Cwd qw(cwd); @@ -38,13 +37,13 @@ init_config(); # Check if curl is installed if (!has_command('curl')) { - print RED, "curl is not installed\n", RESET; + print "curl is not installed\n"; exit 1; } # Check if git is installed if (!has_command('git')) { - print RED, "git is not installed\n", RESET; + print "git is not installed\n"; exit 1; } @@ -60,7 +59,7 @@ if (!$patch) { # Patch check if ($patch !~ /^https?:\/\//) { if (!-r $patch) { - print RED, "Patch file $patch doesn't exist\n", RESET; + print "Patch file $patch doesn't exist\n"; exit 1; } } @@ -83,7 +82,7 @@ if ($patch =~ m{https://(github|gitlab)\.com/[^/]+/([^/]+)/commit/[^/]+}) { # Check if module exists if (!-d "$path/$module") { - print RED, "Module $module doesn't exist\n", RESET; + print "Module $module doesn't exist\n"; exit 1; } @@ -100,7 +99,7 @@ else { # Apply patch using Git my $output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`; if ($output !~ /applied patch.*?cleanly/i) { - print YELLOW, "Patch failed: $output\n", RESET; + print "Patch failed: $output\n"; exit 1; } print "Patch applied successfully to:\n";