source: lab.git/Dev/github/GitHubBackup.pm @ 682d2d9

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

add forks as remotes/'full_name'

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