Skip to content

Commit

Permalink
Merge pull request #7 from Ensembl/feature/github_sync
Browse files Browse the repository at this point in the history
Feature/github sync
  • Loading branch information
andrewyatz committed May 15, 2014
2 parents 9339fdf + 85b7eb0 commit c35e1b1
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 62 deletions.
86 changes: 27 additions & 59 deletions advanced_bin/github-switchbranch
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,29 @@
use strict;
use warnings;

BEGIN {
use Cwd;
use File::Basename;
use File::Spec;
my $dirname = dirname(Cwd::realpath(__FILE__));
my $lib = File::Spec->catdir($dirname, File::Spec->updir(), 'lib');
if(-d $lib) {
unshift(@INC, $lib);
}
else {
die "Cannot find the lib directory in the expected location $lib";
}
};

use EnsEMBL::GitHub qw/parse_oauth_token rest_request/;

use Pod::Usage;
use Getopt::Long;
use JSON;
use HTTP::Tiny;
use File::Spec;
use Fcntl ':mode';

my $base_url = 'https://api.github.com';
my $http = HTTP::Tiny->new();

run();

sub run {
Expand Down Expand Up @@ -44,7 +57,7 @@ sub get_repos {
}
my $organisation = $opts->{organisation};
print "* Fetching all public repositories from GitHub for $organisation\n";
my $json = get_json($opts, "/orgs/${organisation}/repos?type=public", 'GET');
my $json = get_json($opts, 'GET', "/orgs/${organisation}/repos?type=public");
return [ sort map { $_->{name} } @{$json} ];
}

