source: lab.git/Dev/github/GitHubBackup.pm @ 49aca65

Last change on this file since 49aca65 was 49aca65, checked in by Ken-ichi Mito <mitty@…>, 11 years ago

save issue as json and pacth files

  • Property mode set to 100644
File size: 6.2 KB
Line 
1package utils;
2use strict;
3use warnings;
4use utf8;
5use Carp qw(croak);
6
7use LWP::UserAgent;
8use JSON;
9
10sub json_api {
11    my $url = shift;
12   
13    my $ua = LWP::UserAgent->new;
14    my $json = JSON->new->utf8->indent;
15   
16    my $res = $ua->get(
17        "https://api.github.com$url"
18    );
19   
20    $res->is_success or croak $res->status_line;
21   
22    return $json->decode($res->content);
23}
24
25sub get {
26    my $url = shift;
27    my %parameters = @_;
28   
29    my $parameters = '';
30    while (my($key, $value) = each %parameters) {
31        $parameters .= "&$key=$value";
32    }
33   
34    my $page = 1;
35    my $data;
36    while(1) {
37        my $result = json_api("$url?per_page=100&page=$page$parameters");
38        if (ref($result) eq 'ARRAY' && scalar @$result > 0) {
39            push @$data, @$result;
40            $page++;
41           
42            next;
43        }
44        last;
45    }
46   
47    return $data;
48}
49
50package GitHubBackup;
51
52use strict;
53use warnings;
54use utf8;
55use Carp qw(croak);
56use File::Spec;
57
58
59# both hash and hashref are acceptable
60sub new {
61    my $class = shift;
62    my $args = (ref $_[0] eq 'HASH') ? $_[0] : {@_};
63   
64    return bless $args, $class;
65}
66
67sub account {
68    my $self = shift;
69    my $args = shift;
70   
71    if (defined $args) {
72        $self->{repos} = undef;
73        $self->{account} = $args;
74    }
75   
76    return $self->{account};
77}
78
79sub repository {
80    my $self = shift;
81    my $args = shift;
82   
83    if (defined $args) {
84        $self->{repos} = undef;
85        $self->{repository} = $args;
86    }
87   
88    return $self->{repository};
89}
90
91sub directory {
92    my $self = shift;
93    my $args = shift;
94   
95    if (defined $args) {
96        $self->{directory} = File::Spec->rel2abs($args);
97    }
98   
99    return $self->{directory};
100}
101
102sub repos {
103    my $self = shift;
104    return $self->{repos} if ($self->{repos});
105   
106    my $account = $self->account or croak "account is not set";
107    if (my $repository = $self->repository) {
108        $self->{repos} = [
109            GitHubBackup::Repository->new({
110                directory => sub {$self->directory},
111                full_name => "$account/$repository",
112            })
113        ];
114       
115        return $self->{repos};
116    }
117   
118    my $result = utils::get("/users/$account/repos");
119    foreach my $repos (@$result) {
120        push @{$self->{repos}},
121            GitHubBackup::Repository->new({
122                directory => sub {$self->directory},
123                full_name => $repos->{full_name},
124                clone_url => $repos->{clone_url},
125            })
126        ;
127    }
128   
129    return $self->{repos};
130}
131
132sub backup {
133    my $self = shift;
134   
135    foreach my $repos (@{$self->repos}) {
136        $repos->backup;
137    }
138   
139    return $self;
140}
141
142
143package GitHubBackup::Repository;
144
145use strict;
146use warnings;
147use utf8;
148use Carp qw(croak);
149use Git::Repository;
150use File::chdir;
151use File::Spec;
152use File::Path qw(mkpath);
153use LWP::UserAgent;
154use JSON;
155
156
157sub new {
158    my $class = shift;
159    my $args  = shift;
160   
161    if (! exists $args->{clone_url}) {
162        my $result = utils::json_api('/repos/' . $args->{full_name});
163        $args->{clone_url} = $result->{clone_url};
164    }
165   
166    return bless $args, $class;
167}
168
169sub directory {
170    my $self = shift;
171   
172    my $path = $self->{full_name};
173    if (my $base = $self->{directory}->()) {
174        $path = File::Spec->catfile($base, $path);
175    }
176   
177    return $path;
178}
179
180sub sync {
181    my $self = shift;
182    my $url = shift;
183    my $dir = shift;
184   
185    if (-d "$dir") {
186        local $CWD = $dir;
187        print "fetch ", $dir, "\n";
188        Git::Repository->run(fetch => '--all');
189        return $self;
190    }
191   
192    print "clone ", $dir, "\n";
193    mkpath $dir;
194    Git::Repository->run(clone => '--mirror' => $url => $dir);
195   
196    return $self;
197}
198
199sub clone_git {
200    my $self = shift;
201   
202    my $dir = $self->directory . '.git';
203    my $url = $self->{clone_url};
204   
205    $self->sync($url => $dir);
206   
207    return $self;
208}
209
210sub forks {
211    my $self = shift;
212    return $self->{forks} if ($self->{forks});
213   
214    $self->{forks} = utils::get("/repos/" . $self->{full_name} . "/forks");
215   
216    return $self->{forks};
217}
218
219sub set_forks {
220    my $self = shift;
221   
222    my $dir = $self->directory . '.git';
223    local $CWD = $dir;
224   
225    my $remotes = Git::Repository->run(branch => '--remotes');
226    my @fetch;
227    foreach my $fork (@{$self->forks}) {
228        if ($remotes =~ /$fork->{full_name}/) {
229            print "skip ", $fork->{full_name}, "\n";
230            next;
231        }
232        print "add ", $fork->{full_name}, "\n";
233        Git::Repository->run(remote => add => $fork->{full_name} => $fork->{clone_url});
234        push @fetch, $fork->{full_name};
235    }
236   
237    foreach my $fork (@fetch) {
238        print "fetch ", $fork, "\n";
239        Git::Repository->run(fetch => $fork);
240    }
241   
242    return $self;
243}
244
245sub clone_wiki {
246    my $self = shift;
247   
248    my $dir = $self->directory . '.wiki.git';
249    my $url = 'https://github.com/' . $self->{full_name} . '.wiki.git';
250   
251    $self->sync($url => $dir);
252   
253    return $self;
254}
255
256sub issues {
257    my $self = shift;
258    return $self->{issues} if ($self->{issues});
259   
260    my $open   = utils::get("/repos/" . $self->{full_name} . "/issues");
261    my $closed = utils::get("/repos/" . $self->{full_name} . "/issues", state => 'closed');
262   
263    if ($open)   { push @{$self->{issues}}, @$open }
264    if ($closed) { push @{$self->{issues}}, @$closed }
265   
266    return $self->{issues};
267}
268
269sub save_issues {
270    my $self = shift;
271   
272    my $ua = LWP::UserAgent->new;
273    my $json = JSON->new->utf8->indent;
274   
275    my $dir = $self->directory . '.issues';
276    mkpath $dir unless (-d $dir);
277    local $CWD = $dir;
278    foreach my $issue (@{$self->issues}) {
279        my $number = $issue->{number};
280        print "save issue/$number\n";
281       
282        open my $fh, ">$number.json";
283        print $fh $json->encode($issue);
284        close $fh;
285       
286        if (exists $issue->{pull_request}{patch_url}) {
287            $ua->mirror($issue->{pull_request}{patch_url} => "$number.patch");
288        }
289    }
290   
291    return $self;
292}
293
294sub backup {
295    my $self = shift;
296   
297    $self->clone_git;
298    $self->set_forks;
299    $self->clone_wiki;
300    $self->save_issues;
301   
302    return $self;
303}
304
305
3061;
307__END__
Note: See TracBrowser for help on using the repository browser.