fixed up several broken URLs (minor but annoying)
[gitolite.git] / src / commands / mirror
blobb22ec2aa49f1692432e8635f586dbcb77b496dc7
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
5 my $tid;
7 BEGIN {
8 $tid = $ENV{GL_TID} || 0;
9 delete $ENV{GL_TID};
12 use lib $ENV{GL_LIBDIR};
13 use Gitolite::Rc;
14 use Gitolite::Common;
15 use Gitolite::Conf::Load;
17 =for usage
18 Usage 1: gitolite mirror push <copy> <repo>
19 gitolite mirror status <copy> <repo>
20 gitolite mirror status all <repo>
21 gitolite mirror status all all
22 Usage 2: ssh git@master-server mirror push <copy> <repo>
23 ssh git@master-server mirror status <copy> <repo>
25 Forces a push of one repo to one copy.
27 Usage 1 is directly on the master server. Nothing is checked; if the copy
28 accepts it, the push happens, even if the copy is not in any copies
29 option. This is how you do delayed or lagged pushes to servers that do not
30 need real-time updates or have bandwidth/connectivity issues.
32 Usage 2 can be initiated by *any* user who has *any* gitolite access to the
33 master server, but it checks that the copy is in one of the copies options
34 before doing the push.
36 MIRROR STATUS: The usage examples above show what can be done. The 'status
37 all <repo>' usage checks the status of all the copies defined for the given
38 repo. The 'status all all' usage is special, in that it only prints a list of
39 repos that have *some* error, instead of dumping all the error info itself.
41 SERVER LIST: 'gitolite mirror list master <reponame>' and 'gitolite mirror
42 list copies <reponame>' will show you the name of the master server, and list
43 the copy servers, for the repo. They only work on the server command line
44 (any server), but not remotely (from a normal user).
45 =cut
47 usage() if not @ARGV or $ARGV[0] eq '-h';
49 _die "HOSTNAME not set" if not $rc{HOSTNAME};
51 my ( $cmd, $host, $repo ) = @ARGV;
52 $host = 'copies' if $host eq 'slaves';
53 $repo =~ s/\.git$//;
54 usage() if not $repo;
56 if ( $cmd eq 'push' ) {
57 valid_copy( $host, $repo ) if exists $ENV{GL_USER};
58 # will die if host not in copies for repo
60 trace( 1, "TID=$tid host=$host repo=$repo", "gitolite mirror push started" );
61 _chdir( $rc{GL_REPO_BASE} );
62 _chdir("$repo.git");
64 if ( -f "gl-creator" ) {
65 # try to propagate the wild repo, including creator name and gl-perms
66 my $creator = `cat gl-creator`; chomp($creator);
67 trace( 1, `cat gl-perms 2>/dev/null | ssh $host CREATOR=$creator perms -c \\'$repo\\' 2>/dev/null` );
70 my $errors = 0;
71 my $glss = '';
72 for (`git push --mirror $host:$repo 2>&1`) {
73 $errors = 1 if $?;
74 print STDERR "$_" if -t STDERR or exists $ENV{GL_USER};
75 $glss .= $_;
76 chomp;
77 if (/FATAL/) {
78 $errors = 1;
79 gl_log( 'mirror', $_ );
80 } else {
81 trace( 1, "mirror: $_" );
84 # save the mirror push status for this copy if the word 'fatal' is found,
85 # else remove the status file. We don't store "success" output messages;
86 # you can always get those from the log files if you really need them.
87 if ( $glss =~ /fatal/i ) {
88 my $glss_prefix = Gitolite::Common::gen_ts() . "\t$ENV{GL_TID}\t";
89 $glss =~ s/^/$glss_prefix/gm;
90 _print("gl-copy-$host.status", $glss);
91 } else {
92 unlink "gl-copy-$host.status";
95 exit $errors;
96 } elsif ($cmd eq 'status') {
97 if (not exists $ENV{GL_USER} and $repo eq 'all') {
98 # this means 'gitolite mirror status all all'; in this case we only
99 # return a list of repos that *have* status files (indicating some
100 # problem). It's upto you what you do with that list. This is not
101 # allowed to be run remotely; far too wide ranging, sorry.
102 _chdir( $rc{GL_REPO_BASE} );
103 my $phy_repos = list_phy_repos(1);
104 for my $repo ( @{$phy_repos} ) {
105 my @x = glob("$rc{GL_REPO_BASE}/$repo.git/gl-copy-*.status");
106 print "$repo\n" if @x;
108 exit 0;
111 valid_copy( $host, $repo ) if exists $ENV{GL_USER};
112 # will die if host not in copies for repo
114 _chdir( $rc{GL_REPO_BASE} );
115 _chdir("$repo.git");
117 $host = '*' if $host eq 'all';
118 map { print_status($repo, $_) } sort glob("gl-copy-$host.status");
119 } else {
120 # strictly speaking, we could allow some of the possible commands remotely
121 # also, at least for admins. However, these commands are mainly intended
122 # for server-side scripting so I don't care.
123 usage() if $ENV{GL_USER};
125 server_side_commands(@ARGV);
128 # ----------------------------------------------------------------------
130 sub valid_copy {
131 my ( $host, $repo ) = @_;
132 _die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;
134 my %list = repo_copies($repo);
135 _die "'$host' not a valid copy for '$repo'" unless $list{$host};
138 sub repo_copies {
139 my $repo = shift;
141 my $ref = git_config( $repo, "^gitolite-options\\.mirror\\.copies.*" );
142 my %list = map { $_ => 1 } map { split } values %$ref;
144 return %list;
147 sub repo_master {
148 my $repo = shift;
150 my $ref = git_config( $repo, "^gitolite-options\\.mirror\\.master\$" );
151 my @list = map { split } values %$ref;
152 _die "'$repo' seems to have more than one master" if @list > 1;
154 return $list[0] || '';
157 sub print_status {
158 my $repo = shift;
159 my $file = shift;
160 return unless -f $file;
161 my $copy = $1 if $file =~ /^gl-copy-(.+)\.status$/;
162 print "----------\n";
163 print "WARNING: previous mirror push of repo '$repo' to host '$copy' failed, status is:\n";
164 print slurp($file);
165 print "----------\n";
168 # ----------------------------------------------------------------------
169 # server side commands. Very little error checking.
170 # gitolite mirror list master <repo>
171 # gitolite mirror list copies <repo>
173 sub server_side_commands {
174 if ( $cmd eq 'list' ) {
175 if ( $host eq 'master' ) {
176 say repo_master($repo);
177 } elsif ( $host eq 'copies' ) {
178 my %list = repo_copies($repo);
179 say join( " ", sort keys %list );
180 } else {
181 _die "gitolite mirror list master|copies <reponame>";
183 } else {
184 _die "invalid command";