303 lines
6.7 KiB
Perl
303 lines
6.7 KiB
Perl
#! /usr/bin/env perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Copy;
|
|
use File::Path;
|
|
use Cwd qw/abs_path/;
|
|
use Term::ANSIColor;
|
|
use Getopt::Long;
|
|
|
|
use lib "$ENV{DGL_CONF_HOME}/lib";
|
|
use DGLMan;
|
|
|
|
my %OPT;
|
|
GetOptions(\%OPT, "match=s", "skip=s", "confirm", "target=s")
|
|
or die "Bad command line: @ARGV\n";
|
|
|
|
my $CHROOT = $ENV{DGL_CHROOT};
|
|
die "DGL chroot not specified in environment\n" unless $CHROOT;
|
|
|
|
my @COPY_TARGETS = ([ 'dgamelaunch.conf', '//etc' ],
|
|
[ 'utils/auth-save-downloader.pl', '//usr/lib/cgi-bin' ],
|
|
[ 'utils/trigger-rebuild.pl', '//usr/lib/cgi-bin' ],
|
|
[ 'utils/webtiles', '//etc/init.d' ],
|
|
[ 'config.py', "/crawl-master/webserver" ],
|
|
[ 'chroot/data/menus/*.txt', "/dgldir/data/menus" ],
|
|
[ 'chroot/data/*.{rc,macro}', "/dgldir/data" ],
|
|
[ 'chroot/bin/*.sh', '/bin' ],
|
|
[ 'chroot/sbin/*.sh', '/sbin' ]);
|
|
|
|
if ($OPT{match}) {
|
|
@COPY_TARGETS = grep($$_[0] =~ /\Q$OPT{match}/, @COPY_TARGETS);
|
|
}
|
|
if ($OPT{skip}) {
|
|
@COPY_TARGETS = grep($$_[0] !~ /\Q$OPT{skip}/, @COPY_TARGETS);
|
|
}
|
|
if ($OPT{target}) {
|
|
die "--target specified, but more than one file to publish\n"
|
|
if @COPY_TARGETS > 1;
|
|
$COPY_TARGETS[0][1] = $OPT{target};
|
|
}
|
|
|
|
sub change_stat($@) {
|
|
my ($dst, @src) = @_;
|
|
|
|
my $changed = 0;
|
|
my @changed;
|
|
|
|
my $new = 0;
|
|
my @new;
|
|
|
|
for my $file (@src) {
|
|
my $target = target_file($dst, $file);
|
|
if (-l $target) {
|
|
return "ERR: $target is a symlink";
|
|
}
|
|
|
|
my $src_text = source_file_text($file);
|
|
|
|
if (-f $target) {
|
|
my $dst_text = file_text($target);
|
|
if ($src_text ne $dst_text) {
|
|
++$changed;
|
|
push @changed, basename($target);
|
|
}
|
|
} else {
|
|
++$new;
|
|
push @new, basename($file);
|
|
}
|
|
}
|
|
|
|
return { new => \@new,
|
|
changed => \@changed };
|
|
}
|
|
|
|
sub change_exists($) {
|
|
my $stat = shift;
|
|
return @{$$stat{new}} || @{$$stat{changed}};
|
|
}
|
|
|
|
sub change_summary($) {
|
|
my $stat = shift;
|
|
my @report;
|
|
my @new = @{$$stat{new}};
|
|
my @changed = @{$$stat{changed}};
|
|
my $new = scalar(@new);
|
|
my $changed = scalar(@changed);
|
|
push @report, neutral_text("$new new: @new") if @new;
|
|
push @report, neutral_text("$changed changed: @changed") if @changed;
|
|
@report? join(', ', @report) : 'no change'
|
|
}
|
|
|
|
sub publishee_summary($) {
|
|
my ($src, $dst) = @{$_[0]};
|
|
|
|
my @files = glob($src);
|
|
$dst = qualify_directory($dst);
|
|
|
|
my $change_stat = change_stat($dst, @files);
|
|
if (@files == 1 && $files[0] eq $src) {
|
|
("$src -> $dst (" . change_summary($change_stat) . ")",
|
|
$change_stat)
|
|
} else {
|
|
("$src (" . scalar(@files) . " files) -> $dst (" .
|
|
change_summary($change_stat) . ")",
|
|
$change_stat)
|
|
}
|
|
}
|
|
|
|
sub summarize_publishees() {
|
|
my $index = 0;
|
|
print "These are the publish copy targets:\n";
|
|
my $dirty;
|
|
for my $copy_target (@COPY_TARGETS) {
|
|
++$index;
|
|
my ($summary, $stat) = publishee_summary($copy_target);
|
|
print("$index) $summary\n");
|
|
$dirty = 1 if change_exists($stat);
|
|
}
|
|
$dirty
|
|
}
|
|
|
|
my $copy_count = 0;
|
|
sub nth_copy() {
|
|
++$copy_count . ") ";
|
|
}
|
|
|
|
sub qualify_directory($) {
|
|
my $dir = shift;
|
|
if ($dir =~ m{^//}) {
|
|
$dir =~ s{^/}{};
|
|
$dir
|
|
} else {
|
|
"$CHROOT$dir"
|
|
}
|
|
}
|
|
|
|
sub substitute_variable($$) {
|
|
my ($file, $var) = @_;
|
|
die "$file refers to unknown variable $var.\n" unless $ENV{$var};
|
|
$ENV{$var}
|
|
}
|
|
|
|
sub file_needs_banner($) {
|
|
my $filename = shift;
|
|
$filename =~ /(?:dgamelaunch\.conf|\.sh|\.pl)$/
|
|
}
|
|
|
|
sub file_banner($) {
|
|
my $filename = shift;
|
|
my $basename = basename($filename);
|
|
|
|
my $source_file = abs_path($filename);
|
|
|
|
return <<BANNER;
|
|
#############################################################################
|
|
#
|
|
# WARNING! AUTO-GENERATED FILE, DO NOT EDIT.
|
|
#
|
|
# This file ($basename) is automatically generated from $source_file.
|
|
#
|
|
# Do NOT edit this file; edit $source_file instead, and
|
|
# use `dgl publish` to publish your changes.
|
|
#
|
|
#############################################################################
|
|
BANNER
|
|
}
|
|
|
|
sub file_add_banner($$) {
|
|
my ($filename, $text) = @_;
|
|
if ($text =~ /^#!.*\n/) {
|
|
$text =~ s/(^#!.*\n)/$1 . file_banner($filename)/e;
|
|
} else {
|
|
$text =~ s/^/file_banner($filename)/e;
|
|
}
|
|
$text
|
|
}
|
|
|
|
sub substitute_variables($$) {
|
|
my ($filename, $text) = @_;
|
|
|
|
$text = file_add_banner($filename, $text) if file_needs_banner($filename);
|
|
$text =~ s/%%(\w+)%%/ substitute_variable($filename, $1) /ge;
|
|
$text
|
|
}
|
|
|
|
sub basename($) {
|
|
my $file = shift;
|
|
my ($basename) = $file =~ m{.*/(.*)};
|
|
$basename || $file
|
|
}
|
|
|
|
sub target_file($$) {
|
|
my ($dst, $src) = @_;
|
|
my $basename = basename($src);
|
|
"$dst/$basename"
|
|
}
|
|
|
|
sub file_text($) {
|
|
my $source_file = shift;
|
|
|
|
open my $inf, '<', $source_file or die "Can't read $source_file: $!\n";
|
|
binmode $inf;
|
|
my $text = do { local $/; <$inf> };
|
|
close $inf;
|
|
|
|
$text
|
|
}
|
|
|
|
sub source_file_text($) {
|
|
my $source_file = shift;
|
|
substitute_variables($source_file, file_text($source_file))
|
|
}
|
|
|
|
sub copy_file($$) {
|
|
my ($source_file, $dst) = @_;
|
|
my $target_file = target_file($dst, $source_file);
|
|
|
|
if (-l $target_file) {
|
|
die "Can't copy $source_file -> $target_file: destination is a symlink\n";
|
|
}
|
|
|
|
my $text = source_file_text($source_file);
|
|
|
|
my $target_tmp = "$target_file.tmp";
|
|
open my $outf, '>', $target_tmp or die "Can't write $target_tmp: $!\n";
|
|
print $outf $text;
|
|
close $outf;
|
|
|
|
if (-x $source_file) {
|
|
chmod 0755, $target_tmp or
|
|
die "Couldn't +x $target_tmp when copying $source_file -> $dst" ;
|
|
}
|
|
|
|
rename $target_tmp, $target_file or
|
|
die "Couldn't copy $source_file -> $target_file\n";
|
|
}
|
|
|
|
sub copy_files($$) {
|
|
my ($src, $dst) = @_;
|
|
my @files = glob($src);
|
|
if (!@files) {
|
|
die "Can't find files to copy for $src\n";
|
|
}
|
|
$dst = qualify_directory($dst);
|
|
if (!-d($dst)) {
|
|
mkpath($dst) or die "Couldn't create $dst\n";
|
|
}
|
|
|
|
my @quoted_files = map("\Q$_", @files);
|
|
print(nth_copy() . "@files -> $dst ");
|
|
|
|
eval {
|
|
for my $file (@files) {
|
|
copy_file($file, $dst) or
|
|
die "Failed to copy $file -> $dst: $!\n";
|
|
}
|
|
say_good(" [OK]\n");
|
|
};
|
|
if ($@) {
|
|
say_bad(" [ERR]\n\n");
|
|
die $@;
|
|
}
|
|
}
|
|
|
|
sub copy_targets(@) {
|
|
print "Copying files:\n";
|
|
for my $copy_target (@_) {
|
|
copy_files($copy_target->[0], $copy_target->[1]);
|
|
}
|
|
}
|
|
|
|
print "Publishing DGL config files:\n\n";
|
|
my $dirty = summarize_publishees();
|
|
print "\n";
|
|
|
|
my $want_publish = $OPT{confirm};
|
|
if (!$want_publish) {
|
|
if ($dirty) {
|
|
warn <<PUBLISH_HOWTO;
|
|
To publish the new dgl config, run this command as root:
|
|
|
|
@{ [ emphasized_text('dgl publish --confirm') ] }
|
|
|
|
PUBLISH_HOWTO
|
|
}
|
|
else {
|
|
say_coloured('bold green', <<INSYNC);
|
|
DGL config is in sync.
|
|
INSYNC
|
|
}
|
|
exit 0;
|
|
}
|
|
|
|
if ($< != 0) {
|
|
print STDERR "** This script must be run as root to publish changes **\n";
|
|
exit 1;
|
|
}
|
|
|
|
copy_targets(@COPY_TARGETS);
|
|
print "\nAll done.\n";
|