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);
39 while (my($key, $value) = each %parameters) {
40 $parameters .= "&$key=$value";
46 my $result = $self->json_api("$url?per_page=100&page=$page$parameters");
47 if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
48 push @$data, @$result;
62 return $self->{access_token}->();
75 # both hash and hashref are acceptable
78 my $args = (ref $_[0] eq 'HASH') ? $_[0] : {@_};
80 return bless $args, $class;
88 $self->{repos} = undef;
89 $self->{account} = $args;
92 return $self->{account};
100 $self->{repos} = undef;
101 $self->{repository} = $args;
104 return $self->{repository};
112 $self->{directory} = File::Spec->rel2abs($args);
115 return $self->{directory};
123 $self->{access_token} = $args;
126 return $self->{access_token};
132 unless ($self->{api}) {
133 $self->{api} = GitHubBackup::API->new({
134 access_token => sub {$self->access_token},
143 return $self->{repos} if ($self->{repos});
147 my $account = $self->account or croak "account is not set";
149 if (my $repository = $self->repository) {
150 $result = [ $self->api->json_api("/repos/$account/$repository") ];
153 $result = $self->api->get("/users/$account/repos");
156 foreach my $repos (@$result) {
157 push @{$self->{repos}},
158 GitHubBackup::Repository->new({
159 directory => sub {$self->directory},
160 api => sub {$self->api},
166 return $self->{repos};
172 foreach my $repos (@{$self->repos}) {
180 package GitHubBackup::Repository;
189 use File::Path qw(mkpath);
198 return bless $args, $class;
202 return (shift)->{repos}{clone_url};
206 return (shift)->{repos}{full_name};
210 return (shift)->{repos}{has_downloads};
214 return (shift)->{repos}{forks_count};
218 return (shift)->{repos}{has_wiki};
222 return (shift)->{repos}{has_issues};
228 my $path = $self->full_name;
229 if (my $base = $self->{directory}->()) {
230 $path = File::Spec->catfile($base, $path);
239 return $self->{api}->();
246 print $self->full_name, " $message\n";
258 $self->message("++> $dir");
259 Git::Repository->run(fetch => '--all');
263 $self->message("==> $dir");
265 Git::Repository->run(clone => '--mirror' => $url => $dir);
273 my $dir = $self->directory . '.git';
274 my $url = $self->clone_url;
276 $self->sync($url => $dir);
283 return $self->{forks} if ($self->{forks});
285 $self->{forks} = $self->api->get("/repos/" . $self->full_name . "/forks");
287 return $self->{forks};
293 my $dir = $self->directory . '.git';
296 my $remotes = Git::Repository->run(branch => '--remotes');
298 foreach my $fork (@{$self->forks}) {
299 if ($remotes =~ /$fork->{full_name}/) {
300 $self->message("--- ". $fork->{full_name});
303 $self->message("+++ ". $fork->{full_name});
304 Git::Repository->run(remote => add => $fork->{full_name} => $fork->{clone_url});
305 push @fetch, $fork->{full_name};
308 foreach my $fork (@fetch) {
309 $self->message("--> $fork");
310 Git::Repository->run(fetch => $fork);
319 my $dir = $self->directory . '.wiki.git';
320 my $url = 'https://github.com/' . $self->full_name . '.wiki.git';
322 my $ua = LWP::UserAgent->new(max_redirect => 0);
324 'https://github.com/' . $self->full_name . '/wiki'
326 if ($res->code != 200) {
327 $self->message("wiki does not exist");
331 $self->sync($url => $dir);
338 return $self->{issues} if ($self->{issues});
340 my $open = $self->api->get("/repos/" . $self->full_name . "/issues");
341 my $closed = $self->api->get("/repos/" . $self->full_name . "/issues", state => 'closed');
343 if ($open) { push @{$self->{issues}}, @$open }
344 if ($closed) { push @{$self->{issues}}, @$closed }
346 return $self->{issues};
352 my $ua = LWP::UserAgent->new;
353 my $json = JSON->new->utf8->indent;
355 my $dir = $self->directory . '.issues';
356 mkpath $dir unless (-d $dir);
358 foreach my $issue (@{$self->issues}) {
359 my $number = $issue->{number};
360 $self->message("+++ issue/$number");
362 open my $fh, ">$number.json";
363 print $fh $json->encode($issue);
366 if (exists $issue->{pull_request}{patch_url}) {
367 $ua->mirror($issue->{pull_request}{patch_url} => "$number.patch");
377 $self->clone_git if ($self->has_downloads eq 'true');
378 $self->set_forks if ($self->forks_count > 0);
379 $self->clone_wiki if ($self->has_wiki eq 'true');
380 $self->save_issues if ($self->has_issues eq 'true');