[Bps-public-commit] git-sync 01/01: Add Gitolite support to git-sync.
Brett Smith
brett at bestpractical.com
Wed Jun 30 09:26:58 EDT 2021
This is an automated email from the git hooks/post-receive script.
brett pushed a commit to branch scan-gitolite
in repository git-sync.
commit 7dcf21f08a3d60b43c954801582aabdcd040f735
Author: Brett Smith <brett at bestpractical.com>
AuthorDate: Wed Jun 30 09:25:06 2021 -0400
Add Gitolite support to git-sync.
This configuration mode works much like the existing "remote host"
mode. You just add `gitoliteHost` to the configuration instead of
`host`. `path` configuration lines work the same way to limit what gets
cloned.
---
git-sync | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 114 insertions(+), 6 deletions(-)
diff --git a/git-sync b/git-sync
index 6185074..001ecba 100755
--- a/git-sync
+++ b/git-sync
@@ -25,7 +25,7 @@ C<git-sync> also understands C<git-svn> clones, and updates them using
the same rules, but using C<git svn fetch> and C<git svn rebase>
instead of C<git fetch> and C<git pull>.
-There are three configuration modes:
+There are four configuration modes:
=over
@@ -52,6 +52,15 @@ possible.
It can also be configured to add, and keep up-to-update, all
repositories in Github's "network" as remotes.
+=item Gitolite
+
+Repositories that the user can read from a Gitolite server are cloned.
+If a clone already exists, it is updated as above. The set of repositories
+can be limited to those matching a path glob or under a subdirectory.
+
+The configuration must provide the hostname of the Gitolite server.
+It may optionally specify the username as well (default "git").
+
=item Remote
All of the repositories in a specific directory (or set of
@@ -96,6 +105,19 @@ authentication, though this is not required. The C<network> key, if
set, causes C<git-sync> to set up (and fetch) the github network as
remotes for each repository, additionally.
+If a C<gitoliteHost> key is provided, all repositories available from
+that server are cloned and updated:
+
+ [sync "a-gitolite-server"]
+ gitoliteHost = example.com
+ # If not specified, gitoliteUser defaults to "git"
+ gitoliteUser = gitolite
+
+Gitolite sections may include any number of C<path> keys to limit
+which repositories are cloned. Each path that includes a '*' or '?'
+is a glob to match repository names. Otherwise, all repositories
+organized in a directory under C<path> are acted on.
+
The presence of C<host> and C<path> keys configures a remote directory
to clone from. Multiple values for C<path> can be given; alternately,
if only one path is needed, C<host> can be specified as
@@ -123,9 +145,9 @@ find in C</home/alexmv/github>:
github = alexmv
into = /home/alexmv/github
-Remote and Github configurations also support the C<email> key; if set,
-newly-cloned repositories will have C<user.email> set to the given
-value.
+Remote, Github, and Gitolite configurations also support the C<email>
+key; if set, newly-cloned repositories will have C<user.email> set to
+the given value.
Remote configurations automatically attempt to set up an SSH master
connection. C<local> configurations can start one or more SSH master
@@ -151,7 +173,7 @@ default is controlled bt the C<sync.quiet> key in F<.gitconfig>
Remote repositories are not fetched from; the list of repositories
that would be synchronized is listed, and their state. Note that
network access is still needed to obtain a list of repositories for
-L</Github> and L</Remote> directories.
+L</Github>, L</Gitolite>, and L</Remote> directories.
=item C<--log> or C<-l>
@@ -253,7 +275,7 @@ if (@ARGV) {
push @categories, $name;
}
} else {
- $_->{local} = 1 for grep {not $_->{host} and not $_->{github}} values %sync;
+ $_->{local} = 1 for grep {not $_->{host} and not $_->{github} and not $_->{gitolitehost}} values %sync;
@categories = sort {exists $sync{$a}{local} <=> exists $sync{$b}{local} or $a cmp $b} keys %sync;
die qq{No sync targets configured! Edit your ~/.gitconfig to add one.\n}
unless @categories;
@@ -266,6 +288,8 @@ for my $name (@categories) {
if (not $sync{$name}{into}) {
print colored(" No 'into' set, skipping!\n", "red");
+ } elsif (exists $sync{$name}{gitolitehost}) {
+ sync_all_gitolite(%{$sync{$name}});
} elsif (exists $sync{$name}{host}) {
sync_all_remote(%{$sync{$name}});
} elsif (exists $sync{$name}{github}) {
@@ -433,6 +457,90 @@ sub sync_all_remote {
}
}
+sub sync_all_gitolite {
+ my %config = @_;
+
+ my @paths;
+ if (ref($config{path})) {
+ @paths = @{$config{path}};
+ } elsif ($config{path}) {
+ @paths = ($config{path});
+ } else {
+ @paths = ();
+ }
+ my $path_match;
+ if (!@paths) {
+ $path_match = ".";
+ } else {
+ my @path_regexps = map {
+ # path can be a glob. If it has no glob characters, we assume it's
+ # a "subdirectory" and want all repositories under it. Either way,
+ # convert it into a regexp for Gitolite.
+ # Gitolite omits leading slashes, so remove it if specified.
+ s{^/}{};
+ $_ = quotemeta($_);
+ if (/\\[*?]/) {
+ # Case #1: path is a glob, convert it into a regexp
+ s{\\\*}{.*}g;
+ s{\\\?}{.}g;
+ } else {
+ # Case #2: path is a "subdirectory"
+ # Ensure the regexp has a trailing slash
+ s{/?$}{/};
+ }
+ $_;
+ } @paths;
+ $path_match = sprintf("^(%s)", join("|", @path_regexps));
+ }
+
+ print colored(" Only one value valid for 'into' when 'gitolitehost' supplied!\n", "red") and return
+ if ref $config{into};
+
+ unless (-d $config{into}) {
+ print colored(" Creating directory '$config{into}'\n", "bold");
+ print colored(" Directory creation failed: $!\n", "bold red") and return
+ unless eval { File::Path::mkpath($config{into}) };
+ }
+
+ my $gitolite_user = $config{gitoliteuser} || "git";
+ my $gitolite_ssh = "$gitolite_user\@$config{gitolitehost}";
+ my @gitolite_repos = ();
+ my $gitolite_info;
+ unless (open($gitolite_info, "-|", "ssh", "$gitolite_ssh", "info", "-p")) {
+ print colored(" Could not open gitolite info via SSH!\n", "bold red");
+ return;
+ }
+ while (my $repo_line = <$gitolite_info>) {
+ chomp($repo_line);
+ my ($repo_perms, $repo_fullpath) = split(/\t/, $repo_line, 2);
+ push(@gitolite_repos, $repo_fullpath) if (
+ defined($repo_fullpath)
+ and $repo_perms =~ /R/
+ and $repo_fullpath =~ /$path_match/
+ );
+ }
+ close($gitolite_info) or warn "Error closing gitolite pipe: $!";
+ if ($?) {
+ print colored(" gitolite info exited " . ($? >> 8) . "\n", "bold red");
+ return;
+ } elsif (!@gitolite_repos) {
+ print colored(" No repos matched /$path_match/\n", "yellow");
+ }
+
+ for my $repopath (@gitolite_repos) {
+ my ($reponame) = $repopath =~ m{(?:^|/)([^/]*?)(?:\.git)?$};
+ my $into = "$config{into}/$reponame";
+
+ printf " %-40s ", $reponame;
+ next if already($into => 1);
+ if (-e $into) {
+ update($into, 1);
+ } else {
+ clone($into => "$gitolite_ssh:$repopath", $config{email});
+ }
+ }
+}
+
sub start_master {
my ($host) = @_;
printf colored(" %-40s ", "dark"), $host;
--
To stop receiving notification emails like this one, please contact
sysadmin at bestpractical.com.
More information about the Bps-public-commit
mailing list