source: lab.git/Dev/github/GitHubBackup.pm @ 55d0513

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

utils::get with %parameters

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