1package Net::GitHub::V3::Repos;
2
3use Moo;
4
5our $VERSION = '1.02';
6our $AUTHORITY = 'cpan:FAYLAND';
7
8use Carp;
9use URI::Escape;
10use URI;
11use HTTP::Request::Common qw(POST);
12
13with 'Net::GitHub::V3::Query';
14
15sub list {
16    my ( $self, $args ) = @_;
17
18    return $self->query(_repos_arg2url($args));
19}
20
21
22sub next_repo {
23    my ( $self, $args ) = @_;
24
25    return $self->next(_repos_arg2url($args));
26}
27
28sub close_repo {
29    my ( $self, $args ) = @_;
30
31    return $self->close(_repos_arg2url($args));
32}
33
34sub _repos_arg2url {
35    my ($args) = @_;
36
37    # for old
38    unless (ref($args) eq 'HASH') {
39        $args = { type => $args };
40    }
41
42    my $uri = URI->new('/user/repos');
43    $uri->query_form($args);
44    return $uri->as_string;
45}
46
47
48sub list_all {
49    my ( $self, $since ) = @_;
50
51    return $self->query(_all_repos_arg2url($since));
52}
53
54sub next_all_repo {
55    my ( $self, $since ) = @_;
56
57    return $self->next(_all_repos_arg2url($since));
58}
59
60sub close_all_repo {
61    my ( $self, $since ) = @_;
62
63    return $self->close(_all_repos_arg2url($since));
64}
65
66sub _all_repos_arg2url {
67    my ( $since ) = @_;
68    $since ||= 'first';
69    my $u = '/repositories';
70    $u .= '?since=' . $since if $since ne 'first';
71    return $u;
72}
73
74sub list_user {
75    my $self = shift;
76
77    return $self->query($self->_user_repos_arg2url(@_));
78}
79
80sub next_user_repo {
81    my $self = shift;
82
83    return $self->next($self->_user_repos_arg2url(@_));
84}
85
86sub close_user_repo {
87    my $self = shift;
88
89    return $self->close($self->_user_repos_arg2url(@_));
90}
91
92sub _user_repos_arg2url {
93    my ($self, $user, $args) = @_;
94    $user ||= $self->u;
95
96    # for old
97    unless (ref($args) eq 'HASH') {
98        $args = { type => $args };
99    }
100
101    my $uri = URI->new("/users/" . uri_escape($user) . "/repos");
102    $uri->query_form($args);
103    return $uri->as_string;
104}
105
106sub list_org {
107    my $self = shift;
108
109    return $self->query($self->_org_repos_arg2url(@_));
110}
111
112sub next_org_repo {
113    my $self = shift;
114
115    return $self->next($self->_org_repos_arg2url(@_));
116}
117
118sub close_org_repo {
119    my $self = shift;
120
121    return $self->close($self->_org_repos_arg2url(@_));
122}
123
124sub _org_repos_arg2url {
125    my ($self, $org, $type) = @_;
126    $type ||= 'all';
127    my $u = "/orgs/" . uri_escape($org) . "/repos";
128    $u .= '?type=' . $type if $type ne 'all';
129    return $u;
130}
131
132
133sub create {
134    my ( $self, $data ) = @_;
135
136    my $u = '/user/repos';
137    if (exists $data->{org}) {
138        my $o = delete $data->{org};
139        $u = "/orgs/" . uri_escape($o) . "/repos";
140    }
141
142    return $self->query('POST', $u, $data);
143}
144
145sub upload_asset {
146    my $self = shift;
147    unshift @_, $self->u, $self->repo if @_ < 5;
148    my ($user, $repos, $release_id, $name, $content_type, $file_content) = @_;
149
150    my $ua = $self->ua;
151    my $url = $self->upload_url . "/repos/$user/$repos/releases/$release_id/assets?name=" . uri_escape($name);
152    my $req = HTTP::Request->new( 'POST', $url );
153    $req->accept_decodable;
154    $req->content($file_content);
155    $req->header( 'Content-Type', $content_type );
156
157    my $res = $ua->request($req);
158
159    my $data;
160    if ($res->header('Content-Type') and $res->header('Content-Type') =~ 'application/json') {
161        my $json = $res->decoded_content;
162        $data = eval { $self->json->decode($json) };
163        unless ($data) {
164            # We tolerate bad JSON for errors,
165            # otherwise we just rethrow the JSON parsing problem.
166            die unless $res->is_error;
167            $data = { message => $res->message };
168        }
169    } else {
170        $data = { message => $res->message };
171    }
172
173    return wantarray ? %$data : $data;
174}
175
176sub commits {
177    my $self = shift;
178
179    return $self->query($self->_commits_arg2url(@_));
180}
181
182sub next_commit {
183    my $self = shift;
184
185    return $self->next($self->_commits_arg2url(@_));
186}
187
188sub close_commit {
189    my $self = shift;
190
191    return $self->close($self->_commits_arg2url(@_));
192}
193
194sub _commits_arg2url {
195    my $self = shift;
196    if (@_ < 2) {
197        unshift @_, $self->repo;
198        unshift @_, $self->u;
199    }
200    my ($user, $repos, $args) = @_;
201
202    my $uri = URI->new("/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/commits');
203    $uri->query_form($args);
204    return $uri->as_string;
205}
206
207
208
209sub list_deployments {
210    my $self = shift;
211
212    return $self->query($self->deployments_arg2url(@_));
213}
214
215sub next_deployment {
216    my $self = shift;
217
218    return $self->next($self->deployments_arg2url(@_));
219}
220
221sub close_deployment {
222    my $self = shift;
223
224    return $self->close($self->deployments_arg2url(@_));
225}
226
227sub _deployments_arg2url {
228    my $self = shift;
229    if (@_ < 2) {
230        unshift @_, $self->repo;
231        unshift @_, $self->u;
232    }
233    my ($user, $repos, $args) = @_;
234
235    my $uri = URI->new("/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/deployments');
236    $uri->query_form($args);
237    return $uri->as_string;
238}
239
240
241## build methods on fly
242my %__methods = (
243
244    get => { url => "/repos/%s/%s" },
245    update => { url => "/repos/%s/%s", method => 'PATCH', args => 1 },
246    contributors => { url => "/repos/%s/%s/contributors", paginate => 1 },
247    languages => { url => "/repos/%s/%s/languages" },
248    teams     => { url => "/repos/%s/%s/teams", paginate => 1 },
249    tags      => { url => "/repos/%s/%s/tags", paginate => 1 },
250    branches  => { url => "/repos/%s/%s/branches", paginate => 1 },
251    branch => { url => "/repos/%s/%s/branches/%s" },
252    delete => { url => "/repos/%s/%s", method => 'DELETE', check_status => 204 },
253
254    # http://developer.github.com/v3/repos/collaborators/
255    collaborators       => { url => "/repos/%s/%s/collaborators", paginate => 1 },
256    is_collaborator     => { url => "/repos/%s/%s/collaborators/%s", check_status => 204 },
257    add_collaborator    => { url => "/repos/%s/%s/collaborators/%s", method => 'PUT', check_status => 204 },
258    delete_collaborator => { url => "/repos/%s/%s/collaborators/%s", method => 'DELETE', check_status => 204 },
259
260    # http://developer.github.com/v3/repos/commits/
261    commit   => { url => "/repos/:owner/:repo/commits/:ref", v => 2 },
262    comments => { url => "/repos/%s/%s/comments", paginate => 1 },
263    comment  => { url => "/repos/%s/%s/comments/%s" },
264    commit_comments => { url => "/repos/%s/%s/commits/%s/comments", paginate => 1 },
265    create_comment => { url => "/repos/%s/%s/commits/%s/comments", method => 'POST', args => 1 },
266    update_comment => { url => "/repos/%s/%s/comments/%s", method => 'PATCH', args => 1 },
267    delete_comment => { url => "/repos/%s/%s/comments/%s", method => 'DELETE', check_status => 204 },
268    compare_commits => { url => "/repos/%s/%s/compare/%s...%s" },
269
270    # http://developer.github.com/v3/repos/contents/
271    readme => { url => "/repos/%s/%s/readme" },
272    get_content => { url => "/repos/:owner/:repo/contents/:path", v => 2 },
273
274    # http://developer.github.com/v3/repos/downloads/
275    downloads => { url => "/repos/%s/%s/downloads", paginate => 1 },
276    download  => { url => "/repos/%s/%s/downloads/%s" },
277    delete_download => { url => "/repos/%s/%s/downloads/%s", method => 'DELETE', check_status => 204 },
278
279    # http://developer.github.com/v3/repos/releases/
280    releases => { url => "/repos/%s/%s/releases", paginate => 1 },
281    release  => { url => "/repos/%s/%s/releases/%s" },
282    create_release => { url => "/repos/%s/%s/releases", method => 'POST', args => 1 },
283    update_release => { url => "/repos/%s/%s/releases/%s", method => 'PATCH', args => 1 },
284    delete_release => { url => "/repos/%s/%s/releases/%s", method => 'DELETE', check_status => 204 },
285
286    release_assets => { url => "/repos/%s/%s/releases/%s/assets", paginate => 1 },
287    release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s" },
288    update_release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s", method => 'PATCH', args => 1 },
289    delete_release_asset => { url => "/repos/%s/%s/releases/%s/assets/%s", method => 'DELETE', check_status => 204 },
290
291    forks => { url => "/repos/%s/%s/forks", paginate => 1 },
292
293    # http://developer.github.com/v3/repos/keys/
294    keys => { url => "/repos/%s/%s/keys", paginate => 1 },
295    key  => { url => "/repos/%s/%s/keys/%s" },
296    create_key => { url => "/repos/%s/%s/keys", method => 'POST', args => 1 },
297    update_key => { url => "/repos/%s/%s/keys/%s", method => 'PATCH', check_status => 204, args => 1 },
298    delete_key => { url => "/repos/%s/%s/keys/%s", method => 'DELETE', check_status => 204 },
299
300    # http://developer.github.com/v3/repos/watching/
301    watchers => { url => "/repos/%s/%s/watchers", paginate => 1 },
302    is_watching => { url => "/user/watched/%s/%s", is_u_repo => 1, check_status => 204 },
303    watch   => { url => "/user/watched/%s/%s", is_u_repo => 1, method => 'PUT', check_status => 204 },
304    unwatch => { url => "/user/watched/%s/%s", is_u_repo => 1, method => 'DELETE', check_status => 204 },
305
306    subscribers   => { url => "/repos/%s/%s/subscribers", paginate => 1 },
307    subscription  => { url => "/repos/%s/%s/subscription" },
308    is_subscribed => { url => "/repos/%s/%s/subscription", check_status => 200 },
309    subscribe     => { url => "/repos/%s/%s/subscription", method => 'PUT',
310        check_status => 200, args => 1 },
311    unsubscribe   => { url => "/repos/%s/%s/subscription", method => 'DELETE', check_status => 204 },
312
313    # http://developer.github.com/v3/repos/hooks/
314    hooks => { url => "/repos/%s/%s/hooks", paginate => 1 },
315    hook  => { url => "/repos/%s/%s/hooks/%s" },
316    delete_hook => { url => "/repos/%s/%s/hooks/%s", method => 'DELETE', check_status => 204 },
317    test_hook   => { url => "/repos/%s/%s/hooks/%s/test", method => 'POST', check_status => 204 },
318    create_hook => { url => "/repos/%s/%s/hooks", method => 'POST',  args => 1 },
319    update_hook => { url => "/repos/%s/%s/hooks/%s", method => 'PATCH', args => 1 },
320
321    # http://developer.github.com/v3/repos/merging/
322    merges => { url => "/repos/%s/%s/merges", method => 'POST', args => 1 },
323
324    # http://developer.github.com/v3/repos/statuses/
325    list_statuses => { url => "/repos/%s/%s/statuses/%s", paginate => { name => 'status' } },
326    create_status => { url => "/repos/%s/%s/statuses/%s", method => 'POST', args => 1 },
327
328    # https://developer.github.com/v3/repos/deployments
329    create_deployment => { url => "/repos/%s/%s/deployments", method => 'POST', args => 1 },
330    create_deployment_status => { url => "/repos/%s/%s/deployments/%s/statuses", method => 'POST', args => 1},
331    list_deployment_statuses => { url => "/repos/%s/%s/deployments/%s/statuses", method => 'GET', paginate => { name => 'deployment_status' } },
332
333    contributor_stats => { url => "/repos/%s/%s/stats/contributors", method => 'GET'},
334    commit_activity => { url => "/repos/%s/%s/stats/commit_activity", method => 'GET'},
335    code_frequency => { url => "/repos/%s/%s/stats/code_frequency", method => 'GET'},
336    participation => { url => "/repos/%s/%s/stats/participation", method => 'GET'},
337    punch_card => { url => "/repos/%s/%s/stats/punch_card", method => 'GET'},
338
339);
340__build_methods(__PACKAGE__, %__methods);
341
342sub create_download {
343    my $self = shift;
344
345    if (@_ == 1) {
346        unshift @_, $self->repo;
347        unshift @_, $self->u;
348    }
349    my ($user, $repos, $download) = @_;
350
351    my $file = delete $download->{file};
352
353    my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/downloads';
354    my $d = $self->query('POST', $u, $download);
355    if (defined $file) {
356        return $self->upload_download($d, $file);
357    } else {
358        return $d;
359    }
360}
361
362sub upload_download {
363    my $self = shift;
364
365    if (@_ < 3) {
366        unshift @_, $self->repo;
367        unshift @_, $self->u;
368    }
369    my ($user, $repos, $download, $file) = @_;
370
371    # must successful on create_download
372    return 0 unless exists $download->{s3_url};
373
374    ## POST form-data
375    my %data = (
376        Content_Type => 'form-data',
377        Content      => [
378            'key'                   => $download->{path},
379            'acl'                   => $download->{acl},
380            'success_action_status' => 201,
381            'Filename'              => $download->{name},
382            'AWSAccessKeyId'        => $download->{accesskeyid},
383            'Policy'                => $download->{policy},
384            'Signature'             => $download->{signature},
385            'Content-Type'          => $download->{mime_type},
386            'file'                  => [ $file ],
387        ],
388    );
389    my $request = POST $download->{s3_url}, %data;
390    my $res = $self->ua->request($request);
391    return $res->code == 201 ? 1 : 0;
392}
393
394## http://developer.github.com/v3/repos/forks/
395sub create_fork {
396    my $self = shift;
397
398    if (@_ < 2) {
399        unshift @_, $self->repo;
400        unshift @_, $self->u;
401    }
402    my ($user, $repos, $org) = @_;
403
404    my $u = "/repos/" . uri_escape($user) . "/" . uri_escape($repos) . '/forks';
405    $u .= '?org=' . $org if defined $org;
406    return $self->query('POST', $u);
407}
408
409## http://developer.github.com/v3/repos/watching/
410sub watched {
411    my ($self, $user) = @_;
412
413    my $u = $user ? '/users/' . uri_escape($user). '/watched' : '/user/watched';
414    return $self->query($u);
415}
416
417no Moo;
418
4191;
420__END__
421
422=head1 NAME
423
424Net::GitHub::V3::Repos - GitHub Repos API
425
426=head1 SYNOPSIS
427
428    use Net::GitHub::V3;
429
430    my $gh = Net::GitHub::V3->new; # read L<Net::GitHub::V3> to set right authentication info
431    my $repos = $gh->repos;
432
433    # set :user/:repo for simple calls
434    $repos->set_default_user_repo('fayland', 'perl-net-github');
435    my @contributors = $repos->contributors; # don't need pass user and repos
436
437
438=head1 DESCRIPTION
439
440=head2 METHODS
441
442=head3 Repos
443
444L<http://developer.github.com/v3/repos/>
445
446=over 4
447
448=item list
449
450=item list_all
451
452    # All public repositories on Github
453    my @rp = $repos->list_all;
454    # starting at id 500
455    my @rp = $repos->list_all(500);
456
457=item list_user
458
459=item list_org
460
461    my @rp = $repos->list; # or my $rp = $repos->list;
462    my @rp = $repos->list({
463        type => 'private'
464        sort => 'updated'
465    });
466    my @rp = $repos->list_user('c9s');
467    my @rp = $repos->list_user('c9s', {
468        type => 'member'
469    });
470    my @rp = $repos->list_org('perlchina');
471    my @rp = $repos->list_org('perlchina', 'public');
472
473=item next_repo, next_all_repo, next_user_repo, next_org_repo
474
475    # Iterate over your repositories
476    while (my $repo = $repos->next_repo) { ...; }
477    # Iterate over all public repositories
478    while (my $repo = $repos->next_all_repo(500)) { ...; }
479    # Iterate over repositories of another user
480    while (my $repo = $repos->next_user_repo('c9s')) { ...; }
481    # Iterate over repositories of an organisation
482    while (my $repo = $repos->next_org_repo('perlchina','public')) { ...; }
483
484=item create
485
486    # create for yourself
487    my $rp = $repos->create( {
488        "name" => "Hello-World",
489        "description" => "This is your first repo",
490        "homepage" => "https://github.com"
491    } );
492    # create for organization
493    my $rp = $repos->create( {
494        "org"  => "perlchina", ## the organization
495        "name" => "Hello-World",
496        "description" => "This is your first repo",
497        "homepage" => "https://github.com"
498    } );
499
500=item get
501
502    my $rp = $repos->get('fayland', 'perl-net-github');
503
504=back
505
506B<To ease the keyboard, we provied two ways to call any method which starts with :user/:repo>
507
5081. SET user/repos before call methods below
509
510    $gh->set_default_user_repo('fayland', 'perl-net-github'); # take effects for all $gh->
511    $repos->set_default_user_repo('fayland', 'perl-net-github'); # only take effect to $gh->repos
512    my @contributors = $repos->contributors;
513
5142. If it is just for once, we can pass :user, :repo before any arguments
515
516    my @contributors = $repos->contributors($user, $repo);
517
518=over 4
519
520=item update
521
522    $repos->update({ homepage => 'https://metacpan.org/module/Net::GitHub' });
523
524=item delete
525
526    $repos->delete();
527
528=item contributors
529
530=item languages
531
532=item teams
533
534=item tags
535
536=item contributors
537
538    my @contributors = $repos->contributors;
539    my @languages = $repos->languages;
540    my @teams = $repos->teams;
541    my @tags = $repos->tags;
542    my @branches = $repos->branches;
543    my $branch = $repos->branch('master');
544    while (my $contributor = $repos->next_contributor) { ...; }
545    while (my $team = $repos->next_team) { ... ; }
546    while (my $tags = $repos->next_tag) { ... ; }
547
548=back
549
550=head3 Repo Collaborators API
551
552L<http://developer.github.com/v3/repos/collaborators/>
553
554=over 4
555
556=item collaborators
557
558=item is_collaborator
559
560=item add_collaborator
561
562=item delete_collaborator
563
564    my @collaborators = $repos->collaborators;
565    while (my $collaborator = $repos->next_collaborator) { ...; }
566    my $is = $repos->is_collaborator('fayland');
567    $repos->add_collaborator('fayland');
568    $repos->delete_collaborator('fayland');
569
570=back
571
572=head3 Commits API
573
574L<http://developer.github.com/v3/repos/commits/>
575
576=over 4
577
578=item commits
579
580=item commit
581
582    my @commits = $repos->commits;
583    my @commits = $repos->commits({
584        author => 'fayland'
585    });
586    my $commit  = $repos->commit($sha);
587    while (my $commit = $repos->next_commit({...})) { ...; }
588
589=item comments
590
591=item commit_comments
592
593=item create_comment
594
595=item comment
596
597=item update_comment
598
599=item delete_comment
600
601    my @comments = $repos->comments;
602    while (my $comment = $repos->next_comment) { ...; }
603    my @comments = $repos->commit_comments($sha);
604    while (my $comment = $repos->next_commit_comment($sha)) { ...; }
605    my $comment  = $repos->create_comment($sha, {
606        "body" => "Nice change",
607        "commit_id" => "6dcb09b5b57875f334f61aebed695e2e4193db5e",
608        "line" => 1,
609        "path" => "file1.txt",
610        "position" => 4
611    });
612    my $comment = $repos->comment($comment_id);
613    my $comment = $repos->update_comment($comment_id, {
614        "body" => "Nice change"
615    });
616    my $st = $repos->delete_comment($comment_id);
617
618=item compare_commits
619
620    my $diffs = $repos->compare_commits($base, $head);
621
622=back
623
624=head3 Forks API
625
626L<http://developer.github.com/v3/repos/forks/>
627
628=over 4
629
630=item forks
631
632=item create_fork
633
634    my @forks = $repos->forks;
635    while (my $fork = $repos->next_fork) { ...; }
636    my $fork = $repos->create_fork;
637    my $fork = $repos->create_fork($org);
638
639=back
640
641=head3 Repos Deploy Keys API
642
643L<http://developer.github.com/v3/repos/keys/>
644
645=over 4
646
647=item keys
648
649=item key
650
651=item create_key
652
653=item update_key
654
655=item delete_key
656
657    my @keys = $repos->keys;
658    while (my $key = $repos->next_key) { ...; }
659    my $key  = $repos->key($key_id); # get key
660    $repos->create_key( {
661        title => 'title',
662        key   => $key
663    } );
664    $repos->update_key($key_id, {
665        title => $title,
666        key   => $key
667    });
668    $repos->delete_key($key_id);
669
670=back
671
672=head3 Repo Watching API
673
674L<http://developer.github.com/v3/repos/watching/>
675
676=over 4
677
678=item watchers
679
680    my @watchers = $repos->watchers;
681    while (my $watcher = $repos->next_watcher) { ...; }
682
683=item watched
684
685    my @repos = $repos->watched; # what I watched
686    my @repos = $repos->watched('c9s');
687
688=item is_watching
689
690    my $is_watching = $repos->is_watching;
691    my $is_watching = $repos->is_watching('fayland', 'perl-net-github');
692
693=item watch
694
695=item unwatch
696
697    my $st = $repos->watch();
698    my $st = $repos->watch('fayland', 'perl-net-github');
699    my $st = $repos->unwatch();
700    my $st = $repos->unwatch('fayland', 'perl-net-github');
701
702=back
703
704=head3 Subscriptions
705
706Github changed the ideas of Watchers (stars) and Subscriptions (new watchers).
707
708    https://github.com/blog/1204-notifications-stars
709
710The Watchers code in this module predates the terminology change, so the new
711Watcher methods use the GitHub 'subscription' terminology.
712
713=over 4
714
715=item subscribers
716
717Returns a list of subscriber data hashes.
718
719=item next_subscriber
720
721Returns the next subscriber in the list, or undef if there are no more subscribers.
722
723=item is_subscribed
724
725Returns true or false if you are subscribed
726
727    $repos->is_subscribed();
728    $repos->is_subscribed('fayland','perl-net-github');
729
730=item subscription
731
732Returns more information about your subscription to a repo.
733is_subscribed is a shortcut to calling this and checking for
734subscribed => 1.
735
736=item subscribe
737
738Required argument telling github if you want to subscribe or if you want
739to ignore mentions. If you want to change from subscribed to ignores you
740need to unsubscribe first.
741
742    $repos->subscribe('fayland','perl-net-github', { subscribed => 1 })
743    $repos->subscribe('fayland','perl-net-github', { ignored => 1 })
744
745=item unsubscribe
746
747    $repos->unsubscribe('fayland','perl-net-github');
748
749=back
750
751=head3 Hooks API
752
753L<http://developer.github.com/v3/repos/hooks/>
754
755=over 4
756
757=item hooks
758
759=item next_hook
760
761=item hook
762
763=item create_hook
764
765=item update_hook
766
767=item test_hook
768
769=item delete_hook
770
771    my @hooks = $repos->hooks;
772    while (my $hook = $repos->next_hook) { ...; }
773    my $hook  = $repos->hook($hook_id);
774    my $hook  = $repos->create_hook($hook_hash);
775    my $hook  = $repos->update_hook($hook_id, $new_hook_hash);
776    my $st    = $repos->test_hook($hook_id);
777    my $st    = $repos->delete_hook($hook_id);
778
779=back
780
781=head3 Repo Merging API
782
783L<http://developer.github.com/v3/repos/merging/>
784
785=over 4
786
787=item merges
788
789    my $status = $repos->merges( {
790        "base" => "master",
791        "head" => "cool_feature",
792        "commit_message" => "Shipped cool_feature!"
793    } );
794
795=back
796
797=head3 Repo Statuses API
798
799L<http://developer.github.com/v3/repos/statuses/>
800
801=over 4
802
803=item list_statuses
804
805    $gh->set_default_user_repo('fayland', 'perl-net-github');
806    my @statuses = $repos->lists_statuses($sha);
807
808Or:
809
810    my @statuses = $repos->list_statuses('fayland', 'perl-net-github', $sha);
811
812=item next_status
813
814    while (my $status = $repos->next_status($sha)) { ...; }
815
816=item create_status
817
818    $gh->set_default_user_repo('fayland', 'perl-net-github');
819    my %payload = {
820        "state"       => "success",
821        "target_url"  => "https://example.com/build/status",
822        "description" => "The build succeeded!",
823        "context"     => "build/status"
824    };
825    my $status = $repos->create_status($sha, %payload);
826
827Or:
828
829    my %payload = {
830        "state"       => "success",
831        "target_url"  => "https://example.com/build/status",
832        "description" => "The build succeeded!",
833        "context"     => "build/status"
834    };
835    my $status = $repos->create_status(
836        'fayland', 'perl-net-github', $sha, %payload
837    );
838
839=back
840
841=head3 Repo Releases API
842
843L<http://developer.github.com/v3/repos/releases/>
844
845=over 4
846
847=item releases
848
849    my @releases = $repos->releases();
850    while (my $release = $repos->next_release) { ...; }
851
852=item release
853
854    my $release = $repos->release($release_id);
855
856=item create_release
857
858    my $release = $repos->create_release({
859      "tag_name" => "v1.0.0",
860      "target_commitish" => "master",
861      "name" => "v1.0.0",
862      "body" => "Description of the release",
863      "draft" => \1,
864    });
865
866=item update_release
867
868    my $release = $repos->update_release($release_id, {
869      "tag_name" => "v1.0.0",
870      "target_commitish" => "master",
871      "name" => "v1.0.0",
872      "body" => "Description of the release",
873    });
874
875=item delete_release
876
877    $repos->delete_release($release_id);
878
879=item release_assets
880
881    my @release_assets = $repos->release_assets($release_id);
882    while (my $asset = $repos->next_release_asset($release_id)) { ...; }
883
884=item upload_asset
885
886    my $asset = $repos->upload_asset($release_id, $name, $content_type, $file_content);
887
888Check examples/upload_asset.pl for a working example.
889
890=item release_asset
891
892    my $release_asset = $repos->release_asset($release_id, $asset_id);
893
894=item update_release_asset
895
896    my $release_asset = $repos->update_release_asset($release_id, $asset_id, {
897        name" => "foo-1.0.0-osx.zip",
898        "label" => "Mac binary"
899    });
900
901=item delete_release_asset
902
903    my $ok = $repos->delete_release_asset($release_id, $asset_id);
904
905=back
906
907=head3 Contents API
908
909L<https://developer.github.com/v3/repos/contents/>
910
911=over 4
912
913=item get_content
914
915Gets the contents of a file or directory in a repository.
916Specify the file path or directory in $path.
917If you omit $path, you will receive the contents of all files in the repository.
918
919    my $response = $repos->get_content( $owner, $repo, $path )
920        or
921        $repos->get_content(
922            { owner => $owner,  repo => $repo, path => $path },
923         )
924        or
925        $repos->get_content(
926            { owner => $owner,  repo => $repo, path => $path },
927            { ref => 'feature-branch' }
928         )
929
930=back
931
932=head3 Repo Deployment API
933
934L<http://developer.github.com/v3/repos/deployments/>
935
936=over 4
937
938=item list_deployments
939
940    my $response = $repos->list_deployments( $owner, $repo, {
941        'ref' => 'feature-branch',
942    });
943
944=item next_deployment
945
946    while (my $deployment = $repos->next_deployment( $owner, $repo, {
947        'ref' => 'feature-branch',
948    }) { ...; }
949
950=item create_deployment
951
952    my $response = $repos->create_deployment( $owner, $repo, {
953      "ref" => 'feature-branch',
954      "description" => "deploying my new feature",
955    });
956
957=item list_deployment_statuses
958
959    my $response = $repos->list_deployment_statuses( $owner, $repo, $deployment_id );
960
961=item next_deployment_status
962
963    while (my $status = next_deployment_status($o,$r,$id)) { ...; }
964
965=item create_deployment_status
966
967    my $response = $repos->create_deployment_status( $owner, $repo, $deployment_id, {
968        "state": "success",
969        "target_url": "https://example.com/deployment/42/output",
970        "description": "Deployment finished successfully."
971    });
972
973=back
974
975=head3 Repo Statistics API
976
977L<http://developer.github.com/v3/repos/statistics/>
978
979=over 4
980
981=item contributor stats
982
983=item commit activity
984
985=item code frequency
986
987=item participation
988
989=item punch card
990
991    my $contributor_stats   = $repos->contributor_stats($owner, $repo);
992    my $commit_activity     = $repos->commit_activity($owner, $repo);
993    my $code_freq           = $repos->code_frequency($owner, $repo);
994    my $participation       = $repos->participation($owner, $repo);
995    my $punch_card          = $repos->punch_card($owner, $repo);
996
997=back
998
999=head1 AUTHOR & COPYRIGHT & LICENSE
1000
1001Refer L<Net::GitHub>
1002