mirror of
https://github.com/webmin/webmin.git
synced 2025-07-23 00:30:33 +00:00

https://forum.virtualmin.com/t/re-moving-sites-from-directadmin-to-virtualmin/128503/33?u=ilia
237 lines
5.1 KiB
Perl
Executable File
237 lines
5.1 KiB
Perl
Executable File
#!/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 File::Basename;
|
|
use Cwd qw(cwd);
|
|
|
|
my %opt;
|
|
GetOptions(
|
|
'help|h' => \$opt{'help'},
|
|
'config|c=s' => \$opt{'config'},
|
|
);
|
|
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('..');
|
|
}
|
|
}
|
|
|
|
# Init core
|
|
my $config_dir = $opt{'config'} || '/etc/webmin';
|
|
$ENV{'WEBMIN_CONFIG'} = $config_dir;
|
|
push(@INC, $path);
|
|
eval 'use WebminCore';
|
|
init_config();
|
|
|
|
# Check if curl is installed
|
|
if (!has_command('curl')) {
|
|
print "\"curl\" command is not installed\n";
|
|
exit 1;
|
|
}
|
|
|
|
# Check if git is installed
|
|
if (!has_command('patch')) {
|
|
if (!has_command('git')) {
|
|
print "Neither \"patch\" nor \"git\" commands are installed\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
|
|
# Get patch URL or file
|
|
my $patch = $ARGV[0];
|
|
|
|
# Params check
|
|
if (!$patch) {
|
|
pod2usage(0);
|
|
exit 1;
|
|
}
|
|
|
|
# Patch check
|
|
if ($patch !~ /^https?:\/\//) {
|
|
if (!-r $patch) {
|
|
print "Patch file $patch doesn't exist\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
elsif ($patch =~ /^https?:\/\/(github|gitlab)\.com/ &&
|
|
$patch !~ /\.patch$/ && $patch !~ /\.diff$/) {
|
|
$patch .= '.patch';
|
|
}
|
|
|
|
# Parse module name from URL
|
|
my $module = "";
|
|
if ($patch =~ m{
|
|
(?|
|
|
# GitHub/GitLab commit URL
|
|
https://(?:github|gitlab)\.com/([^/]+)/([^/]+)/commit/([^/]+)
|
|
|
|
|
# GitHub pull request commit URL
|
|
https://github\.com/([^/]+)/([^/]+)/pull/\d+/commits/([^/]+)
|
|
|
|
|
# GitLab merge request URL with commit ID
|
|
https://gitlab\.com/([^/]+)/([^/]+)/-/merge_requests/\d+/diffs\?commit_id=([^&]+)
|
|
|
|
|
# GitHub raw URL
|
|
https://raw\.githubusercontent\.com/([^/]+)/([^/]+)/([^/]+)/(.+)
|
|
|
|
|
# GitLab raw URL
|
|
https://gitlab\.com/([^/]+)/([^/]+)/-/raw/([^/]+)/(.+)
|
|
)
|
|
}x) {
|
|
$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 module exists
|
|
if (!-d "$path/$module") {
|
|
print "Module '$module' doesn't exist\n";
|
|
exit 1;
|
|
}
|
|
|
|
# Prepare patch command
|
|
my $cmd;
|
|
my $direct;
|
|
my $filename;
|
|
my $dir;
|
|
my $output;
|
|
|
|
# If raw URL given, try to download and just replace the whole file
|
|
if ($patch =~ m{^https?://raw\.githubusercontent\.com/} ||
|
|
$patch =~ m{^https?://gitlab\.com/.*?/-/raw/}) {
|
|
if ($patch =~ m{
|
|
.*? # Non-greedy match of everything up to the branch name
|
|
(?:master|main)/ # Branches name
|
|
(.*/)? # Capture all directories after the branch (group 1)
|
|
([^/]+)$ # Filename (group 2)
|
|
}x)
|
|
{
|
|
$direct = 1;
|
|
$dir = $1 // "";
|
|
$filename = $2;
|
|
}
|
|
else {
|
|
print "Patch failed: Can't parse file name from URL\n";
|
|
exit 1;
|
|
}
|
|
my $cd = "$path/$module/$dir";
|
|
$cd =~ s|/+|/|g;
|
|
chdir "$cd";
|
|
$cmd = "curl -w \"%{http_code}\\n\" -s -o $filename @{[quotemeta($patch)]}";
|
|
}
|
|
# Download command
|
|
elsif ($patch =~ /^https?:\/\//) {
|
|
$cmd = "curl -L -s @{[quotemeta($patch)]}";
|
|
chdir "$path/$module";
|
|
}
|
|
# Local file
|
|
else {
|
|
$cmd = "cat @{[quotemeta($patch)]}";
|
|
}
|
|
|
|
# Download file directly
|
|
if ($direct) {
|
|
$output = `$cmd 2>&1`;
|
|
if ($output != 200) {
|
|
print "Patch failed: Cannot download '$filename'. HTTP status code: $output\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
# Apply patch using patch command
|
|
elsif (has_command('patch')) {
|
|
$output = `$cmd 2>&1 | patch -p1 --verbose 2>&1`;
|
|
if ($output !~ /succeeded/i) {
|
|
print "Patch failed: $output\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
# Apply patch using git command
|
|
else {
|
|
$output = `$cmd 2>&1 | git apply --reject --verbose --whitespace=fix 2>&1`;
|
|
if ($output !~ /applied patch.*?cleanly/i) {
|
|
print "Patch failed: $output\n";
|
|
exit 1;
|
|
}
|
|
}
|
|
|
|
# Print results
|
|
if ($direct) {
|
|
print "File replaced successfully:\n";
|
|
if ($dir) {
|
|
$dir = $dir ? "/$dir" : "";
|
|
$dir =~ s|^/||;
|
|
}
|
|
print " $dir$filename\n";
|
|
system("sed -i '1s|^#!/usr/local/bin/perl|#!/usr/bin/perl|' \"$filename\"");
|
|
}
|
|
else {
|
|
print "Patch applied successfully to:\n";
|
|
print " $1\n" while $output =~ /^(?|Applied patch\s+(\S+)|patching file\s+(\S+))/mg;
|
|
}
|
|
|
|
# Restart Webmin
|
|
system("$config_dir/restart");
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
patch
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
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.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
webmin patch patch-url/file
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over
|
|
|
|
=item --help, -h
|
|
|
|
Give this help list.
|
|
|
|
=item --config, -c
|
|
|
|
Specify the full path to the Webmin configuration directory. Defaults to
|
|
C</etc/webmin>
|
|
|
|
Examples of usage:
|
|
|
|
Apply a patch from a URL.
|
|
|
|
- webmin patch https://github.com/webmin/webmin/commit/e6a2bb15b0.patch
|
|
|
|
- webmin patch https://github.com/virtualmin/virtualmin-gpl/commit/f4433153d
|
|
|
|
Apply a patch from local file.
|
|
|
|
- cd /usr/libexec/webmin/virtual-server/pro &&
|
|
webmin patch /root/virtualmin-pro/patches/patch-1.patch
|
|
|
|
=back
|
|
|
|
=head1 LICENSE AND COPYRIGHT
|
|
|
|
Copyright 2024 Ilia Ross <ilia@virtualmin.com>
|