-package utils;
+package GitHubBackup::API;
use strict;
use warnings;
use utf8;
use LWP::UserAgent;
use JSON;
-my $ua = LWP::UserAgent->new;
-my $json = JSON->new->utf8->indent;
+
+sub new {
+ my $class = shift;
+ my $args = shift;
+
+ return bless $args, $class;
+}
sub json_api {
- my $url = shift;
+ my $self = shift;
+ my $url = shift;
+
+ my $ua = LWP::UserAgent->new;
+ my $json = JSON->new->utf8->indent;
my $res = $ua->get(
"https://api.github.com$url"
return $json->decode($res->content);
}
+sub get {
+ my $self = shift;
+ my $url = shift;
+ my %parameters = @_;
+
+ if ($self->access_token) {
+ $parameters{access_token} = $self->access_token;
+ }
+ my $parameters = '';
+ while (my($key, $value) = each %parameters) {
+ $parameters .= "&$key=$value";
+ }
+
+ my $page = 1;
+ my $data = [];
+ while(1) {
+ my $result = $self->json_api("$url?per_page=100&page=$page$parameters");
+ if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
+ push @$data, @$result;
+ $page++;
+
+ next;
+ }
+ last;
+ }
+
+ return $data;
+}
+
+sub access_token {
+ my $self = shift;
+
+ return $self->{access_token}->();
+}
+
package GitHubBackup;
return $self->{directory};
}
+sub access_token {
+ my $self = shift;
+ my $args = shift;
+
+ if (defined $args) {
+ $self->{access_token} = $args;
+ }
+
+ return $self->{access_token};
+}
+
+sub api {
+ my $self = shift;
+
+ unless ($self->{api}) {
+ $self->{api} = GitHubBackup::API->new({
+ access_token => sub {$self->access_token},
+ });
+ }
+
+ return $self->{api};
+}
+
sub repos {
my $self = shift;
return $self->{repos} if ($self->{repos});
+ $self->{repos} = [];
+
my $account = $self->account or croak "account is not set";
+ my $token = ($self->access_token) ? "?access_token=" . $self->access_token : '';
+ my $result;
if (my $repository = $self->repository) {
- $self->{repos} = [
- GitHubBackup::Repository->new({
- directory => sub {$self->directory},
- full_name => "$account/$repository",
- })
- ];
-
- return $self->{repos};
+ $result = [ $self->api->json_api("/repos/$account/$repository$token") ];
}
-
- my $page = 1;
- my @repos;
- while (1) {
- my $result = utils::json_api("/users/$account/repos?per_page=100&page=$page");
- if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
- push @repos, @$result;
- $page++;
-
- next;
- }
- last;
+ else {
+ $result = $self->api->get("/users/$account/repos");
}
- foreach my $repos (@repos) {
+ foreach my $repos (@$result) {
push @{$self->{repos}},
GitHubBackup::Repository->new({
directory => sub {$self->directory},
- full_name => $repos->{full_name},
- clone_url => $repos->{clone_url},
+ api => sub {$self->api},
+ repos => $repos,
})
;
}
use File::chdir;
use File::Spec;
use File::Path qw(mkpath);
+use LWP::UserAgent;
+use JSON;
+
sub new {
my $class = shift;
my $args = shift;
- if (! exists $args->{clone_url}) {
- my $result = utils::json_api('/repos/' . $args->{full_name});
- $args->{clone_url} = $result->{clone_url};
- }
-
return bless $args, $class;
}
+sub clone_url {
+ return (shift)->{repos}{clone_url};
+}
+
+sub full_name {
+ return (shift)->{repos}{full_name};
+}
+
+sub has_downloads {
+ return (shift)->{repos}{has_downloads};
+}
+
+sub forks_count {
+ return (shift)->{repos}{forks_count};
+}
+
+sub has_wiki {
+ return (shift)->{repos}{has_wiki};
+}
+
+sub has_issues {
+ return (shift)->{repos}{has_issues};
+}
+
sub directory {
my $self = shift;
- my $path = $self->{full_name};
+ my $path = $self->full_name;
if (my $base = $self->{directory}->()) {
$path = File::Spec->catfile($base, $path);
}
return $path;
}
+sub api {
+ my $self = shift;
+
+ return $self->{api}->();
+}
+
+sub message {
+ my $self = shift;
+ my $message = shift;
+
+ print $self->full_name, " $message\n";
+
+ return $self;
+}
+
sub sync {
my $self = shift;
my $url = shift;
if (-d "$dir") {
local $CWD = $dir;
- print "fetch ", $dir, "\n";
+ $self->message("fetch --all $dir");
Git::Repository->run(fetch => '--all');
return $self;
}
- print "clone ", $dir, "\n";
+ $self->message("clone --mirror $dir");
mkpath $dir;
Git::Repository->run(clone => '--mirror' => $url => $dir);
my $self = shift;
my $dir = $self->directory . '.git';
- my $url = $self->{clone_url};
+ my $url = $self->clone_url;
$self->sync($url => $dir);
return $self;
}
-sub get_forks {
+sub forks {
my $self = shift;
return $self->{forks} if ($self->{forks});
- my $page = 1;
- while (1) {
- my $result = utils::json_api("/repos/" . $self->{full_name} . "/forks?per_page=100&page=$page");
- if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
- push @{$self->{forks}}, @$result;
- $page++;
-
- next;
- }
- last;
- }
+ $self->{forks} = $self->api->get("/repos/" . $self->full_name . "/forks");
- return $self;
+ return $self->{forks};
}
sub set_forks {
my $self = shift;
- $self->get_forks;
-
my $dir = $self->directory . '.git';
local $CWD = $dir;
my $remotes = Git::Repository->run(branch => '--remotes');
my @fetch;
- foreach my $fork (@{$self->{forks}}) {
+ foreach my $fork (@{$self->forks}) {
if ($remotes =~ /$fork->{full_name}/) {
- print "skip ", $fork->{full_name}, "\n";
+ $self->message("have ". $fork->{full_name});
next;
}
- print "add ", $fork->{full_name}, "\n";
+ $self->message("remote add ". $fork->{full_name});
Git::Repository->run(remote => add => $fork->{full_name} => $fork->{clone_url});
push @fetch, $fork->{full_name};
}
foreach my $fork (@fetch) {
- print "fetch ", $fork, "\n";
+ $self->message("fetch $fork");
Git::Repository->run(fetch => $fork);
}
my $self = shift;
my $dir = $self->directory . '.wiki.git';
- my $url = 'https://github.com/' . $self->{full_name} . '.wiki.git';
+ my $url = 'https://github.com/' . $self->full_name . '.wiki.git';
+
+ my $ua = LWP::UserAgent->new(max_redirect => 0);
+ my $res = $ua->head(
+ 'https://github.com/' . $self->full_name . '/wiki'
+ );
+ if ($res->code != 200) {
+ $self->message("wiki does not exist");
+ return $self;
+ }
$self->sync($url => $dir);
return $self;
}
+sub issues {
+ my $self = shift;
+ return $self->{issues} if ($self->{issues});
+
+ my $open = $self->api->get("/repos/" . $self->full_name . "/issues");
+ my $closed = $self->api->get("/repos/" . $self->full_name . "/issues", state => 'closed');
+
+ if ($open) { push @{$self->{issues}}, @$open }
+ if ($closed) { push @{$self->{issues}}, @$closed }
+
+ return $self->{issues};
+}
+
sub save_issues {
my $self = shift;
+
+ my $ua = LWP::UserAgent->new;
+ my $json = JSON->new->utf8->indent;
+
+ my $dir = $self->directory . '.issues';
+ mkpath $dir unless (-d $dir);
+ local $CWD = $dir;
+ foreach my $issue (@{$self->issues}) {
+ my $number = $issue->{number};
+ $self->message("save issue/$number");
+
+ open my $fh, ">$number.json";
+ print $fh $json->encode($issue);
+ close $fh;
+
+ if (exists $issue->{pull_request}{patch_url}) {
+ $ua->mirror($issue->{pull_request}{patch_url} => "$number.patch");
+ }
+ }
+
+ return $self;
}
sub backup {
my $self = shift;
- $self->clone_git;
- $self->set_forks;
- $self->clone_wiki;
- $self->save_issues;
+ $self->clone_git if ($self->has_downloads eq 'true');
+ $self->set_forks if ($self->forks_count > 0);
+ $self->clone_wiki if ($self->has_wiki eq 'true');
+ $self->save_issues if ($self->has_issues eq 'true');
return $self;
}