1 package GitHubBackup::API;
14 return bless $args, $class;
21 my $ua = LWP::UserAgent->new;
22 my $json = JSON->new->utf8->indent;
25 "https://api.github.com$url"
28 $res->is_success or croak $res->status_line;
30 return $json->decode($res->content);
38 if ($self->access_token) {
39 $parameters{access_token} = $self->access_token;
42 while (my($key, $value) = each %parameters) {
43 $parameters .= "&$key=$value";
49 my $result = $self->json_api("$url?per_page=100&page=$page$parameters");
50 if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
51 push @$data, @$result;
65 return $self->{access_token}->();
78 # both hash and hashref are acceptable
81 my $args = (ref $_[0] eq 'HASH') ? $_[0] : {@_};
83 return bless $args, $class;
91 $self->{repos} = undef;
92 $self->{account} = $args;
95 return $self->{account};
103 $self->{repos} = undef;
104 $self->{repository} = $args;
107 return $self->{repository};
115 $self->{directory} = File::Spec->rel2abs($args);
118 return $self->{directory};
126 $self->{access_token} = $args;
129 return $self->{access_token};
135 unless ($self->{api}) {
136 $self->{api} = GitHubBackup::API->new({
137 access_token => sub {$self->access_token},
146 return $self->{repos} if ($self->{repos});
150 my $account = $self->account or croak "account is not set";
151 my $token = ($self->access_token) ? "?access_token=" . $self->access_token : '';
153 if (my $repository = $self->repository) {
154 $result = [ $self->api->json_api("/repos/$account/$repository$token") ];
157 $result = $self->api->get("/users/$account/repos");
160 foreach my $repos (@$result) {
161 push @{$self->{repos}},
162 GitHubBackup::Repository->new({
163 directory => sub {$self->directory},
164 api => sub {$self->api},
170 return $self->{repos};
176 foreach my $repos (@{$self->repos}) {
184 package GitHubBackup::Repository;
193 use File::Path qw(mkpath);
202 return bless $args, $class;
206 return (shift)->{repos}{clone_url};
210 return (shift)->{repos}{full_name};
214 return (shift)->{repos}{has_downloads};
218 return (shift)->{repos}{forks_count};
222 return (shift)->{repos}{has_wiki};
226 return (shift)->{repos}{has_issues};
232 my $path = $self->full_name;
233 if (my $base = $self->{directory}->()) {
234 $path = File::Spec->catfile($base, $path);
243 return $self->{api}->();
250 print $self->full_name, " $message\n";
262 $self->message("++> $dir");
263 Git::Repository->run(fetch => '--all');
267 $self->message("==> $dir");
269 Git::Repository->run(clone => '--mirror' => $url => $dir);
277 my $dir = $self->directory . '.git';
278 my $url = $self->clone_url;
280 $self->sync($url => $dir);
287 return $self->{forks} if ($self->{forks});
289 $self->{forks} = $self->api->get("/repos/" . $self->full_name . "/forks");
291 return $self->{forks};
297 my $dir = $self->directory . '.git';
300 my $remotes = Git::Repository->run(branch => '--remotes');
302 foreach my $fork (@{$self->forks}) {
303 if ($remotes =~ /$fork->{full_name}/) {
304 $self->message("--- ". $fork->{full_name});
307 $self->message("+++ ". $fork->{full_name});
308 Git::Repository->run(remote => add => $fork->{full_name} => $fork->{clone_url});
309 push @fetch, $fork->{full_name};
312 foreach my $fork (@fetch) {
313 $self->message("--> $fork");
314 Git::Repository->run(fetch => $fork);
323 my $dir = $self->directory . '.wiki.git';
324 my $url = 'https://github.com/' . $self->full_name . '.wiki.git';
326 my $ua = LWP::UserAgent->new(max_redirect => 0);
328 'https://github.com/' . $self->full_name . '/wiki'
330 if ($res->code != 200) {
331 $self->message("wiki does not exist");
335 $self->sync($url => $dir);
342 return $self->{issues} if ($self->{issues});
344 my $open = $self->api->get("/repos/" . $self->full_name . "/issues");
345 my $closed = $self->api->get("/repos/" . $self->full_name . "/issues", state => 'closed');
347 if ($open) { push @{$self->{issues}}, @$open }
348 if ($closed) { push @{$self->{issues}}, @$closed }
350 return $self->{issues};
356 my $ua = LWP::UserAgent->new;
357 my $json = JSON->new->utf8->indent;
359 my $dir = $self->directory . '.issues';
360 mkpath $dir unless (-d $dir);
362 foreach my $issue (@{$self->issues}) {
363 my $number = $issue->{number};
364 $self->message("+++ issue/$number");
366 open my $fh, ">$number.json";
367 print $fh $json->encode($issue);
370 if (exists $issue->{pull_request}{patch_url}) {
371 $ua->mirror($issue->{pull_request}{patch_url} => "$number.patch");
381 $self->clone_git if ($self->has_downloads eq 'true');
382 $self->set_forks if ($self->forks_count > 0);
383 $self->clone_wiki if ($self->has_wiki eq 'true');
384 $self->save_issues if ($self->has_issues eq 'true');