tindent - gitzone - git-based zone management tool for static and dynamic domains HTML git clone https://git.parazyd.org/gitzone DIR Log DIR Files DIR Refs --- DIR commit 24a356f140401c265ef5b65a3edf3b617e56fad8 DIR parent 975d717bb8204ccbdbb2243df471849b9c6e9b6e HTML Author: tg(x) <*@tg-x.net> Date: Sat, 1 Jun 2013 07:13:15 +0200 indent Diffstat: M bin/gitzone | 496 ++++++++++++++++---------------- M bin/gitzone-shell | 42 ++++++++++++++++---------------- 2 files changed, 269 insertions(+), 269 deletions(-) --- DIR diff --git a/bin/gitzone b/bin/gitzone t@@ -67,330 +67,330 @@ $SIG{__DIE__} = \&cleanup; cleanup; sub git { - my ($args, $print, $ret) = @_; - $ret ||=0; - print "% git $args\n" if $verbosity >= 2; - $_ = `$git $args 2>&1`; - $print = 1 if !defined $print && $verbosity >= 1; - if ($print) { - #my $cwd = cwd; s/$cwd//g; # print relative paths - print; - } - if ($ret >= 0 && $? >> 8 != $ret) { - my ($package, $filename, $line) = caller; - print; - die "Died at line $line.\n"; - } - return $_; + my ($args, $print, $ret) = @_; + $ret ||=0; + print "% git $args\n" if $verbosity >= 2; + $_ = `$git $args 2>&1`; + $print = 1 if !defined $print && $verbosity >= 1; + if ($print) { + #my $cwd = cwd; s/$cwd//g; # print relative paths + print; + } + if ($ret >= 0 && $? >> 8 != $ret) { + my ($package, $filename, $line) = caller; + print; + die "Died at line $line.\n"; + } + return $_; } # Load BIND config files specified in the $repos config variable. # First load the -default key, then the $repo key. sub load_repo_config { - my $key = shift || '-default'; - - # move files not in a dir to a . dir for easier processing - for my $file (keys %{$repos->{$key}}) { - next if ref $repos->{$key}->{$file} eq 'HASH'; - $repos->{$key}->{'.'}->{$file} = $repos->{$key}->{$file}; - delete $repos->{$key}->{$file}; - } - - for my $dir (keys %{$repos->{$key}}) { - my $d = $repos->{$key}->{$dir}; - for my $file (keys %$d) { - $d->{$file} = $default_view if $d->{$file} eq 1; - $d->{$file} = [$d->{$file}] if ref $d->{$file} ne 'ARRAY'; - next unless $file =~ m,^/,; - if (-f $file) { - open FILE, '<', $file or die $!; - while (<FILE>) { - if (/^\s*zone\s+"([^"]+)"/) { - $repos->{$repo}->{$dir}->{$1} = $d->{$file}; - } - } - close FILE; - } - delete $d->{$file} if $key ne '-default'; + my $key = shift || '-default'; + + # move files not in a dir to a . dir for easier processing + for my $file (keys %{$repos->{$key}}) { + next if ref $repos->{$key}->{$file} eq 'HASH'; + $repos->{$key}->{'.'}->{$file} = $repos->{$key}->{$file}; + delete $repos->{$key}->{$file}; + } + + for my $dir (keys %{$repos->{$key}}) { + my $d = $repos->{$key}->{$dir}; + for my $file (keys %$d) { + $d->{$file} = $default_view if $d->{$file} eq 1; + $d->{$file} = [$d->{$file}] if ref $d->{$file} ne 'ARRAY'; + next unless $file =~ m,^/,; + if (-f $file) { + open FILE, '<', $file or die $!; + while (<FILE>) { + if (/^\s*zone\s+"([^"]+)"/) { + $repos->{$repo}->{$dir}->{$1} = $d->{$file}; + } + } + close FILE; + } + delete $d->{$file} if $key ne '-default'; + } } - } - load_repo_config($repo) if $key eq '-default'; + load_repo_config($repo) if $key eq '-default'; } sub check_what_changed { - my ($old, $new) = @_; + my ($old, $new) = @_; - # diff with empty tree if there's no previous commit - $old = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' if !$old || $old =~ /^0+$/; + # diff with empty tree if there's no previous commit + $old = '4b825dc642cb6eb9a060e54bf8d69288fbee4904' if !$old || $old =~ /^0+$/; - $_ = git "diff --raw ". ($new ? "$old..$new" : $old); + $_ = git "diff --raw ". ($new ? "$old..$new" : $old); - # parse diff output, add only valid zone names to %files for parsing - $files{$1} = 0 while m,^:(?:[\w.]+\s+){5}([a-z0-9./-]+)$,gm; + # parse diff output, add only valid zone names to %files for parsing + $files{$1} = 0 while m,^:(?:[\w.]+\s+){5}([a-z0-9./-]+)$,gm; } sub process_files { - $files{$_} = 0 for @_; - process_file($_) for keys %files; - check_zones(); - - if (@changed_files) { - print "adding changed files: @changed_files\n" if $verbosity >= 2; - git "add @changed_files"; - } + $files{$_} = 0 for @_; + process_file($_) for keys %files; + check_zones(); + + if (@changed_files) { + print "adding changed files: @changed_files\n" if $verbosity >= 2; + git "add @changed_files"; + } } sub process_file { - my ($file, $depth) = @_; - my (@newfile, $changed, @inc_by); - print ">> process_file($file)\n" if $verbosity >= 3; - - return 0 if $files{$file}; # already processed - return -1 unless -f $file; - - print ">>> processing $file\n" if $verbosity >= 3; - $files{$_}++; - - open FILE, '<', $file or die $!; - my $n = 0; - while (<FILE>) { - $n++; - my $line = $_; - if (/^(.*)(\b\d+\b)(.*?;AUTO_INCREMENT\b.*)$/) { - # increment serial where marked with ;AUTO_INCREMENT - # if length of serial is 10 and starts with 20 treat it as a date - my ($a,$s,$z) = ($1,int $2,$3); - $date ||= strftime '%Y%m%d', localtime; - $s = ($s =~ /^$date/ || $s < 2000000000 || $s >= 2100000000) ? $s + 1 : $date.'00'; - $line = "$a$s$z\n"; - $changed = 1; - } elsif (/^(\s*\$INCLUDE\s+)(\S+)(.*)$/) { - my ($a,$inc_file,$z) = ($1,$2,$3); - unless ($unrestricted_includes) { - # check $INCLUDE lines for files outside the repo dir - unless ($inc_file =~ m,^$repo/, && $inc_file !~ /\.\./) { - close FILE; - die "Error in $file:$n: invalid included file name, it should start with: $repo/\n"; - } - } - } else { - if ($n == 1 && /^;INCLUDED_BY\s+(.*)$/) { - @inc_by = split /\s+/, $1; - } + my ($file, $depth) = @_; + my (@newfile, $changed, @inc_by); + print ">> process_file($file)\n" if $verbosity >= 3; + + return 0 if $files{$file}; # already processed + return -1 unless -f $file; + + print ">>> processing $file\n" if $verbosity >= 3; + $files{$_}++; + + open FILE, '<', $file or die $!; + my $n = 0; + while (<FILE>) { + $n++; + my $line = $_; + if (/^(.*)(\b\d+\b)(.*?;AUTO_INCREMENT\b.*)$/) { + # increment serial where marked with ;AUTO_INCREMENT + # if length of serial is 10 and starts with 20 treat it as a date + my ($a,$s,$z) = ($1,int $2,$3); + $date ||= strftime '%Y%m%d', localtime; + $s = ($s =~ /^$date/ || $s < 2000000000 || $s >= 2100000000) ? $s + 1 : $date.'00'; + $line = "$a$s$z\n"; + $changed = 1; + } elsif (/^(\s*\$INCLUDE\s+)(\S+)(.*)$/) { + my ($a,$inc_file,$z) = ($1,$2,$3); + unless ($unrestricted_includes) { + # check $INCLUDE lines for files outside the repo dir + unless ($inc_file =~ m,^$repo/, && $inc_file !~ /\.\./) { + close FILE; + die "Error in $file:$n: invalid included file name, it should start with: $repo/\n"; + } + } + } else { + if ($n == 1 && /^;INCLUDED_BY\s+(.*)$/) { + @inc_by = split /\s+/, $1; + } + } + push @newfile, $line; } - push @newfile, $line; - } - close FILE; + close FILE; - if ($changed) { - print ">>> $file changed, saving\n" if $verbosity >= 3; + if ($changed) { + print ">>> $file changed, saving\n" if $verbosity >= 3; - open FILE, '>', $file or die $!; - print FILE for @newfile; - close FILE; + open FILE, '>', $file or die $!; + print FILE for @newfile; + close FILE; - push @changed_files, $file; - } + push @changed_files, $file; + } - if ($depth++ < $max_depth) { - process_file($_, $depth) for @inc_by; - } else { - print "Warning: ;INCLUDED_BY is followed only up to $max_depth levels,\n". - " the following files are not reloaded: @inc_by\n"; - } + if ($depth++ < $max_depth) { + process_file($_, $depth) for @inc_by; + } else { + print "Warning: ;INCLUDED_BY is followed only up to $max_depth levels,\n". + " the following files are not reloaded: @inc_by\n"; + } - return 1; + return 1; } sub check_zones { - print ">> check_zones: ,",%files,"\n" if $verbosity >= 3; - for my $file (keys %files) { - my ($zone, $dir) = fileparse $file; - $zone =~ s/\.signed$//; - $dir = substr $dir, 0, -1; - # skip files with errors and those that are not in the config - next unless $files{$file} > 0 && exists $repos->{$repo}->{$dir}->{$zone}; - - print "Checking zone $zone\n"; - print `$named_checkzone -w .. '$zone' '$repo/$file'`; - clean_exit 1 if $?; # error, reject push - push @zones, $file; - } + print ">> check_zones: ,",%files,"\n" if $verbosity >= 3; + for my $file (keys %files) { + my ($zone, $dir) = fileparse $file; + $zone =~ s/\.signed$//; + $dir = substr $dir, 0, -1; + # skip files with errors and those that are not in the config + next unless $files{$file} > 0 && exists $repos->{$repo}->{$dir}->{$zone}; + + print "Checking zone $zone\n"; + print `$named_checkzone -w .. '$zone' '$repo/$file'`; + clean_exit 1 if $?; # error, reject push + push @zones, $file; + } } sub save_list_file { - if (@zones) { - print "Zone check passed: @zones\n"; - # save changed zone list for post-receive hook - open FILE, '>>', $list_file or die $!; - print FILE join(' ', @zones), "\n"; - close FILE; - } else { - print "No zones to reload\n"; - } + if (@zones) { + print "Zone check passed: @zones\n"; + # save changed zone list for post-receive hook + open FILE, '>>', $list_file or die $!; + print FILE join(' ', @zones), "\n"; + close FILE; + } else { + print "No zones to reload\n"; + } } sub load_list_file { - return unless -f $list_file; - my %zones; - open FILE, '<', $list_file or die $!; - while (<FILE>) { - $zones{$_} = 1 for split /[\s\n\r]+/; - } - close FILE; - @zones = keys %zones; + return unless -f $list_file; + my %zones; + open FILE, '<', $list_file or die $!; + while (<FILE>) { + $zones{$_} = 1 for split /[\s\n\r]+/; + } + close FILE; + @zones = keys %zones; } sub install_zones { - print "Reloading changed zones: @zones\n"; + print "Reloading changed zones: @zones\n"; - my $cwd = cwd; + my $cwd = cwd; - chdir "$zone_dir/$repo" or die $!; - git "clone $cwd ." unless -d '.git'; - git 'fetch'; - git 'reset --hard remotes/origin/master'; + chdir "$zone_dir/$repo" or die $!; + git "clone $cwd ." unless -d '.git'; + git 'fetch'; + git 'reset --hard remotes/origin/master'; - for my $file (@zones) { - my ($zone, $dir) = fileparse $file; - $zone =~ s/\.signed$//; - $dir = substr $dir, 0, -1; - my $view = $repos->{$repo}->{$dir}->{$zone}; - print "$_/$zone: ", `$rndc reload '$zone' $class $_` for @$view; - } + for my $file (@zones) { + my ($zone, $dir) = fileparse $file; + $zone =~ s/\.signed$//; + $dir = substr $dir, 0, -1; + my $view = $repos->{$repo}->{$dir}->{$zone}; + print "$_/$zone: ", `$rndc reload '$zone' $class $_` for @$view; + } - unlink $list_file; + unlink $list_file; } # save working dir state # (git stash wouldn't work without conflicts if there's a # change in both the index & working tree in the same file) sub stash_save { - $stash_file = File::Temp::tempnam('.git', '.gitzone-stash-'); - print "Saving working tree to $stash_file\n"; - git "update-index --refresh -q", 0, -1; - git "diff >$stash_file"; - git 'checkout .'; + $stash_file = File::Temp::tempnam('.git', '.gitzone-stash-'); + print "Saving working tree to $stash_file\n"; + git "update-index --refresh -q", 0, -1; + git "diff >$stash_file"; + git 'checkout .'; } # restore working dir sub stash_pop { - print "Restoring working tree from $stash_file\n"; - git "apply --reject --whitespace=nowarn $stash_file", 1, -1; - unlink $stash_file unless $?; + print "Restoring working tree from $stash_file\n"; + git "apply --reject --whitespace=nowarn $stash_file", 1, -1; + unlink $stash_file unless $?; } sub pre_receive { - my ($old, $new, $ref); - - while (<STDIN>) { # <old-value> SP <new-value> SP <ref-name> LF - print if $verbosity >= 1; - next unless m,(\w+) (\w+) ([\w/]+),; - next if $3 ne 'refs/heads/master'; # only process master branch - die "Denied branch 'new', choose another name\n" if $3 eq 'refs/head/new'; - ($old, $new, $ref) = ($1, $2, $3); - } - - # nothing for master branch, exit - clean_exit 0 unless $ref; - - # checkout changes - git "checkout -qf $new"; - check_what_changed($old, $new); - load_repo_config; - process_files; - git "commit -nm 'auto increment: @changed_files'", 1 if @changed_files; - save_list_file; - - # save new commits in a new branch - git 'checkout -B new'; + my ($old, $new, $ref); + + while (<STDIN>) { # <old-value> SP <new-value> SP <ref-name> LF + print if $verbosity >= 1; + next unless m,(\w+) (\w+) ([\w/]+),; + next if $3 ne 'refs/heads/master'; # only process master branch + die "Denied branch 'new', choose another name\n" if $3 eq 'refs/head/new'; + ($old, $new, $ref) = ($1, $2, $3); + } + + # nothing for master branch, exit + clean_exit 0 unless $ref; + + # checkout changes + git "checkout -qf $new"; + check_what_changed($old, $new); + load_repo_config; + process_files; + git "commit -nm 'auto increment: @changed_files'", 1 if @changed_files; + save_list_file; + + # save new commits in a new branch + git 'checkout -B new'; } sub pre_commit { - stash_save; + stash_save; - $cleanup = sub { - # reset any changes, e.g. auto inc. - git 'checkout .'; - stash_pop; - }; + $cleanup = sub { + # reset any changes, e.g. auto inc. + git 'checkout .'; + stash_pop; + }; - git 'rev-parse --verify HEAD', 0, -1; - check_what_changed($? ? undef : 'HEAD'); - load_repo_config; - process_files; + git 'rev-parse --verify HEAD', 0, -1; + check_what_changed($? ? undef : 'HEAD'); + load_repo_config; + process_files; - $cleanup = sub { - stash_pop; - }; + $cleanup = sub { + stash_pop; + }; - save_list_file; + save_list_file; } sub post_receive { - print "\n"; + print "\n"; - # move master to new - git 'checkout -f master'; - git 'reset --hard new'; + # move master to new + git 'checkout -f master'; + git 'reset --hard new'; - load_repo_config; - load_list_file; - install_zones; + load_repo_config; + load_list_file; + install_zones; - print "Done. Don't forget to pull if you use auto increment.\n"; + print "Done. Don't forget to pull if you use auto increment.\n"; } sub post_commit { - print "\n"; + print "\n"; - load_repo_config; - load_list_file; - install_zones; - print "Done.\n"; + load_repo_config; + load_list_file; + install_zones; + print "Done.\n"; } sub update_record { - my ($c, $file, @record) = split /\s+/, shift; - my ($ip) = $ENV{SSH_CLIENT} =~ /^([\d.]+|[a-f\d:]+)\s/i or die "Invalid IP address\n"; - my $re = qr/^\s*/i; - $re = qr/$re$_\s+/i for (@record); - my $matched = 0; - my $changed = 0; - my @newfile; - - git 'checkout -f master'; - - open FILE, '<', $file or die "$file: $!"; - while (<FILE>) { - my $line = $_; - if (!$matched && s/($re)([\d.]+|[a-f\d:]+)/$1$ip/i) { - print "Matched record:\n$line"; - $matched = 1; - if ($line ne "$1$ip\n") { - $changed = 1; - $line = "$1$ip\n"; - print "Updating it with:\n$line"; - } else { - print "Not updating: already up-to-date\n"; - close FILE; - clean_exit 0; - } + my ($c, $file, @record) = split /\s+/, shift; + my ($ip) = $ENV{SSH_CLIENT} =~ /^([\d.]+|[a-f\d:]+)\s/i or die "Invalid IP address\n"; + my $re = qr/^\s*/i; + $re = qr/$re$_\s+/i for (@record); + my $matched = 0; + my $changed = 0; + my @newfile; + + git 'checkout -f master'; + + open FILE, '<', $file or die "$file: $!"; + while (<FILE>) { + my $line = $_; + if (!$matched && s/($re)([\d.]+|[a-f\d:]+)/$1$ip/i) { + print "Matched record:\n$line"; + $matched = 1; + if ($line ne "$1$ip\n") { + $changed = 1; + $line = "$1$ip\n"; + print "Updating it with:\n$line"; + } else { + print "Not updating: already up-to-date\n"; + close FILE; + clean_exit 0; + } + } + push @newfile, $line; } - push @newfile, $line; - } - close FILE; - die "No matching record in $file: @record\n" unless $matched; + close FILE; + die "No matching record in $file: @record\n" unless $matched; - open FILE, '>', $file or die $!; - print FILE for @newfile; - close FILE; + open FILE, '>', $file or die $!; + print FILE for @newfile; + close FILE; - git "commit -nm 'update-record: $file' '$file'", 1; + git "commit -nm 'update-record: $file' '$file'", 1; - load_repo_config; - process_files $file; - git "commit -nm 'auto increment: @changed_files'", 1 if @changed_files; - install_zones if @zones; + load_repo_config; + process_files $file; + git "commit -nm 'auto increment: @changed_files'", 1 if @changed_files; + install_zones if @zones; } DIR diff --git a/bin/gitzone-shell b/bin/gitzone-shell t@@ -34,35 +34,35 @@ git=/usr/bin/git grep=/bin/grep function error { - echo "fatal: What do you think I am? A shell?" - exit 128 + echo "fatal: What do you think I am? A shell?" + exit 128 } if [ "$1" != "-c" ]; then error; fi cmd=$2 if [[ "$cmd" == git-upload-pack* ]]; then - $git upload-pack $repo_dir/$repo + $git upload-pack $repo_dir/$repo elif [[ "$cmd" == git-receive-pack* ]]; then - $git receive-pack $repo_dir/$repo + $git receive-pack $repo_dir/$repo elif [[ "$cmd" == update-record* ]]; then - cd $repo_dir/$repo/.git - $gitzone $config update-record "$cmd" + cd $repo_dir/$repo/.git + $gitzone $config update-record "$cmd" elif [ -f $allow_key_mgmt_file ]; then - if [ "$cmd" == list-keys ]; then - cat .ssh/authorized_keys - elif [[ "$cmd" == add-key* ]]; then - key="${cmd:8}" - echo "$key" >> .ssh/authorized_keys && \ - echo "key added" - elif [[ "$cmd" == del-key* ]]; then - key="${cmd:8}" - $grep -v "$key" .ssh/authorized_keys > .ssh/authorized_keys-new && \ - mv .ssh/authorized_keys-new .ssh/authorized_keys && \ - echo "key deleted" - else - error - fi + if [ "$cmd" == list-keys ]; then + cat .ssh/authorized_keys + elif [[ "$cmd" == add-key* ]]; then + key="${cmd:8}" + echo "$key" >> .ssh/authorized_keys && \ + echo "key added" + elif [[ "$cmd" == del-key* ]]; then + key="${cmd:8}" + $grep -v "$key" .ssh/authorized_keys > .ssh/authorized_keys-new && \ + mv .ssh/authorized_keys-new .ssh/authorized_keys && \ + echo "key deleted" + else + error + fi else - error + error fi