diff --git a/src/Gitolite/Conf/Load.pm b/src/Gitolite/Conf/Load.pm index 752da49..b987662 100644 --- a/src/Gitolite/Conf/Load.pm +++ b/src/Gitolite/Conf/Load.pm @@ -5,11 +5,15 @@ package Gitolite::Conf::Load; @EXPORT = qw( load + access git_config + option repo_missing + check_repo_write_enabled creator + vrefs lister_dispatch ); @@ -154,6 +158,15 @@ sub repo_missing { return not -d "$rc{GL_REPO_BASE}/$repo.git"; } +sub check_repo_write_enabled { + my ($repo) = shift; + for my $f ("$ENV{HOME}/.gitolite.down", "$rc{GL_REPO_BASE}/$repo.git/.gitolite.down") { + next unless -f $f; + _die slurp($f) if -s $f; + _die "sorry, writes are currently disabled (no more info available)"; + } +} + # ---------------------------------------------------------------------- sub load_common { diff --git a/src/commands/writes b/src/commands/writes new file mode 100755 index 0000000..d530452 --- /dev/null +++ b/src/commands/writes @@ -0,0 +1,50 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use lib $ENV{GL_BINDIR}; +use Gitolite::Rc; +use Gitolite::Common; +use Gitolite::Conf::Load; + +=for usage +Usage: gitolite writes on|off |@all + +'on' enables, 'off' disables, writes (pushes) to the named repo or all repos. + +With 'off', any subsequent text is taken to be the message to be shown to +users when their pushes get rejected. If it is not supplied, it will take it +from STDIN; this allows longer messages. +=cut + +usage() if not @ARGV or @ARGV < 2 or $ARGV[0] eq '-h'; + +usage() if $ARGV[0] ne 'on' and $ARGV[0] ne 'off'; +my $on = ( shift eq 'on' ); + +my $repo = shift; + +my $msg = join( " ", @ARGV ); +# try STDIN only if no msg found in args *and* it's an 'off' command +if ( not $msg and not $on ) { + say2 "...please type the message to be shown to users:"; + $msg = join( "", <> ); +} + +my $sf = ".gitolite.down"; +my $rb = $rc{GL_REPO_BASE}; + +if ( $repo eq '@all' ) { + target( $ENV{HOME} ); +} else { + target("$rb/$repo.git"); +} + +sub target { + my $repodir = shift; + if ($on) { + unlink "$repodir/$sf"; + } else { + _print( "$repodir/$sf", $msg ); + } +} diff --git a/src/gitolite-shell b/src/gitolite-shell index 99f2cce..2bf6a78 100755 --- a/src/gitolite-shell +++ b/src/gitolite-shell @@ -82,6 +82,7 @@ sub main { trigger( 'ACCESS_CHECK', $repo, $user, $aa, 'any', $ret ); _die $ret if $ret =~ /DENIED/; + check_repo_write_enabled($repo) if $aa eq 'W'; trigger( 'PRE_GIT', $repo, $user, $aa, 'any', $verb ); my $repodir = "'$rc{GL_REPO_BASE}/$repo.git'"; _system( "git", "shell", "-c", "$verb $repodir" ); diff --git a/t/writes.t b/t/writes.t new file mode 100755 index 0000000..08a8143 --- /dev/null +++ b/t/writes.t @@ -0,0 +1,115 @@ +#!/usr/bin/perl +use strict; +use warnings; + +# this is hardcoded; change it if needed +use lib "src"; +use Gitolite::Test; +use Cwd; +my $workdir = getcwd(); + +# 'gitolite writes' command +# ---------------------------------------------------------------------- + +my $sf = ".gitolite.down"; + +try "plan 58"; +try "DEF POK = !/DENIED/; !/failed to push/"; + +# delete the down file +unlink "$ENV{HOME}/$sf"; + +# add foo, bar/..* repos to the config and push +confreset;confadd ' + repo foo + RW = u1 + R = u2 + + repo bar/..* + C = u2 u4 u6 + RW = CREATOR +'; + +try "ADMIN_PUSH set1; !/FATAL/" or die text(); + +try " + # clone and push to foo + CLONE u1 foo; ok + cd foo; ok + tc f1; ok + PUSH u1 master; ok; /new branch/ + + # auto-clone and push to bar/u2 + cd .. + CLONE u2 bar/u2; ok; /appear to have cloned an empty/ + /Initialized empty/ + cd u2; + tc f2 + PUSH u2 master; ok; + + # disable site with some message + gitolite writes off \@all testing site-wide disable; ok + + # try push foo and see fail + message + cd ../foo; ok + tc f3; ok + PUSH u1; !ok; /testing site-wide disable/ + # try push bar/u2 and ... + cd ../u2; ok + tc f4; ok + PUSH u2; !ok; /testing site-wide disable/ + + # try auto-create push bar/u4 and this works!! + cd .. + CLONE u4 bar/u4; ok; /appear to have cloned an empty/ + /Initialized empty/ + !/testing site-wide disable/ + cd u4; ok + + # enable site + gitolite writes on \@all; ok + + # try same 3 again + + # try push foo and see fail + message + cd ../foo; ok + tc g3; ok + PUSH u1; ok; /master -> master/ + # try push bar/u2 and ... + cd ../u2; ok + tc g4; ok + PUSH u2; ok; /master -> master/ + + # try auto-create push bar/u4 and this works!! + cd .. + CLONE u6 bar/u6; ok; /appear to have cloned an empty/ + /Initialized empty/ + !/testing site-wide disable/ + cd u6; ok + + # disable just foo + gitolite writes off foo foo down + + # try push foo and see the message + cd ../foo; ok + tc g3; ok + PUSH u1; !ok; /foo down/ + !/testing site-wide disable/ + # push bar/u2 ok + cd ../u2 + tc g4 + PUSH u2; ok; /master -> master/ + + # enable foo, disable bar/u2 + gitolite writes on foo + gitolite writes off bar/u2 the bar is closed + + # try both + cd ../foo; ok + tc h3; ok + PUSH u1; ok; /master -> master/ + # push bar/u2 ok + cd ../u2 + tc h4 + PUSH u2; !ok; /the bar is closed/ +";