Expand All @@ -53,7 +66,7 @@ sub default_branch {
my $branch = $opts->{branch};
my $organisation = $opts->{organisation};
print "* Checking for default_branch against user branch $branch\n";
my $json = get_json($opts, "/repos/${organisation}/${repo}", 'GET');
my $json = get_json($opts, 'GET', "/repos/${organisation}/${repo}");
return 1 if $json->{default_branch} eq $branch;
return 0;
}
Expand All @@ -63,7 +76,7 @@ sub has_branch {
my $branch = $opts->{branch};
my $organisation = $opts->{organisation};
print "* Checking if $repo has branch $branch\n";
my $json = get_json($opts, "/repos/${organisation}/${repo}/branches", 'GET');
my $json = get_json($opts, 'GET', "/repos/${organisation}/${repo}/branches");
foreach my $branch_spec (@{$json}) {
if($branch_spec->{name} eq $branch) {
return 1;
Expand All @@ -81,70 +94,25 @@ sub update_branch {

my $organisation = $opts->{organisation};
my $branch = $opts->{branch};
my $token = get_oauth_token($opts);
print "* Updating $repo to branch $branch\n";
my $response = $http->request('PATCH', "$base_url/repos/${organisation}/${repo}", {
headers => { Authorization => 'token '.$token},
content => encode_json({name => $repo, default_branch => $branch}),
});
die "Failed to update (/repos/${organisation}/${repo})! STATUS: $response->{status} REASON: $response->{reason}\n" unless $response->{success};
print "* Done\n";
get_json($opts, 'PATCH', "/repos/${organisation}/${repo}", {name => $repo, default_branch => $branch});
print "* Done\n";
return;
}

sub get_json {
my ($opts, $url, $method) = @_;
my ($opts, $method, $url, $content) = @_;
my $token = get_oauth_token($opts);
my $options = { headers => { } };
$options->{headers}->{Authorization} = 'token '.$token;
my $response = $http->request($method, $base_url.$url, $options);
die "Failed to fetch (${url})! STATUS: $response->{status} REASON: $response->{reason}\n" unless $response->{success};
return decode_json($response->{content});
return rest_request($method, $url, $token, $content);
}

sub get_oauth_token {
my ($opts) = @_;
if($opts->{oauth}) {
return $opts->{oauth};
}
return $opts->{oauth} if $opts->{oauth};
return if ! $opts->{oauth_file};
my $path = $opts->{oauth_file};
my $abs_path = File::Spec->rel2abs($path);
if(! -f $abs_path) {
die "Cannot find a file at the path $abs_path";
}

my ($vol, $dirs, $file) = File::Spec->splitpath($abs_path);

# Check the permisssions of the directory. User only
my $dir_path = File::Spec->catdir($vol, $dirs);
my $dir_mode = (stat($dir_path))[2];
if(($dir_mode & S_IRWXO) != 0) { #Exclude others from reading/writing/executing
die "Other users have read/write/execute access to dir $dir_path";
}
if((($dir_mode & S_IRWXG) >> 3) != 0) { #Exclude groups from reading/writing/executing
die "Group users have read/write/execute access to dir $dir_path";
}

# Then check the permissions of the file. User only
my $file_mode = (stat($abs_path))[2];
if(($file_mode & S_IRWXO) != 0) { #Exclude others from reading/writing/executing the file
die "Other users have read/write/execute access to path $abs_path";
}
if((($file_mode & S_IRWXG) >> 3) != 0) { #Exclude group from reading/writing/executing the file
die "Group users have read/write/execute access to path $abs_path";
}

#Slurp file in and push into the OAuth variable
{
local $/ = undef;
open my $fh, '<', $abs_path or die "Cannot open $abs_path for reading: $!";
my $slurp = <$fh>;
close $fh;
$slurp =~ s/\s//g; # remove any whitespace
$opts->{oauth} = $slurp;
}

return $opts->{oauth};
my $token = parse_oauth_token($path);
return $opts->{oauth} = $token;
}

sub parse_command_line {
Expand Down
175 changes: 175 additions & 0 deletions advanced_bin/github-sync
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env perl

use strict;
use warnings;

BEGIN {
use Cwd;
use File::Basename;
use File::Spec;
my $dirname = dirname(Cwd::realpath(__FILE__));
my $lib = File::Spec->catdir($dirname, File::Spec->updir(), 'lib');
if(-d $lib) {
unshift(@INC, $lib);
}
else {
die "Cannot find the lib directory in the expected location $lib";
}
};

use EnsEMBL::Git qw/bare_clone fetch/;
use EnsEMBL::GitHub qw/parse_oauth_token rest_request/;
use Pod::Usage;
use Getopt::Long;

run();

sub run {
my $opts = parse_command_line();
sync_repos($opts);
return;
}

sub sync_repos {
my ($opts) = @_;
my $repos = get_repos($opts);
foreach my $repo (@{$repos}) {
my $name = $repo->{name};
my $url = $repo->{clone_url};
print "* Working with $name\n" if $opts->{verbose};
my $dir = File::Spec->catdir($opts->{dir}, $name.'.git');
my $action;
my $cwd = cwd();
if(! -d $dir) {
print "* $dir does not exist; cloning repo rather than fetching\n" if $opts->{verbose};
if( ! $opts->{'dry-run'}) {
chdir $opts->{dir};
bare_clone($url, $opts->{verbose}) or die 'Cannot clone repo '.$name;
}
}
else {
print "* Fetching new information from repo (code and tags)\n" if $opts->{verbose};
if(! $opts->{'dry-run'}) {
chdir $dir;
fetch(undef, $opts->{verbose}) or die 'Cannot fetch new commits for '.$name;
fetch('tags', $opts->{verbose}) or die 'Cannot fetch tags for '.$name;
}
}
chdir $cwd;
}
}

sub get_repos {
my ($opts) = @_;
my $organisation = $opts->{organisation};
my $oauth_token = get_oauth_token($opts);
my $repos = rest_request('GET', "/orgs/${organisation}/repos", $oauth_token);
return $repos unless @{$opts->{repository}};
my %lookup = map { $_, 1 } @{$opts->{repository}};
return [ grep { $lookup{$_->{name}} } @{$repos} ];
}

sub get_oauth_token {
my ($opts) = @_;
return $opts->{oauth} if $opts->{oauth};
return if ! $opts->{oauth_file};
my $path = $opts->{oauth_file};
my $token = parse_oauth_token($path);
return $opts->{oauth} = $token;
}

sub parse_command_line {
my $opts = {
repository => [],
organisation => 'Ensembl',
help => 0,
man => 0
};

GetOptions($opts, qw/
repository|repo=s@
dir|directory=s
organisation|organization=s
oauth=s
oauth_file=s
dry-run
verbose
help|?
man
/) or pod2usage(2);

pod2usage(1) if $opts->{help};
pod2usage(-exitval => 0, -verbose => 2) if $opts->{man};

if(!$opts->{dir}) {
pod2usage(-exitval => 1, -verbose => 1, -msg => 'You must specify a --directory to work with');
}

return $opts;
}
__END__
=pod
=head1 NAME
github-sync - Sync an organisation's repositories
=head1 SYNOPSIS
github-sync --directory DIR [--organisation ORG] [--repository REPO] [--oauth OAUTH || --oauth_file FILE] [--dry-run] [-h] [-m]
# Sync all repos in current dir
github-sync --directory $PWD
# Sync a selection
github-sync --directory $PWD --repository ensembl-hive
# Dry run with chatter
github-sync --directory $PWD --dry-run --verbose
=head1 DESCRIPTION
A script to pull down all available repositories from a GitHub organisation and attempts to pull the latest set of commits down from the remote GitHub repositories.
=head1 OPTIONS
=over 8
=item B<--directory>
Specify the directory to synchronize against
=item B<--organisation|organization>
The GitHub organisation to list repositories for. Defaults to Ensembl
=item B<--repository|repo>
The repository to use. If not specified we use all public repositories
=item B<--oauth>
The OAuth token to use. More information is available from L<http://developer.github.com/v3/#authentication> and can be generated from your personal settings page. Not required.
=item B<--oauth_file>
The file which contains the OAuth key. Must be just the OAuth token (any whitespace will be removed). The file must be read/write only by the user and its containing directory must be read/write/execute by the user alone. Not required.
=item B<--dry-run>
Do not fetch or clone
=item B<--help>
Print the help information
=item B<--man>
Print a man page
=back
=cut
15 changes: 12 additions & 3 deletions lib/EnsEMBL/Git.pm
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ our @EXPORT = qw/
json
is_git_repo is_tree_clean is_origin_uptodate is_in_merge
can_fastforward_merge
clone checkout checkout_tracking branch pull fetch rebase ff_merge no_ff_merge git_push shallow_clone
clone checkout checkout_tracking branch pull fetch rebase ff_merge no_ff_merge git_push shallow_clone bare_clone
status
rev_parse branch_exists current_branch
get_config add_config unset_all_config
Expand Down Expand Up @@ -110,19 +110,28 @@ sub is_in_merge {
sub clone {
my ($remote_url, $verbose, $remote) = @_;
my $v = $verbose ? '--verbose' : q{};
return system_ok("git clone -o $remote $v $remote_url");
$remote //= 'origin';
return system_ok("git clone $v -o $remote $remote_url");
}

# Perform a clone but do not bring everything down
sub shallow_clone {
my ($remote_url, $verbose, $remote, $depth, $branch) = @_;
$depth ||= 1;
$depth //= 1;
$remote //= 'origin';
my $v = $verbose ? '--verbose' : q{};
my $b = $branch ? "--branch $branch" : q{};
my $d = $depth ? "--depth $depth" : q{};
return system_ok("git clone -o $remote $v $d $b $remote_url");
}

# Perform a clone but do not create a working sandbox (so just the contents of .git)
sub bare_clone {
my ($remote_url, $verbose) = @_;
my $v = $verbose ? '--verbose' : q{};
return system_ok("git clone $v --bare $remote_url");
}

# Attempt to find the given branch locally by looking for its ref. If no ref is found then we have no branch
sub branch_exists {
my ($branch, $remote) = @_;
Expand Down
Loading

0 comments on commit c35e1b1

Please sign in to comment.