summaryrefslogtreecommitdiffstats
path: root/t/mirror-test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 14:17:27 +0000
commitaae1a14ea756102251351d96e2567b4986d30e2b (patch)
treea1af617672e26aee4c1031a3aa83e8ff08f6a0a5 /t/mirror-test
parentInitial commit. (diff)
downloadgitolite3-upstream.tar.xz
gitolite3-upstream.zip
Adding upstream version 3.6.12.upstream/3.6.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rwxr-xr-xt/mirror-test445
-rw-r--r--t/mirror-test-rc162
-rwxr-xr-xt/mirror-test-setup.sh196
-rw-r--r--t/mirror-test-ssh-config11
4 files changed, 814 insertions, 0 deletions
diff --git a/t/mirror-test b/t/mirror-test
new file mode 100755
index 0000000..3ace59b
--- /dev/null
+++ b/t/mirror-test
@@ -0,0 +1,445 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+
+# you need 3 disposable userids: sam, frodo, gollum. Then the test user (say
+# "g3") needs to be able to sudo into them. Put this in /etc/sudoers:
+
+# g3 ALL = (sam,frodo,gollum) NOPASSWD: ALL
+
+$ENV{TSH_ERREXIT} = 1;
+
+# this is hardcoded; change it if needed
+use lib "src/lib";
+use Gitolite::Test;
+use Cwd;
+my $workdir = getcwd();
+my $h = $ENV{HOME};
+my ($t, $t2); # temp vars
+
+# basic tests
+# ----------------------------------------------------------------------
+
+try "plan 152";
+## try "DEF POK = !/DENIED/; !/failed to push/";
+
+## confreset;confadd '
+
+## ';
+
+## try "ADMIN_PUSH set1; !/FATAL/" or die text();
+
+# ----------------------------------------------------------------------
+
+# switch keys
+sub swk {
+ my $h = $ENV{HOME};
+ my $k = shift;
+ system("cp $h/.ssh/$k $h/.ssh/id_rsa");
+ system("cp $h/.ssh/$k.pub $h/.ssh/id_rsa.pub");
+}
+
+sub all {
+ try "F " . join(" ", @_);
+ try "S " . join(" ", @_);
+ try "G " . join(" ", @_);
+}
+
+try "
+ DEF F = sudo -u frodo -i
+ DEF S = sudo -u sam -i
+ DEF G = sudo -u gollum -i
+";
+
+my $bd = `gitolite query-rc -n GL_BINDIR`;
+
+try "
+ $bd/../t/mirror-test-setup.sh; ok or die mirror setup shell script failed
+ /hello server-frodo, this is frodo/
+ /hello server-sam, this is frodo/
+ /hello server-gollum, this is frodo/
+ /hello server-frodo, this is sam/
+ /hello server-sam, this is sam/
+ /hello server-gollum, this is sam/
+ /hello server-frodo, this is gollum/
+ /hello server-sam, this is gollum/
+ /hello server-gollum, this is gollum/
+ /hello admin, this is frodo/
+ /Initialized empty .*/gitolite-admin.git/
+ /Initialized empty .*/r1.git/
+ /Initialized empty .*/r2.git/
+ /Initialized empty .*/testing.git/
+ /Initialized empty .*/gitolite-admin.git/
+ /Initialized empty .*/r1.git/
+ /Initialized empty .*/r2.git/
+ /Initialized empty .*/testing.git/
+ /Initialized empty .*/gitolite-admin.git/
+ /Initialized empty .*/r1.git/
+ /Initialized empty .*/r2.git/
+ /Initialized empty .*/testing.git/
+";
+
+# ----------------------------------------------------------------------
+# SECTION 1: gitolite-admin shenanigans
+
+# push to frodo and see sam and gollum change
+try "
+ git clone frodo\@localhost:gitolite-admin fga
+ ok; /Cloning into 'fga'.../
+ cd fga; ok
+ cp $h/.ssh/u?.pub keydir; ok
+ git add keydir; ok
+ git commit -m 6keys; ok
+ git push; ok
+ /To localhost:gitolite-admin/
+ /master -> master/
+ sleep 5
+ git rev-parse HEAD
+";
+
+chomp($t = text());
+
+try "
+ git ls-remote sam\@localhost:gitolite-admin
+ ok; /$t/
+ git ls-remote gollum\@localhost:gitolite-admin
+ ok; /$t/
+";
+
+try "
+ cd ..
+
+";
+
+# push to sam and see frodo and gollum change
+try "
+ git clone sam\@localhost:gitolite-admin sga
+ ok; /Cloning into 'sga'.../
+ cd sga; ok
+ empty; ok
+ git push; ok
+ /To localhost:gitolite-admin/
+ /master -> master/
+ sleep 5
+ git rev-parse HEAD
+";
+
+chomp($t = text());
+
+try "
+ git ls-remote frodo\@localhost:gitolite-admin
+ ok; /$t/
+ git ls-remote gollum\@localhost:gitolite-admin
+ ok; /$t/
+";
+
+try "
+ cd ..
+
+";
+
+# push to gollum and fail at gollum
+try "
+ git clone gollum\@localhost:gitolite-admin gga
+ ok; /Cloning into 'gga'.../
+ cd gga; ok
+ empty; ok
+ git push; !ok
+ !/To localhost:gitolite-admin/
+ !/master -> master/
+ /gollum: pushing 'gitolite-admin' to copy 'gollum' not allowed/
+ git rev-parse HEAD
+";
+
+chomp($t2 = text());
+
+try "
+ git ls-remote frodo\@localhost:gitolite-admin
+ ok; /$t/; !/$t2/
+ git ls-remote sam\@localhost:gitolite-admin
+ ok; /$t/; !/$t2/
+ git ls-remote gollum\@localhost:gitolite-admin
+ ok; /$t/; !/$t2/
+";
+
+# fake out the gollum failure to continue the redirected push and fail at frodo
+try "
+ sudo -u gollum -i gitolite git-config -r gitolite-admin .
+ ok
+ /redirectOK.*sam/
+ !/redirectOK.*gollum/
+
+ sudo -u gollum -i bash -c 'echo repo gitolite-admin > junk'
+ sudo -u gollum -i bash -c 'echo option mirror.redirectOK-1 = gollum >> junk'
+ sudo -u gollum -i bash -c 'cat junk >> .gitolite/conf/gitolite.conf'
+ sudo -u gollum -i gitolite compile
+ sudo -u gollum -i gitolite git-config -r gitolite-admin .
+ ok
+ /redirectOK.*sam/
+ /redirectOK.*gollum/
+
+ git push; !ok
+ /frodo: redirection not allowed from 'gollum'/
+ !/To gollum\@localhost:gitolite-admin/
+ !/master -> master/
+";
+
+# reset gollum via frodo
+try "
+ cd ..
+ rm -rf fga
+ git clone frodo\@localhost:gitolite-admin fga
+ ok; /Cloning into 'fga'.../
+ cd fga; ok
+ empty; ok
+ git push; ok
+ /To localhost:gitolite-admin/
+ /master -> master/
+ sleep 5
+
+ sudo -u gollum -i gitolite git-config -r gitolite-admin .
+ ok
+ /redirectOK.*sam/
+ !/redirectOK.*gollum/
+
+ git rev-parse HEAD
+";
+
+chomp($t = text());
+
+try "
+ git ls-remote sam\@localhost:gitolite-admin
+ ok; /$t/
+ git ls-remote gollum\@localhost:gitolite-admin
+ ok; /$t/
+";
+
+# ----------------------------------------------------------------------
+# user repo shenanigans
+
+# for a recap of the perms see t/mirror-test-setup.sh
+
+try "
+ cd ..
+ pwd
+ /tmp/tsh_tempdir/ or die not in the right place
+" or die;
+
+swk('u1');
+
+# u1 sam r1, R ok, W ok
+try "
+ rm -rf fga sga gga
+
+ git clone sam\@localhost:r1 sr1
+ /Cloning into 'sr1'.../
+ /warning: You appear to have cloned an empty repository/
+ cd sr1
+ empty
+ git push origin master
+ /new branch/; /master -> master/
+ sleep 5
+ git rev-parse HEAD
+";
+chomp($t = text());
+
+# u1 sam r1, W mirrors to frodo but not gollum
+try "
+ git ls-remote sam\@localhost:r1
+ /$t/
+ git ls-remote frodo\@localhost:r1
+ /$t/
+ git ls-remote gollum\@localhost:r1
+ /gollum: 'r1' is mirrored but not here/
+";
+
+swk("u2");
+try "
+ empty
+ git rev-parse HEAD
+";
+chomp($t2 = text());
+
+# u2 sam r2 W ok, mirrors to all
+try "
+ git push sam\@localhost:r2 master
+ /new branch/; /master -> master/
+ /master -> master/
+ sleep 5
+ git ls-remote frodo\@localhost:r2
+ !/$t/
+ /$t2/
+ git ls-remote gollum\@localhost:r2
+ !/$t/
+ /$t2/
+";
+
+swk("u1");
+
+# u1 gollum r1 -- "known unknown" :-)
+# u1 frodo r1 R ok, W not ok
+# u1 sam r1 R ok, W ok
+try "
+ cd ..
+ rm -rf sr1
+
+ git clone gollum\@localhost:r1 fr1
+ /gollum: 'r1' is mirrored but not here/
+
+ git clone frodo\@localhost:r1 fr1; ok
+ cd fr1
+ empty
+ git push
+ /frodo: pushing 'r1' to copy 'frodo' not allowed/
+ cd ..
+ git clone sam\@localhost:r1 sr1; ok
+ cd sr1
+ empty
+ git push; ok
+ /master -> master/
+ sleep 5
+ git rev-parse HEAD
+";
+chomp($t = text());
+
+# u1 sam r1 W mirrored to frodo but not gollum
+try "
+ git ls-remote sam\@localhost:r1
+ /$t/
+ git ls-remote frodo\@localhost:r1
+ /$t/
+
+ git ls-remote gollum\@localhost:r1
+ /gollum: 'r1' is mirrored but not here/
+
+ git reset --hard HEAD^; ok
+ tc a
+ git push; !ok
+ /rejected/
+ /failed to push/
+
+ git push -f
+ /\\+ .......\\.\\.\\........ master -> master .forced update/
+ sleep 5
+";
+
+swk("u2");
+
+# u2 frodo r1 R ok, W not allowed (no redirectOK)
+# u2 frodo r2 W ok
+try "
+ cd ..
+ rm -rf fr1 sr1
+
+ git clone frodo\@localhost:r1 fr1; ok
+ cd fr1
+ tc b
+ git push
+ /frodo: pushing 'r1' to copy 'frodo' not allowed/
+ cd ..
+ git clone frodo\@localhost:r2 fr2; ok
+ cd fr2
+ tc c
+ git push
+ /master -> master/
+ sleep 5
+ git rev-parse HEAD
+";
+chomp($t = text());
+
+# u2 frodo r2 W mirrors to sam and gollum
+try "
+ git ls-remote sam\@localhost:r2
+ /$t/
+ git ls-remote gollum\@localhost:r2
+ /$t/
+
+ git reset --hard HEAD^; ok
+ tc d
+ git push
+ /rejected/
+ /failed to push/
+
+ git push -f
+ /\\+ .......\\.\\.\\........ master -> master .forced update/
+ sleep 5
+
+ cd ..
+ rm -rf fr1 fr2
+";
+
+swk("u3");
+
+# u3 frodo r2 R ok W ok
+try "
+ git clone frodo\@localhost:r2 fr2; ok
+ cd fr2
+ tc e
+ git push; ok
+ sleep 5
+
+ git rev-parse HEAD
+";
+chomp($t = text());
+
+# u3 frodo r2 W mirrors to sam and gollum
+try "
+ git ls-remote sam\@localhost:r2
+ /$t/
+ git ls-remote gollum\@localhost:r2
+ /$t/
+
+ git reset --hard HEAD^; ok
+ tc f
+ git push
+ /rejected/
+ /failed to push/
+
+ sleep 10
+ git push -f
+ /\\+ refs/heads/master r2 u3 DENIED by fallthru/
+ /hook declined/
+ /rejected/
+";
+
+# ----------------------------------------------------------------------
+# all those vague edge cases where the two servers have totally wrong ideas
+# about each other
+
+swk('u1');
+
+try "sudo -u frodo -i ls .gitolite/logs";
+chomp($t = text());
+my $lfn = ".gitolite/logs/$t";
+
+try "
+ ssh sam\@localhost mirror push frodo lfrodo; !ok
+ /FATAL: frodo: 'lfrodo' is local/
+
+ ssh sam\@localhost mirror push frodo mboth; !ok
+ /FATAL: frodo: 'mboth' is native/
+
+ ssh sam\@localhost mirror push frodo mnotsam; !ok
+ /FATAL: frodo: 'sam' is not the master for 'mnotsam'/
+
+ cd ..
+ git clone sam\@localhost:lfrodo2 lfrodo2; ok
+ cd lfrodo2
+ empty
+ git push origin master; !ok
+ /FATAL: frodo: 'lfrodo2' is local/
+
+ cd ..
+ git clone sam\@localhost:nnfrodo nnfrodo; ok
+ cd nnfrodo
+ empty
+ git push origin master; !ok
+ /FATAL: frodo: 'nnfrodo' is not native/
+
+ cd ..
+ git clone sam\@localhost:nvsfrodo nvsfrodo; ok
+ cd nvsfrodo
+ empty
+ git push origin master; !ok
+ /FATAL: frodo: 'sam' is not a valid copy for 'nvsfrodo'/
+";
diff --git a/t/mirror-test-rc b/t/mirror-test-rc
new file mode 100644
index 0000000..1d76783
--- /dev/null
+++ b/t/mirror-test-rc
@@ -0,0 +1,162 @@
+# This file is in perl syntax. But you do NOT need to know perl to edit it --
+# just mind the commas, use single quotes unless you know what you're doing,
+# and make sure the brackets and braces stay matched up!
+
+# (Tip: perl allows a comma after the last item in a list also!)
+
+# HELP for commands (see COMMANDS list below) can be had by running the
+# command with "-h" as the sole argument.
+
+# HELP for all the other FEATURES can be found in the documentation (look for
+# "list of non-core programs shipped with gitolite" in the master index) or
+# directly in the corresponding source file.
+
+%RC = (
+
+ # ------------------------------------------------------------------
+
+ HOSTNAME => '%HOSTNAME',
+
+ # default umask gives you perms of '0700'; see the rc file docs for
+ # how/why you might change this
+ UMASK => 0077,
+
+ # look in the "GIT-CONFIG" section in the README for what to do
+ GIT_CONFIG_KEYS => '',
+
+ # comment out if you don't need all the extra detail in the logfile
+ LOG_EXTRA => 1,
+
+ # roles. add more roles (like MANAGER, TESTER, ...) here.
+ # WARNING: if you make changes to this hash, you MUST run 'gitolite
+ # compile' afterward, and possibly also 'gitolite trigger POST_COMPILE'
+ ROLES => {
+ READERS => 1,
+ WRITERS => 1,
+ },
+ # uncomment (and change) this if you wish
+ # DEFAULT_ROLE_PERMS => 'READERS @all',
+
+ # CACHE => 'Redis',
+
+ # ------------------------------------------------------------------
+
+ # rc variables used by various features
+
+ # the 'info' command prints this as additional info, if it is set
+ # SITE_INFO => 'Please see http://blahblah/gitolite for more help',
+
+ # the 'desc' command uses this
+ # WRITER_CAN_UPDATE_DESC => 1,
+
+ # the CpuTime feature uses these
+ # display user, system, and elapsed times to user after each git operation
+ # DISPLAY_CPU_TIME => 1,
+ # display a warning if total CPU times (u, s, cu, cs) crosses this limit
+ # CPU_TIME_WARN_LIMIT => 0.1,
+
+ # the Mirroring feature needs this
+ # HOSTNAME => "foo",
+
+ # if you enabled 'Shell', you need this
+ # SHELL_USERS_LIST => "$ENV{HOME}/.gitolite.shell-users",
+
+ # ------------------------------------------------------------------
+
+ # List of commands and features to enable
+
+ ENABLE => [
+
+ # COMMANDS
+
+ # These are the commands enabled by default
+ 'help',
+ 'desc',
+ 'info',
+ 'perms',
+ 'writable',
+
+ 'mirror',
+
+ # Uncomment or add new commands here.
+ # 'create',
+ # 'fork',
+ # 'mirror',
+ # 'sskm',
+ # 'D',
+
+ # These FEATURES are enabled by default.
+
+ # essential (unless you're using smart-http mode)
+ 'ssh-authkeys',
+
+ # creates git-config enties from gitolite.conf file entries like 'config foo.bar = baz'
+ 'git-config',
+
+ # creates git-daemon-export-ok files; if you don't use git-daemon, comment this out
+ 'daemon',
+
+ # creates projects.list file; if you don't use gitweb, comment this out
+ 'gitweb',
+
+ # These FEATURES are disabled by default; uncomment to enable. If you
+ # need to add new ones, ask on the mailing list :-)
+
+ # user-visible behaviour
+
+ # prevent wild repos auto-create on fetch/clone
+ # 'no-create-on-read',
+ # no auto-create at all (don't forget to enable the 'create' command!)
+ # 'no-auto-create',
+
+ # access a repo by another (possibly legacy) name
+ # 'Alias',
+
+ # give some users direct shell access
+ # 'Shell',
+
+ # system admin stuff
+
+ # enable mirroring (don't forget to set the HOSTNAME too!)
+ 'Mirroring',
+
+ # allow people to submit pub files with more than one key in them
+ # 'ssh-authkeys-split',
+
+ # selective read control hack
+ # 'partial-copy',
+
+ # manage local, gitolite-controlled, copies of read-only upstream repos
+ # 'upstream',
+
+ # updates 'description' file instead of 'gitweb.description' config item
+ # 'cgit',
+
+ # performance, logging, monitoring...
+
+ # be nice
+ # 'renice 10',
+
+ # log CPU times (user, system, cumulative user, cumulative system)
+ # 'CpuTime',
+
+ # syntactic_sugar for gitolite.conf and included files
+
+ # allow backslash-escaped continuation lines in gitolite.conf
+ # 'continuation-lines',
+
+ # create implicit user groups from directory names in keydir/
+ # 'keysubdirs-as-groups',
+
+ ],
+
+);
+
+# ------------------------------------------------------------------------------
+# per perl rules, this should be the last line in such a file:
+1;
+
+# Local variables:
+# mode: perl
+# End:
+# vim: set syn=perl:
diff --git a/t/mirror-test-setup.sh b/t/mirror-test-setup.sh
new file mode 100755
index 0000000..32bc462
--- /dev/null
+++ b/t/mirror-test-setup.sh
@@ -0,0 +1,196 @@
+#!/bin/bash
+
+set -e
+hosts="frodo sam gollum"
+mainhost=frodo
+
+# setup software
+bd=`gitolite query-rc -n GL_BINDIR`
+mkdir -p /tmp/g3
+rm -rf /tmp/g3/src
+cp -a $bd /tmp/g3/src
+chmod -R go+rX /tmp/g3
+
+# setup symlinks in frodo, sam, and gollum's accounts
+for h in $hosts
+do
+ sudo -u $h -i bash -c "rm -rf *.pub bin .ssh projects.list repositories .gitolite .gitolite.rc"
+done
+
+[ "$1" = "clear" ] && exit
+
+cd /tmp/g3
+[ -d keys ] || {
+ mkdir keys
+ cd keys
+ for h in $hosts
+ do
+ ssh-keygen -N '' -q -f server-$h -C $h
+ chmod go+r /tmp/g3/keys/server-$h
+ done
+ cp $bd/../t/mirror-test-ssh-config ssh-config
+}
+chmod -R go+rX /tmp/g3
+
+for h in $hosts
+do
+ sudo -u $h -i bash -c "mkdir -p bin; ln -sf /tmp/g3/src/gitolite bin; mkdir -p .ssh; chmod 0700 .ssh"
+
+ sudo -u $h -i cp /tmp/g3/keys/ssh-config .ssh/config
+ sudo -u $h -i cp /tmp/g3/keys/server-$h .ssh/id_rsa
+ sudo -u $h -i cp /tmp/g3/keys/server-$h.pub .ssh/id_rsa.pub
+ sudo -u $h -i chmod go-rwx .ssh/id_rsa .ssh/config
+
+done
+
+# add all pubkeys to all servers
+for h in $hosts
+do
+ sudo -u $h -i gitolite setup -a admin
+ for j in $hosts
+ do
+ sudo -u $h -i gitolite setup -pk /tmp/g3/keys/server-$j.pub
+ echo sudo _u $j _i ssh $h@localhost info
+ sudo -u $j -i ssh -o StrictHostKeyChecking=no $h@localhost info
+ done
+ echo ----
+done
+
+# now copy our admin key to the main host
+cd;cd .ssh
+cp admin id_rsa; cp admin.pub id_rsa.pub
+cp admin.pub /tmp/g3/keys; chmod go+r /tmp/g3/keys/admin.pub
+sudo -u $mainhost -i gitolite setup -pk /tmp/g3/keys/admin.pub
+ssh $mainhost@localhost info
+
+lines="
+repo gitolite-admin
+ option mirror.master = frodo
+ option mirror.copies-1 = sam gollum
+ option mirror.redirectOK = sam
+
+repo r1
+ RW+ = u1
+ RW = u2
+ R = u3
+ option mirror.master = sam
+ option mirror.copies-1 = frodo
+
+repo r2
+ RW+ = u2
+ RW = u3
+ R = u4
+ option mirror.master = sam
+ option mirror.copies-1 = frodo gollum
+ option mirror.redirectOK = all
+
+include \"%HOSTNAME.conf\"
+"
+
+lines2="
+repo l-%HOSTNAME
+RW = u1
+"
+
+# for each server, set the HOSTNAME to the rc, add the mirror options to the
+# conf file, and compile
+for h in $hosts
+do
+ cat $bd/../t/mirror-test-rc | perl -pe "s/%HOSTNAME/$h/" > /tmp/g3/temp
+ chmod go+rX /tmp/g3/temp
+ sudo -u $h -i cp /tmp/g3/temp .gitolite.rc
+ echo "$lines" | sudo -u $h -i sh -c 'cat >> .gitolite/conf/gitolite.conf'
+ echo "$lines2" | sudo -u $h -i sh -c "cat >> .gitolite/conf/$h.conf"
+ sudo -u $h -i gitolite setup
+done
+
+# goes on frodo
+lines="
+# local to frodo but sam thinks frodo is a copy
+repo lfrodo
+RW = u1
+
+# both think they're master
+repo mboth
+RW = u1
+option mirror.master = frodo
+option mirror.copies = sam
+
+# frodo thinks someone else is the master but sam thinks he is
+repo mnotsam
+RW = u1
+option mirror.master = merry
+option mirror.copies = frodo
+
+# local to frodo but sam thinks frodo is a master and redirect is OK
+repo lfrodo2
+RW = u1
+
+# non-native to frodo but sam thinks frodo is master
+repo nnfrodo
+RW = u1
+option mirror.master = gollum
+option mirror.copies = frodo
+option mirror.redirectOK = all
+
+# sam is not a valid copy to send stuff to frodo
+repo nvsfrodo
+RW = u1
+option mirror.master = frodo
+option mirror.copies = gollum
+option mirror.redirectOK = all
+"
+
+echo "$lines" | sudo -u frodo -i sh -c "cat >> .gitolite/conf/frodo.conf"
+
+# goes on sam
+lines="
+# local to frodo but sam thinks frodo is a copy
+repo lfrodo
+RW = u1
+option mirror.master = sam
+option mirror.copies = frodo
+
+# both think they're master
+repo mboth
+RW = u1
+option mirror.master = sam
+option mirror.copies = frodo
+
+# frodo thinks someone else is the master but sam thinks he is
+repo mnotsam
+RW = u1
+option mirror.master = sam
+option mirror.copies = frodo
+
+# local to frodo but sam thinks frodo is a master and redirect is OK
+repo lfrodo2
+RW = u1
+option mirror.master = frodo
+option mirror.copies = sam
+option mirror.redirectOK = all
+
+# non-native to frodo but sam thinks frodo is master
+repo nnfrodo
+RW = u1
+option mirror.master = frodo
+option mirror.copies = sam
+option mirror.redirectOK = all
+
+# sam is not a valid copy to send stuff to frodo
+repo nvsfrodo
+RW = u1
+option mirror.master = frodo
+option mirror.copies = sam
+option mirror.redirectOK = all
+"
+
+echo "$lines" | sudo -u sam -i sh -c "cat >> .gitolite/conf/sam.conf"
+
+for h in $hosts
+do
+ sudo -u $h -i gitolite setup
+done
+
+# that ends the setup phase
+echo ======================================================================
diff --git a/t/mirror-test-ssh-config b/t/mirror-test-ssh-config
new file mode 100644
index 0000000..40de6d7
--- /dev/null
+++ b/t/mirror-test-ssh-config
@@ -0,0 +1,11 @@
+host frodo
+ user frodo
+ hostname localhost
+
+host sam
+ user sam
+ hostname localhost
+
+host gollum
+ user gollum
+ hostname localhost