App-gh
view release on metacpan or search on metacpan
lib/App/gh/Command/All.pm view on Meta::CPAN
package App::gh::Command::All;
use utf8;
use warnings;
use strict;
use base qw(App::gh::Command);
use File::Path qw(mkpath rmtree);
use App::gh::Utils qw(
info
error
notice
generate_repo_uri
run_git_fetch
build_git_clone_command
dialog_yes_default
);
use Scope::Guard qw(guard);
use Cwd ();
sub options { (
"verbose" => "verbose",
"prompt" => "prompt",
"into=s" => "into",
"exclude=s@" => "exclude",
"s|skip-exists" => "skip_exists",
"ssh" => "protocol_ssh", # git@github.com:c9s/repo.git
"http" => "protocol_http", # http://github.com/c9s/repo.git
"https" => "protocol_https", # https://github.com/c9s/repo.git
"git|ro" => "protocol_git", # git://github.com/c9s/repo.git
"tags" => "tags",
"q|quiet" => "quiet",
"bare" => "bare",
"mirror" => "mirror",
"b|branch=s" => "branch",
"recursive" => "recursive",
"origin" => "origin",
"p|prefix=s" => "prefix",
"f|force" => "force",
) }
#
# @param string $user github user id
# @param string $type github repository type (all,owner,member,all)
# @see http://developer.github.com/v3/repos/
sub run {
my $self = shift;
my $user = shift;
my $type = shift || 'owner';
# turn off buffering
$|++;
my $cwd = Cwd::getcwd();
$self->{into} ||= $user;
die 'Need account id.' unless $user;
if( $self->{into} ) {
info "Cloning all repositories into @{[ $self->{into} ]}";
mkpath [ $self->{into} ];
chdir $self->{into};
}
info "Getting repositories from $user...";
my @repos = App::gh->github->repos->list_user($user,$type);
unless(@repos) {
info "Found no repository to clone, exiting";
return;
}
my $into = Cwd::getcwd();
my $cloned = 0;
my $total = scalar(@repos);
info "Found " . $total . " repositories to clone:";
print " " x 8 . join " " , map { $_->{name} } @repos;
print "\n";
return unless dialog_yes_default "Are you sure to continue ?";
my $progress = sub {
return sprintf "[%d/%d]",
++$cloned,
$total;
};
my $exclude = do {
my $arr = ref $self->{exclude} eq 'ARRAY' ? $self->{exclude} : [];
+{map { $_ => 1 } @$arr};
};
for my $repo ( @repos ) {
my $local_repo = $repo->{name};
next if exists $exclude->{ $local_repo };
my $uri = generate_repo_uri($user,$repo->{name},$self);
my @command = build_git_clone_command($uri,$self);
$local_repo = $self->{prefix} . $local_repo if $self->{prefix};
push @command , $local_repo;
if( -e $local_repo) {
if( $self->{skip_exists} ) {
info "Found $local_repo, skip.";
next;
}
elsif( $self->{force} ) {
notice "Force mode, Deleting original $local_repo";
rmtree $local_repo or do {
error "Could not remove '$local_repo', skip.\n";
next;
};
}
else {
# fetch and jump to next
info $progress->() . " $local_repo exists, fetching...";
chdir($local_repo);
run_git_fetch {
all => 1,
quiet => $self->{quiet},
tags => $self->{tags},
};
chdir($into);
next;
}
}
if( $self->{prompt} ) {
next unless dialog_yes_default "Clong " . $repo->{name} . ' ?';
}
info sprintf "%s Cloning %s (%d/%d) ...",
$progress->(),
$repo->{full_name},
$repo->{watchers},
$repo->{forks};
my $cmd = join " ",@command;
qx($cmd);
if( $self->{tags} ) {
chdir $local_repo;
run_git_fetch {
all => 1,
quiet => $self->{quiet},
tags => $self->{tags},
};
chdir $into;
}
}
# for my $repo ( @{ $repolist } ) {
# # =================
# # End of conditions for skipping clone
#
#
# if( -e $local_repo_dir ) {
# my $cwd = Cwd::getcwd();
# chdir $local_repo_dir;
# my $guard = guard { chdir $cwd }; # switch back
# print "Updating $local_repo_dir from remotes ..." . $print_progress->() . "\n";
#
# if( qx{ git config --get core.bare } =~ /\Atrue\n?\Z/ ) {
# # Here I assume remote.<remote>.mirror is automatically set.
# # bacause --bare and --mirror do the set-up.
# qx{ git fetch --all };
# }
# else {
# my $flags = qq();
# $flags .= qq{ -q } unless $self->{verbose};
#
# # prune deleted remote branches
# qx{git remote update --prune};
#
# # fetch all remotes
# qx{ git fetch --all };
#
# # update current working repo
# qx{ git pull $flags --rebase --all };
# }
# }
# else {
# # Support old git (which does not support `git clone --mirror`)
# if ($self->{mirror}) {
# my $cwd = Cwd::getcwd();
# chdir $local_repo_dir;
# my $guard = guard { chdir $cwd }; # switch back
# qx{ git config remote.origin.fetch '+refs/*:refs/*' };
# qx{ git config remote.origin.url $uri };
# qx{ git config remote.origin.mirror true };
# }
# }
# }
# print "Done\n";
}
1;
__END__
=head1 NAME
App::gh::Command::All - clone/update all repositories from one
=head1 DESCRIPTION
If you need a mirror of repos from one, you will need this command.
If repos exists, clone command will pull changes for these repos from remotes.
=head1 USAGE
$ mkdir github
$ cd github
To clone c9s' repos:
$ gh all c9s
Once you have all repos cloned, to update them, you only need to run all
command again:
$ gh all c9s
=head1 OPTIONS
Genernal Options:
--prompt
prompt for each cloning repo.
--into {path}
clone repos into a {path}.
--skip-exists, -s
skip existed repos.
--verbose
verbose output.
--bare
clone repos as bare repos.
this option adds postfix ".git" to directory.
e.g.: "{dirname}.git"
--mirror
clone repos as mirror repos.
this option adds postfix ".git" to directory.
e.g.: "{dirname}.git"
--prefix {prefix}
Add prefix to repository name.
--force, -f
remove existed repos before cloning repos.
Clone URL format:
--ssh
--http
--https
--git
=cut
( run in 1.244 second using v1.01-cache-2.11-cpan-0b5f733616e )