@@ -53,8 +53,7 @@ require ( | |||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 | |||
github.com/golang-jwt/jwt v3.2.2+incompatible | |||
github.com/golang/snappy v0.0.4 // indirect | |||
github.com/google/go-github/v37 v37.0.0 | |||
github.com/google/go-querystring v1.1.0 // indirect | |||
github.com/google/go-github/v39 v39.2.0 | |||
github.com/google/uuid v1.2.0 | |||
github.com/gorilla/context v1.1.1 | |||
github.com/gorilla/feeds v1.1.1 |
@@ -562,8 +562,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | |||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | |||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
github.com/google/go-github/v37 v37.0.0 h1:rCspN8/6kB1BAJWZfuafvHhyfIo5fkAulaP/3bOQ/tM= | |||
github.com/google/go-github/v37 v37.0.0/go.mod h1:LM7in3NmXDrX58GbEHy7FtNLbI2JijX93RnMKvWG3m4= | |||
github.com/google/go-github/v39 v39.2.0 h1:rNNM311XtPOz5rDdsJXAp2o8F67X9FnROXTvto3aSnQ= | |||
github.com/google/go-github/v39 v39.2.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE= | |||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= | |||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | |||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= |
@@ -8,7 +8,7 @@ package migrations | |||
import ( | |||
"errors" | |||
"github.com/google/go-github/v37/github" | |||
"github.com/google/go-github/v39/github" | |||
) | |||
var ( |
@@ -23,7 +23,7 @@ import ( | |||
"code.gitea.io/gitea/modules/structs" | |||
"code.gitea.io/gitea/modules/util" | |||
"github.com/google/go-github/v37/github" | |||
"github.com/google/go-github/v39/github" | |||
"golang.org/x/oauth2" | |||
) | |||
@@ -26,6 +26,7 @@ Alex Bramley <a.bramley@gmail.com> | |||
Alex Orr <Alexorr.CSE@gmail.com> | |||
Alex Unger <zyxancf@gmail.com> | |||
Alexander Harkness <me@bearbin.net> | |||
Alexis Gauthiez <alexis.gauthiez@gmail.com> | |||
Ali Farooq <ali.farooq0@pm.me> | |||
Allen Sun <shlallen1990@gmail.com> | |||
Amey Sakhadeo <me@ameyms.com> | |||
@@ -50,6 +51,7 @@ Arıl Bozoluk <arilbozoluk@hotmail.com> | |||
Asier Marruedo <asiermarruedo@gmail.com> | |||
Austin Burdine <acburdine@gmail.com> | |||
Austin Dizzy <dizzy@wow.com> | |||
Azuka Okuleye <azuka@zatechcorp.com> | |||
Ben Batha <bhbatha@gmail.com> | |||
Benjamen Keroack <benjamen@dollarshaveclub.com> | |||
Beshr Kayali <beshrkayali@gmail.com> | |||
@@ -63,6 +65,7 @@ Brad Moylan <moylan.brad@gmail.com> | |||
Bradley Falzon <brad@teambrad.net> | |||
Bradley McAllister <brad.mcallister@hotmail.com> | |||
Brandon Cook <phylake@gmail.com> | |||
Brett Logan <lindluni@github.com> | |||
Brian Egizi <brian@mojotech.com> | |||
Bryan Boreham <bryan@weave.works> | |||
Cami Diez <diezcami@gmail.com> | |||
@@ -72,7 +75,9 @@ Carlos Tadeu Panato Junior <ctadeu@gmail.com> | |||
chandresh-pancholi <chandreshpancholi007@gmail.com> | |||
Charles Fenwick Elliott <Charles@FenwickElliott.io> | |||
Charlie Yan <charlieyan08@gmail.com> | |||
Chmouel Boudjnah <chmouel@chmouel.com> | |||
Chris King <chriskingnet@gmail.com> | |||
Chris Mc <prince.chrismc@gmail.com> | |||
Chris Raborg <craborg57@gmail.com> | |||
Chris Roche <chris@vsco.co> | |||
Chris Schaefer <chris@dtzq.com> | |||
@@ -89,6 +94,7 @@ Daniel Nilsson <daniel.nilsson1989@gmail.com> | |||
Daoq <masseto2002@gmail.com> | |||
Dave Du Cros <davidducros@gmail.com> | |||
Dave Henderson <dhenderson@gmail.com> | |||
Dave Perrett <hello@daveperrett.com> | |||
Dave Protasowski <dprotaso@gmail.com> | |||
David Deng <daviddengcn@gmail.com> | |||
David J. M. Karlsen <david@davidkarlsen.com> | |||
@@ -97,6 +103,7 @@ David Ji <github.com/davidji99> | |||
David Lopez Reyes <davidlopezre@gmail.com> | |||
Davide Zipeto <dawez1@gmail.com> | |||
Dennis Webb <dennis@bluesentryit.com> | |||
Derek Jobst <derekjobst@gmail.com> | |||
Dhi Aurrahman <diorahman@rockybars.com> | |||
Diego Lapiduz <diego.lapiduz@cfpb.gov> | |||
Dmitri Shuralyov <shurcooL@gmail.com> | |||
@@ -114,6 +121,7 @@ Erick Fejta <erick@fejta.com> | |||
Erik Nobel <hendrik.nobel@transferwise.com> | |||
erwinvaneyk <erwinvaneyk@gmail.com> | |||
Evan Elias <evanjelias@gmail.com> | |||
Fabian Holler <fabian.holler@simplesurance.de> | |||
Fabrice <fabrice.vaillant@student.ecp.fr> | |||
Felix Geisendörfer <felix@debuggable.com> | |||
Filippo Valsorda <hi@filippo.io> | |||
@@ -140,15 +148,19 @@ haya14busa <hayabusa1419@gmail.com> | |||
Huy Tr <kingbazoka@gmail.com> | |||
huydx <doxuanhuy@gmail.com> | |||
i2bskn <i2bskn@gmail.com> | |||
Ikko Ashimine <eltociear@gmail.com> | |||
Ioannis Georgoulas <igeorgoulas21@gmail.com> | |||
Isao Jonas <isao.jonas@gmail.com> | |||
ishan upadhyay <ishanupadhyay412@gmail.com> | |||
isqua <isqua@isqua.ru> | |||
Jacob Valdemar <jan@lunar.app> | |||
Jake Krammer <jake.krammer1@gmail.com> | |||
Jake White <jake@jwhite.network> | |||
Jameel Haffejee <RC1140@republiccommandos.co.za> | |||
James Cockbain <james.cockbain@ibm.com> | |||
James Loh <github@jloh.co> | |||
Jan Kosecki <jan.kosecki91@gmail.com> | |||
Jan Švábík <jansvabik@jansvabik.cz> | |||
Javier Campanini <jcampanini@palantir.com> | |||
Jef LeCompte <jeffreylec@gmail.com> | |||
Jens Rantil <jens.rantil@gmail.com> | |||
@@ -180,6 +192,7 @@ Katrina Owen <kytrinyx@github.com> | |||
Kautilya Tripathi <tripathi.kautilya@gmail.com> | |||
Keita Urashima <ursm@ursm.jp> | |||
Kevin Burke <kev@inburke.com> | |||
Kirill <g4s8.public@gmail.com> | |||
Konrad Malawski <konrad.malawski@project13.pl> | |||
Kookheon Kwon <kucuny@gmail.com> | |||
Krzysztof Kowalczyk <kkowalczyk@gmail.com> | |||
@@ -189,6 +202,7 @@ kyokomi <kyoko1220adword@gmail.com> | |||
Laurent Verdoïa <verdoialaurent@gmail.com> | |||
Liam Galvin <liam@liam-galvin.co.uk> | |||
Lovro Mažgon <lovro.mazgon@gmail.com> | |||
Luca Campese <me@campesel.net> | |||
Lucas Alcantara <lucasalcantaraf@gmail.com> | |||
Luke Evers <me@lukevers.com> | |||
Luke Kysow <lkysow@gmail.com> | |||
@@ -196,6 +210,7 @@ Luke Roberts <email@luke-roberts.co.uk> | |||
Luke Young <luke@hydrantlabs.org> | |||
lynn [they] <lynncyrin@gmail.com> | |||
Maksim Zhylinski <uzzable@gmail.com> | |||
Marc Binder <marcandrebinder@gmail.com> | |||
Marcelo Carlos <marcelo@permutive.com> | |||
Mark Tareshawty <tarebyte@github.com> | |||
Martin Holman <me@martinholman.co.nz> | |||
@@ -204,6 +219,7 @@ Martins Sipenko <martins.sipenko@gmail.com> | |||
Marwan Sulaiman <marwan.sameer@gmail.com> | |||
Masayuki Izumi <m@izum.in> | |||
Mat Geist <matgeist@gmail.com> | |||
Matin Rahmanian <itsmatinx@gmail.com> | |||
Matt <alpmatthew@gmail.com> | |||
Matt Brender <mjbrender@gmail.com> | |||
Matt Gaunt <matt@gauntface.co.uk> | |||
@@ -215,6 +231,7 @@ Michael Tiller <michael.tiller@gmail.com> | |||
Michał Glapa <michal.glapa@gmail.com> | |||
Michelangelo Morrillo <michelangelo@morrillo.it> | |||
Mukundan Senthil <mukundan314@gmail.com> | |||
Munia Balayil <munia.247@gmail.com> | |||
Nadav Kaner <nadavkaner1@gmail.com> | |||
Nathan VanBenschoten <nvanbenschoten@gmail.com> | |||
Navaneeth Suresh <navaneeths1998@gmail.com> | |||
@@ -229,6 +246,7 @@ ns-cweber <cweber@narrativescience.com> | |||
Ole Orhagen <ole.orhagen@northern.tech> | |||
Oleg Kovalov <iamolegkovalov@gmail.com> | |||
Ondřej Kupka <ondra.cap@gmail.com> | |||
Ori Talmor <talmorori@gmail.com> | |||
Pablo Pérez Schröder <pablo.perezschroder@wetransfer.com> | |||
Palash Nigam <npalash25@gmail.com> | |||
Panagiotis Moustafellos <pmoust@gmail.com> | |||
@@ -265,11 +283,13 @@ Ricco Førgaard <ricco@fiskeben.dk> | |||
Rob Figueiredo <robfig@yext.com> | |||
Rohit Upadhyay <urohit011@gmail.com> | |||
Ronak Jain <ronakjain@outlook.in> | |||
Ross Gustafson <srgustafson8@icloud.com> | |||
Ruben Vereecken <rubenvereecken@gmail.com> | |||
Russell Boley <raboley@gmail.com> | |||
Ryan Leung <rleungx@gmail.com> | |||
Ryan Lower <rpjlower@gmail.com> | |||
Ryo Nakao <nakabonne@gmail.com> | |||
Saaarah <sarah.liusy@gmail.com> | |||
Safwan Olaimat <safwan.olaimat@gmail.com> | |||
Sahil Dua <sahildua2305@gmail.com> | |||
saisi <saisi@users.noreply.github.com> | |||
@@ -332,4 +352,6 @@ Yannick Utard <yannickutard@gmail.com> | |||
Yicheng Qin <qycqycqycqycqyc@gmail.com> | |||
Yosuke Akatsuka <yosuke.akatsuka@access-company.com> | |||
Yumikiyo Osanai <yumios.art@gmail.com> | |||
Yusuke Kuoka <ykuoka@gmail.com> | |||
Zach Latta <zach@zachlatta.com> | |||
zhouhaibing089 <zhouhaibing089@gmail.com> |
@@ -36,12 +36,15 @@ type CreateRunnerGroupRequest struct { | |||
SelectedRepositoryIDs []int64 `json:"selected_repository_ids,omitempty"` | |||
// Runners represent a list of runner IDs to add to the runner group. | |||
Runners []int64 `json:"runners,omitempty"` | |||
// If set to True, public repos can use this runner group | |||
AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` | |||
} | |||
// UpdateRunnerGroupRequest represents a request to update a Runner group for an organization. | |||
type UpdateRunnerGroupRequest struct { | |||
Name *string `json:"name,omitempty"` | |||
Visibility *string `json:"visibility,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Visibility *string `json:"visibility,omitempty"` | |||
AllowsPublicRepositories *bool `json:"allows_public_repositories,omitempty"` | |||
} | |||
// SetRepoAccessRunnerGroupRequest represents a request to replace the list of repositories | |||
@@ -156,8 +159,13 @@ func (s *ActionsService) UpdateOrganizationRunnerGroup(ctx context.Context, org | |||
// ListRepositoryAccessRunnerGroup lists the repositories with access to a self-hosted runner group configured in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/actions#list-repository-access-to-a-self-hosted-runner-group-in-an-organization | |||
func (s *ActionsService) ListRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64) (*ListRepositories, *Response, error) { | |||
func (s *ActionsService) ListRepositoryAccessRunnerGroup(ctx context.Context, org string, groupID int64, opts *ListOptions) (*ListRepositories, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/actions/runner-groups/%v/repositories", org, groupID) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err |
@@ -264,6 +264,65 @@ func (s *ActionsService) ListEnabledReposInOrg(ctx context.Context, owner string | |||
return repos, resp, nil | |||
} | |||
// SetEnabledReposInOrg replaces the list of selected repositories that are enabled for GitHub Actions in an organization.. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/actions#set-selected-repositories-enabled-for-github-actions-in-an-organization | |||
func (s *ActionsService) SetEnabledReposInOrg(ctx context.Context, owner string, repositoryIDs []int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/actions/permissions/repositories", owner) | |||
req, err := s.client.NewRequest("PUT", u, struct { | |||
IDs []int64 `json:"selected_repository_ids"` | |||
}{IDs: repositoryIDs}) | |||
if err != nil { | |||
return nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
if err != nil { | |||
return resp, err | |||
} | |||
return resp, nil | |||
} | |||
// AddEnabledReposInOrg adds a repository to the list of selected repositories that are enabled for GitHub Actions in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/actions#enable-a-selected-repository-for-github-actions-in-an-organization | |||
func (s *ActionsService) AddEnabledReposInOrg(ctx context.Context, owner string, repositoryID int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/actions/permissions/repositories/%v", owner, repositoryID) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
if err != nil { | |||
return resp, err | |||
} | |||
return resp, nil | |||
} | |||
// RemoveEnabledRepoInOrg removes a single repository from the list of enabled repos for GitHub Actions in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/actions#disable-a-selected-repository-for-github-actions-in-an-organization | |||
func (s *ActionsService) RemoveEnabledRepoInOrg(ctx context.Context, owner string, repositoryID int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/actions/permissions/repositories/%v", owner, repositoryID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
if err != nil { | |||
return resp, err | |||
} | |||
return resp, nil | |||
} | |||
// GetOrganizationRunner gets a specific self-hosted runner for an organization using its runner ID. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/actions/#get-a-self-hosted-runner-for-an-organization |
@@ -302,7 +302,7 @@ func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org, | |||
u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name) | |||
type repoIDs struct { | |||
SelectedIDs SelectedRepoIDs `json:"selected_repository_ids,omitempty"` | |||
SelectedIDs SelectedRepoIDs `json:"selected_repository_ids"` | |||
} | |||
req, err := s.client.NewRequest("PUT", u, repoIDs{SelectedIDs: ids}) |
@@ -38,6 +38,8 @@ type WorkflowJob struct { | |||
Name *string `json:"name,omitempty"` | |||
Steps []*TaskStep `json:"steps,omitempty"` | |||
CheckRunURL *string `json:"check_run_url,omitempty"` | |||
// Labels represents runner labels from the `runs-on:` key from a GitHub Actions workflow. | |||
Labels []string `json:"labels,omitempty"` | |||
} | |||
// Jobs represents a slice of repository action workflow job. | |||
@@ -145,5 +147,4 @@ func (s *ActionsService) getWorkflowLogsFromURL(ctx context.Context, u string, f | |||
resp, err = s.getWorkflowLogsFromURL(ctx, u, false) | |||
} | |||
return resp, err | |||
} |
@@ -14,31 +14,33 @@ import ( | |||
// WorkflowRun represents a repository action workflow run. | |||
type WorkflowRun struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
HeadBranch *string `json:"head_branch,omitempty"` | |||
HeadSHA *string `json:"head_sha,omitempty"` | |||
RunNumber *int `json:"run_number,omitempty"` | |||
Event *string `json:"event,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Conclusion *string `json:"conclusion,omitempty"` | |||
WorkflowID *int64 `json:"workflow_id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
PullRequests []*PullRequest `json:"pull_requests,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
JobsURL *string `json:"jobs_url,omitempty"` | |||
LogsURL *string `json:"logs_url,omitempty"` | |||
CheckSuiteURL *string `json:"check_suite_url,omitempty"` | |||
ArtifactsURL *string `json:"artifacts_url,omitempty"` | |||
CancelURL *string `json:"cancel_url,omitempty"` | |||
RerunURL *string `json:"rerun_url,omitempty"` | |||
HeadCommit *HeadCommit `json:"head_commit,omitempty"` | |||
WorkflowURL *string `json:"workflow_url,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
HeadRepository *Repository `json:"head_repository,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
HeadBranch *string `json:"head_branch,omitempty"` | |||
HeadSHA *string `json:"head_sha,omitempty"` | |||
RunNumber *int `json:"run_number,omitempty"` | |||
Event *string `json:"event,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Conclusion *string `json:"conclusion,omitempty"` | |||
WorkflowID *int64 `json:"workflow_id,omitempty"` | |||
CheckSuiteID *int64 `json:"check_suite_id,omitempty"` | |||
CheckSuiteNodeID *string `json:"check_suite_node_id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
PullRequests []*PullRequest `json:"pull_requests,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
JobsURL *string `json:"jobs_url,omitempty"` | |||
LogsURL *string `json:"logs_url,omitempty"` | |||
CheckSuiteURL *string `json:"check_suite_url,omitempty"` | |||
ArtifactsURL *string `json:"artifacts_url,omitempty"` | |||
CancelURL *string `json:"cancel_url,omitempty"` | |||
RerunURL *string `json:"rerun_url,omitempty"` | |||
HeadCommit *HeadCommit `json:"head_commit,omitempty"` | |||
WorkflowURL *string `json:"workflow_url,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
HeadRepository *Repository `json:"head_repository,omitempty"` | |||
} | |||
// WorkflowRuns represents a slice of repository action workflow run. | |||
@@ -49,10 +51,11 @@ type WorkflowRuns struct { | |||
// ListWorkflowRunsOptions specifies optional parameters to ListWorkflowRuns. | |||
type ListWorkflowRunsOptions struct { | |||
Actor string `url:"actor,omitempty"` | |||
Branch string `url:"branch,omitempty"` | |||
Event string `url:"event,omitempty"` | |||
Status string `url:"status,omitempty"` | |||
Actor string `url:"actor,omitempty"` | |||
Branch string `url:"branch,omitempty"` | |||
Event string `url:"event,omitempty"` | |||
Status string `url:"status,omitempty"` | |||
Created string `url:"created,omitempty"` | |||
ListOptions | |||
} | |||
@@ -51,7 +51,7 @@ type NotificationListOptions struct { | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/activity/#list-notifications-for-the-authenticated-user | |||
func (s *ActivityService) ListNotifications(ctx context.Context, opts *NotificationListOptions) ([]*Notification, *Response, error) { | |||
u := fmt.Sprintf("notifications") | |||
u := "notifications" | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err |
@@ -47,6 +47,10 @@ type InstallationTokenOptions struct { | |||
// Providing repository IDs restricts the access of an installation token to specific repositories. | |||
RepositoryIDs []int64 `json:"repository_ids,omitempty"` | |||
// The names of the repositories that the installation token can access. | |||
// Providing repository names restricts the access of an installation token to specific repositories. | |||
Repositories []string `json:"repositories,omitempty"` | |||
// The permissions granted to the access token. | |||
// The permissions object includes the permission names and their access type. | |||
Permissions *InstallationPermissions `json:"permissions,omitempty"` |
@@ -0,0 +1,48 @@ | |||
// Copyright 2021 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
) | |||
// GetHookConfig returns the webhook configuration for a GitHub App. | |||
// The underlying transport must be authenticated as an app. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/apps#get-a-webhook-configuration-for-an-app | |||
func (s *AppsService) GetHookConfig(ctx context.Context) (*HookConfig, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "app/hook/config", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
config := new(HookConfig) | |||
resp, err := s.client.Do(ctx, req, &config) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return config, resp, nil | |||
} | |||
// UpdateHookConfig updates the webhook configuration for a GitHub App. | |||
// The underlying transport must be authenticated as an app. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/apps#update-a-webhook-configuration-for-an-app | |||
func (s *AppsService) UpdateHookConfig(ctx context.Context, config *HookConfig) (*HookConfig, *Response, error) { | |||
req, err := s.client.NewRequest("PATCH", "app/hook/config", config) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(HookConfig) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} |
@@ -13,6 +13,7 @@ import ( | |||
// AppConfig describes the configuration of a GitHub App. | |||
type AppConfig struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Slug *string `json:"slug,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
Owner *User `json:"owner,omitempty"` | |||
Name *string `json:"name,omitempty"` |
@@ -78,6 +78,8 @@ type CheckSuite struct { | |||
AfterSHA *string `json:"after,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Conclusion *string `json:"conclusion,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
App *App `json:"app,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
PullRequests []*PullRequest `json:"pull_requests,omitempty"` |
@@ -18,9 +18,45 @@ import ( | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/code-scanning/ | |||
type CodeScanningService service | |||
// Rule represents the complete details of GitHub Code Scanning alert type. | |||
type Rule struct { | |||
ID *string `json:"id,omitempty"` | |||
Severity *string `json:"severity,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
SecuritySeverityLevel *string `json:"security_severity_level,omitempty"` | |||
FullDescription *string `json:"full_description,omitempty"` | |||
Tags []string `json:"tags,omitempty"` | |||
Help *string `json:"help,omitempty"` | |||
} | |||
// Location represents the exact location of the GitHub Code Scanning Alert in the scanned project. | |||
type Location struct { | |||
Path *string `json:"path,omitempty"` | |||
StartLine *int `json:"start_line,omitempty"` | |||
EndLine *int `json:"end_line,omitempty"` | |||
StartColumn *int `json:"start_column,omitempty"` | |||
EndColumn *int `json:"end_column,omitempty"` | |||
} | |||
// Message is a part of MostRecentInstance struct which provides the appropriate message when any action is performed on the analysis object. | |||
type Message struct { | |||
Text *string `json:"text,omitempty"` | |||
} | |||
// MostRecentInstance provides details of the most recent instance of this alert for the default branch or for the specified Git reference. | |||
type MostRecentInstance struct { | |||
Ref *string `json:"ref,omitempty"` | |||
AnalysisKey *string `json:"analysis_key,omitempty"` | |||
Environment *string `json:"environment,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
CommitSHA *string `json:"commit_sha,omitempty"` | |||
Message *Message `json:"message,omitempty"` | |||
Location *Location `json:"location,omitempty"` | |||
Classifications []string `json:"classifications,omitempty"` | |||
} | |||
// Tool represents the tool used to generate a GitHub Code Scanning Alert. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#list-code-scanning-alerts-for-a-repository | |||
type Tool struct { | |||
Name *string `json:"name,omitempty"` | |||
GUID *string `json:"guid,omitempty"` | |||
@@ -31,16 +67,22 @@ type Tool struct { | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/code-scanning#list-code-scanning-alerts-for-a-repository | |||
type Alert struct { | |||
RuleID *string `json:"rule_id,omitempty"` | |||
RuleSeverity *string `json:"rule_severity,omitempty"` | |||
RuleDescription *string `json:"rule_description,omitempty"` | |||
Tool *Tool `json:"tool,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
Open *bool `json:"open,omitempty"` | |||
ClosedBy *User `json:"closed_by,omitempty"` | |||
ClosedAt *Timestamp `json:"closed_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
RuleID *string `json:"rule_id,omitempty"` | |||
RuleSeverity *string `json:"rule_severity,omitempty"` | |||
RuleDescription *string `json:"rule_description,omitempty"` | |||
Rule *Rule `json:"rule,omitempty"` | |||
Tool *Tool `json:"tool,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
ClosedBy *User `json:"closed_by,omitempty"` | |||
ClosedAt *Timestamp `json:"closed_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
MostRecentInstance *MostRecentInstance `json:"most_recent_instance,omitempty"` | |||
DismissedBy *User `json:"dismissed_by,omitempty"` | |||
DismissedAt *Timestamp `json:"dismissed_at,omitempty"` | |||
DismissedReason *string `json:"dismissed_reason,omitempty"` | |||
InstancesURL *string `json:"instances_url,omitempty"` | |||
} | |||
// ID returns the ID associated with an alert. It is the number at the end of the security alert's URL. | |||
@@ -73,6 +115,8 @@ type AlertListOptions struct { | |||
// Return code scanning alerts for a specific branch reference. The ref must be formatted as heads/<branch name>. | |||
Ref string `url:"ref,omitempty"` | |||
ListOptions | |||
} | |||
// ListAlertsForRepo lists code scanning alerts for a repository. |
@@ -8,7 +8,7 @@ Package github provides a client for using the GitHub API. | |||
Usage: | |||
import "github.com/google/go-github/v37/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) | |||
import "github.com/google/go-github/v39/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) | |||
import "github.com/google/go-github/github" // with go modules disabled | |||
Construct a new GitHub client, then use the various services on the client to | |||
@@ -73,6 +73,10 @@ BasicAuthTransport. | |||
GitHub Apps authentication can be provided by the | |||
https://github.com/bradleyfalzon/ghinstallation package. | |||
It supports both authentication as an installation, using an installation access token, | |||
and as an app, using a JWT. | |||
To authenticate as an installation: | |||
import "github.com/bradleyfalzon/ghinstallation" | |||
@@ -89,6 +93,23 @@ https://github.com/bradleyfalzon/ghinstallation package. | |||
// Use client... | |||
} | |||
To authenticate as an app, using a JWT: | |||
import "github.com/bradleyfalzon/ghinstallation" | |||
func main() { | |||
// Wrap the shared transport for use with the application ID 1. | |||
atr, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, 1, "2016-10-19.private-key.pem") | |||
if err != nil { | |||
// Handle error. | |||
} | |||
// Use app transport with client | |||
client := github.NewClient(&http.Client{Transport: atr}) | |||
// Use client... | |||
} | |||
Rate Limiting | |||
GitHub imposes a rate limit on all API clients. Unauthenticated clients are | |||
@@ -112,7 +133,7 @@ To detect an API rate limit error, you can check if its type is *github.RateLimi | |||
} | |||
Learn more about GitHub rate limiting at | |||
https://docs.github.com/en/free-pro-team@latest/rest/reference/#rate-limiting. | |||
https://docs.github.com/en/free-pro-team@latest/rest/overview/resources-in-the-rest-api#rate-limiting. | |||
Accepted Status | |||
@@ -138,7 +159,7 @@ instead designed to work with a caching http.Transport. We recommend using | |||
https://github.com/gregjones/httpcache for that. | |||
Learn more about GitHub conditional requests at | |||
https://docs.github.com/en/free-pro-team@latest/rest/reference/#conditional-requests. | |||
https://docs.github.com/en/free-pro-team@latest/rest/overview/resources-in-the-rest-api#conditional-requests. | |||
Creating and Updating Resources | |||
@@ -30,6 +30,8 @@ func (e Event) String() string { | |||
// a value of the corresponding struct type will be returned. | |||
func (e *Event) ParsePayload() (payload interface{}, err error) { | |||
switch *e.Type { | |||
case "BranchProtectionRuleEvent": | |||
payload = &BranchProtectionRuleEvent{} | |||
case "CheckRunEvent": | |||
payload = &CheckRunEvent{} | |||
case "CheckSuiteEvent": | |||
@@ -124,6 +126,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) { | |||
payload = &WatchEvent{} | |||
case "WorkflowDispatchEvent": | |||
payload = &WorkflowDispatchEvent{} | |||
case "WorkflowJobEvent": | |||
payload = &WorkflowJobEvent{} | |||
case "WorkflowRunEvent": | |||
payload = &WorkflowRunEvent{} | |||
} |
@@ -15,6 +15,19 @@ type RequestedAction struct { | |||
Identifier string `json:"identifier"` // The integrator reference of the action requested by the user. | |||
} | |||
// BranchProtectionRuleEvent triggered when a check suite is "created", "edited", or "deleted". | |||
// The Webhook event name is "branch_protection_rule". | |||
// | |||
// GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#branch_protection_rule | |||
type BranchProtectionRuleEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Rule *BranchProtectionRule `json:"rule,omitempty"` | |||
Changes *ProtectionChanges `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
} | |||
// CheckRunEvent is triggered when a check run is "created", "completed", or "rerequested". | |||
// The Webhook event name is "check_run". | |||
// | |||
@@ -130,6 +143,9 @@ type DeployKeyEvent struct { | |||
// The deploy key resource. | |||
Key *Key `json:"key,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// DeploymentEvent represents a deployment. | |||
@@ -186,7 +202,8 @@ type GitHubAppAuthorizationEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Sender *User `json:"sender,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// Page represents a single Wiki page. | |||
@@ -367,6 +384,10 @@ type IssueCommentEvent struct { | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
// The following field is only present when the webhook is triggered on | |||
// a repository belonging to an organization. | |||
Organization *Organization `json:"organization,omitempty"` | |||
} | |||
// IssuesEvent is triggered when an issue is opened, edited, deleted, transferred, | |||
@@ -478,6 +499,9 @@ type MetaEvent struct { | |||
// This will contain different keys based on the type of webhook it is: repository, | |||
// organization, business, app, or GitHub Marketplace. | |||
Hook *Hook `json:"hook,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted. | |||
@@ -551,6 +575,9 @@ type PackageEvent struct { | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether | |||
@@ -796,6 +823,10 @@ type PushEvent struct { | |||
Pusher *User `json:"pusher,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
// The following field is only present when the webhook is triggered on | |||
// a repository belonging to an organization. | |||
Organization *Organization `json:"organization,omitempty"` | |||
} | |||
func (p PushEvent) String() string { | |||
@@ -939,6 +970,9 @@ type RepositoryVulnerabilityAlertEvent struct { | |||
//The repository of the vulnerable dependency. | |||
Repository *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// RepositoryVulnerabilityAlert represents a repository security alert. | |||
@@ -969,9 +1003,10 @@ type StarEvent struct { | |||
StarredAt *Timestamp `json:"starred_at,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// StatusEvent is triggered when the status of a Git commit changes. | |||
@@ -1049,6 +1084,9 @@ type UserEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Enterprise *Enterprise `json:"enterprise,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// WatchEvent is related to starring a repository, not watching. See this API | |||
@@ -1078,9 +1116,28 @@ type WorkflowDispatchEvent struct { | |||
Workflow *string `json:"workflow,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// WorkflowJobEvent is triggered when a job is queued, started or completed. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#workflow_job | |||
type WorkflowJobEvent struct { | |||
WorkflowJob *WorkflowJob `json:"workflow_job,omitempty"` | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
// Org is not nil when the webhook is configured for an organization or the event | |||
// occurs from activity in a repository owned by an organization. | |||
Org *Organization `json:"organization,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// WorkflowRunEvent is triggered when a GitHub Actions workflow run is requested or completed. | |||
@@ -1092,7 +1149,8 @@ type WorkflowRunEvent struct { | |||
WorkflowRun *WorkflowRun `json:"workflow_run,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} |
@@ -37,6 +37,8 @@ const ( | |||
headerRateReset = "X-RateLimit-Reset" | |||
headerOTP = "X-GitHub-OTP" | |||
headerTokenExpiration = "GitHub-Authentication-Token-Expiration" | |||
mediaTypeV3 = "application/vnd.github.v3+json" | |||
defaultMediaType = "application/octet-stream" | |||
mediaTypeV3SHA = "application/vnd.github.v3.sha" | |||
@@ -74,9 +76,6 @@ const ( | |||
// https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/ | |||
mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json" | |||
// https://developer.github.com/changes/2017-02-09-community-health/ | |||
mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json" | |||
// https://developer.github.com/changes/2017-05-23-coc-api/ | |||
mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json" | |||
@@ -179,6 +178,7 @@ type Client struct { | |||
PullRequests *PullRequestsService | |||
Reactions *ReactionsService | |||
Repositories *RepositoriesService | |||
SCIM *SCIMService | |||
Search *SearchService | |||
Teams *TeamsService | |||
Users *UsersService | |||
@@ -188,6 +188,14 @@ type service struct { | |||
client *Client | |||
} | |||
// Client returns the http.Client used by this GitHub client. | |||
func (c *Client) Client() *http.Client { | |||
c.clientMu.Lock() | |||
defer c.clientMu.Unlock() | |||
clientCopy := *c.client | |||
return &clientCopy | |||
} | |||
// ListOptions specifies the optional parameters to various List methods that | |||
// support offset pagination. | |||
type ListOptions struct { | |||
@@ -212,6 +220,9 @@ type ListCursorOptions struct { | |||
// A cursor, as given in the Link header. If specified, the query only searches for events before this cursor. | |||
Before string `url:"before,omitempty"` | |||
// A cursor, as given in the Link header. If specified, the query continues the search using this cursor. | |||
Cursor string `url:"cursor,omitempty"` | |||
} | |||
// UploadOptions specifies the parameters to methods that support uploads. | |||
@@ -295,6 +306,7 @@ func NewClient(httpClient *http.Client) *Client { | |||
c.PullRequests = (*PullRequestsService)(&c.common) | |||
c.Reactions = (*ReactionsService)(&c.common) | |||
c.Repositories = (*RepositoriesService)(&c.common) | |||
c.SCIM = (*SCIMService)(&c.common) | |||
c.Search = (*SearchService)(&c.common) | |||
c.Teams = (*TeamsService)(&c.common) | |||
c.Users = (*UsersService)(&c.common) | |||
@@ -445,9 +457,17 @@ type Response struct { | |||
// calling the endpoint again. | |||
NextPageToken string | |||
// For APIs that support cursor pagination, such as RepositoriesService.ListHookDeliveries, | |||
// the following field will be populated to point to the next page. | |||
// Set ListCursorOptions.Cursor to this value when calling the endpoint again. | |||
Cursor string | |||
// Explicitly specify the Rate type so Rate's String() receiver doesn't | |||
// propagate to Response. | |||
Rate Rate | |||
// token's expiration date | |||
TokenExpiration Timestamp | |||
} | |||
// newResponse creates a new Response for the provided http.Response. | |||
@@ -456,6 +476,7 @@ func newResponse(r *http.Response) *Response { | |||
response := &Response{Response: r} | |||
response.populatePageValues() | |||
response.Rate = parseRate(r) | |||
response.TokenExpiration = parseTokenExpiration(r) | |||
return response | |||
} | |||
@@ -481,7 +502,21 @@ func (r *Response) populatePageValues() { | |||
if err != nil { | |||
continue | |||
} | |||
page := url.Query().Get("page") | |||
q := url.Query() | |||
if cursor := q.Get("cursor"); cursor != "" { | |||
for _, segment := range segments[1:] { | |||
switch strings.TrimSpace(segment) { | |||
case `rel="next"`: | |||
r.Cursor = cursor | |||
} | |||
} | |||
continue | |||
} | |||
page := q.Get("page") | |||
if page == "" { | |||
continue | |||
} | |||
@@ -499,7 +534,6 @@ func (r *Response) populatePageValues() { | |||
case `rel="last"`: | |||
r.LastPage, _ = strconv.Atoi(page) | |||
} | |||
} | |||
} | |||
} | |||
@@ -522,6 +556,17 @@ func parseRate(r *http.Response) Rate { | |||
return rate | |||
} | |||
// parseTokenExpiration parses the TokenExpiration related headers. | |||
func parseTokenExpiration(r *http.Response) Timestamp { | |||
var exp Timestamp | |||
if v := r.Header.Get(headerTokenExpiration); v != "" { | |||
if t, err := time.Parse("2006-01-02 03:04:05 MST", v); err == nil { | |||
exp = Timestamp{t.Local()} | |||
} | |||
} | |||
return exp | |||
} | |||
type requestContext uint8 | |||
const ( | |||
@@ -663,10 +708,10 @@ func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rat | |||
return nil | |||
} | |||
// compareHttpResponse returns whether two http.Response objects are equal or not. | |||
// compareHTTPResponse returns whether two http.Response objects are equal or not. | |||
// Currently, only StatusCode is checked. This function is used when implementing the | |||
// Is(error) bool interface for the custom error types in this package. | |||
func compareHttpResponse(r1, r2 *http.Response) bool { | |||
func compareHTTPResponse(r1, r2 *http.Response) bool { | |||
if r1 == nil && r2 == nil { | |||
return true | |||
} | |||
@@ -716,7 +761,7 @@ func (r *ErrorResponse) Is(target error) bool { | |||
} | |||
if r.Message != v.Message || (r.DocumentationURL != v.DocumentationURL) || | |||
!compareHttpResponse(r.Response, v.Response) { | |||
!compareHTTPResponse(r.Response, v.Response) { | |||
return false | |||
} | |||
@@ -782,7 +827,7 @@ func (r *RateLimitError) Is(target error) bool { | |||
return r.Rate == v.Rate && | |||
r.Message == v.Message && | |||
compareHttpResponse(r.Response, v.Response) | |||
compareHTTPResponse(r.Response, v.Response) | |||
} | |||
// AcceptedError occurs when GitHub returns 202 Accepted response with an | |||
@@ -836,7 +881,7 @@ func (r *AbuseRateLimitError) Is(target error) bool { | |||
return r.Message == v.Message && | |||
r.RetryAfter == v.RetryAfter && | |||
compareHttpResponse(r.Response, v.Response) | |||
compareHTTPResponse(r.Response, v.Response) | |||
} | |||
// sanitizeURL redacts the client_secret parameter from the URL which may be |
@@ -18,6 +18,7 @@ import ( | |||
"errors" | |||
"fmt" | |||
"hash" | |||
"io" | |||
"io/ioutil" | |||
"mime" | |||
"net/http" | |||
@@ -31,19 +32,20 @@ const ( | |||
// sha256Prefix and sha512Prefix are provided for future compatibility. | |||
sha256Prefix = "sha256" | |||
sha512Prefix = "sha512" | |||
// sha1SignatureHeader is the GitHub header key used to pass the HMAC-SHA1 hexdigest. | |||
sha1SignatureHeader = "X-Hub-Signature" | |||
// sha256SignatureHeader is the GitHub header key used to pass the HMAC-SHA256 hexdigest. | |||
sha256SignatureHeader = "X-Hub-Signature-256" | |||
// eventTypeHeader is the GitHub header key used to pass the event type. | |||
eventTypeHeader = "X-Github-Event" | |||
// deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. | |||
deliveryIDHeader = "X-Github-Delivery" | |||
// SHA1SignatureHeader is the GitHub header key used to pass the HMAC-SHA1 hexdigest. | |||
SHA1SignatureHeader = "X-Hub-Signature" | |||
// SHA256SignatureHeader is the GitHub header key used to pass the HMAC-SHA256 hexdigest. | |||
SHA256SignatureHeader = "X-Hub-Signature-256" | |||
// EventTypeHeader is the GitHub header key used to pass the event type. | |||
EventTypeHeader = "X-Github-Event" | |||
// DeliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. | |||
DeliveryIDHeader = "X-Github-Delivery" | |||
) | |||
var ( | |||
// eventTypeMapping maps webhooks types to their corresponding go-github struct types. | |||
eventTypeMapping = map[string]string{ | |||
"branch_protection_rule": "BranchProtectionRuleEvent", | |||
"check_run": "CheckRunEvent", | |||
"check_suite": "CheckSuiteEvent", | |||
"commit_comment": "CommitCommentEvent", | |||
@@ -91,6 +93,7 @@ var ( | |||
"user": "UserEvent", | |||
"watch": "WatchEvent", | |||
"workflow_dispatch": "WorkflowDispatchEvent", | |||
"workflow_job": "WorkflowJobEvent", | |||
"workflow_run": "WorkflowRunEvent", | |||
} | |||
) | |||
@@ -139,7 +142,7 @@ func messageMAC(signature string) ([]byte, func() hash.Hash, error) { | |||
return buf, hashFunc, nil | |||
} | |||
// ValidatePayload validates an incoming GitHub Webhook event request | |||
// ValidatePayload validates an incoming GitHub Webhook event request body | |||
// and returns the (JSON) payload. | |||
// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". | |||
// If the Content-Type is neither then an error is returned. | |||
@@ -150,25 +153,19 @@ func messageMAC(signature string) ([]byte, func() hash.Hash, error) { | |||
// Example usage: | |||
// | |||
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
// payload, err := github.ValidatePayload(r, s.webhookSecretKey) | |||
// // read signature from request | |||
// signature := "" | |||
// payload, err := github.ValidatePayloadFromBody(r.Header.Get("Content-Type"), r.Body, signature, s.webhookSecretKey) | |||
// if err != nil { ... } | |||
// // Process payload... | |||
// } | |||
// | |||
func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err error) { | |||
func ValidatePayloadFromBody(contentType string, readable io.Reader, signature string, secretToken []byte) (payload []byte, err error) { | |||
var body []byte // Raw body that GitHub uses to calculate the signature. | |||
ct := r.Header.Get("Content-Type") | |||
mediatype, _, err := mime.ParseMediaType(ct) | |||
if err != nil { | |||
mediatype = "" | |||
} | |||
switch mediatype { | |||
switch contentType { | |||
case "application/json": | |||
var err error | |||
if body, err = ioutil.ReadAll(r.Body); err != nil { | |||
if body, err = ioutil.ReadAll(readable); err != nil { | |||
return nil, err | |||
} | |||
@@ -182,7 +179,7 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e | |||
const payloadFormParam = "payload" | |||
var err error | |||
if body, err = ioutil.ReadAll(r.Body); err != nil { | |||
if body, err = ioutil.ReadAll(readable); err != nil { | |||
return nil, err | |||
} | |||
@@ -195,17 +192,13 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e | |||
payload = []byte(form.Get(payloadFormParam)) | |||
default: | |||
return nil, fmt.Errorf("Webhook request has unsupported Content-Type %q", ct) | |||
return nil, fmt.Errorf("webhook request has unsupported Content-Type %q", contentType) | |||
} | |||
// Only validate the signature if a secret token exists. This is intended for | |||
// local development only and all webhooks should ideally set up a secret token. | |||
if len(secretToken) > 0 { | |||
sig := r.Header.Get(sha256SignatureHeader) | |||
if sig == "" { | |||
sig = r.Header.Get(sha1SignatureHeader) | |||
} | |||
if err := ValidateSignature(sig, body, secretToken); err != nil { | |||
if err := ValidateSignature(signature, body, secretToken); err != nil { | |||
return nil, err | |||
} | |||
} | |||
@@ -213,6 +206,36 @@ func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err e | |||
return payload, nil | |||
} | |||
// ValidatePayload validates an incoming GitHub Webhook event request | |||
// and returns the (JSON) payload. | |||
// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". | |||
// If the Content-Type is neither then an error is returned. | |||
// secretToken is the GitHub Webhook secret token. | |||
// If your webhook does not contain a secret token, you can pass nil or an empty slice. | |||
// This is intended for local development purposes only and all webhooks should ideally set up a secret token. | |||
// | |||
// Example usage: | |||
// | |||
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
// payload, err := github.ValidatePayload(r, s.webhookSecretKey) | |||
// if err != nil { ... } | |||
// // Process payload... | |||
// } | |||
// | |||
func ValidatePayload(r *http.Request, secretToken []byte) (payload []byte, err error) { | |||
signature := r.Header.Get(SHA256SignatureHeader) | |||
if signature == "" { | |||
signature = r.Header.Get(SHA1SignatureHeader) | |||
} | |||
contentType, _, err := mime.ParseMediaType(r.Header.Get("Content-Type")) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return ValidatePayloadFromBody(contentType, r.Body, signature, secretToken) | |||
} | |||
// ValidateSignature validates the signature for the given payload. | |||
// signature is the GitHub hash signature delivered in the X-Hub-Signature header. | |||
// payload is the JSON payload sent by GitHub Webhooks. | |||
@@ -234,14 +257,14 @@ func ValidateSignature(signature string, payload, secretToken []byte) error { | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/hooks/#webhook-headers | |||
func WebHookType(r *http.Request) string { | |||
return r.Header.Get(eventTypeHeader) | |||
return r.Header.Get(EventTypeHeader) | |||
} | |||
// DeliveryID returns the unique delivery ID of webhook request r. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/hooks/#webhook-headers | |||
func DeliveryID(r *http.Request) string { | |||
return r.Header.Get(deliveryIDHeader) | |||
return r.Header.Get(DeliveryIDHeader) | |||
} | |||
// ParseWebHook parses the event payload. For recognized event types, a |
@@ -24,6 +24,9 @@ type HookConfig struct { | |||
ContentType *string `json:"content_type,omitempty"` | |||
InsecureSSL *string `json:"insecure_ssl,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
// Secret is returned obfuscated by GitHub, but it can be set for outgoing requests. | |||
Secret *string `json:"secret,omitempty"` | |||
} | |||
// AuditEntry describes the fields that may be represented by various audit-log "action" entries. |
@@ -0,0 +1,73 @@ | |||
// Copyright 2021 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListHookDeliveries lists webhook deliveries for a webhook configured in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/orgs#list-deliveries-for-an-organization-webhook | |||
func (s *OrganizationsService) ListHookDeliveries(ctx context.Context, org string, id int64, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries", org, id) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
deliveries := []*HookDelivery{} | |||
resp, err := s.client.Do(ctx, req, &deliveries) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return deliveries, resp, nil | |||
} | |||
// GetHookDelivery returns a delivery for a webhook configured in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/orgs#get-a-webhook-delivery-for-an-organization-webhook | |||
func (s *OrganizationsService) GetHookDelivery(ctx context.Context, owner string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries/%v", owner, hookID, deliveryID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(HookDelivery) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// RedeliverHookDelivery redelivers a delivery for a webhook configured in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/orgs#redeliver-a-delivery-for-an-organization-webhook | |||
func (s *OrganizationsService) RedeliverHookDelivery(ctx context.Context, owner string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%v/deliveries/%v/attempts", owner, hookID, deliveryID) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(HookDelivery) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} |
@@ -0,0 +1,149 @@ | |||
// Copyright 2021 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// List the packages for an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#list-packages-for-an-organization | |||
func (s *OrganizationsService) ListPackages(ctx context.Context, org string, opts *PackageListOptions) ([]*Package, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages", org) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var packages []*Package | |||
resp, err := s.client.Do(ctx, req, &packages) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return packages, resp, nil | |||
} | |||
// Get a package by name from an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#get-a-package-for-an-organization | |||
func (s *OrganizationsService) GetPackage(ctx context.Context, org, packageType, packageName string) (*Package, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v", org, packageType, packageName) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var pack *Package | |||
resp, err := s.client.Do(ctx, req, &pack) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return pack, resp, nil | |||
} | |||
// Delete a package from an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#delete-a-package-for-an-organization | |||
func (s *OrganizationsService) DeletePackage(ctx context.Context, org, packageType, packageName string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v", org, packageType, packageName) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Restore a package to an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#restore-a-package-for-an-organization | |||
func (s *OrganizationsService) RestorePackage(ctx context.Context, org, packageType, packageName string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v/restore", org, packageType, packageName) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Get all versions of a package in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#get-all-package-versions-for-a-package-owned-by-an-organization | |||
func (s *OrganizationsService) PackageGetAllVersions(ctx context.Context, org, packageType, packageName string, opts *PackageListOptions) ([]*PackageVersion, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions", org, packageType, packageName) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var versions []*PackageVersion | |||
resp, err := s.client.Do(ctx, req, &versions) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return versions, resp, nil | |||
} | |||
// Get a specific version of a package in an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#get-a-package-version-for-an-organization | |||
func (s *OrganizationsService) PackageGetVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*PackageVersion, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v", org, packageType, packageName, packageVersionID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var version *PackageVersion | |||
resp, err := s.client.Do(ctx, req, &version) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return version, resp, nil | |||
} | |||
// Delete a package version from an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#delete-package-version-for-an-organization | |||
func (s *OrganizationsService) PackageDeleteVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v", org, packageType, packageName, packageVersionID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Restore a package version to an organization. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/packages#restore-package-version-for-an-organization | |||
func (s *OrganizationsService) PackageRestoreVersion(ctx context.Context, org, packageType, packageName string, packageVersionID int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/packages/%v/%v/versions/%v/restore", org, packageType, packageName, packageVersionID) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@@ -16,6 +16,10 @@ type Package struct { | |||
Owner *User `json:"owner,omitempty"` | |||
PackageVersion *PackageVersion `json:"package_version,omitempty"` | |||
Registry *PackageRegistry `json:"registry,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
VersionCount *int64 `json:"version_count,omitempty"` | |||
Visibility *string `json:"visibility,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
} | |||
func (p Package) String() string { | |||
@@ -24,24 +28,28 @@ func (p Package) String() string { | |||
// PackageVersion represents a GitHub package version. | |||
type PackageVersion struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Version *string `json:"version,omitempty"` | |||
Summary *string `json:"summary,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
BodyHTML *string `json:"body_html,omitempty"` | |||
Release *PackageRelease `json:"release,omitempty"` | |||
Manifest *string `json:"manifest,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
TagName *string `json:"tag_name,omitempty"` | |||
TargetCommitish *string `json:"target_commitish,omitempty"` | |||
TargetOID *string `json:"target_oid,omitempty"` | |||
Draft *bool `json:"draft,omitempty"` | |||
Prerelease *bool `json:"prerelease,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
PackageFiles []*PackageFile `json:"package_files,omitempty"` | |||
Author *User `json:"author,omitempty"` | |||
InstallationCommand *string `json:"installation_command,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Version *string `json:"version,omitempty"` | |||
Summary *string `json:"summary,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
BodyHTML *string `json:"body_html,omitempty"` | |||
Release *PackageRelease `json:"release,omitempty"` | |||
Manifest *string `json:"manifest,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
TagName *string `json:"tag_name,omitempty"` | |||
TargetCommitish *string `json:"target_commitish,omitempty"` | |||
TargetOID *string `json:"target_oid,omitempty"` | |||
Draft *bool `json:"draft,omitempty"` | |||
Prerelease *bool `json:"prerelease,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
PackageFiles []*PackageFile `json:"package_files,omitempty"` | |||
Author *User `json:"author,omitempty"` | |||
InstallationCommand *string `json:"installation_command,omitempty"` | |||
Metadata *PackageMetadata `json:"metadata,omitempty"` | |||
PackageHTMLURL *string `json:"package_html_url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
} | |||
func (pv PackageVersion) String() string { | |||
@@ -99,3 +107,37 @@ type PackageRegistry struct { | |||
func (r PackageRegistry) String() string { | |||
return Stringify(r) | |||
} | |||
// PackageListOptions represents the optional list options for a package. | |||
type PackageListOptions struct { | |||
// Visibility of packages "public", "internal" or "private". | |||
Visibility *string `url:"visibility,omitempty"` | |||
// PackageType represents the type of package. | |||
// It can be one of "npm", "maven", "rubygems", "nuget", "docker", or "container". | |||
PackageType *string `url:"package_type,omitempty"` | |||
// State of package either "active" or "deleted". | |||
State *string `url:"state,omitempty"` | |||
ListOptions | |||
} | |||
// PackageMetadata represents metadata from a package. | |||
type PackageMetadata struct { | |||
PackageType *string `json:"package_type,omitempty"` | |||
Container *PackageContainerMetadata `json:"container,omitempty"` | |||
} | |||
func (r PackageMetadata) String() string { | |||
return Stringify(r) | |||
} | |||
// PackageContainerMetadata represents container metadata for docker container packages. | |||
type PackageContainerMetadata struct { | |||
Tags []string `json:"tags,omitempty"` | |||
} | |||
func (r PackageContainerMetadata) String() string { | |||
return Stringify(r) | |||
} |
@@ -125,10 +125,6 @@ func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo strin | |||
// GetReview fetches the specified pull request review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#get-a-review-for-a-pull-request | |||
func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) | |||
@@ -149,10 +145,6 @@ func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, | |||
// DeletePendingReview deletes the specified pull request pending review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#delete-a-pending-review-for-a-pull-request | |||
func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) | |||
@@ -173,10 +165,6 @@ func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, re | |||
// ListReviewComments lists all the comments for the specified review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#list-comments-for-a-pull-request-review | |||
func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opts *ListOptions) ([]*PullRequestComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) | |||
@@ -201,10 +189,6 @@ func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, rep | |||
// CreateReview creates a new review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#create-a-review-for-a-pull-request | |||
// | |||
// In order to use multi-line comments, you must use the "comfort fade" preview. | |||
@@ -289,10 +273,6 @@ func (s *PullRequestsService) UpdateReview(ctx context.Context, owner, repo stri | |||
// SubmitReview submits a specified review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#submit-a-review-for-a-pull-request | |||
func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) | |||
@@ -313,10 +293,6 @@ func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo stri | |||
// DismissReview dismisses a specified review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/pulls/#dismiss-a-review-for-a-pull-request | |||
func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) |
@@ -60,6 +60,7 @@ type Repository struct { | |||
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` | |||
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` | |||
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` | |||
AllowAutoMerge *bool `json:"allow_auto_merge,omitempty"` | |||
DeleteBranchOnMerge *bool `json:"delete_branch_on_merge,omitempty"` | |||
Topics []string `json:"topics,omitempty"` | |||
Archived *bool `json:"archived,omitempty"` | |||
@@ -320,6 +321,7 @@ type createRepoRequest struct { | |||
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` | |||
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` | |||
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` | |||
AllowAutoMerge *bool `json:"allow_auto_merge,omitempty"` | |||
DeleteBranchOnMerge *bool `json:"delete_branch_on_merge,omitempty"` | |||
} | |||
@@ -362,6 +364,7 @@ func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repo | |||
AllowSquashMerge: repo.AllowSquashMerge, | |||
AllowMergeCommit: repo.AllowMergeCommit, | |||
AllowRebaseMerge: repo.AllowRebaseMerge, | |||
AllowAutoMerge: repo.AllowAutoMerge, | |||
DeleteBranchOnMerge: repo.DeleteBranchOnMerge, | |||
} | |||
@@ -764,6 +767,50 @@ type Protection struct { | |||
RequiredConversationResolution *RequiredConversationResolution `json:"required_conversation_resolution"` | |||
} | |||
// BranchProtectionRule represents the rule applied to a repositories branch. | |||
type BranchProtectionRule struct { | |||
ID *int64 `json:"id,omitempty"` | |||
RepositoryID *int64 `json:"repository_id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
PullRequestReviewsEnforcementLevel *string `json:"pull_request_reviews_enforcement_level,omitempty"` | |||
RequiredApprovingReviewCount *int `json:"required_approving_review_count,omitempty"` | |||
DismissStaleReviewsOnPush *bool `json:"dismiss_stale_reviews_on_push,omitempty"` | |||
AuthorizedDismissalActorsOnly *bool `json:"authorized_dismissal_actors_only,omitempty"` | |||
IgnoreApprovalsFromContributors *bool `json:"ignore_approvals_from_contributors,omitempty"` | |||
RequireCodeOwnerReview *bool `json:"require_code_owner_review,omitempty"` | |||
RequiredStatusChecks []string `json:"required_status_checks,omitempty"` | |||
RequiredStatusChecksEnforcementLevel *string `json:"required_status_checks_enforcement_level,omitempty"` | |||
StrictRequiredStatusChecksPolicy *bool `json:"strict_required_status_checks_policy,omitempty"` | |||
SignatureRequirementEnforcementLevel *string `json:"signature_requirement_enforcement_level,omitempty"` | |||
LinearHistoryRequirementEnforcementLevel *string `json:"linear_history_requirement_enforcement_level,omitempty"` | |||
AdminEnforced *bool `json:"admin_enforced,omitempty"` | |||
AllowForcePushesEnforcementLevel *string `json:"allow_force_pushes_enforcement_level,omitempty"` | |||
AllowDeletionsEnforcementLevel *string `json:"allow_deletions_enforcement_level,omitempty"` | |||
MergeQueueEnforcementLevel *string `json:"merge_queue_enforcement_level,omitempty"` | |||
RequiredDeploymentsEnforcementLevel *string `json:"required_deployments_enforcement_level,omitempty"` | |||
RequiredConversationResolutionLevel *string `json:"required_conversation_resolution_level,omitempty"` | |||
AuthorizedActorsOnly *bool `json:"authorized_actors_only,omitempty"` | |||
AuthorizedActorNames []string `json:"authorized_actor_names,omitempty"` | |||
} | |||
// ProtectionChanges represents the changes to the rule if the BranchProtection was edited. | |||
type ProtectionChanges struct { | |||
AuthorizedActorsOnly *AuthorizedActorsOnly `json:"authorized_actors_only,omitempty"` | |||
AuthorizedActorNames *AuthorizedActorNames `json:"authorized_actor_names,omitempty"` | |||
} | |||
// AuthorizedActorNames represents who are authorized to edit the branch protection rules. | |||
type AuthorizedActorNames struct { | |||
From []string `json:"from,omitempty"` | |||
} | |||
// AuthorizedActorsOnly represents if the branche rule can be edited by authorized actors only. | |||
type AuthorizedActorsOnly struct { | |||
From *bool `json:"from,omitempty"` | |||
} | |||
// ProtectionRequest represents a request to create/edit a branch's protection. | |||
type ProtectionRequest struct { | |||
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` | |||
@@ -776,6 +823,9 @@ type ProtectionRequest struct { | |||
AllowForcePushes *bool `json:"allow_force_pushes,omitempty"` | |||
// Allows deletion of the protected branch by anyone with write access to the repository. | |||
AllowDeletions *bool `json:"allow_deletions,omitempty"` | |||
// RequiredConversationResolution, if set to true, requires all comments | |||
// on the pull request to be resolved before it can be merged to a protected branch. | |||
RequiredConversationResolution *bool `json:"required_conversation_resolution,omitempty"` | |||
} | |||
// RequiredStatusChecks represents the protection status of a individual branch. |
@@ -0,0 +1,102 @@ | |||
// Copyright 2021 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// AutolinkOptions specifies parameters for RepositoriesService.AddAutolink method. | |||
type AutolinkOptions struct { | |||
KeyPrefix *string `json:"key_prefix,omitempty"` | |||
URLTemplate *string `json:"url_template,omitempty"` | |||
} | |||
// Autolink represents autolinks to external resources like JIRA issues and Zendesk tickets. | |||
type Autolink struct { | |||
ID *int64 `json:"id,omitempty"` | |||
KeyPrefix *string `json:"key_prefix,omitempty"` | |||
URLTemplate *string `json:"url_template,omitempty"` | |||
} | |||
// ListAutolinks returns a list of autolinks configured for the given repository. | |||
// Information about autolinks are only available to repository administrators. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#list-all-autolinks-of-a-repository | |||
func (s *RepositoriesService) ListAutolinks(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Autolink, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var autolinks []*Autolink | |||
resp, err := s.client.Do(ctx, req, &autolinks) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return autolinks, resp, nil | |||
} | |||
// AddAutolink creates an autolink reference for a repository. | |||
// Users with admin access to the repository can create an autolink. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#create-an-autolink-reference-for-a-repository | |||
func (s *RepositoriesService) AddAutolink(ctx context.Context, owner, repo string, opts *AutolinkOptions) (*Autolink, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/autolinks", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
al := new(Autolink) | |||
resp, err := s.client.Do(ctx, req, al) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return al, resp, nil | |||
} | |||
// GetAutolink returns a single autolink reference by ID that was configured for the given repository. | |||
// Information about autolinks are only available to repository administrators. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#get-an-autolink-reference-of-a-repository | |||
func (s *RepositoriesService) GetAutolink(ctx context.Context, owner, repo string, id int64) (*Autolink, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var autolink *Autolink | |||
resp, err := s.client.Do(ctx, req, &autolink) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return autolink, resp, nil | |||
} | |||
// DeleteAutolink deletes a single autolink reference by ID that was configured for the given repository. | |||
// Information about autolinks are only available to repository administrators. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#delete-an-autolink-reference-from-a-repository | |||
func (s *RepositoriesService) DeleteAutolink(ctx context.Context, owner, repo string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/autolinks/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@@ -150,8 +150,12 @@ func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo strin | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#get-a-single-commit | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#get-a-commit | |||
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) { | |||
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string, opts *ListOptions) (*RepositoryCommit, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
@@ -224,11 +228,15 @@ func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, re | |||
// CompareCommits compares a range of commits with each other. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#compare-two-commits | |||
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) { | |||
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string, opts *ListOptions) (*CommitsComparison, *Response, error) { | |||
escapedBase := url.QueryEscape(base) | |||
escapedHead := url.QueryEscape(head) | |||
u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, escapedBase, escapedHead) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { |
@@ -15,13 +15,16 @@ import ( | |||
type Metric struct { | |||
Name *string `json:"name"` | |||
Key *string `json:"key"` | |||
SPDXID *string `json:"spdx_id"` | |||
URL *string `json:"url"` | |||
HTMLURL *string `json:"html_url"` | |||
NodeID *string `json:"node_id"` | |||
} | |||
// CommunityHealthFiles represents the different files in the community health metrics response. | |||
type CommunityHealthFiles struct { | |||
CodeOfConduct *Metric `json:"code_of_conduct"` | |||
CodeOfConductFile *Metric `json:"code_of_conduct_file"` | |||
Contributing *Metric `json:"contributing"` | |||
IssueTemplate *Metric `json:"issue_template"` | |||
PullRequestTemplate *Metric `json:"pull_request_template"` | |||
@@ -31,14 +34,17 @@ type CommunityHealthFiles struct { | |||
// CommunityHealthMetrics represents a response containing the community metrics of a repository. | |||
type CommunityHealthMetrics struct { | |||
HealthPercentage *int `json:"health_percentage"` | |||
Files *CommunityHealthFiles `json:"files"` | |||
UpdatedAt *time.Time `json:"updated_at"` | |||
HealthPercentage *int `json:"health_percentage"` | |||
Description *string `json:"description"` | |||
Documentation *string `json:"documentation"` | |||
Files *CommunityHealthFiles `json:"files"` | |||
UpdatedAt *time.Time `json:"updated_at"` | |||
ContentReportsEnabled *bool `json:"content_reports_enabled"` | |||
} | |||
// GetCommunityHealthMetrics retrieves all the community health metrics for a repository. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/free-pro-team@latest/rest/reference/repos/#get-community-profile-metrics | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#get-community-profile-metrics | |||
func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
@@ -46,9 +52,6 @@ func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, own | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) | |||
metrics := &CommunityHealthMetrics{} | |||
resp, err := s.client.Do(ctx, req, metrics) | |||
if err != nil { |
@@ -132,7 +132,7 @@ func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, | |||
for _, contents := range dirContents { | |||
if *contents.Name == filename { | |||
if contents.DownloadURL == nil || *contents.DownloadURL == "" { | |||
return nil, resp, fmt.Errorf("No download link found for %s", filepath) | |||
return nil, resp, fmt.Errorf("no download link found for %s", filepath) | |||
} | |||
dlResp, err := s.client.client.Get(*contents.DownloadURL) | |||
if err != nil { | |||
@@ -141,7 +141,7 @@ func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, | |||
return dlResp.Body, &Response{Response: dlResp}, nil | |||
} | |||
} | |||
return nil, resp, fmt.Errorf("No file named %s found in %s", filename, dir) | |||
return nil, resp, fmt.Errorf("no file named %s found in %s", filename, dir) | |||
} | |||
// DownloadContentsWithMeta is identical to DownloadContents but additionally | |||
@@ -162,7 +162,7 @@ func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owne | |||
for _, contents := range dirContents { | |||
if *contents.Name == filename { | |||
if contents.DownloadURL == nil || *contents.DownloadURL == "" { | |||
return nil, contents, resp, fmt.Errorf("No download link found for %s", filepath) | |||
return nil, contents, resp, fmt.Errorf("no download link found for %s", filepath) | |||
} | |||
dlResp, err := s.client.client.Get(*contents.DownloadURL) | |||
if err != nil { | |||
@@ -171,7 +171,7 @@ func (s *RepositoriesService) DownloadContentsWithMeta(ctx context.Context, owne | |||
return dlResp.Body, contents, &Response{Response: dlResp}, nil | |||
} | |||
} | |||
return nil, nil, resp, fmt.Errorf("No file named %s found in %s", filename, dir) | |||
return nil, nil, resp, fmt.Errorf("no file named %s found in %s", filename, dir) | |||
} | |||
// GetContents can return either the metadata and content of a single file |
@@ -0,0 +1,117 @@ | |||
// Copyright 2021 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"encoding/json" | |||
"fmt" | |||
) | |||
// HookDelivery represents the data that is received from GitHub's Webhook Delivery API | |||
// | |||
// GitHub API docs: | |||
// - https://docs.github.com/en/rest/reference/repos#list-deliveries-for-a-repository-webhook | |||
// - https://docs.github.com/en/rest/reference/repos#get-a-delivery-for-a-repository-webhook | |||
type HookDelivery struct { | |||
ID *int64 `json:"id"` | |||
GUID *string `json:"guid"` | |||
DeliveredAt *Timestamp `json:"delivered_at"` | |||
Redelivery *bool `json:"redelivery"` | |||
Duration *float64 `json:"duration"` | |||
Status *string `json:"status"` | |||
StatusCode *int `json:"status_code"` | |||
Event *string `json:"event"` | |||
Action *string `json:"action"` | |||
InstallationID *string `json:"installation_id"` | |||
RepositoryID *int64 `json:"repository_id"` | |||
// Request is populated by GetHookDelivery. | |||
Request *HookRequest `json:"request,omitempty"` | |||
// Response is populated by GetHookDelivery. | |||
Response *HookResponse `json:"response,omitempty"` | |||
} | |||
func (d HookDelivery) String() string { | |||
return Stringify(d) | |||
} | |||
// HookRequest is a part of HookDelivery that contains | |||
// the HTTP headers and the JSON payload of the webhook request. | |||
type HookRequest struct { | |||
Headers map[string]string `json:"headers,omitempty"` | |||
RawPayload *json.RawMessage `json:"payload,omitempty"` | |||
} | |||
func (r HookRequest) String() string { | |||
return Stringify(r) | |||
} | |||
// HookResponse is a part of HookDelivery that contains | |||
// the HTTP headers and the response body served by the webhook endpoint. | |||
type HookResponse struct { | |||
Headers map[string]string `json:"headers,omitempty"` | |||
RawPayload *json.RawMessage `json:"payload,omitempty"` | |||
} | |||
func (r HookResponse) String() string { | |||
return Stringify(r) | |||
} | |||
// ListHookDeliveries lists webhook deliveries for a webhook configured in a repository. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#list-deliveries-for-a-repository-webhook | |||
func (s *RepositoriesService) ListHookDeliveries(ctx context.Context, owner, repo string, id int64, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries", owner, repo, id) | |||
u, err := addOptions(u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
deliveries := []*HookDelivery{} | |||
resp, err := s.client.Do(ctx, req, &deliveries) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return deliveries, resp, nil | |||
} | |||
// GetHookDelivery returns a delivery for a webhook configured in a repository. | |||
// | |||
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#get-a-delivery-for-a-repository-webhook | |||
func (s *RepositoriesService) GetHookDelivery(ctx context.Context, owner, repo string, hookID, deliveryID int64) (*HookDelivery, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries/%v", owner, repo, hookID, deliveryID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(HookDelivery) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// ParseRequestPayload parses the request payload. For recognized event types, | |||
// a value of the corresponding struct type will be returned. | |||
func (d *HookDelivery) ParseRequestPayload() (interface{}, error) { | |||
eType, ok := eventTypeMapping[*d.Event] | |||
if !ok { | |||
return nil, fmt.Errorf("unsupported event type %q", *d.Event) | |||
} | |||
e := &Event{Type: &eType, RawPayload: d.Request.RawPayload} | |||
return e.ParsePayload() | |||
} |