source: lab.git/Dev/github/GitHubBackup.pm @ fc647d5

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

utils::get gives hashref tree from API

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