Compare commits

...

24 Commits

Author SHA1 Message Date
techknowlogick
8a51c48eb6 Changelog for 1.12.4 release (#12687)
Co-authored-by: zeripath <art27@cantab.net>
2020-09-03 17:00:13 -04:00
6543
0fa538e552 [Backport] Fix comment broken issue ref dependence (#12651) (#12692)
* deleteIssuesByRepoID: delete related CommentTypeRemoveDependency & CommentTypeAddDependency comments too

* Ignore ErrIssueNotExist on comment.LoadDepIssueDetails()

* CI.restart()
2020-09-03 17:23:36 +08:00
6543
69e4b6910b Make default StaticRootPath compile time settable (#12371) (#12652)
Make it possible to compile the default location of StaticRootPath independent from AppWorkPath

Co-authored-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Andrew Thornton <art27@cantab.net>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
2020-09-02 19:12:24 -04:00
techknowlogick
0e9dcc9500 When reading expired sessions - expire them (#12686) (#12690)
* When reading expired sessions - expire them

Update to latest macaron/session following merge of
https://gitea.com/macaron/session/pulls/11

Also remove old memory provider as 11 updates the memory provider to
make it unnecessary.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* and macaron/session/pulls/12

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-09-02 18:51:56 -04:00
6543
87f02d90cf Escape provider name in oauth2 provider redirect (#12650)
Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Andrew Thornton <art27@cantab.net>
2020-08-30 23:55:19 +01:00
John Olheiser
21cd7ab812 Mark Cache with ini tag (#12605) (#12611)
Signed-off-by: jolheiser <john.olheiser@gmail.com>
2020-08-26 11:53:14 -04:00
John Olheiser
981216c9fe Escape Email in forgot_password.tmpl (#12610) (#12612)
Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-08-26 10:12:09 -05:00
techknowlogick
cfbfb73c56 go1.15 on windows (#12589) (#12593)
We don't support go1.15 on 1.12.x branch, however this will allow users who chose to build with go1.15 on windows to be successful
2020-08-24 18:38:47 -04:00
zeripath
4a548a0332 Fix diff path unquoting (#12554) (#12575)
Backport #12554

* Fix diff path unquoting

services/gitdiff/gitdiff.go whereby there it assumed that the path would
always be quoted on both sides

This PR simplifies the code here and uses fmt.Fscanf to parse the
strings as necessary.

Fix #12546

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add testcase as per @mrsdizzie

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-08-23 16:58:09 +03:00
zeripath
8bf2ee1e02 Skip SSPI authentication attempts for /api/internal (#12556) (#12559)
Backport #12556

SSPI fails badly on authentication attempts to /api/internal which
it can never succesfully authenticate.

Fix #11260

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lauris BH <lauris@nix.lv>
2020-08-22 17:09:14 -04:00
zeripath
a687980412 Default empty merger list to those with write permissions (#12535) (#12560)
Backport #12535

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-08-22 23:35:56 +03:00
zeripath
1f85815a3b models: break out of loop (#12386) (#12561)
Backport #12386

Co-authored-by: Lars Lehtonen <lars.lehtonen@gmail.com>
2020-08-22 16:22:07 +01:00
silverwind
ee5e5a5093 Improve HTML escaping helper (#12562)
The previous method did not escape single quotes which under some
circumstances can lead to XSS vulnerabilites and the fact that it
depends on jQuery is also not ideal. Replace it with a lightweight
module.
2020-08-22 13:36:56 +01:00
zeripath
03ba12aabf Prevent NPE on commenting on lines with invalidated comments (#12549) (#12550)
* Prevent NPE on commenting on lines with invalidated comments

Only check for a review if we are replying to a previous review.

Prevent the NPE in #12239 by assuming that a comment without a Review is
non-pending.

Fix #12239

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Add hack around to show the broken comments

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-08-21 10:52:20 +03:00
John Olheiser
24ed1b5feb Remove hardcoded ES indexername (#12521) (#12526)
Co-authored-by: Wim <wim@42.be>
2020-08-18 21:42:22 -04:00
zeripath
8282697734 Keys should not verify revoked email addresses (#12486) (#12495)
Backport #12486

Fix #6778

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-08-17 12:06:31 -04:00
techknowlogick
ec48618d40 Fix bug preventing transfer to private organization (#12497) (#12501)
* Fix bug preventing transfer to private organization

The code assessing whether a private organization was visible to a user before
allowing transfer was incorrect due to testing membership the wrong way round

This PR fixes this issue and renames the function performing the test to be
clearer.

Further looking at the API for transfer repository - no testing was
performed to ensure that the acting user could actually see the new
owning organization.

Signed-off-by: Andrew Thornton <art27@cantab.net>

* change IsUserPartOfOrg everywhere

Co-authored-by: zeripath <art27@cantab.net>
2020-08-17 09:32:33 +03:00
techknowlogick
f0dd07129a Do not add prefix on http/https submodule links (#12477) (#12479)
Fix #12345

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-08-13 11:53:40 -04:00
techknowlogick
6d3b8141df Fix ignored login on compare (#12476) (#12478)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2020-08-12 16:36:22 -04:00
techknowlogick
13c4c7a132 Match GH with Commit page (#12425) (#12431)
Co-authored-by: zeripath <art27@cantab.net>

Co-authored-by: zeripath <art27@cantab.net>
2020-08-05 15:49:12 +08:00
techknowlogick
6015d30dd6 Fix incorrect error logging in Stats indexer and OAuth2 (#12387) (#12422)
* Fix incorrect logging in oauth2.go

Fix #11945

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Handle ErrAlreadyInQueue in stats indexer

Fix #12380

Signed-off-by: Andrew Thornton <art27@cantab.net>

* Fixes type in error message of indexer

Add the missing character in the error message.

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: Lieven Hollevoet <hollie@lika.be>

Co-authored-by: zeripath <art27@cantab.net>
Co-authored-by: Lieven Hollevoet <hollie@lika.be>
2020-08-04 16:27:43 +08:00
6543
b1cfb0d7a2 [Vendor] upgrade google/go-github to v32.1.0 (#12361) (#12390)
* upgrate go-github client to v32.1.0

* migrate
2020-07-31 12:02:23 -04:00
6543
48a423a8a8 Rendoer emoji's of Commit message on feed-page (#12373) 2020-07-29 17:09:47 -04:00
zeripath
cc8a7c9345 Git 2.28 no longer permits diff with ... on unrelated branches (#12370)
Backport #12364

Signed-off-by: Andrew Thornton <art27@cantab.net>
2020-07-29 12:42:22 -04:00
162 changed files with 9525 additions and 2946 deletions

View File

@@ -4,6 +4,33 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.12.4](https://github.com/go-gitea/gitea/releases/tag/v1.12.4) - 2020-09-02
* SECURITY
* Escape provider name in oauth2 provider redirect (#12648) (#12650)
* Escape Email on password reset page (#12610) (#12612)
* When reading expired sessions - expire them (#12686) (#12690)
* ENHANCEMENTS
* StaticRootPath configurable at compile time (#12371) (#12652)
* BUGFIXES
* Fix to show an issue that is related to a deleted issue (#12651) (#12692)
* Expire time acknowledged for cache (#12605) (#12611)
* Fix diff path unquoting (#12554) (#12575)
* Improve HTML escaping helper (#12562)
* models: break out of loop (#12386) (#12561)
* Default empty merger list to those with write permissions (#12535) (#12560)
* Skip SSPI authentication attempts for /api/internal (#12556) (#12559)
* Prevent NPE on commenting on lines with invalidated comments (#12549) (#12550)
* Remove hardcoded ES indexername (#12521) (#12526)
* Fix bug preventing transfer to private organization (#12497) (#12501)
* Keys should not verify revoked email addresses (#12486) (#12495)
* Do not add prefix on http/https submodule links (#12477) (#12479)
* Fix ignored login on compare (#12476) (#12478)
* Fix incorrect error logging in Stats indexer and OAuth2 (#12387) (#12422)
* Upgrade google/go-github to v32.1.0 (#12361) (#12390)
* Render emoji's of Commit message on feed-page (#12373)
* Fix handling of diff on unrelated branches when Git 2.28 used (#12370)
## [1.12.3](https://github.com/go-gitea/gitea/releases/tag/v1.12.3) - 2020-07-28
* BUGFIXES

View File

@@ -40,8 +40,10 @@ endif
ifeq ($(OS), Windows_NT)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= gitea.exe
else
GOFLAGS := -v
EXECUTABLE ?= gitea
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
@@ -54,7 +56,6 @@ endif
GOFMT ?= gofmt -s
GOFLAGS := -v
EXTRA_GOFLAGS ?=
MAKE_VERSION := $(shell $(MAKE) -v | head -n 1)
@@ -531,7 +532,7 @@ release-windows: | $(DIST_DIRS)
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
GO111MODULE=off $(GO) get -u src.techknowlogick.com/xgo; \
fi
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
CGO_CFLAGS="$(CGO_CFLAGS)" GO111MODULE=off xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq ($(CI),drone)
cp /build/* $(DIST)/binaries
endif

View File

@@ -141,7 +141,7 @@ Gitea will search for a number of things from the `CustomPath`. By default this
the `custom/` directory in the current working directory when running Gitea. It will also
look for its configuration file `CustomConf` in `$CustomPath/conf/app.ini`, and will use the
current working directory as the relative base path `AppWorkPath` for a number configurable
values.
values. Finally the static files will be served from `StaticRootPath` which defaults to the `AppWorkPath`.
These values, although useful when developing, may conflict with downstream users preferences.
@@ -152,6 +152,7 @@ using the `LDFLAGS` environment variable for `make`. The appropriate settings ar
* To set the `CustomPath` use `LDFLAGS="-X \"code.gitea.io/gitea/modules/setting.CustomPath=custom-path\""`
* For `CustomConf` you should use `-X \"code.gitea.io/gitea/modules/setting.CustomConf=conf.ini\"`
* For `AppWorkPath` you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkPath=working-path\"`
* For `StaticRootPath` you should use `-X \"code.gitea.io/gitea/modules/setting.StaticRootPath=static-root-path\"`
Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build`
with the appropriate `TAGS` as above.

4
go.mod
View File

@@ -15,7 +15,7 @@ require (
gitea.com/macaron/i18n v0.0.0-20190822004228-474e714e2223
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a
gitea.com/macaron/macaron v1.4.0
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7
github.com/BurntSushi/toml v0.3.1
github.com/PuerkitoBio/goquery v1.5.0
@@ -48,7 +48,7 @@ require (
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/golang/protobuf v1.4.1 // indirect
github.com/google/go-github/v24 v24.0.1
github.com/google/go-github/v32 v32.1.0
github.com/google/uuid v1.1.1
github.com/gorilla/context v1.1.1
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect

12
go.sum
View File

@@ -37,8 +37,8 @@ gitea.com/macaron/macaron v1.4.0 h1:FY1QDGqyuUzs21K6ChkbYbRUfwL7v2aUrhNEJ0IgsAw=
gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY=
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0=
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA=
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d h1:XLww3CvnFZkXVwauN67fniDaIpIqsE+9KVcxlZKlvLU=
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY=
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e h1:BHoJ/xWNt6FrVsL54JennM9HPIQlnbmRvmaC5DO65pU=
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY=
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14mefLzGluqV7w2mGU3u+iZU+jCeWk=
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
@@ -315,10 +315,8 @@ github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M=
github.com/google/go-github/v32 v32.1.0 h1:GWkQOdXqviCPx7Q7Fj+KyPoGm4SwHRh8rheoPhd27II=
github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -664,7 +662,6 @@ go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -737,7 +734,6 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@@ -98,9 +98,10 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool {
}
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64) bool {
func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64, permissionInRepo Permission) bool {
if !protectBranch.EnableMergeWhitelist {
return true
// Then we need to fall back on whether the user has write permission
return permissionInRepo.CanWrite(UnitTypeCode)
}
if base.Int64sContains(protectBranch.MergeWhitelistUserIDs, userID) {

View File

@@ -286,6 +286,9 @@ func parseGPGKey(ownerID int64, e *openpgp.Entity) (*GPGKey, error) {
emails := make([]*EmailAddress, 0, len(e.Identities))
for _, ident := range e.Identities {
if ident.Revocation != nil {
continue
}
email := strings.ToLower(strings.TrimSpace(ident.UserId.Email))
for _, e := range userEmails {
if e.Email == email {

View File

@@ -1953,6 +1953,11 @@ func deleteIssuesByRepoID(sess Engine, repoID int64) (attachmentPaths []string,
return
}
if _, err = sess.In("dependent_issue_id", deleteCond).
Delete(&Comment{}); err != nil {
return
}
var attachments []*Attachment
if err = sess.In("issue_id", deleteCond).
Find(&attachments); err != nil {

View File

@@ -435,7 +435,7 @@ func hasOrgVisible(e Engine, org *User, user *User) bool {
return true
}
if (org.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !org.isUserPartOfOrg(e, user.ID) {
if (org.Visibility == structs.VisibleTypePrivate || user.IsRestricted) && !org.hasMemberWithUserID(e, user.ID) {
return false
}
return true

View File

@@ -27,12 +27,13 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st
var gitRepo *git.Repository
var err error
Loop:
for _, rule := range rules {
switch rule {
case never:
return false, "", &ErrWontSign{never}
case always:
break
break Loop
case pubkey:
keys, err := ListGPGKeys(u.ID, ListOptions{})
if err != nil {

View File

@@ -609,12 +609,12 @@ func (u *User) IsUserOrgOwner(orgID int64) bool {
return isOwner
}
// IsUserPartOfOrg returns true if user with userID is part of the u organisation.
func (u *User) IsUserPartOfOrg(userID int64) bool {
return u.isUserPartOfOrg(x, userID)
// HasMemberWithUserID returns true if user with userID is part of the u organisation.
func (u *User) HasMemberWithUserID(userID int64) bool {
return u.hasMemberWithUserID(x, userID)
}
func (u *User) isUserPartOfOrg(e Engine, userID int64) bool {
func (u *User) hasMemberWithUserID(e Engine, userID int64) bool {
isMember, err := isOrganizationMember(e, u.ID, userID)
if err != nil {
log.Error("IsOrganizationMember: %v", err)

View File

@@ -6,6 +6,7 @@ package oauth2
import (
"net/http"
"net/url"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -119,7 +120,7 @@ func RemoveProvider(providerName string) {
// used to create different types of goth providers
func createProvider(providerName, providerType, clientID, clientSecret, openIDConnectAutoDiscoveryURL string, customURLMapping *CustomURLMapping) (goth.Provider, error) {
callbackURL := setting.AppURL + "user/oauth2/" + providerName + "/callback"
callbackURL := setting.AppURL + "user/oauth2/" + url.PathEscape(providerName) + "/callback"
var provider goth.Provider
var err error

View File

@@ -93,7 +93,7 @@ func (o *OAuth2) userIDFromToken(ctx *macaron.Context) int64 {
}
t, err := models.GetAccessTokenBySHA(tokenSHA)
if err != nil {
if models.IsErrAccessTokenNotExist(err) || models.IsErrAccessTokenEmpty(err) {
if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
log.Error("GetAccessTokenBySHA: %v", err)
}
return 0
@@ -121,7 +121,7 @@ func (o *OAuth2) VerifyAuthData(ctx *macaron.Context, sess session.Store) *model
return nil
}
if !isAPIPath(ctx) && !isAttachmentDownload(ctx) {
if isInternalPath(ctx) || !isAPIPath(ctx) && !isAttachmentDownload(ctx) {
return nil
}

View File

@@ -100,6 +100,11 @@ func isAPIPath(ctx *macaron.Context) bool {
return strings.HasPrefix(ctx.Req.URL.Path, "/api/")
}
// isInternalPath returns true if the specified URL is an internal API path
func isInternalPath(ctx *macaron.Context) bool {
return strings.HasPrefix(ctx.Req.URL.Path, "/api/internal/")
}
// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
func isAttachmentDownload(ctx *macaron.Context) bool {
return strings.HasPrefix(ctx.Req.URL.Path, "/attachments/") && ctx.Req.Method == "GET"

View File

@@ -148,6 +148,8 @@ func (s *SSPI) shouldAuthenticate(ctx *macaron.Context) (shouldAuth bool) {
} else if ctx.Req.FormValue("auth_with_sspi") == "1" {
shouldAuth = true
}
} else if isInternalPath(ctx) {
shouldAuth = false
} else if isAPIPath(ctx) || isAttachmentDownload(ctx) {
shouldAuth = true
}

View File

@@ -67,8 +67,12 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
}
if user != nil {
permission, err := models.GetUserRepoPermission(repo, user)
if err != nil {
return nil, err
}
branch.UserCanPush = bp.CanUserPush(user.ID)
branch.UserCanMerge = bp.IsUserMergeWhitelisted(user.ID)
branch.UserCanMerge = bp.IsUserMergeWhitelisted(user.ID, permission)
}
return branch, nil

View File

@@ -271,11 +271,12 @@ func AllCommitsCount(repoPath string) (int64, error) {
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
}
func commitsCount(repoPath, revision, relpath string) (int64, error) {
func commitsCount(repoPath string, revision, relpath []string) (int64, error) {
cmd := NewCommand("rev-list", "--count")
cmd.AddArguments(revision)
cmd.AddArguments(revision...)
if len(relpath) > 0 {
cmd.AddArguments("--", relpath)
cmd.AddArguments("--")
cmd.AddArguments(relpath...)
}
stdout, err := cmd.RunInDir(repoPath)
@@ -288,7 +289,7 @@ func commitsCount(repoPath, revision, relpath string) (int64, error) {
// CommitsCount returns number of total commits of until given revision.
func CommitsCount(repoPath, revision string) (int64, error) {
return commitsCount(repoPath, revision, "")
return commitsCount(repoPath, []string{revision}, []string{})
}
// CommitsCount returns number of total commits of until current revision.

View File

@@ -293,7 +293,7 @@ func (repo *Repository) FileChangedBetweenCommits(filename, id1, id2 string) (bo
// FileCommitsCount return the number of files at a revison
func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
return commitsCount(repo.Path, revision, file)
return commitsCount(repo.Path, []string{revision}, []string{file})
}
// CommitsByFileAndRange return the commits according revison file and the page
@@ -319,6 +319,11 @@ func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, pag
// FilesCountBetween return the number of files changed between two commits
func (repo *Repository) FilesCountBetween(startCommitID, endCommitID string) (int, error) {
stdout, err := NewCommand("diff", "--name-only", startCommitID+"..."+endCommitID).RunInDir(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if startCommitID and endCommitID have become unrelated.
// previously it would return the results of git diff --name-only startCommitID endCommitID so let's try that...
stdout, err = NewCommand("diff", "--name-only", startCommitID, endCommitID).RunInDir(repo.Path)
}
if err != nil {
return 0, err
}
@@ -333,6 +338,11 @@ func (repo *Repository) CommitsBetween(last *Commit, before *Commit) (*list.List
stdout, err = NewCommand("rev-list", last.ID.String()).RunInDirBytes(repo.Path)
} else {
stdout, err = NewCommand("rev-list", before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
stdout, err = NewCommand("rev-list", before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
}
}
if err != nil {
return nil, err
@@ -348,6 +358,11 @@ func (repo *Repository) CommitsBetweenLimit(last *Commit, before *Commit, limit,
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), last.ID.String()).RunInDirBytes(repo.Path)
} else {
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String()+"..."+last.ID.String()).RunInDirBytes(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list --max-count n before last so let's try that...
stdout, err = NewCommand("rev-list", "--max-count", strconv.Itoa(limit), "--skip", strconv.Itoa(skip), before.ID.String(), last.ID.String()).RunInDirBytes(repo.Path)
}
}
if err != nil {
return nil, err
@@ -373,7 +388,14 @@ func (repo *Repository) CommitsBetweenIDs(last, before string) (*list.List, erro
// CommitsCountBetween return numbers of commits between two commits
func (repo *Repository) CommitsCountBetween(start, end string) (int64, error) {
return commitsCount(repo.Path, start+"..."+end, "")
count, err := commitsCount(repo.Path, []string{start + "..." + end}, []string{})
if err != nil && strings.Contains(err.Error(), "no merge base") {
// future versions of git >= 2.28 are likely to return an error if before and last have become unrelated.
// previously it would return the results of git rev-list before last so let's try that...
return commitsCount(repo.Path, []string{start, end}, []string{})
}
return count, err
}
// commitsBefore the limit is depth, not total number of returned commits.

View File

@@ -6,6 +6,7 @@
package git
import (
"bytes"
"container/list"
"fmt"
"io"
@@ -66,7 +67,7 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string)
compareInfo := new(CompareInfo)
compareInfo.MergeBase, remoteBranch, err = repo.GetMergeBase(tmpRemote, baseBranch, headBranch)
if err == nil {
// We have a common base
// We have a common base - therefore we know that ... should work
logs, err := NewCommand("log", compareInfo.MergeBase+"..."+headBranch, prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil {
return nil, err
@@ -85,6 +86,11 @@ func (repo *Repository) GetCompareInfo(basePath, baseBranch, headBranch string)
// Count number of changed files.
stdout, err := NewCommand("diff", "--name-only", remoteBranch+"..."+headBranch).RunInDir(repo.Path)
if err != nil && strings.Contains(err.Error(), "no merge base") {
// git >= 2.28 now returns an error if base and head have become unrelated.
// previously it would return the results of git diff --name-only base head so let's try that...
stdout, err = NewCommand("diff", "--name-only", remoteBranch, headBranch).RunInDir(repo.Path)
}
if err != nil {
return nil, err
}
@@ -108,12 +114,24 @@ func (repo *Repository) GetDiff(base, head string, w io.Writer) error {
// GetPatch generates and returns format-patch data between given revisions.
func (repo *Repository) GetPatch(base, head string, w io.Writer) error {
return NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
RunInDirPipeline(repo.Path, w, nil)
stderr := new(bytes.Buffer)
err := NewCommand("format-patch", "--binary", "--stdout", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand("format-patch", "--binary", "--stdout", base, head).
RunInDirPipeline(repo.Path, w, nil)
}
return err
}
// GetDiffFromMergeBase generates and return patch data from merge base to head
func (repo *Repository) GetDiffFromMergeBase(base, head string, w io.Writer) error {
return NewCommand("diff", "-p", "--binary", base+"..."+head).
RunInDirPipeline(repo.Path, w, nil)
stderr := new(bytes.Buffer)
err := NewCommand("diff", "-p", "--binary", base+"..."+head).
RunInDirPipeline(repo.Path, w, stderr)
if err != nil && bytes.Contains(stderr.Bytes(), []byte("no merge base")) {
return NewCommand("diff", "-p", "--binary", base, head).
RunInDirPipeline(repo.Path, w, nil)
}
return err
}

View File

@@ -97,13 +97,13 @@ func getRefURL(refURL, urlPrefix, repoFullName string) string {
for _, scheme := range supportedSchemes {
if ref.Scheme == scheme {
if urlPrefixHostname == refHostname {
return urlPrefix + path.Clean(path.Join("/", ref.Path))
} else if ref.Scheme == "http" || ref.Scheme == "https" {
if ref.Scheme == "http" || ref.Scheme == "https" {
if len(ref.User.Username()) > 0 {
return ref.Scheme + "://" + fmt.Sprintf("%v", ref.User) + "@" + ref.Host + ref.Path
}
return ref.Scheme + "://" + ref.Host + ref.Path
} else if urlPrefixHostname == refHostname {
return urlPrefix + path.Clean(path.Join("/", ref.Path))
} else {
return "http://" + refHostname + ref.Path
}

View File

@@ -28,6 +28,7 @@ func TestGetRefURL(t *testing.T) {
{"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "https://try.gitea.io/go-gitea/gitea"},
{"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "https://127.0.0.1:3000/go-gitea/gitea"},
{"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "https://gitea.com:3000/user1/repo1"},
{"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "user/repo2", "https://example.gitea.com/gitea/user1/repo1"},
{"https://username:password@github.com/username/repository.git", "/", "username/repository2", "https://username:password@github.com/username/repository"},
{"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", ""},
{"git@localhost:user/repo", "https://localhost/", "user2/repo1", "https://localhost/user/repo"},

View File

@@ -171,7 +171,7 @@ func InitIssueIndexer(syncReindex bool) {
log.Debug("Created Bleve Indexer")
case "elasticsearch":
graceful.GetManager().RunWithShutdownFns(func(_, atTerminate func(context.Context, func())) {
issueIndexer, err := NewElasticSearchIndexer(setting.Indexer.IssueConnStr, "gitea_issues")
issueIndexer, err := NewElasticSearchIndexer(setting.Indexer.IssueConnStr, setting.Indexer.IssueIndexerName)
if err != nil {
log.Fatal("Unable to initialize Elastic Search Issue Indexer at connection: %s Error: %v", setting.Indexer.IssueConnStr, err)
}

View File

@@ -21,7 +21,7 @@ func handle(data ...queue.Data) {
for _, datum := range data {
opts := datum.(int64)
if err := indexer.Index(opts); err != nil {
log.Error("stats queue idexer.Index(%d) failed: %v", opts, err)
log.Error("stats queue indexer.Index(%d) failed: %v", opts, err)
}
}
}
@@ -39,5 +39,11 @@ func initStatsQueue() error {
// UpdateRepoIndexer update a repository's entries in the indexer
func UpdateRepoIndexer(repo *models.Repository) error {
return statsQueue.Push(repo.ID)
if err := statsQueue.Push(repo.ID); err != nil {
if err != queue.ErrAlreadyInQueue {
return err
}
log.Debug("Repo ID: %d already queued", repo.ID)
}
return nil
}

View File

@@ -8,7 +8,7 @@ package migrations
import (
"errors"
"github.com/google/go-github/v24/github"
"github.com/google/go-github/v32/github"
)
var (

View File

@@ -18,7 +18,7 @@ import (
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"github.com/google/go-github/v24/github"
"github.com/google/go-github/v32/github"
"golang.org/x/oauth2"
)
@@ -364,7 +364,7 @@ func (g *GithubDownloaderV3) GetIssues(page, perPage int) ([]*base.Issue, bool,
}
var labels = make([]*base.Label, 0, len(issue.Labels))
for _, l := range issue.Labels {
labels = append(labels, convertGithubLabel(&l))
labels = append(labels, convertGithubLabel(l))
}
var email string
@@ -425,8 +425,8 @@ func (g *GithubDownloaderV3) GetComments(issueNumber int64) ([]*base.Comment, er
asc = "asc"
)
opt := &github.IssueListCommentsOptions{
Sort: created,
Direction: asc,
Sort: &created,
Direction: &asc,
ListOptions: github.ListOptions{
PerPage: 100,
},

View File

@@ -1,216 +0,0 @@
// Copyright 2013 Beego Authors
// Copyright 2014 The Macaron Authors
// Copyright 2019 The Gitea Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package session
import (
"container/list"
"fmt"
"sync"
"time"
"gitea.com/macaron/session"
)
// MemStore represents a in-memory session store implementation.
type MemStore struct {
sid string
lock sync.RWMutex
data map[interface{}]interface{}
lastAccess time.Time
}
// NewMemStore creates and returns a memory session store.
func NewMemStore(sid string) *MemStore {
return &MemStore{
sid: sid,
data: make(map[interface{}]interface{}),
lastAccess: time.Now(),
}
}
// Set sets value to given key in session.
func (s *MemStore) Set(key, val interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
s.data[key] = val
return nil
}
// Get gets value by given key in session.
func (s *MemStore) Get(key interface{}) interface{} {
s.lock.RLock()
defer s.lock.RUnlock()
return s.data[key]
}
// Delete deletes a key from session.
func (s *MemStore) Delete(key interface{}) error {
s.lock.Lock()
defer s.lock.Unlock()
delete(s.data, key)
return nil
}
// ID returns current session ID.
func (s *MemStore) ID() string {
return s.sid
}
// Release releases resource and save data to provider.
func (*MemStore) Release() error {
return nil
}
// Flush deletes all session data.
func (s *MemStore) Flush() error {
s.lock.Lock()
defer s.lock.Unlock()
s.data = make(map[interface{}]interface{})
return nil
}
// MemProvider represents a in-memory session provider implementation.
type MemProvider struct {
lock sync.RWMutex
maxLifetime int64
data map[string]*list.Element
// A priority list whose lastAccess newer gets higher priority.
list *list.List
}
// Init initializes memory session provider.
func (p *MemProvider) Init(maxLifetime int64, _ string) error {
p.lock.Lock()
p.maxLifetime = maxLifetime
p.lock.Unlock()
return nil
}
// update expands time of session store by given ID.
func (p *MemProvider) update(sid string) error {
p.lock.Lock()
defer p.lock.Unlock()
if e, ok := p.data[sid]; ok {
e.Value.(*MemStore).lastAccess = time.Now()
p.list.MoveToFront(e)
return nil
}
return nil
}
// Read returns raw session store by session ID.
func (p *MemProvider) Read(sid string) (_ session.RawStore, err error) {
p.lock.RLock()
e, ok := p.data[sid]
p.lock.RUnlock()
if ok {
if err = p.update(sid); err != nil {
return nil, err
}
return e.Value.(*MemStore), nil
}
// Create a new session.
p.lock.Lock()
defer p.lock.Unlock()
s := NewMemStore(sid)
p.data[sid] = p.list.PushBack(s)
return s, nil
}
// Exist returns true if session with given ID exists.
func (p *MemProvider) Exist(sid string) bool {
p.lock.RLock()
defer p.lock.RUnlock()
_, ok := p.data[sid]
return ok
}
// Destroy deletes a session by session ID.
func (p *MemProvider) Destroy(sid string) error {
p.lock.Lock()
defer p.lock.Unlock()
e, ok := p.data[sid]
if !ok {
return nil
}
p.list.Remove(e)
delete(p.data, sid)
return nil
}
// Regenerate regenerates a session store from old session ID to new one.
func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
if p.Exist(sid) {
return nil, fmt.Errorf("new sid '%s' already exists", sid)
}
s, err := p.Read(oldsid)
if err != nil {
return nil, err
}
if err = p.Destroy(oldsid); err != nil {
return nil, err
}
s.(*MemStore).sid = sid
p.lock.Lock()
defer p.lock.Unlock()
p.data[sid] = p.list.PushBack(s)
return s, nil
}
// Count counts and returns number of sessions.
func (p *MemProvider) Count() int {
return p.list.Len()
}
// GC calls GC to clean expired sessions.
func (p *MemProvider) GC() {
p.lock.RLock()
for {
// No session in the list.
e := p.list.Back()
if e == nil {
break
}
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() {
p.lock.RUnlock()
p.lock.Lock()
p.list.Remove(e)
delete(p.data, e.Value.(*MemStore).sid)
p.lock.Unlock()
p.lock.RLock()
} else {
break
}
}
p.lock.RUnlock()
}

View File

@@ -5,7 +5,6 @@
package session
import (
"container/list"
"encoding/json"
"fmt"
"sync"
@@ -37,7 +36,7 @@ func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
// This is only slightly more wrong than modules/setting/session.go:23
switch opts.Provider {
case "memory":
o.provider = &MemProvider{list: list.New(), data: make(map[string]*list.Element)}
o.provider = &session.MemProvider{}
case "file":
o.provider = &session.FileProvider{}
case "redis":

View File

@@ -23,7 +23,7 @@ type Cache struct {
var (
// CacheService the global cache
CacheService = struct {
Cache
Cache `ini:"cache"`
LastCommit struct {
Enabled bool

View File

@@ -666,7 +666,10 @@ func NewContext() {
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
if len(StaticRootPath) == 0 {
StaticRootPath = AppWorkPath
}
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()

5
package-lock.json generated
View File

@@ -4007,6 +4007,11 @@
"es6-symbol": "^3.1.1"
}
},
"escape-goat": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz",
"integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",

View File

@@ -18,6 +18,7 @@
"cssnano": "4.1.10",
"domino": "2.1.5",
"dropzone": "5.7.0",
"escape-goat": "3.0.0",
"fast-glob": "3.2.2",
"file-loader": "6.0.0",
"fomantic-ui": "2.8.4",

View File

@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
api "code.gitea.io/gitea/modules/structs"
repo_service "code.gitea.io/gitea/services/repository"
)
@@ -53,13 +54,21 @@ func Transfer(ctx *context.APIContext, opts api.TransferRepoOption) {
newOwner, err := models.GetUserByName(opts.NewOwner)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Error(http.StatusNotFound, "GetUserByName", err)
ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
return
}
ctx.InternalServerError(err)
return
}
if newOwner.Type == models.UserTypeOrganization {
if !ctx.User.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !newOwner.HasMemberWithUserID(ctx.User.ID) {
// The user shouldn't know about this organization
ctx.Error(http.StatusNotFound, "", "The new owner does not exist or cannot be found")
return
}
}
var teams []*models.Team
if opts.TeamIDs != nil {
if !newOwner.IsOrganization() {

View File

@@ -39,6 +39,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env []
_ = stdoutWriter.Close()
}()
// This is safe as force pushes are already forbidden
err = git.NewCommand("rev-list", oldCommitID+"..."+newCommitID).
RunInDirTimeoutEnvFullPipelineFunc(env, -1, repo.Path,
stdoutWriter, nil, nil,
@@ -70,6 +71,7 @@ func checkFileProtection(oldCommitID, newCommitID string, patterns []glob.Glob,
_ = stdoutWriter.Close()
}()
// This use of ... is safe as force-pushes have already been ruled out.
err = git.NewCommand("diff", "--name-only", oldCommitID+"..."+newCommitID).
RunInDirTimeoutEnvFullPipelineFunc(env, -1, repo.Path,
stdoutWriter, nil, nil,

View File

@@ -969,8 +969,10 @@ func ViewIssue(ctx *context.Context) {
}
} else if comment.Type == models.CommentTypeRemoveDependency || comment.Type == models.CommentTypeAddDependency {
if err = comment.LoadDepIssueDetails(); err != nil {
ctx.ServerError("LoadDepIssueDetails", err)
return
if !models.IsErrIssueNotExist(err) {
ctx.ServerError("LoadDepIssueDetails", err)
return
}
}
} else if comment.Type == models.CommentTypeCode || comment.Type == models.CommentTypeReview {
comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink,

View File

@@ -381,7 +381,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
}
if newOwner.Type == models.UserTypeOrganization {
if !ctx.User.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !ctx.User.IsUserPartOfOrg(newOwner.ID) {
if !ctx.User.IsAdmin && newOwner.Visibility == structs.VisibleTypePrivate && !newOwner.HasMemberWithUserID(ctx.User.ID) {
// The user shouldn't know about this organization
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_owner_name"), tplSettingsOptions, nil)
return

View File

@@ -713,7 +713,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/:id", repo.MilestoneIssuesAndPulls)
}, reqRepoIssuesOrPullsReader, context.RepoRef())
m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists).
Get(repo.SetDiffViewStyle, repo.CompareDiff).
Get(ignSignIn, repo.SetDiffViewStyle, repo.CompareDiff).
Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(auth.CreateIssueForm{}), repo.CompareAndPullRequestPost)
}, context.RepoAssignment(), context.UnitTypes())

View File

@@ -18,7 +18,6 @@ import (
"os"
"os/exec"
"sort"
"strconv"
"strings"
"code.gitea.io/gitea/models"
@@ -532,40 +531,28 @@ func ParsePatch(maxLines, maxLineCharacters, maxFiles int, reader io.Reader) (*D
break
}
var middle int
// Note: In case file name is surrounded by double quotes (it happens only in git-shell).
// e.g. diff --git "a/xxx" "b/xxx"
hasQuote := line[len(cmdDiffHead)] == '"'
if hasQuote {
middle = strings.Index(line, ` "b/`)
var a string
var b string
rd := strings.NewReader(line[len(cmdDiffHead):])
char, _ := rd.ReadByte()
_ = rd.UnreadByte()
if char == '"' {
fmt.Fscanf(rd, "%q ", &a)
} else {
middle = strings.Index(line, " b/")
fmt.Fscanf(rd, "%s ", &a)
}
beg := len(cmdDiffHead)
a := line[beg+2 : middle]
b := line[middle+3:]
if hasQuote {
// Keep the entire string in double quotes for now
a = line[beg:middle]
b = line[middle+1:]
var err error
a, err = strconv.Unquote(a)
if err != nil {
return nil, fmt.Errorf("Unquote: %v", err)
}
b, err = strconv.Unquote(b)
if err != nil {
return nil, fmt.Errorf("Unquote: %v", err)
}
// Now remove the /a /b
a = a[2:]
b = b[2:]
char, _ = rd.ReadByte()
_ = rd.UnreadByte()
if char == '"' {
fmt.Fscanf(rd, "%q", &b)
} else {
fmt.Fscanf(rd, "%s", &b)
}
a = a[2:]
b = b[2:]
curFile = &DiffFile{
Name: b,

View File

@@ -76,6 +76,23 @@ func TestParsePatch(t *testing.T) {
}
println(result)
var diff2a = `diff --git "a/A \\ B" b/A/B
--- "a/A \\ B"
+++ b/A/B
@@ -1,3 +1,6 @@
# gitea-github-migrator
+
+ Build Status
- Latest Release
Docker Pulls
+ cut off
+ cut off`
result, err = ParsePatch(setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles, strings.NewReader(diff2a))
if err != nil {
t.Errorf("ParsePatch failed: %s", err)
}
println(result)
var diff3 = `diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md

View File

@@ -544,7 +544,7 @@ func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *mod
return false, err
}
if (p.CanWrite(models.UnitTypeCode) && pr.ProtectedBranch == nil) || (pr.ProtectedBranch != nil && pr.ProtectedBranch.IsUserMergeWhitelisted(user.ID)) {
if (p.CanWrite(models.UnitTypeCode) && pr.ProtectedBranch == nil) || (pr.ProtectedBranch != nil && pr.ProtectedBranch.IsUserMergeWhitelisted(user.ID, p)) {
return true, nil
}

View File

@@ -29,7 +29,7 @@ func CreateCodeComment(doer *models.User, gitRepo *git.Repository, issue *models
// - Comments that are part of a review
// - Comments that reply to an existing review
if !isReview {
if !isReview && replyReviewID != 0 {
// It's not part of a review; maybe a reply to a review comment or a single comment.
// Check if there are reviews for that line already; if there are, this is a reply
if existsReview, err = models.ReviewExists(issue, treePath, line); err != nil {

View File

@@ -35,9 +35,9 @@
{{if .Author}}
<img class="ui avatar image" src="{{.Author.RelAvatarLink}}" />
{{if .Author.FullName}}
<a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong> {{if .IsSigned}}&lt;{{.Commit.Author.Email}}&gt;{{end}}</a>
<a href="{{.Author.HomeLink}}"><strong>{{.Author.FullName}}</strong></a>
{{else}}
<a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong> {{if .IsSigned}}&lt;{{.Commit.Author.Email}}&gt;{{end}}</a>
<a href="{{.Author.HomeLink}}"><strong>{{.Commit.Author.Name}}</strong></a>
{{end}}
{{else}}
<img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
@@ -49,7 +49,7 @@
<span class="text grey">{{svg "octicon-git-commit" 16}}{{.i18n.Tr "repo.diff.committed_by"}}</span>
{{if ne .Verification.CommittingUser.ID 0}}
<img class="ui avatar image" src="{{.Verification.CommittingUser.RelAvatarLink}}" />
<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong> &lt;{{.Commit.Committer.Email}}&gt;</a>
<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a>
{{else}}
<img class="ui avatar image" src="{{AvatarLink .Commit.Committer.Email}}" />
<strong>{{.Commit.Committer.Name}}</strong>
@@ -94,7 +94,7 @@
<span class="ui text">{{.i18n.Tr "repo.commits.signed_by_untrusted_user_unmatched"}}:</span>
{{end}}
<img class="ui avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" />
<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong> <{{.Verification.SigningEmail}}></a>
<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong></a>
<span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
{{else}}
<i class="icons" title="{{.i18n.Tr "gpg.default_key"}}">
@@ -103,7 +103,7 @@
</i>
<span class="ui text">{{.i18n.Tr "repo.commits.signed_by"}}:</span>
<img class="ui avatar image" src="{{AvatarLink .Verification.SigningEmail}}" />
<strong>{{.Verification.SigningUser.Name}}</strong> <{{.Verification.SigningEmail}}>
<strong>{{.Verification.SigningUser.Name}}</strong>
<span class="pull-right"><span class="ui text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="cogs icon" title="{{.i18n.Tr "gpg.default_key"}}"></i>{{.Verification.SigningKey.KeyID}}</span>
{{end}}
{{else if .Verification.Warning}}

View File

@@ -149,7 +149,10 @@
{{if gt (len $line.Comments) 0}}
{{$resolved := (index $line.Comments 0).IsResolved}}
{{$resolveDoer := (index $line.Comments 0).ResolveDoer}}
{{$isNotPending := (not (eq (index $line.Comments 0).Review.Type 0))}}
{{$isNotPending := false}}
{{if (index $line.Comments 0).Review}}
{{$isNotPending = (not (eq (index $line.Comments 0).Review.Type 0))}}
{{end}}
<tr class="add-code-comment">
<td class="lines-num"></td>
<td class="lines-type-marker"></td>

View File

@@ -25,7 +25,10 @@
{{if gt (len $line.Comments) 0}}
{{$resolved := (index $line.Comments 0).IsResolved}}
{{$resolveDoer := (index $line.Comments 0).ResolveDoer}}
{{$isNotPending := (not (eq (index $line.Comments 0).Review.Type 0))}}
{{$isNotPending := false}}
{{if (index $line.Comments 0).Review}}
{{$isNotPending = (not (eq (index $line.Comments 0).Review.Type 0))}}
{{end}}
<tr>
<td colspan="2" class="lines-num"></td>
<td class="add-comment-left add-comment-right" colspan="2">

View File

@@ -366,6 +366,122 @@
</div>
{{end}}
</div>
{{else if and (eq .Type 21) (eq .ReviewID 0)}}
<div class="timeline-item-group">
<div class="timeline-item event" id="{{.HashTag}}">
{{if .OriginalAuthor }}
{{else}}
<a class="timeline-avatar"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
<img src="{{.Poster.RelAvatarLink}}">
</a>
{{end}}
<span class="badge grey">{{svg "octicon-comment" 16}}</span>
<span class="text grey">
{{if .OriginalAuthor }}
<span class="text black"><i class="fa {{MigrationIcon $.Repository.GetOriginalURLHostname}}" aria-hidden="true"></i> {{ .OriginalAuthor }}</span><span class="text grey"> {{if $.Repository.OriginalURL}}</span><span class="text migrate">({{$.i18n.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname | Safe }}){{end}}</span>
{{else}}
<a class="author"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>{{.Poster.GetDisplayName}}</a>
{{end}}
{{$.i18n.Tr "repo.issues.review.comment" $createdStr | Safe}}
</span>
</div>
<div class="timeline-item event">
{{$filename := .TreePath}}
{{$line := .Line}}
<div class="ui segments">
<div class="ui segment">
{{$invalid := .Invalidated}}
{{$resolved := .IsResolved}}
{{$ignore := .LoadResolveDoer}}
{{$resolveDoer := .ResolveDoer}}
{{$isNotPending := true}}
{{if or $invalid $resolved}}
<button id="show-outdated-{{.ID}}" data-comment="{{.ID}}" class="ui compact right labeled button show-outdated">
{{svg "octicon-unfold" 16}}
{{if $invalid }}
{{$.i18n.Tr "repo.issues.review.show_outdated"}}
{{else}}
{{$.i18n.Tr "repo.issues.review.show_resolved"}}
{{end}}
</button>
<button id="hide-outdated-{{.ID}}" data-comment="{{.ID}}" class="hide ui compact right labeled button hide-outdated">
{{svg "octicon-fold" 16}}
{{if $invalid}}
{{$.i18n.Tr "repo.issues.review.hide_outdated"}}
{{else}}
{{$.i18n.Tr "repo.issues.review.hide_resolved"}}
{{end}}
</button>
{{end}}
<a href="{{.CodeCommentURL}}" class="file-comment">{{$filename}}</a>
</div>
{{$diff := (CommentMustAsDiff .)}}
{{if $diff}}
{{$file := (index $diff.Files 0)}}
<div id="code-preview-{{.ID}}" class="ui table segment{{if or $invalid $resolved}} hide{{end}}">
<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}">
<div class="file-body file-code code-view code-diff code-diff-unified">
<table>
<tbody>
{{template "repo/diff/section_unified" dict "file" $file "root" $}}
</tbody>
</table>
</div>
</div>
</div>
{{end}}
<div id="code-comments-{{.ID}}" class="ui segment{{if or $invalid $resolved}} hide{{end}}">
<div class="ui comments">
{{ $createdSubStr:= TimeSinceUnix .CreatedUnix $.Lang }}
<div class="comment" id="{{.HashTag}}">
{{if not .OriginalAuthor }}
<a class="avatar">
<img src="{{.Poster.RelAvatarLink}}">
</a>
{{end}}
<div class="content">
<div class="code-comment-content">
<span class="text grey">
{{if .OriginalAuthor }}
<span class="text black"><i class="fa {{MigrationIcon $.Repository.GetOriginalURLHostname}}" aria-hidden="true"></i> {{ .OriginalAuthor }}</span><span class="text grey"> {{if $.Repository.OriginalURL}}</span><span class="text migrate">({{$.i18n.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname | Safe }}){{end}}</span>
{{else}}
<a class="author"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>{{.Poster.GetDisplayName}}</a>
{{end}}
{{$.i18n.Tr "repo.issues.commented_at" .HashTag $createdSubStr | Safe}}
<div class="text">
<div class="render-content markdown">
{{if .RenderedContent}}
{{.RenderedContent|Str2html}}
{{else}}
<span class="no-content">{{$.i18n.Tr "repo.issues.no_content"}}</span>
{{end}}
</div>
<div class="raw-content hide">{{.Content}}</div>
</div>
</span>
</div>
</div>
</div>
</div>
{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" .ReviewID "root" $ "comment" .}}
{{if and $.CanMarkConversation $isNotPending}}
<button class="ui tiny button resolve-conversation" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{.ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" >
{{if $resolved}}
{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}}
{{else}}
{{$.i18n.Tr "repo.issues.review.resolve_conversation"}}
{{end}}
</button>
{{end}}
{{if $resolved}}
<span class="ui grey text"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span>
{{end}}
</div>
</div>
</div>
</div>
{{else if eq .Type 22}}
<div class="timeline-item-group">
<div class="timeline-item event" id="{{.HashTag}}">

View File

@@ -10,7 +10,7 @@
<div class="ui attached segment">
{{template "base/alert" .}}
{{if .IsResetSent}}
<p>{{.i18n.Tr "auth.reset_password_mail_sent_prompt" .Email .ResetPwdCodeLives | Str2html}}</p>
<p>{{.i18n.Tr "auth.reset_password_mail_sent_prompt" (Escape .Email) .ResetPwdCodeLives | Str2html}}</p>
{{else if .IsResetRequest}}
<div class="required inline field {{if .Err_Email}}error{{end}}">
<label for="email">{{.i18n.Tr "email"}}</label>

View File

@@ -79,7 +79,7 @@
{{ $repoLink := .GetRepoLink}}
{{if $push.Commits}}
{{range $push.Commits}}
<li><img class="img-8" src="{{$push.AvatarLink .AuthorEmail}}"> <a class="commit-id" href="{{$repoLink}}/commit/{{.Sha1}}">{{ShortSha .Sha1}}</a> <span class="text truncate light grey">{{.Message}}</span></li>
<li><img class="img-8" src="{{$push.AvatarLink .AuthorEmail}}"> <a class="commit-id" href="{{$repoLink}}/commit/{{.Sha1}}">{{ShortSha .Sha1}}</a> <span class="text truncate light grey">{{.Message | RenderEmoji}}</span></li>
{{end}}
{{end}}
{{if and (gt $push.Len 1) $push.CompareURL}}<li><a href="{{AppSubUrl}}/{{$push.CompareURL}}">{{$.i18n.Tr "action.compare_commits" $push.Len}} »</a></li>{{end}}

View File

@@ -53,7 +53,7 @@
<li>
<ul class="user-orgs">
{{range .Orgs}}
{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.IsUserPartOfOrg $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
{{if (or .Visibility.IsPublic (and ($.SignedUser) (or .Visibility.IsLimited (and (.HasMemberWithUserID $.SignedUserID) .Visibility.IsPrivate) ($.IsAdmin))))}}
<li>
<a href="{{.HomeLink}}"><img class="ui image poping up" src="{{.RelAvatarLink}}" data-content="{{.Name}}" data-position="top center" data-variation="tiny inverted"></a>
</li>

View File

@@ -133,7 +133,15 @@ func (p *FileProvider) Read(sid string) (_ RawStore, err error) {
defer p.lock.RUnlock()
var f *os.File
ok := false
if com.IsFile(filename) {
modTime, err := com.FileMTime(filename)
if err != nil {
return nil, err
}
ok = (modTime + p.maxlifetime) >= time.Now().Unix()
}
if ok {
f, err = os.OpenFile(filename, os.O_RDONLY, 0600)
} else {
f, err = os.Create(filename)

View File

@@ -96,6 +96,8 @@ type MemProvider struct {
// Init initializes memory session provider.
func (p *MemProvider) Init(maxLifetime int64, _ string) error {
p.lock.Lock()
p.list = list.New()
p.data = make(map[string]*list.Element)
p.maxLifetime = maxLifetime
p.lock.Unlock()
return nil
@@ -120,7 +122,8 @@ func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
e, ok := p.data[sid]
p.lock.RUnlock()
if ok {
// Only restore if the session is still alive.
if ok && (e.Value.(*MemStore).lastAccess.Unix()+p.maxLifetime) >= time.Now().Unix() {
if err = p.update(sid); err != nil {
return nil, err
}
@@ -130,7 +133,10 @@ func (p *MemProvider) Read(sid string) (_ RawStore, err error) {
// Create a new session.
p.lock.Lock()
defer p.lock.Unlock()
if ok {
p.list.Remove(e)
delete(p.data, sid)
}
s := NewMemStore(sid)
p.data[sid] = p.list.PushBack(s)
return s, nil
@@ -213,5 +219,5 @@ func (p *MemProvider) GC() {
}
func init() {
Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)})
Register("memory", &MemProvider{})
}

View File

@@ -120,18 +120,20 @@ func (p *MysqlProvider) Init(expire int64, connStr string) (err error) {
// Read returns raw session store by session ID.
func (p *MysqlProvider) Read(sid string) (session.RawStore, error) {
now := time.Now().Unix()
var data []byte
err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data)
expiry := now
err := p.c.QueryRow("SELECT data, expiry FROM session WHERE `key`=?", sid).Scan(&data, &expiry)
if err == sql.ErrNoRows {
_, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)",
sid, "", time.Now().Unix())
sid, "", now)
}
if err != nil {
return nil, err
}
var kv map[interface{}]interface{}
if len(data) == 0 {
if len(data) == 0 || expiry+p.expire <= now {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob(data)

View File

@@ -121,18 +121,20 @@ func (p *PostgresProvider) Init(maxlifetime int64, connStr string) (err error) {
// Read returns raw session store by session ID.
func (p *PostgresProvider) Read(sid string) (session.RawStore, error) {
now := time.Now().Unix()
var data []byte
err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data)
expiry := now
err := p.c.QueryRow("SELECT data, expiry FROM session WHERE key=$1", sid).Scan(&data, &expiry)
if err == sql.ErrNoRows {
_, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)",
sid, "", time.Now().Unix())
sid, "", now)
}
if err != nil {
return nil, err
}
var kv map[interface{}]interface{}
if len(data) == 0 {
if len(data) == 0 || expiry+p.maxlifetime <= now {
kv = make(map[interface{}]interface{})
} else {
kv, err = session.DecodeGob(data)

View File

@@ -1,230 +0,0 @@
// Copyright 2016 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"
"time"
)
// AppsService provides access to the installation related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/
type AppsService service
// App represents a GitHub App.
type App struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
// InstallationToken represents an installation token.
type InstallationToken struct {
Token *string `json:"token,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
}
// InstallationPermissions lists the permissions for metadata, contents, issues and single file for an installation.
type InstallationPermissions struct {
Metadata *string `json:"metadata,omitempty"`
Contents *string `json:"contents,omitempty"`
Issues *string `json:"issues,omitempty"`
SingleFile *string `json:"single_file,omitempty"`
}
// Installation represents a GitHub Apps installation.
type Installation struct {
ID *int64 `json:"id,omitempty"`
AppID *int64 `json:"app_id,omitempty"`
TargetID *int64 `json:"target_id,omitempty"`
Account *User `json:"account,omitempty"`
AccessTokensURL *string `json:"access_tokens_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
TargetType *string `json:"target_type,omitempty"`
SingleFileName *string `json:"single_file_name,omitempty"`
RepositorySelection *string `json:"repository_selection,omitempty"`
Events []string `json:"events,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
func (i Installation) String() string {
return Stringify(i)
}
// Get a single GitHub App. Passing the empty string will get
// the authenticated GitHub App.
//
// Note: appSlug is just the URL-friendly name of your GitHub App.
// You can find this on the settings page for your GitHub App
// (e.g., https://github.com/settings/apps/:app_slug).
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
var u string
if appSlug != "" {
u = fmt.Sprintf("apps/%v", appSlug)
} else {
u = "app"
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
app := new(App)
resp, err := s.client.Do(ctx, req, app)
if err != nil {
return nil, resp, err
}
return app, resp, nil
}
// ListInstallations lists the installations that the current GitHub App has.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-installations
func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("app/installations", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var i []*Installation
resp, err := s.client.Do(ctx, req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}
// GetInstallation returns the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
}
// ListUserInstallations lists installations that are accessible to the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user
func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("user/installations", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var i struct {
Installations []*Installation `json:"installations"`
}
resp, err := s.client.Do(ctx, req, &i)
if err != nil {
return nil, resp, err
}
return i.Installations, resp, nil
}
// CreateInstallationToken creates a new installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) {
u := fmt.Sprintf("app/installations/%v/access_tokens", id)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
t := new(InstallationToken)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// FindOrganizationInstallation finds the organization's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
}
// FindRepositoryInstallation finds the repository's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
}
// FindRepositoryInstallationByID finds the repository's installation information.
//
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation.
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id))
}
// FindUserInstallation finds the user's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
}
func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
i := new(Installation)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}

View File

@@ -1,457 +0,0 @@
// Copyright 2018 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"
"strings"
"time"
)
// TeamsService provides access to the team-related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/teams/
type TeamsService service
// Team represents a team within a GitHub organization. Teams are used to
// manage access to an organization's repositories.
type Team struct {
ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
URL *string `json:"url,omitempty"`
Slug *string `json:"slug,omitempty"`
// Permission specifies the default permission for repositories owned by the team.
Permission *string `json:"permission,omitempty"`
// Privacy identifies the level of privacy this team should have.
// Possible values are:
// secret - only visible to organization owners and members of this team
// closed - visible to all members of this organization
// Default is "secret".
Privacy *string `json:"privacy,omitempty"`
MembersCount *int `json:"members_count,omitempty"`
ReposCount *int `json:"repos_count,omitempty"`
Organization *Organization `json:"organization,omitempty"`
MembersURL *string `json:"members_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
Parent *Team `json:"parent,omitempty"`
// LDAPDN is only available in GitHub Enterprise and when the team
// membership is synchronized with LDAP.
LDAPDN *string `json:"ldap_dn,omitempty"`
}
func (t Team) String() string {
return Stringify(t)
}
// Invitation represents a team member's invitation status.
type Invitation struct {
ID *int64 `json:"id,omitempty"`
Login *string `json:"login,omitempty"`
Email *string `json:"email,omitempty"`
// Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'.
Role *string `json:"role,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
Inviter *User `json:"inviter,omitempty"`
TeamCount *int `json:"team_count,omitempty"`
InvitationTeamURL *string `json:"invitation_team_url,omitempty"`
}
func (i Invitation) String() string {
return Stringify(i)
}
// ListTeams lists all of the teams for an organization.
//
// GitHub API docs: https://developer.github.com/v3/teams/#list-teams
func (s *TeamsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/teams", org)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// GetTeam fetches a team by ID.
//
// GitHub API docs: https://developer.github.com/v3/teams/#get-team
func (s *TeamsService) GetTeam(ctx context.Context, team int64) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// NewTeam represents a team to be created or modified.
type NewTeam struct {
Name string `json:"name"` // Name of the team. (Required.)
Description *string `json:"description,omitempty"`
Maintainers []string `json:"maintainers,omitempty"`
RepoNames []string `json:"repo_names,omitempty"`
ParentTeamID *int64 `json:"parent_team_id,omitempty"`
// Deprecated: Permission is deprecated when creating or editing a team in an org
// using the new GitHub permission model. It no longer identifies the
// permission a team has on its repos, but only specifies the default
// permission a repo is initially added with. Avoid confusion by
// specifying a permission value when calling AddTeamRepo.
Permission *string `json:"permission,omitempty"`
// Privacy identifies the level of privacy this team should have.
// Possible values are:
// secret - only visible to organization owners and members of this team
// closed - visible to all members of this organization
// Default is "secret".
Privacy *string `json:"privacy,omitempty"`
// LDAPDN may be used in GitHub Enterprise when the team membership
// is synchronized with LDAP.
LDAPDN *string `json:"ldap_dn,omitempty"`
}
func (s NewTeam) String() string {
return Stringify(s)
}
// CreateTeam creates a new team within an organization.
//
// GitHub API docs: https://developer.github.com/v3/teams/#create-team
func (s *TeamsService) CreateTeam(ctx context.Context, org string, team NewTeam) (*Team, *Response, error) {
u := fmt.Sprintf("orgs/%v/teams", org)
req, err := s.client.NewRequest("POST", u, team)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// EditTeam edits a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/#edit-team
func (s *TeamsService) EditTeam(ctx context.Context, id int64, team NewTeam) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", id)
req, err := s.client.NewRequest("PATCH", u, team)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// DeleteTeam deletes a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/#delete-team
func (s *TeamsService) DeleteTeam(ctx context.Context, team int64) (*Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
return s.client.Do(ctx, req, nil)
}
// ListChildTeams lists child teams for a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/#list-child-teams
func (s *TeamsService) ListChildTeams(ctx context.Context, teamID int64, opt *ListOptions) ([]*Team, *Response, error) {
u := fmt.Sprintf("teams/%v/teams", teamID)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// ListTeamRepos lists the repositories that the specified team has access to.
//
// GitHub API docs: https://developer.github.com/v3/teams/#list-team-repos
func (s *TeamsService) ListTeamRepos(ctx context.Context, team int64, opt *ListOptions) ([]*Repository, *Response, error) {
u := fmt.Sprintf("teams/%v/repos", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when topics API fully launches.
headers := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview}
req.Header.Set("Accept", strings.Join(headers, ", "))
var repos []*Repository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// IsTeamRepo checks if a team manages the specified repository. If the
// repository is managed by team, a Repository is returned which includes the
// permissions team has for that repo.
//
// GitHub API docs: https://developer.github.com/v3/teams/#check-if-a-team-manages-a-repository
func (s *TeamsService) IsTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Repository, *Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
headers := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview}
req.Header.Set("Accept", strings.Join(headers, ", "))
repository := new(Repository)
resp, err := s.client.Do(ctx, req, repository)
if err != nil {
return nil, resp, err
}
return repository, resp, nil
}
// TeamAddTeamRepoOptions specifies the optional parameters to the
// TeamsService.AddTeamRepo method.
type TeamAddTeamRepoOptions struct {
// Permission specifies the permission to grant the team on this repository.
// Possible values are:
// pull - team members can pull, but not push to or administer this repository
// push - team members can pull and push, but not administer this repository
// admin - team members can pull, push and administer this repository
//
// If not specified, the team's permission attribute will be used.
Permission string `json:"permission,omitempty"`
}
// AddTeamRepo adds a repository to be managed by the specified team. The
// specified repository must be owned by the organization to which the team
// belongs, or a direct fork of a repository owned by the organization.
//
// GitHub API docs: https://developer.github.com/v3/teams/#add-team-repo
func (s *TeamsService) AddTeamRepo(ctx context.Context, team int64, owner string, repo string, opt *TeamAddTeamRepoOptions) (*Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveTeamRepo removes a repository from being managed by the specified
// team. Note that this does not delete the repository, it just removes it
// from the team.
//
// GitHub API docs: https://developer.github.com/v3/teams/#remove-team-repo
func (s *TeamsService) RemoveTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Response, error) {
u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListUserTeams lists a user's teams
// GitHub API docs: https://developer.github.com/v3/teams/#list-user-teams
func (s *TeamsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) {
u := "user/teams"
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// ListTeamProjects lists the organization projects for a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/#list-team-projects
func (s *TeamsService) ListTeamProjects(ctx context.Context, teamID int64) ([]*Project, *Response, error) {
u := fmt.Sprintf("teams/%v/projects", teamID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var projects []*Project
resp, err := s.client.Do(ctx, req, &projects)
if err != nil {
return nil, resp, err
}
return projects, resp, nil
}
// ReviewTeamProjects checks whether a team has read, write, or admin
// permissions for an organization project.
//
// GitHub API docs: https://developer.github.com/v3/teams/#review-a-team-project
func (s *TeamsService) ReviewTeamProjects(ctx context.Context, teamID, projectID int64) (*Project, *Response, error) {
u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
projects := &Project{}
resp, err := s.client.Do(ctx, req, &projects)
if err != nil {
return nil, resp, err
}
return projects, resp, nil
}
// TeamProjectOptions specifies the optional parameters to the
// TeamsService.AddTeamProject method.
type TeamProjectOptions struct {
// Permission specifies the permission to grant to the team for this project.
// Possible values are:
// "read" - team members can read, but not write to or administer this project.
// "write" - team members can read and write, but not administer this project.
// "admin" - team members can read, write and administer this project.
//
Permission *string `json:"permission,omitempty"`
}
// AddTeamProject adds an organization project to a team. To add a project to a team or
// update the team's permission on a project, the authenticated user must have admin
// permissions for the project.
//
// GitHub API docs: https://developer.github.com/v3/teams/#add-or-update-team-project
func (s *TeamsService) AddTeamProject(ctx context.Context, teamID, projectID int64, opt *TeamProjectOptions) (*Response, error) {
u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
return s.client.Do(ctx, req, nil)
}
// RemoveTeamProject removes an organization project from a team. An organization owner or
// a team maintainer can remove any project from the team. To remove a project from a team
// as an organization member, the authenticated user must have "read" access to both the team
// and project, or "admin" access to the team or project.
// Note: This endpoint removes the project from the team, but does not delete it.
//
// GitHub API docs: https://developer.github.com/v3/teams/#remove-team-project
func (s *TeamsService) RemoveTeamProject(ctx context.Context, teamID int64, projectID int64) (*Response, error) {
u := fmt.Sprintf("teams/%v/projects/%v", teamID, projectID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
acceptHeaders := []string{mediaTypeNestedTeamsPreview, mediaTypeProjectsPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,155 +0,0 @@
// Copyright 2018 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"
)
// DiscussionComment represents a GitHub dicussion in a team.
type DiscussionComment struct {
Author *User `json:"author,omitempty"`
Body *string `json:"body,omitempty"`
BodyHTML *string `json:"body_html,omitempty"`
BodyVersion *string `json:"body_version,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
LastEditedAt *Timestamp `json:"last_edited_at,omitempty"`
DiscussionURL *string `json:"discussion_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Number *int `json:"number,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
}
func (c DiscussionComment) String() string {
return Stringify(c)
}
// DiscussionCommentListOptions specifies optional parameters to the
// TeamServices.ListComments method.
type DiscussionCommentListOptions struct {
// Sorts the discussion comments by the date they were created.
// Accepted values are asc and desc. Default is desc.
Direction string `url:"direction,omitempty"`
}
// ListComments lists all comments on a team discussion.
// Authenticated user must grant read:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#list-comments
func (s *TeamsService) ListComments(ctx context.Context, teamID int64, discussionNumber int, options *DiscussionCommentListOptions) ([]*DiscussionComment, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v/comments", teamID, discussionNumber)
u, err := addOptions(u, options)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
var comments []*DiscussionComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// GetComment gets a specific comment on a team discussion.
// Authenticated user must grant read:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#get-a-single-comment
func (s *TeamsService) GetComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int) (*DiscussionComment, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
discussionComment := &DiscussionComment{}
resp, err := s.client.Do(ctx, req, discussionComment)
if err != nil {
return nil, resp, err
}
return discussionComment, resp, nil
}
// CreateComment creates a new discussion post on a team discussion.
// Authenticated user must grant write:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#create-a-comment
func (s *TeamsService) CreateComment(ctx context.Context, teamID int64, discsusionNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v/comments", teamID, discsusionNumber)
req, err := s.client.NewRequest("POST", u, comment)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
discussionComment := &DiscussionComment{}
resp, err := s.client.Do(ctx, req, discussionComment)
if err != nil {
return nil, resp, err
}
return discussionComment, resp, nil
}
// EditComment edits the body text of a discussion comment.
// Authenticated user must grant write:discussion scope.
// User is allowed to edit body of a comment only.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#edit-a-comment
func (s *TeamsService) EditComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
discussionComment := &DiscussionComment{}
resp, err := s.client.Do(ctx, req, discussionComment)
if err != nil {
return nil, resp, err
}
return discussionComment, resp, nil
}
// DeleteComment deletes a comment on a team discussion.
// Authenticated user must grant write:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussion_comments/#delete-a-comment
func (s *TeamsService) DeleteComment(ctx context.Context, teamID int64, discussionNumber, commentNumber int) (*Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v", teamID, discussionNumber, commentNumber)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,160 +0,0 @@
// Copyright 2018 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"
)
// TeamDiscussion represents a GitHub dicussion in a team.
type TeamDiscussion struct {
Author *User `json:"author,omitempty"`
Body *string `json:"body,omitempty"`
BodyHTML *string `json:"body_html,omitempty"`
BodyVersion *string `json:"body_version,omitempty"`
CommentsCount *int `json:"comments_count,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
LastEditedAt *Timestamp `json:"last_edited_at,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Number *int `json:"number,omitempty"`
Pinned *bool `json:"pinned,omitempty"`
Private *bool `json:"private,omitempty"`
TeamURL *string `json:"team_url,omitempty"`
Title *string `json:"title,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
}
func (d TeamDiscussion) String() string {
return Stringify(d)
}
// DiscussionListOptions specifies optional parameters to the
// TeamServices.ListDiscussions method.
type DiscussionListOptions struct {
// Sorts the discussion by the date they were created.
// Accepted values are asc and desc. Default is desc.
Direction string `url:"direction,omitempty"`
}
// ListDiscussions lists all discussions on team's page.
// Authenticated user must grant read:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussions/#list-discussions
func (s *TeamsService) ListDiscussions(ctx context.Context, teamID int64, options *DiscussionListOptions) ([]*TeamDiscussion, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions", teamID)
u, err := addOptions(u, options)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
var teamDiscussions []*TeamDiscussion
resp, err := s.client.Do(ctx, req, &teamDiscussions)
if err != nil {
return nil, resp, err
}
return teamDiscussions, resp, nil
}
// GetDiscussion gets a specific discussion on a team's page.
// Authenticated user must grant read:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussions/#get-a-single-discussion
func (s *TeamsService) GetDiscussion(ctx context.Context, teamID int64, discussionNumber int) (*TeamDiscussion, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
teamDiscussion := &TeamDiscussion{}
resp, err := s.client.Do(ctx, req, teamDiscussion)
if err != nil {
return nil, resp, err
}
return teamDiscussion, resp, nil
}
// CreateDiscussion creates a new discussion post on a team's page.
// Authenticated user must grant write:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussions/#create-a-discussion
func (s *TeamsService) CreateDiscussion(ctx context.Context, teamID int64, discussion TeamDiscussion) (*TeamDiscussion, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions", teamID)
req, err := s.client.NewRequest("POST", u, discussion)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
teamDiscussion := &TeamDiscussion{}
resp, err := s.client.Do(ctx, req, teamDiscussion)
if err != nil {
return nil, resp, err
}
return teamDiscussion, resp, nil
}
// EditDiscussion edits the title and body text of a discussion post.
// Authenticated user must grant write:discussion scope.
// User is allowed to change Title and Body of a discussion only.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussions/#edit-a-discussion
func (s *TeamsService) EditDiscussion(ctx context.Context, teamID int64, discussionNumber int, discussion TeamDiscussion) (*TeamDiscussion, *Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber)
req, err := s.client.NewRequest("PATCH", u, discussion)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
teamDiscussion := &TeamDiscussion{}
resp, err := s.client.Do(ctx, req, teamDiscussion)
if err != nil {
return nil, resp, err
}
return teamDiscussion, resp, nil
}
// DeleteDiscussion deletes a discussion from team's page.
// Authenticated user must grant write:discussion scope.
//
// GitHub API docs: https://developer.github.com/v3/teams/discussions/#delete-a-discussion
func (s *TeamsService) DeleteDiscussion(ctx context.Context, teamID int64, discussionNumber int) (*Response, error) {
u := fmt.Sprintf("teams/%v/discussions/%v", teamID, discussionNumber)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeTeamDiscussionsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,174 +0,0 @@
// Copyright 2018 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"
)
// TeamListTeamMembersOptions specifies the optional parameters to the
// TeamsService.ListTeamMembers method.
type TeamListTeamMembersOptions struct {
// Role filters members returned by their role in the team. Possible
// values are "all", "member", "maintainer". Default is "all".
Role string `url:"role,omitempty"`
ListOptions
}
// ListTeamMembers lists all of the users who are members of the specified
// team.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#list-team-members
func (s *TeamsService) ListTeamMembers(ctx context.Context, team int64, opt *TeamListTeamMembersOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("teams/%v/members", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
var members []*User
resp, err := s.client.Do(ctx, req, &members)
if err != nil {
return nil, resp, err
}
return members, resp, nil
}
// IsTeamMember checks if a user is a member of the specified team.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#get-team-member
//
// Deprecated: This API has been marked as deprecated in the Github API docs,
// TeamsService.GetTeamMembership method should be used instead.
func (s *TeamsService) IsTeamMember(ctx context.Context, team int64, user string) (bool, *Response, error) {
u := fmt.Sprintf("teams/%v/members/%v", team, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
member, err := parseBoolResponse(err)
return member, resp, err
}
// GetTeamMembership returns the membership status for a user in a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#get-team-membership
func (s *TeamsService) GetTeamMembership(ctx context.Context, team int64, user string) (*Membership, *Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeNestedTeamsPreview)
t := new(Membership)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// TeamAddTeamMembershipOptions specifies the optional
// parameters to the TeamsService.AddTeamMembership method.
type TeamAddTeamMembershipOptions struct {
// Role specifies the role the user should have in the team. Possible
// values are:
// member - a normal member of the team
// maintainer - a team maintainer. Able to add/remove other team
// members, promote other team members to team
// maintainer, and edit the teams name and description
//
// Default value is "member".
Role string `json:"role,omitempty"`
}
// AddTeamMembership adds or invites a user to a team.
//
// In order to add a membership between a user and a team, the authenticated
// user must have 'admin' permissions to the team or be an owner of the
// organization that the team is associated with.
//
// If the user is already a part of the team's organization (meaning they're on
// at least one other team in the organization), this endpoint will add the
// user to the team.
//
// If the user is completely unaffiliated with the team's organization (meaning
// they're on none of the organization's teams), this endpoint will send an
// invitation to the user via email. This newly-created membership will be in
// the "pending" state until the user accepts the invitation, at which point
// the membership will transition to the "active" state and the user will be
// added as a member of the team.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#add-or-update-team-membership
func (s *TeamsService) AddTeamMembership(ctx context.Context, team int64, user string, opt *TeamAddTeamMembershipOptions) (*Membership, *Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, nil, err
}
t := new(Membership)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// RemoveTeamMembership removes a user from a team.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#remove-team-membership
func (s *TeamsService) RemoveTeamMembership(ctx context.Context, team int64, user string) (*Response, error) {
u := fmt.Sprintf("teams/%v/memberships/%v", team, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListPendingTeamInvitations get pending invitaion list in team.
// Warning: The API may change without advance notice during the preview period.
// Preview features are not supported for production use.
//
// GitHub API docs: https://developer.github.com/v3/teams/members/#list-pending-team-invitations
func (s *TeamsService) ListPendingTeamInvitations(ctx context.Context, team int64, opt *ListOptions) ([]*Invitation, *Response, error) {
u := fmt.Sprintf("teams/%v/invitations", team)
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var pendingInvitations []*Invitation
resp, err := s.client.Do(ctx, req, &pendingInvitations)
if err != nil {
return nil, resp, err
}
return pendingInvitations, resp, nil
}

View File

@@ -9,22 +9,31 @@
# their employer, as appropriate).
178inaba <masahiro.furudate@gmail.com>
2BFL <imqksl@gmail.com>
413x <dedifferentiator@gmail.com>
Abhinav Gupta <mail@abhinavg.net>
adrienzieba <adrien.zieba@appdirect.com>
Ahmed Hagy <a.akram93@gmail.com>
Aidan Steele <aidan.steele@glassechidna.com.au>
Ainsley Chong <ainsley.chong@gmail.com>
ajz01 <azdenek@yahoo.com>
Akeda Bagus <akeda@x-team.com>
Akhil Mohan <akhilerm@gmail.com>
Alec Thomas <alec@swapoff.org>
Aleks Clark <aleks.clark@gmail.com>
Alex Bramley <a.bramley@gmail.com>
Alex Orr <Alexorr.CSE@gmail.com>
Alexander Harkness <me@bearbin.net>
Allen Sun <shlallen1990@gmail.com>
Amey Sakhadeo <me@ameyms.com>
Anders Janmyr <anders@janmyr.com>
Andreas Garnæs <https://github.com/andreas>
Andrew Ryabchun <aryabchun@mail.ua>
Andy Grunwald <andygrunwald@gmail.com>
Andy Hume <andyhume@gmail.com>
Andy Lindeman <andy@lindeman.io>
angie pinilla <angelinepinilla@gmail.com>
anjanashenoy <anjanashenoy1@gmail.com>
Anshuman Bhartiya <anshuman.bhartiya@gmail.com>
Antoine <antoine.tu@mail.mcgill.ca>
Antoine Pelisse <apelisse@gmail.com>
@@ -33,6 +42,7 @@ appilon <apilon@hashicorp.com>
Aravind <aravindkp@outlook.in>
Arda Kuyumcu <kuyumcuarda@gmail.com>
Arıl Bozoluk <arilbozoluk@hotmail.com>
Austin Burdine <acburdine@gmail.com>
Austin Dizzy <dizzy@wow.com>
Ben Batha <bhbatha@gmail.com>
Benjamen Keroack <benjamen@dollarshaveclub.com>
@@ -47,13 +57,18 @@ Brandon Cook <phylake@gmail.com>
Brian Egizi <brian@mojotech.com>
Bryan Boreham <bryan@weave.works>
Cami Diez <diezcami@gmail.com>
Carl Johnson <me@carlmjohnson.net>
Carlos Alexandro Becker <caarlos0@gmail.com>
Carlos Tadeu Panato Junior <ctadeu@gmail.com>
chandresh-pancholi <chandreshpancholi007@gmail.com>
Charles Fenwick Elliott <Charles@FenwickElliott.io>
Charlie Yan <charlieyan08@gmail.com>
Chris King <chriskingnet@gmail.com>
Chris Raborg <craborg57@gmail.com>
Chris Roche <chris@vsco.co>
Chris Schaefer <chris@dtzq.com>
chrisforrette <chris@chrisforrette.com>
Christian Muehlhaeuser <muesli@gmail.com>
Christoph Sassenberg <defsprite@gmail.com>
Colin Misare <github.com/cmisare>
Craig Peterson <cpeterson@stackoverflow.com>
@@ -61,10 +76,14 @@ Cristian Maglie <c.maglie@bug.st>
Daehyeok Mun <daehyeok@gmail.com>
Daniel Leavitt <daniel.leavitt@gmail.com>
Daniel Nilsson <daniel.nilsson1989@gmail.com>
Daoq <masseto2002@gmail.com>
Dave Du Cros <davidducros@gmail.com>
Dave Henderson <dhenderson@gmail.com>
Dave Protasowski <dprotaso@gmail.com>
David Deng <daviddengcn@gmail.com>
David Jannotta <djannotta@gmail.com>
David Ji <github.com/davidji99>
David Lopez Reyes <davidlopezre@gmail.com>
Davide Zipeto <dawez1@gmail.com>
Dennis Webb <dennis@bluesentryit.com>
Dhi Aurrahman <diorahman@rockybars.com>
@@ -74,22 +93,26 @@ dmnlk <seikima2demon@gmail.com>
Don Petersen <don@donpetersen.net>
Doug Turner <doug.turner@gmail.com>
Drew Fradette <drew.fradette@gmail.com>
Eivind <eivindkn@gmail.com>
Eli Uriegas <seemethere101@gmail.com>
Elliott Beach <elliott2.71828@gmail.com>
Emerson Wood <emersonwood94@gmail.com>
eperm <staffordworrell@gmail.com>
Erick Fejta <erick@fejta.com>
erwinvaneyk <erwinvaneyk@gmail.com>
Evan Elias <evanjelias@gmail.com>
Fabrice <fabrice.vaillant@student.ecp.fr>
Felix Geisendörfer <felix@debuggable.com>
Filippo Valsorda <hi@filippo.io>
Florian Forster <ff@octo.it>
Francesc Gil <xescugil@gmail.com>
Francis <hello@francismakes.com>
Francisco Guimarães <francisco.cpg@gmail.com>
Fredrik Jönsson <fredrik.jonsson@izettle.com>
Garrett Squire <garrettsquire@gmail.com>
George Kontridze <george.kontridze@gmail.com>
Georgy Buranov <gburanov@gmail.com>
Glen Mailer <glenjamin@gmail.com>
Gnahz <p@oath.pl>
Google Inc.
Grachev Mikhail <work@mgrachev.com>
@@ -99,13 +122,16 @@ Guz Alexander <kalimatas@gmail.com>
Guðmundur Bjarni Ólafsson <gudmundur@github.com>
Hanno Hecker <hanno.hecker@zalando.de>
Hari haran <hariharan.uno@gmail.com>
haya14busa <haya14busa@gmail.com>
haya14busa <hayabusa1419@gmail.com>
Huy Tr <kingbazoka@gmail.com>
huydx <doxuanhuy@gmail.com>
i2bskn <i2bskn@gmail.com>
Ioannis Georgoulas <igeorgoulas21@gmail.com>
Isao Jonas <isao.jonas@gmail.com>
isqua <isqua@isqua.ru>
Jameel Haffejee <RC1140@republiccommandos.co.za>
James Cockbain <james.cockbain@ibm.com>
Jan Kosecki <jan.kosecki91@gmail.com>
Javier Campanini <jcampanini@palantir.com>
Jens Rantil <jens.rantil@gmail.com>
@@ -117,6 +143,7 @@ Joan Saum <joan.saum@epitech.eu>
Joe Tsai <joetsai@digital-static.net>
John Barton <jrbarton@gmail.com>
John Engelman <john.r.engelman@gmail.com>
Jordan Brockopp <jdbro94@gmail.com>
Jordan Sussman <jordansail22@gmail.com>
Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com>
JP Phillips <jonphill9@gmail.com>
@@ -124,11 +151,12 @@ jpbelanger-mtl <jp.belanger@gmail.com>
Juan Basso <jrbasso@gmail.com>
Julien Garcia Gonzalez <garciagonzalez.julien@gmail.com>
Julien Rostand <jrostand@users.noreply.github.com>
Junya Kono <junya03dance@gmail.com>
Justin Abrahms <justin@abrah.ms>
Jusung Lee <e.jusunglee@gmail.com>
jzhoucliqr <jzhou@cliqr.com>
kadern0 <kaderno@gmail.com>
Katrina Owen <kytrinyx@github.com>
Kautilya Tripathi < tripathi.kautilya@gmail.com>
Kautilya Tripathi <tripathi.kautilya@gmail.com>
Keita Urashima <ursm@ursm.jp>
Kevin Burke <kev@inburke.com>
@@ -137,15 +165,21 @@ Kookheon Kwon <kucuny@gmail.com>
Krzysztof Kowalczyk <kkowalczyk@gmail.com>
Kshitij Saraogi <KshitijSaraogi@gmail.com>
kyokomi <kyoko1220adword@gmail.com>
Laurent Verdoïa <verdoialaurent@gmail.com>
Liam Galvin <liam@liam-galvin.co.uk>
Lovro Mažgon <lovro.mazgon@gmail.com>
Lucas Alcantara <lucasalcantaraf@gmail.com>
Luke Evers <me@lukevers.com>
Luke Kysow <lkysow@gmail.com>
Luke Roberts <email@luke-roberts.co.uk>
Luke Young <luke@hydrantlabs.org>
lynn [they] <lynncyrin@gmail.com>
Maksim Zhylinski <uzzable@gmail.com>
Mark Tareshawty <tarebyte@github.com>
Martin-Louis Bright <mlbright@gmail.com>
Martins Sipenko <martins.sipenko@gmail.com>
Marwan Sulaiman <marwan.sameer@gmail.com>
Masayuki Izumi <m@izum.in>
Mat Geist <matgeist@gmail.com>
Matt <alpmatthew@gmail.com>
Matt Brender <mjbrender@gmail.com>
@@ -155,6 +189,7 @@ Maxime Bury <maxime.bury@gmail.com>
Michael Spiegel <michael.m.spiegel@gmail.com>
Michael Tiller <michael.tiller@gmail.com>
Michał Glapa <michal.glapa@gmail.com>
Nadav Kaner <nadavkaner1@gmail.com>
Nathan VanBenschoten <nvanbenschoten@gmail.com>
Navaneeth Suresh <navaneeths1998@gmail.com>
Neil O'Toole <neilotoole@apache.org>
@@ -163,6 +198,7 @@ Nick Spragg <nick.spragg@bbc.co.uk>
Nikhita Raghunath <nikitaraghunath@gmail.com>
Noah Zoschke <noah+sso2@convox.com>
ns-cweber <cweber@narrativescience.com>
Ole Orhagen <ole.orhagen@northern.tech>
Oleg Kovalov <iamolegkovalov@gmail.com>
Ondřej Kupka <ondra.cap@gmail.com>
Palash Nigam <npalash25@gmail.com>
@@ -170,29 +206,43 @@ Panagiotis Moustafellos <pmoust@gmail.com>
Parham Alvani <parham.alvani@gmail.com>
Parker Moore <parkrmoore@gmail.com>
parkhyukjun89 <park.hyukjun89@gmail.com>
Patrick DeVivo <patrick.devivo@gmail.com>
Patrick Marabeas <patrick@marabeas.io>
Pavel Shtanko <pavel.shtanko@gmail.com>
Pete Wagner <thepwagner@github.com>
Petr Shevtsov <petr.shevtsov@gmail.com>
Pierre Carrier <pierre@meteor.com>
Piotr Zurek <p.zurek@gmail.com>
Pratik Mallya <pratik.mallya@gmail.com>
Qais Patankar <qaisjp@gmail.com>
Quang Le Hong <iamquang95@gmail.com>
Quentin Leffray <fiahil@gmail.com>
Quinn Slack <qslack@qslack.com>
Rackspace US, Inc.
Radek Simko <radek.simko@gmail.com>
Radliński Ignacy <radlinsk@student.agh.edu.pl>
Rajat Jindal <rajatjindal83@gmail.com>
Rajendra arora <rajendraarora16@yahoo.com>
Ranbir Singh <binkkatal.r@gmail.com>
Ravi Shekhar Jethani <rsjethani@gmail.com>
RaviTeja Pothana <ravi-teja@live.com>
rc1140 <jameel@republiccommandos.co.za>
Red Hat, Inc.
Reinier Timmer <reinier.timmer@ah.nl>
Ricco Førgaard <ricco@fiskeben.dk>
Rob Figueiredo <robfig@yext.com>
Rohit Upadhyay <urohit011@gmail.com>
Ronak Jain <ronakjain@outlook.in>
Ruben Vereecken <rubenvereecken@gmail.com>
Ryan Leung <rleungx@gmail.com>
Ryan Lower <rpjlower@gmail.com>
Ryo Nakao <nakabonne@gmail.com>
Safwan Olaimat <safwan.olaimat@gmail.com>
Sahil Dua <sahildua2305@gmail.com>
saisi <saisi@users.noreply.github.com>
Sam Minnée <sam@silverstripe.com>
Sandeep Sukhani <sandeep.d.sukhani@gmail.com>
Sander Knape <s.knape88@gmail.com>
Sander van Harmelen <svanharmelen@schubergphilis.com>
Sanket Payghan <sanket.payghan8@gmail.com>
Sarasa Kisaragi <lingsamuelgrace@gmail.com>
@@ -200,30 +250,46 @@ Sean Wang <sean@decrypted.org>
Sebastian Mandrean <sebastian.mandrean@gmail.com>
Sebastian Mæland Pedersen <sem.pedersen@stud.uis.no>
Sergey Romanov <xxsmotur@gmail.com>
Sergio Garcia <sergio.garcia@gmail.com>
Sevki <s@sevki.org>
Shagun Khemka <shagun.khemka60@gmail.com>
shakeelrao <shakeelrao79@gmail.com>
Shawn Catanzarite <me@shawncatz.com>
Shawn Smith <shawnpsmith@gmail.com>
Shibasis Patel <patelshibasis@gmail.com>
Shrikrishna Singh <krishnasingh.ss30@gmail.com>
sona-tar <sona.zip@gmail.com>
SoundCloud, Ltd.
Sridhar Mocherla <srmocher@microsoft.com>
SriVignessh Pss <sriknowledge@gmail.com>
Stefan Sedich <stefan.sedich@gmail.com>
Stian Eikeland <stian@eikeland.se>
Suhaib Mujahid <suhaibmujahid@gmail.com>
Szymon Kodrebski <simonkey007@gmail.com>
Takayuki Watanabe <takanabe.w@gmail.com>
Taketoshi Fujiwara <taketoshi.fujiwara@gmail.com>
Tasya Aditya Rukmana <tadityar@gmail.com>
Thomas Bruyelle <thomas.bruyelle@gmail.com>
Timothée Peignier <timothee.peignier@tryphon.org>
tkhandel <tarunkhandelwal.iitr@gmail.com>
Trey Tacon <ttacon@gmail.com>
ttacon <ttacon@gmail.com>
Vaibhav Singh <vaibhav.singh.14cse@bml.edu.in>
Varadarajan Aravamudhan <varadaraajan@gmail.com>
Victor Castell <victor@victorcastell.com>
Victor Vrantchan <vrancean+github@gmail.com>
vikkyomkar <vikky.omkar@samsung.com>
Vlad Ungureanu <vladu@palantir.com>
Wasim Thabraze <wasim@thabraze.me>
Weslei Juan Moser Pereira <wesleimsr@gmail.com>
Will Maier <wcmaier@gmail.com>
Willem D'Haeseleer <dhwillem@gmail.com>
William Bailey <mail@williambailey.org.uk>
William Cooke <pipeston@gmail.com>
xibz <impactbchang@gmail.com>
Yann Malet <yann.malet@gmail.com>
Yannick Utard <yannickutard@gmail.com>
Yicheng Qin <qycqycqycqycqyc@gmail.com>
Yosuke Akatsuka <yosuke.akatsuka@access-company.com>
Yumikiyo Osanai <yumios.art@gmail.com>
Zach Latta <zach@zachlatta.com>

View File

@@ -0,0 +1,12 @@
// Copyright 2020 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
// ActionsService handles communication with the actions related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/actions/
type ActionsService service

View File

@@ -0,0 +1,164 @@
// Copyright 2020 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"
"net/http"
"net/url"
)
// Artifact reprents a GitHub artifact. Artifacts allow sharing
// data between jobs in a workflow and provide storage for data
// once a workflow is complete.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/
type Artifact struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Name *string `json:"name,omitempty"`
SizeInBytes *int64 `json:"size_in_bytes,omitempty"`
ArchiveDownloadURL *string `json:"archive_download_url,omitempty"`
Expired *bool `json:"expired,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
ExpiresAt *Timestamp `json:"expires_at,omitempty"`
}
// ArtifactList represents a list of GitHub artifacts.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/
type ArtifactList struct {
TotalCount *int64 `json:"total_count,omitempty"`
Artifacts []*Artifact `json:"artifacts,omitempty"`
}
// ListArtifacts lists all artifacts that belong to a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/#list-artifacts-for-a-repository
func (s *ActionsService) ListArtifacts(ctx context.Context, owner, repo string, opts *ListOptions) (*ArtifactList, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/artifacts", 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
}
artifactList := new(ArtifactList)
resp, err := s.client.Do(ctx, req, artifactList)
if err != nil {
return nil, resp, err
}
return artifactList, resp, nil
}
// ListWorkflowRunArtifacts lists all artifacts that belong to a workflow run.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/#list-workflow-run-artifacts
func (s *ActionsService) ListWorkflowRunArtifacts(ctx context.Context, owner, repo string, runID int64, opts *ListOptions) (*ArtifactList, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/artifacts", owner, repo, runID)
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
}
artifactList := new(ArtifactList)
resp, err := s.client.Do(ctx, req, artifactList)
if err != nil {
return nil, resp, err
}
return artifactList, resp, nil
}
// GetArtifact gets a specific artifact for a workflow run.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/#get-an-artifact
func (s *ActionsService) GetArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Artifact, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
artifact := new(Artifact)
resp, err := s.client.Do(ctx, req, artifact)
if err != nil {
return nil, resp, err
}
return artifact, resp, nil
}
// DownloadArtifact gets a redirect URL to download an archive for a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/#download-an-artifact
func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo string, artifactID int64, followRedirects bool) (*url.URL, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v/zip", owner, repo, artifactID)
resp, err := s.getDownloadArtifactFromURL(ctx, u, followRedirects)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusFound {
return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
}
parsedURL, err := url.Parse(resp.Header.Get("Location"))
return parsedURL, newResponse(resp), nil
}
func (s *ActionsService) getDownloadArtifactFromURL(ctx context.Context, u string, followRedirects bool) (*http.Response, error) {
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
var resp *http.Response
// Use http.DefaultTransport if no custom Transport is configured
req = withContext(ctx, req)
if s.client.client.Transport == nil {
resp, err = http.DefaultTransport.RoundTrip(req)
} else {
resp, err = s.client.client.Transport.RoundTrip(req)
}
if err != nil {
return nil, err
}
resp.Body.Close()
// If redirect response is returned, follow it
if followRedirects && resp.StatusCode == http.StatusMovedPermanently {
u = resp.Header.Get("Location")
resp, err = s.getDownloadArtifactFromURL(ctx, u, false)
}
return resp, err
}
// DeleteArtifact deletes a workflow run artifact.
//
// GitHub API docs: https://developer.github.com/v3/actions/artifacts/#delete-an-artifact
func (s *ActionsService) DeleteArtifact(ctx context.Context, owner, repo string, artifactID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v", owner, repo, artifactID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -0,0 +1,277 @@
// Copyright 2020 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"
)
// RunnerApplicationDownload represents a binary for the self-hosted runner application that can be downloaded.
type RunnerApplicationDownload struct {
OS *string `json:"os,omitempty"`
Architecture *string `json:"architecture,omitempty"`
DownloadURL *string `json:"download_url,omitempty"`
Filename *string `json:"filename,omitempty"`
}
// ListRunnerApplicationDownloads lists self-hosted runner application binaries that can be downloaded and run.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#list-runner-applications-for-a-repository
func (s *ActionsService) ListRunnerApplicationDownloads(ctx context.Context, owner, repo string) ([]*RunnerApplicationDownload, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners/downloads", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var rads []*RunnerApplicationDownload
resp, err := s.client.Do(ctx, req, &rads)
if err != nil {
return nil, resp, err
}
return rads, resp, nil
}
// RegistrationToken represents a token that can be used to add a self-hosted runner to a repository.
type RegistrationToken struct {
Token *string `json:"token,omitempty"`
ExpiresAt *Timestamp `json:"expires_at,omitempty"`
}
// CreateRegistrationToken creates a token that can be used to add a self-hosted runner.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#create-a-registration-token-for-a-repository
func (s *ActionsService) CreateRegistrationToken(ctx context.Context, owner, repo string) (*RegistrationToken, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners/registration-token", owner, repo)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
registrationToken := new(RegistrationToken)
resp, err := s.client.Do(ctx, req, registrationToken)
if err != nil {
return nil, resp, err
}
return registrationToken, resp, nil
}
// Runner represents a self-hosted runner registered with a repository.
type Runner struct {
ID *int64 `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
OS *string `json:"os,omitempty"`
Status *string `json:"status,omitempty"`
}
// Runners represents a collection of self-hosted runners for a repository.
type Runners struct {
TotalCount int `json:"total_count"`
Runners []*Runner `json:"runners"`
}
// ListRunners lists all the self-hosted runners for a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#list-self-hosted-runners-for-a-repository
func (s *ActionsService) ListRunners(ctx context.Context, owner, repo string, opts *ListOptions) (*Runners, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners", 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
}
runners := &Runners{}
resp, err := s.client.Do(ctx, req, &runners)
if err != nil {
return nil, resp, err
}
return runners, resp, nil
}
// GetRunner gets a specific self-hosted runner for a repository using its runner ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#get-a-self-hosted-runner-for-a-repository
func (s *ActionsService) GetRunner(ctx context.Context, owner, repo string, runnerID int64) (*Runner, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners/%v", owner, repo, runnerID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
runner := new(Runner)
resp, err := s.client.Do(ctx, req, runner)
if err != nil {
return nil, resp, err
}
return runner, resp, nil
}
// RemoveToken represents a token that can be used to remove a self-hosted runner from a repository.
type RemoveToken struct {
Token *string `json:"token,omitempty"`
ExpiresAt *Timestamp `json:"expires_at,omitempty"`
}
// CreateRemoveToken creates a token that can be used to remove a self-hosted runner from a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#create-a-remove-token-for-a-repository
func (s *ActionsService) CreateRemoveToken(ctx context.Context, owner, repo string) (*RemoveToken, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners/remove-token", owner, repo)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
removeToken := new(RemoveToken)
resp, err := s.client.Do(ctx, req, removeToken)
if err != nil {
return nil, resp, err
}
return removeToken, resp, nil
}
// RemoveRunner forces the removal of a self-hosted runner in a repository using the runner id.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#delete-a-self-hosted-runner-from-a-repository
func (s *ActionsService) RemoveRunner(ctx context.Context, owner, repo string, runnerID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runners/%v", owner, repo, runnerID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListOrganizationRunnerApplicationDownloads lists self-hosted runner application binaries that can be downloaded and run.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#list-runner-applications-for-an-organization
func (s *ActionsService) ListOrganizationRunnerApplicationDownloads(ctx context.Context, owner string) ([]*RunnerApplicationDownload, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners/downloads", owner)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var rads []*RunnerApplicationDownload
resp, err := s.client.Do(ctx, req, &rads)
if err != nil {
return nil, resp, err
}
return rads, resp, nil
}
// CreateOrganizationRegistrationToken creates a token that can be used to add a self-hosted runner to an organization.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#create-a-registration-token-for-an-organization
func (s *ActionsService) CreateOrganizationRegistrationToken(ctx context.Context, owner string) (*RegistrationToken, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners/registration-token", owner)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
registrationToken := new(RegistrationToken)
resp, err := s.client.Do(ctx, req, registrationToken)
if err != nil {
return nil, resp, err
}
return registrationToken, resp, nil
}
// ListOrganizationRunners lists all the self-hosted runners for an organization.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#list-self-hosted-runners-for-an-organization
func (s *ActionsService) ListOrganizationRunners(ctx context.Context, owner string, opts *ListOptions) (*Runners, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners", owner)
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
}
runners := &Runners{}
resp, err := s.client.Do(ctx, req, &runners)
if err != nil {
return nil, resp, err
}
return runners, resp, nil
}
// GetOrganizationRunner gets a specific self-hosted runner for an organization using its runner ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#get-a-self-hosted-runner-for-an-organization
func (s *ActionsService) GetOrganizationRunner(ctx context.Context, owner string, runnerID int64) (*Runner, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners/%v", owner, runnerID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
runner := new(Runner)
resp, err := s.client.Do(ctx, req, runner)
if err != nil {
return nil, resp, err
}
return runner, resp, nil
}
// CreateOrganizationRemoveToken creates a token that can be used to remove a self-hosted runner from an organization.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#create-a-remove-token-for-an-organization
func (s *ActionsService) CreateOrganizationRemoveToken(ctx context.Context, owner string) (*RemoveToken, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners/remove-token", owner)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
removeToken := new(RemoveToken)
resp, err := s.client.Do(ctx, req, removeToken)
if err != nil {
return nil, resp, err
}
return removeToken, resp, nil
}
// RemoveOrganizationRunner forces the removal of a self-hosted runner from an organization using the runner id.
//
// GitHub API docs: https://developer.github.com/v3/actions/self-hosted-runners/#delete-a-self-hosted-runner-from-an-organization
func (s *ActionsService) RemoveOrganizationRunner(ctx context.Context, owner string, runnerID int64) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/runners/%v", owner, runnerID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -0,0 +1,299 @@
// Copyright 2020 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"
)
// PublicKey represents the public key that should be used to encrypt secrets.
type PublicKey struct {
KeyID *string `json:"key_id"`
Key *string `json:"key"`
}
// GetRepoPublicKey gets a public key that should be used for secret encryption.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-a-repository-public-key
func (s *ActionsService) GetRepoPublicKey(ctx context.Context, owner, repo string) (*PublicKey, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/secrets/public-key", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
pubKey := new(PublicKey)
resp, err := s.client.Do(ctx, req, pubKey)
if err != nil {
return nil, resp, err
}
return pubKey, resp, nil
}
// GetOrgPublicKey gets a public key that should be used for secret encryption.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-an-organization-public-key
func (s *ActionsService) GetOrgPublicKey(ctx context.Context, org string) (*PublicKey, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/public-key", org)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
pubKey := new(PublicKey)
resp, err := s.client.Do(ctx, req, pubKey)
if err != nil {
return nil, resp, err
}
return pubKey, resp, nil
}
// Secret represents a repository action secret.
type Secret struct {
Name string `json:"name"`
CreatedAt Timestamp `json:"created_at"`
UpdatedAt Timestamp `json:"updated_at"`
Visibility string `json:"visibility,omitempty"`
SelectedRepositoriesURL string `json:"selected_repositories_url,omitempty"`
}
// Secrets represents one item from the ListSecrets response.
type Secrets struct {
TotalCount int `json:"total_count"`
Secrets []*Secret `json:"secrets"`
}
// ListRepoSecrets lists all secrets available in a repository
// without revealing their encrypted values.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#list-repository-secrets
func (s *ActionsService) ListRepoSecrets(ctx context.Context, owner, repo string, opts *ListOptions) (*Secrets, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/secrets", 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
}
secrets := new(Secrets)
resp, err := s.client.Do(ctx, req, &secrets)
if err != nil {
return nil, resp, err
}
return secrets, resp, nil
}
// GetRepoSecret gets a single repository secret without revealing its encrypted value.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-a-repository-secret
func (s *ActionsService) GetRepoSecret(ctx context.Context, owner, repo, name string) (*Secret, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
secret := new(Secret)
resp, err := s.client.Do(ctx, req, secret)
if err != nil {
return nil, resp, err
}
return secret, resp, nil
}
// SelectedRepoIDs are the repository IDs that have access to the secret.
type SelectedRepoIDs []int64
// EncryptedSecret represents a secret that is encrypted using a public key.
//
// The value of EncryptedValue must be your secret, encrypted with
// LibSodium (see documentation here: https://libsodium.gitbook.io/doc/bindings_for_other_languages)
// using the public key retrieved using the GetPublicKey method.
type EncryptedSecret struct {
Name string `json:"-"`
KeyID string `json:"key_id"`
EncryptedValue string `json:"encrypted_value"`
Visibility string `json:"visibility,omitempty"`
SelectedRepositoryIDs SelectedRepoIDs `json:"selected_repository_ids,omitempty"`
}
// CreateOrUpdateRepoSecret creates or updates a repository secret with an encrypted value.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#create-or-update-a-repository-secret
func (s *ActionsService) CreateOrUpdateRepoSecret(ctx context.Context, owner, repo string, eSecret *EncryptedSecret) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, eSecret.Name)
req, err := s.client.NewRequest("PUT", u, eSecret)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// DeleteRepoSecret deletes a secret in a repository using the secret name.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#delete-a-repository-secret
func (s *ActionsService) DeleteRepoSecret(ctx context.Context, owner, repo, name string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/secrets/%v", owner, repo, name)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListOrgSecrets lists all secrets available in an organization
// without revealing their encrypted values.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#list-organization-secrets
func (s *ActionsService) ListOrgSecrets(ctx context.Context, org string, opts *ListOptions) (*Secrets, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets", 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
}
secrets := new(Secrets)
resp, err := s.client.Do(ctx, req, &secrets)
if err != nil {
return nil, resp, err
}
return secrets, resp, nil
}
// GetOrgSecret gets a single organization secret without revealing its encrypted value.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#get-an-organization-secret
func (s *ActionsService) GetOrgSecret(ctx context.Context, org, name string) (*Secret, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
secret := new(Secret)
resp, err := s.client.Do(ctx, req, secret)
if err != nil {
return nil, resp, err
}
return secret, resp, nil
}
// CreateOrUpdateOrgSecret creates or updates an organization secret with an encrypted value.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#create-or-update-an-organization-secret
func (s *ActionsService) CreateOrUpdateOrgSecret(ctx context.Context, org string, eSecret *EncryptedSecret) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, eSecret.Name)
req, err := s.client.NewRequest("PUT", u, eSecret)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// SelectedReposList represents the list of repositories selected for an organization secret.
type SelectedReposList struct {
TotalCount *int `json:"total_count,omitempty"`
Repositories []*Repository `json:"repositories,omitempty"`
}
// ListSelectedReposForOrgSecret lists all repositories that have access to a secret.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#list-selected-repositories-for-an-organization-secret
func (s *ActionsService) ListSelectedReposForOrgSecret(ctx context.Context, org, name string) (*SelectedReposList, *Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
result := new(SelectedReposList)
resp, err := s.client.Do(ctx, req, result)
if err != nil {
return nil, resp, err
}
return result, resp, nil
}
// SetSelectedReposForOrgSecret sets the repositories that have access to a secret.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#set-selected-repositories-for-an-organization-secret
func (s *ActionsService) SetSelectedReposForOrgSecret(ctx context.Context, org, name string, ids SelectedRepoIDs) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories", org, name)
type repoIDs struct {
SelectedIDs SelectedRepoIDs `json:"selected_repository_ids,omitempty"`
}
req, err := s.client.NewRequest("PUT", u, repoIDs{SelectedIDs: ids})
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// AddSelectedRepoToOrgSecret adds a repository to an organization secret.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#add-selected-repository-to-an-organization-secret
func (s *ActionsService) AddSelectedRepoToOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// RemoveSelectedRepoFromOrgSecret removes a repository from an organization secret.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#remove-selected-repository-from-an-organization-secret
func (s *ActionsService) RemoveSelectedRepoFromOrgSecret(ctx context.Context, org, name string, repo *Repository) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v/repositories/%v", org, name, *repo.ID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// DeleteOrgSecret deletes a secret in an organization using the secret name.
//
// GitHub API docs: https://developer.github.com/v3/actions/secrets/#delete-an-organization-secret
func (s *ActionsService) DeleteOrgSecret(ctx context.Context, org, name string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/actions/secrets/%v", org, name)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -0,0 +1,149 @@
// Copyright 2020 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"
"net/http"
"net/url"
)
// TaskStep represents a single task step from a sequence of tasks of a job.
type TaskStep struct {
Name *string `json:"name,omitempty"`
Status *string `json:"status,omitempty"`
Conclusion *string `json:"conclusion,omitempty"`
Number *int64 `json:"number,omitempty"`
StartedAt *Timestamp `json:"started_at,omitempty"`
CompletedAt *Timestamp `json:"completed_at,omitempty"`
}
// WorkflowJob represents a repository action workflow job.
type WorkflowJob struct {
ID *int64 `json:"id,omitempty"`
RunID *int64 `json:"run_id,omitempty"`
RunURL *string `json:"run_url,omitempty"`
NodeID *string `json:"node_id,omitempty"`
HeadSHA *string `json:"head_sha,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Status *string `json:"status,omitempty"`
Conclusion *string `json:"conclusion,omitempty"`
StartedAt *Timestamp `json:"started_at,omitempty"`
CompletedAt *Timestamp `json:"completed_at,omitempty"`
Name *string `json:"name,omitempty"`
Steps []*TaskStep `json:"steps,omitempty"`
CheckRunURL *string `json:"check_run_url,omitempty"`
}
// Jobs represents a slice of repository action workflow job.
type Jobs struct {
TotalCount *int `json:"total_count,omitempty"`
Jobs []*WorkflowJob `json:"jobs,omitempty"`
}
// ListWorkflowJobsOptions specifies optional parameters to ListWorkflowJobs.
type ListWorkflowJobsOptions struct {
// Filter specifies how jobs should be filtered by their completed_at timestamp.
// Possible values are:
// latest - Returns jobs from the most recent execution of the workflow run
// all - Returns all jobs for a workflow run, including from old executions of the workflow run
//
// Default value is "latest".
Filter string `url:"filter,omitempty"`
ListOptions
}
// ListWorkflowJobs lists all jobs for a workflow run.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-jobs/#list-jobs-for-a-workflow-run
func (s *ActionsService) ListWorkflowJobs(ctx context.Context, owner, repo string, runID int64, opts *ListWorkflowJobsOptions) (*Jobs, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/actions/runs/%v/jobs", owner, repo, runID)
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
}
jobs := new(Jobs)
resp, err := s.client.Do(ctx, req, &jobs)
if err != nil {
return nil, resp, err
}
return jobs, resp, nil
}
// GetWorkflowJobByID gets a specific job in a workflow run by ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-jobs/#get-a-job-for-a-workflow-run
func (s *ActionsService) GetWorkflowJobByID(ctx context.Context, owner, repo string, jobID int64) (*WorkflowJob, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v", owner, repo, jobID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
job := new(WorkflowJob)
resp, err := s.client.Do(ctx, req, job)
if err != nil {
return nil, resp, err
}
return job, resp, nil
}
// GetWorkflowJobLogs gets a redirect URL to download a plain text file of logs for a workflow job.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-jobs/#download-job-logs-for-a-workflow-run
func (s *ActionsService) GetWorkflowJobLogs(ctx context.Context, owner, repo string, jobID int64, followRedirects bool) (*url.URL, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v/logs", owner, repo, jobID)
resp, err := s.getWorkflowLogsFromURL(ctx, u, followRedirects)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusFound {
return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
}
parsedURL, err := url.Parse(resp.Header.Get("Location"))
return parsedURL, newResponse(resp), err
}
func (s *ActionsService) getWorkflowLogsFromURL(ctx context.Context, u string, followRedirects bool) (*http.Response, error) {
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
var resp *http.Response
// Use http.DefaultTransport if no custom Transport is configured
req = withContext(ctx, req)
if s.client.client.Transport == nil {
resp, err = http.DefaultTransport.RoundTrip(req)
} else {
resp, err = s.client.client.Transport.RoundTrip(req)
}
if err != nil {
return nil, err
}
resp.Body.Close()
// If redirect response is returned, follow it
if followRedirects && resp.StatusCode == http.StatusMovedPermanently {
u = resp.Header.Get("Location")
resp, err = s.getWorkflowLogsFromURL(ctx, u, false)
}
return resp, err
}

View File

@@ -0,0 +1,235 @@
// Copyright 2020 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"
"net/http"
"net/url"
)
// WorkflowRun represents a repository action workflow run.
type WorkflowRun struct {
ID *int64 `json:"id,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"`
}
// WorkflowRuns represents a slice of repository action workflow run.
type WorkflowRuns struct {
TotalCount *int `json:"total_count,omitempty"`
WorkflowRuns []*WorkflowRun `json:"workflow_runs,omitempty"`
}
// 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"`
ListOptions
}
// WorkflowRunUsage represents a usage of a specific workflow run.
type WorkflowRunUsage struct {
Billable *WorkflowRunEnvironment `json:"billable,omitempty"`
RunDurationMS *int64 `json:"run_duration_ms,omitempty"`
}
// WorkflowRunEnvironment represents different runner environments available for a workflow run.
type WorkflowRunEnvironment struct {
Ubuntu *WorkflowRunBill `json:"UBUNTU,omitempty"`
MacOS *WorkflowRunBill `json:"MACOS,omitempty"`
Windows *WorkflowRunBill `json:"WINDOWS,omitempty"`
}
// WorkflowRunBill specifies billable time for a specific environment in a workflow run.
type WorkflowRunBill struct {
TotalMS *int64 `json:"total_ms,omitempty"`
Jobs *int `json:"jobs,omitempty"`
}
func (s *ActionsService) listWorkflowRuns(ctx context.Context, endpoint string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
u, err := addOptions(endpoint, opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
runs := new(WorkflowRuns)
resp, err := s.client.Do(ctx, req, &runs)
if err != nil {
return nil, resp, err
}
return runs, resp, nil
}
// ListWorkflowRunsByID lists all workflow runs by workflow ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs
func (s *ActionsService) ListWorkflowRunsByID(ctx context.Context, owner, repo string, workflowID int64, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/actions/workflows/%v/runs", owner, repo, workflowID)
return s.listWorkflowRuns(ctx, u, opts)
}
// ListWorkflowRunsByFileName lists all workflow runs by workflow file name.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs
func (s *ActionsService) ListWorkflowRunsByFileName(ctx context.Context, owner, repo, workflowFileName string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/actions/workflows/%v/runs", owner, repo, workflowFileName)
return s.listWorkflowRuns(ctx, u, opts)
}
// ListRepositoryWorkflowRuns lists all workflow runs for a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#list-workflow-runs-for-a-repository
func (s *ActionsService) ListRepositoryWorkflowRuns(ctx context.Context, owner, repo string, opts *ListWorkflowRunsOptions) (*WorkflowRuns, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/actions/runs", 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
}
runs := new(WorkflowRuns)
resp, err := s.client.Do(ctx, req, &runs)
if err != nil {
return nil, resp, err
}
return runs, resp, nil
}
// GetWorkflowRunByID gets a specific workflow run by ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#get-a-workflow-run
func (s *ActionsService) GetWorkflowRunByID(ctx context.Context, owner, repo string, runID int64) (*WorkflowRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v", owner, repo, runID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
run := new(WorkflowRun)
resp, err := s.client.Do(ctx, req, run)
if err != nil {
return nil, resp, err
}
return run, resp, nil
}
// RerunWorkflow re-runs a workflow by ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#re-run-a-workflow
func (s *ActionsService) RerunWorkflowByID(ctx context.Context, owner, repo string, runID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/rerun", owner, repo, runID)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// CancelWorkflowRunByID cancels a workflow run by ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#cancel-a-workflow-run
func (s *ActionsService) CancelWorkflowRunByID(ctx context.Context, owner, repo string, runID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/cancel", owner, repo, runID)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// GetWorkflowRunLogs gets a redirect URL to download a plain text file of logs for a workflow run.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#download-workflow-run-logs
func (s *ActionsService) GetWorkflowRunLogs(ctx context.Context, owner, repo string, runID int64, followRedirects bool) (*url.URL, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/logs", owner, repo, runID)
resp, err := s.getWorkflowLogsFromURL(ctx, u, followRedirects)
if err != nil {
return nil, nil, err
}
if resp.StatusCode != http.StatusFound {
return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status)
}
parsedURL, err := url.Parse(resp.Header.Get("Location"))
return parsedURL, newResponse(resp), err
}
// DeleteWorkflowRunLogs deletes all logs for a workflow run.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#delete-workflow-run-logs
func (s *ActionsService) DeleteWorkflowRunLogs(ctx context.Context, owner, repo string, runID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/logs", owner, repo, runID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// GetWorkflowRunUsageByID gets a specific workflow usage run by run ID in the unit of billable milliseconds.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflow-runs/#get-workflow-run-usage
func (s *ActionsService) GetWorkflowRunUsageByID(ctx context.Context, owner, repo string, runID int64) (*WorkflowRunUsage, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/timing", owner, repo, runID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
workflowRunUsage := new(WorkflowRunUsage)
resp, err := s.client.Do(ctx, req, workflowRunUsage)
if err != nil {
return nil, resp, err
}
return workflowRunUsage, resp, nil
}

View File

@@ -0,0 +1,138 @@
// Copyright 2020 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"
)
// Workflow represents a repository action workflow.
type Workflow struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
State *string `json:"state,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
BadgeURL *string `json:"badge_url,omitempty"`
}
// Workflows represents a slice of repository action workflows.
type Workflows struct {
TotalCount *int `json:"total_count,omitempty"`
Workflows []*Workflow `json:"workflows,omitempty"`
}
// WorkflowUsage represents a usage of a specific workflow.
type WorkflowUsage struct {
Billable *WorkflowEnvironment `json:"billable,omitempty"`
}
// WorkflowEnvironment represents different runner environments available for a workflow.
type WorkflowEnvironment struct {
Ubuntu *WorkflowBill `json:"UBUNTU,omitempty"`
MacOS *WorkflowBill `json:"MACOS,omitempty"`
Windows *WorkflowBill `json:"WINDOWS,omitempty"`
}
// WorkflowBill specifies billable time for a specific environment in a workflow.
type WorkflowBill struct {
TotalMS *int64 `json:"total_ms,omitempty"`
}
// ListWorkflows lists all workflows in a repository.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflows/#list-repository-workflows
func (s *ActionsService) ListWorkflows(ctx context.Context, owner, repo string, opts *ListOptions) (*Workflows, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/actions/workflows", 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
}
workflows := new(Workflows)
resp, err := s.client.Do(ctx, req, &workflows)
if err != nil {
return nil, resp, err
}
return workflows, resp, nil
}
// GetWorkflowByID gets a specific workflow by ID.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflows/#get-a-workflow
func (s *ActionsService) GetWorkflowByID(ctx context.Context, owner, repo string, workflowID int64) (*Workflow, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v", owner, repo, workflowID)
return s.getWorkflow(ctx, u)
}
// GetWorkflowByFileName gets a specific workflow by file name.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflows/#get-a-workflow
func (s *ActionsService) GetWorkflowByFileName(ctx context.Context, owner, repo, workflowFileName string) (*Workflow, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v", owner, repo, workflowFileName)
return s.getWorkflow(ctx, u)
}
func (s *ActionsService) getWorkflow(ctx context.Context, url string) (*Workflow, *Response, error) {
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
workflow := new(Workflow)
resp, err := s.client.Do(ctx, req, workflow)
if err != nil {
return nil, resp, err
}
return workflow, resp, nil
}
// GetWorkflowUsageByID gets a specific workflow usage by ID in the unit of billable milliseconds.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflows/#get-workflow-usage
func (s *ActionsService) GetWorkflowUsageByID(ctx context.Context, owner, repo string, workflowID int64) (*WorkflowUsage, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/timing", owner, repo, workflowID)
return s.getWorkflowUsage(ctx, u)
}
// GetWorkflowUsageByFileName gets a specific workflow usage by file name in the unit of billable milliseconds.
//
// GitHub API docs: https://developer.github.com/v3/actions/workflows/#get-workflow-usage
func (s *ActionsService) GetWorkflowUsageByFileName(ctx context.Context, owner, repo, workflowFileName string) (*WorkflowUsage, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/actions/workflows/%v/timing", owner, repo, workflowFileName)
return s.getWorkflowUsage(ctx, u)
}
func (s *ActionsService) getWorkflowUsage(ctx context.Context, url string) (*WorkflowUsage, *Response, error) {
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
workflowUsage := new(WorkflowUsage)
resp, err := s.client.Do(ctx, req, workflowUsage)
if err != nil {
return nil, resp, err
}
return workflowUsage, resp, nil
}

View File

@@ -29,13 +29,13 @@ type Feeds struct {
CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"`
CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"`
Links *struct {
Timeline *FeedLink `json:"timeline,omitempty"`
User *FeedLink `json:"user,omitempty"`
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"`
CurrentUser *FeedLink `json:"current_user,omitempty"`
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"`
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"`
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"`
Timeline *FeedLink `json:"timeline,omitempty"`
User *FeedLink `json:"user,omitempty"`
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"`
CurrentUser *FeedLink `json:"current_user,omitempty"`
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"`
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"`
CurrentUserOrganizations []*FeedLink `json:"current_user_organizations,omitempty"`
} `json:"_links,omitempty"`
}

View File

@@ -13,8 +13,8 @@ import (
// ListEvents drinks from the firehose of all public events across GitHub.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events
func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) {
u, err := addOptions("events", opt)
func (s *ActivityService) ListEvents(ctx context.Context, opts *ListOptions) ([]*Event, *Response, error) {
u, err := addOptions("events", opts)
if err != nil {
return nil, nil, err
}
@@ -36,9 +36,9 @@ func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*
// ListRepositoryEvents lists events for a repository.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events
func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) {
func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Event, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/events", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -59,10 +59,10 @@ func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo
// ListIssueEventsForRepository lists issue events for a repository.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository
func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-issue-events-for-a-repository
func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opts *ListOptions) ([]*IssueEvent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -84,9 +84,9 @@ func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owne
// ListEventsForRepoNetwork lists public events for a network of repositories.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories
func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) {
func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Event, *Response, error) {
u := fmt.Sprintf("networks/%v/%v/events", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -107,10 +107,10 @@ func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, r
// ListEventsForOrganization lists public events for an organization.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization
func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-organization-events
func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opts *ListOptions) ([]*Event, *Response, error) {
u := fmt.Sprintf("orgs/%v/events", org)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -132,15 +132,16 @@ func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org str
// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is
// true, only public events will be returned.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user
func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-the-authenticated-user
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-user
func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opts *ListOptions) ([]*Event, *Response, error) {
var u string
if publicOnly {
u = fmt.Sprintf("users/%v/events/public", user)
} else {
u = fmt.Sprintf("users/%v/events", user)
}
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -162,15 +163,16 @@ func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user st
// ListEventsReceivedByUser lists the events received by a user. If publicOnly is
// true, only public events will be returned.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received
func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-received-by-the-authenticated-user
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-received-by-a-user
func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opts *ListOptions) ([]*Event, *Response, error) {
var u string
if publicOnly {
u = fmt.Sprintf("users/%v/received_events/public", user)
} else {
u = fmt.Sprintf("users/%v/received_events", user)
}
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -192,10 +194,10 @@ func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user str
// ListUserEventsForOrganization provides the users organization dashboard. You
// must be authenticated as the user to view this.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization
func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-organization-events-for-the-authenticated-user
func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opts *ListOptions) ([]*Event, *Response, error) {
u := fmt.Sprintf("users/%v/events/orgs/%v", user, org)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

View File

@@ -49,10 +49,10 @@ type NotificationListOptions struct {
// ListNotifications lists all notifications for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications
func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-notifications-for-the-authenticated-user
func (s *ActivityService) ListNotifications(ctx context.Context, opts *NotificationListOptions) ([]*Notification, *Response, error) {
u := fmt.Sprintf("notifications")
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -74,10 +74,10 @@ func (s *ActivityService) ListNotifications(ctx context.Context, opt *Notificati
// ListRepositoryNotifications lists all notifications in a given repository
// for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository
func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-repository-notifications-for-the-authenticated-user
func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opts *NotificationListOptions) ([]*Notification, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -118,7 +118,7 @@ func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead ti
// MarkRepositoryNotificationsRead marks all notifications up to lastRead in
// the specified repository as read.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-repository-notifications-as-read
func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) {
opts := &markReadOptions{
LastReadAt: lastRead,
@@ -134,7 +134,7 @@ func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, o
// GetThread gets the specified notification thread.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread
func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) {
u := fmt.Sprintf("notifications/threads/%v", id)
@@ -169,7 +169,7 @@ func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Respo
// GetThreadSubscription checks to see if the authenticated user is subscribed
// to a thread.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription-for-the-authenticated-user
func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) {
u := fmt.Sprintf("notifications/threads/%v/subscription", id)

View File

@@ -26,9 +26,9 @@ type Stargazer struct {
// ListStargazers lists people who have starred the specified repo.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers
func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) {
func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opts *ListOptions) ([]*Stargazer, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -67,15 +67,16 @@ type ActivityListStarredOptions struct {
// ListStarred lists all the repos starred by a user. Passing the empty string
// will list the starred repositories for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred
func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-starred-by-a-user
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-starred-by-the-authenticated-user
func (s *ActivityService) ListStarred(ctx context.Context, user string, opts *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/starred", user)
} else {
u = "user/starred"
}
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -100,7 +101,7 @@ func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *Act
// IsStarred checks if a repository is starred by authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository
// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-a-repository-is-starred-by-the-authenticated-user
func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) {
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
@@ -114,7 +115,7 @@ func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bo
// Star a repository as the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository-for-the-authenticated-user
func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) {
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
req, err := s.client.NewRequest("PUT", u, nil)
@@ -126,7 +127,7 @@ func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Respon
// Unstar a repository as the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository-for-the-authenticated-user
func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) {
u := fmt.Sprintf("user/starred/%v/%v", owner, repo)
req, err := s.client.NewRequest("DELETE", u, nil)

View File

@@ -28,9 +28,9 @@ type Subscription struct {
// ListWatchers lists watchers of a particular repo.
//
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers
func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) {
func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opts *ListOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -52,15 +52,16 @@ func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string,
// ListWatched lists the repositories the specified user is watching. Passing
// the empty string will fetch watched repos for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched
func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-watched-by-a-user
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-watched-by-the-authenticated-user
func (s *ActivityService) ListWatched(ctx context.Context, user string, opts *ListOptions) ([]*Repository, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/subscriptions", user)
} else {
u = "user/subscriptions"
}
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}

View File

@@ -62,6 +62,24 @@ func (m UserLDAPMapping) String() string {
return Stringify(m)
}
// Enterprise represents the GitHub enterprise profile.
type Enterprise struct {
ID *int `json:"id,omitempty"`
Slug *string `json:"slug,omitempty"`
Name *string `json:"name,omitempty"`
NodeID *string `json:"node_id,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
Description *string `json:"description,omitempty"`
WebsiteURL *string `json:"website_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
func (m Enterprise) String() string {
return Stringify(m)
}
// UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user.
//
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user

View File

@@ -0,0 +1,89 @@
// Copyright 2019 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"
)
// createOrgRequest is a subset of Organization and is used internally
// by CreateOrg to pass only the known fields for the endpoint.
type createOrgRequest struct {
Login *string `json:"login,omitempty"`
Admin *string `json:"admin,omitempty"`
}
// CreateOrg creates a new organization in GitHub Enterprise.
//
// Note that only a subset of the org fields are used and org must
// not be nil.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#create-an-organization
func (s *AdminService) CreateOrg(ctx context.Context, org *Organization, admin string) (*Organization, *Response, error) {
u := "admin/organizations"
orgReq := &createOrgRequest{
Login: org.Login,
Admin: &admin,
}
req, err := s.client.NewRequest("POST", u, orgReq)
if err != nil {
return nil, nil, err
}
o := new(Organization)
resp, err := s.client.Do(ctx, req, o)
if err != nil {
return nil, resp, err
}
return o, resp, nil
}
// renameOrgRequest is a subset of Organization and is used internally
// by RenameOrg and RenameOrgByName to pass only the known fields for the endpoint.
type renameOrgRequest struct {
Login *string `json:"login,omitempty"`
}
// RenameOrgResponse is the response given when renaming an Organization.
type RenameOrgResponse struct {
Message *string `json:"message,omitempty"`
URL *string `json:"url,omitempty"`
}
// RenameOrg renames an organization in GitHub Enterprise.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#rename-an-organization
func (s *AdminService) RenameOrg(ctx context.Context, org *Organization, newName string) (*RenameOrgResponse, *Response, error) {
return s.RenameOrgByName(ctx, *org.Login, newName)
}
// RenameOrgByName renames an organization in GitHub Enterprise using its current name.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/orgs/#rename-an-organization
func (s *AdminService) RenameOrgByName(ctx context.Context, org, newName string) (*RenameOrgResponse, *Response, error) {
u := fmt.Sprintf("admin/organizations/%v", org)
orgReq := &renameOrgRequest{
Login: &newName,
}
req, err := s.client.NewRequest("PATCH", u, orgReq)
if err != nil {
return nil, nil, err
}
o := new(RenameOrgResponse)
resp, err := s.client.Do(ctx, req, o)
if err != nil {
return nil, resp, err
}
return o, resp, nil
}

View File

@@ -10,7 +10,7 @@ import (
"fmt"
)
// AdminStats represents a variety of stats of a Github Enterprise
// AdminStats represents a variety of stats of a GitHub Enterprise
// installation.
type AdminStats struct {
Issues *IssueStats `json:"issues,omitempty"`
@@ -147,7 +147,7 @@ func (s RepoStats) String() string {
return Stringify(s)
}
// GetAdminStats returns a variety of metrics about a Github Enterprise
// GetAdminStats returns a variety of metrics about a GitHub Enterprise
// installation.
//
// Please note that this is only available to site administrators,

View File

@@ -0,0 +1,133 @@
// Copyright 2019 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"
)
// createUserRequest is a subset of User and is used internally
// by CreateUser to pass only the known fields for the endpoint.
type createUserRequest struct {
Login *string `json:"login,omitempty"`
Email *string `json:"email,omitempty"`
}
// CreateUser creates a new user in GitHub Enterprise.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-a-new-user
func (s *AdminService) CreateUser(ctx context.Context, login, email string) (*User, *Response, error) {
u := "admin/users"
userReq := &createUserRequest{
Login: &login,
Email: &email,
}
req, err := s.client.NewRequest("POST", u, userReq)
if err != nil {
return nil, nil, err
}
var user User
resp, err := s.client.Do(ctx, req, &user)
if err != nil {
return nil, resp, err
}
return &user, resp, nil
}
// DeleteUser deletes a user in GitHub Enterprise.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-a-user
func (s *AdminService) DeleteUser(ctx context.Context, username string) (*Response, error) {
u := "admin/users/" + username
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
}
// ImpersonateUserOptions represents the scoping for the OAuth token.
type ImpersonateUserOptions struct {
Scopes []string `json:"scopes,omitempty"`
}
// OAuthAPP represents the GitHub Site Administrator OAuth app.
type OAuthAPP struct {
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
ClientID *string `json:"client_id,omitempty"`
}
func (s OAuthAPP) String() string {
return Stringify(s)
}
// UserAuthorization represents the impersonation response.
type UserAuthorization struct {
ID *int64 `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Scopes []string `json:"scopes,omitempty"`
Token *string `json:"token,omitempty"`
TokenLastEight *string `json:"token_last_eight,omitempty"`
HashedToken *string `json:"hashed_token,omitempty"`
App *OAuthAPP `json:"app,omitempty"`
Note *string `json:"note,omitempty"`
NoteURL *string `json:"note_url,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
Fingerprint *string `json:"fingerprint,omitempty"`
}
// CreateUserImpersonation creates an impersonation OAuth token.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-an-impersonation-oauth-token
func (s *AdminService) CreateUserImpersonation(ctx context.Context, username string, opts *ImpersonateUserOptions) (*UserAuthorization, *Response, error) {
u := fmt.Sprintf("admin/users/%s/authorizations", username)
req, err := s.client.NewRequest("POST", u, opts)
if err != nil {
return nil, nil, err
}
a := new(UserAuthorization)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}
// DeleteUserImpersonation deletes an impersonation OAuth token.
//
// GitHub Enterprise API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-an-impersonation-oauth-token
func (s *AdminService) DeleteUserImpersonation(ctx context.Context, username string) (*Response, error) {
u := fmt.Sprintf("admin/users/%s/authorizations", username)
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
}

349
vendor/github.com/google/go-github/v32/github/apps.go generated vendored Normal file
View File

@@ -0,0 +1,349 @@
// Copyright 2016 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"
"time"
)
// AppsService provides access to the installation related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/apps/
type AppsService service
// App represents a GitHub App.
type App 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"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
Events []string `json:"events,omitempty"`
}
// InstallationToken represents an installation token.
type InstallationToken struct {
Token *string `json:"token,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
Repositories []*Repository `json:"repositories,omitempty"`
}
// InstallationTokenOptions allow restricting a token's access to specific repositories.
type InstallationTokenOptions struct {
// The IDs of the repositories that the installation token can access.
// Providing repository IDs restricts the access of an installation token to specific repositories.
RepositoryIDs []int64 `json:"repository_ids,omitempty"`
// The permissions granted to the access token.
// The permissions object includes the permission names and their access type.
Permissions *InstallationPermissions `json:"permissions,omitempty"`
}
// InstallationPermissions lists the repository and organization permissions for an installation.
//
// Permission names taken from:
// https://developer.github.com/v3/apps/permissions/
// https://developer.github.com/enterprise/v3/apps/permissions/
type InstallationPermissions struct {
Administration *string `json:"administration,omitempty"`
Blocking *string `json:"blocking,omitempty"`
Checks *string `json:"checks,omitempty"`
Contents *string `json:"contents,omitempty"`
ContentReferences *string `json:"content_references,omitempty"`
Deployments *string `json:"deployments,omitempty"`
Emails *string `json:"emails,omitempty"`
Followers *string `json:"followers,omitempty"`
Issues *string `json:"issues,omitempty"`
Metadata *string `json:"metadata,omitempty"`
Members *string `json:"members,omitempty"`
OrganizationAdministration *string `json:"organization_administration,omitempty"`
OrganizationHooks *string `json:"organization_hooks,omitempty"`
OrganizationPlan *string `json:"organization_plan,omitempty"`
OrganizationPreReceiveHooks *string `json:"organization_pre_receive_hooks,omitempty"`
OrganizationProjects *string `json:"organization_projects,omitempty"`
OrganizationUserBlocking *string `json:"organization_user_blocking,omitempty"`
Packages *string `json:"packages,omitempty"`
Pages *string `json:"pages,omitempty"`
PullRequests *string `json:"pull_requests,omitempty"`
RepositoryHooks *string `json:"repository_hooks,omitempty"`
RepositoryProjects *string `json:"repository_projects,omitempty"`
RepositoryPreReceiveHooks *string `json:"repository_pre_receive_hooks,omitempty"`
SingleFile *string `json:"single_file,omitempty"`
Statuses *string `json:"statuses,omitempty"`
TeamDiscussions *string `json:"team_discussions,omitempty"`
VulnerabilityAlerts *string `json:"vulnerability_alerts,omitempty"`
}
// Installation represents a GitHub Apps installation.
type Installation struct {
ID *int64 `json:"id,omitempty"`
AppID *int64 `json:"app_id,omitempty"`
TargetID *int64 `json:"target_id,omitempty"`
Account *User `json:"account,omitempty"`
AccessTokensURL *string `json:"access_tokens_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
TargetType *string `json:"target_type,omitempty"`
SingleFileName *string `json:"single_file_name,omitempty"`
RepositorySelection *string `json:"repository_selection,omitempty"`
Events []string `json:"events,omitempty"`
Permissions *InstallationPermissions `json:"permissions,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// Attachment represents a GitHub Apps attachment.
type Attachment struct {
ID *int64 `json:"id,omitempty"`
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
}
func (i Installation) String() string {
return Stringify(i)
}
// Get a single GitHub App. Passing the empty string will get
// the authenticated GitHub App.
//
// Note: appSlug is just the URL-friendly name of your GitHub App.
// You can find this on the settings page for your GitHub App
// (e.g., https://github.com/settings/apps/:app_slug).
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-the-authenticated-app
// GitHub API docs: https://developer.github.com/v3/apps/#get-an-app
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) {
var u string
if appSlug != "" {
u = fmt.Sprintf("apps/%v", appSlug)
} else {
u = "app"
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
app := new(App)
resp, err := s.client.Do(ctx, req, app)
if err != nil {
return nil, resp, err
}
return app, resp, nil
}
// ListInstallations lists the installations that the current GitHub App has.
//
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-the-authenticated-app
func (s *AppsService) ListInstallations(ctx context.Context, opts *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("app/installations", opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var i []*Installation
resp, err := s.client.Do(ctx, req, &i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}
// GetInstallation returns the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-an-installation-for-the-authenticated-app
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id))
}
// ListUserInstallations lists installations that are accessible to the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-app-installations-accessible-to-the-user-access-token
func (s *AppsService) ListUserInstallations(ctx context.Context, opts *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("user/installations", opts)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
var i struct {
Installations []*Installation `json:"installations"`
}
resp, err := s.client.Do(ctx, req, &i)
if err != nil {
return nil, resp, err
}
return i.Installations, resp, nil
}
// SuspendInstallation suspends the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#suspend-an-app-installation
func (s *AppsService) SuspendInstallation(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("app/installations/%v/suspended", id)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// UnsuspendInstallation unsuspends the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#unsuspend-an-app-installation
func (s *AppsService) UnsuspendInstallation(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("app/installations/%v/suspended", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// DeleteInstallation deletes the specified installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/#delete-an-installation-for-the-authenticated-app
func (s *AppsService) DeleteInstallation(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("app/installations/%v", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
return s.client.Do(ctx, req, nil)
}
// CreateInstallationToken creates a new installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-an-installation-access-token-for-an-app
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64, opts *InstallationTokenOptions) (*InstallationToken, *Response, error) {
u := fmt.Sprintf("app/installations/%v/access_tokens", id)
req, err := s.client.NewRequest("POST", u, opts)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
t := new(InstallationToken)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// CreateAttachment creates a new attachment on user comment containing a url.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#create-a-content-attachment
func (s *AppsService) CreateAttachment(ctx context.Context, contentReferenceID int64, title, body string) (*Attachment, *Response, error) {
u := fmt.Sprintf("content_references/%v/attachments", contentReferenceID)
payload := &Attachment{Title: String(title), Body: String(body)}
req, err := s.client.NewRequest("POST", u, payload)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept headers when APIs fully launch.
req.Header.Set("Accept", mediaTypeReactionsPreview)
m := &Attachment{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// FindOrganizationInstallation finds the organization's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-an-organization-installation-for-the-authenticated-app
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org))
}
// FindRepositoryInstallation finds the repository's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-repository-installation-for-the-authenticated-app
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo))
}
// FindRepositoryInstallationByID finds the repository's installation information.
//
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation.
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id))
}
// FindUserInstallation finds the user's installation information.
//
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-user-installation-for-the-authenticated-app
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) {
return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user))
}
func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) {
req, err := s.client.NewRequest("GET", url, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeIntegrationPreview)
i := new(Installation)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}

View File

@@ -12,9 +12,9 @@ import (
// ListRepos lists the repositories that are accessible to the authenticated installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories
func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) {
u, err := addOptions("installation/repositories", opt)
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-app-installation
func (s *AppsService) ListRepos(ctx context.Context, opts *ListOptions) ([]*Repository, *Response, error) {
u, err := addOptions("installation/repositories", opts)
if err != nil {
return nil, nil, err
}
@@ -41,10 +41,10 @@ func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repos
// ListUserRepos lists repositories that are accessible
// to the authenticated user for an installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation
func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-access-token
func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opts *ListOptions) ([]*Repository, *Response, error) {
u := fmt.Sprintf("user/installations/%v/repositories", id)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -70,7 +70,7 @@ func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOpti
// AddRepository adds a single repository to an installation.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation
// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-a-repository-to-an-app-installation
func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) {
u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID)
req, err := s.client.NewRequest("PUT", u, nil)
@@ -90,7 +90,7 @@ func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (
// RemoveRepository removes a single repository from an installation.
//
// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation
// GitHub API docs: https://developer.github.com/v3/apps/installations/#remove-a-repository-from-an-app-installation
func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) {
u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID)
req, err := s.client.NewRequest("DELETE", u, nil)
@@ -101,3 +101,16 @@ func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64
return s.client.Do(ctx, req, nil)
}
// RevokeInstallationToken revokes an installation token.
//
// GitHub API docs: https://developer.github.com/v3/apps/installations/#revoke-an-installation-access-token
func (s *AppsService) RevokeInstallationToken(ctx context.Context) (*Response, error) {
u := "installation/token"
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -0,0 +1,53 @@
// Copyright 2019 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"
)
const (
mediaTypeAppManifestPreview = "application/vnd.github.fury-preview+json"
)
// AppConfig describes the configuration of a GitHub App.
type AppConfig struct {
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
ExternalURL *string `json:"external_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
ClientID *string `json:"client_id,omitempty"`
ClientSecret *string `json:"client_secret,omitempty"`
WebhookSecret *string `json:"webhook_secret,omitempty"`
PEM *string `json:"pem,omitempty"`
}
// CompleteAppManifest completes the App manifest handshake flow for the given
// code.
//
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-github-app-from-a-manifest
func (s *AppsService) CompleteAppManifest(ctx context.Context, code string) (*AppConfig, *Response, error) {
u := fmt.Sprintf("app-manifests/%s/conversions", code)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeAppManifestPreview)
cfg := new(AppConfig)
resp, err := s.client.Do(ctx, req, cfg)
if err != nil {
return nil, resp, err
}
return cfg, resp, nil
}

View File

@@ -68,6 +68,7 @@ type MarketplacePlanAccount struct {
URL *string `json:"url,omitempty"`
Type *string `json:"type,omitempty"`
ID *int64 `json:"id,omitempty"`
NodeID *string `json:"node_id,omitempty"`
Login *string `json:"login,omitempty"`
Email *string `json:"email,omitempty"`
OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"`
@@ -78,9 +79,9 @@ type MarketplacePlanAccount struct {
// ListPlans lists all plans for your Marketplace listing.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing
func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) {
func (s *MarketplaceService) ListPlans(ctx context.Context, opts *ListOptions) ([]*MarketplacePlan, *Response, error) {
uri := s.marketplaceURI("plans")
u, err := addOptions(uri, opt)
u, err := addOptions(uri, opts)
if err != nil {
return nil, nil, err
}
@@ -102,9 +103,9 @@ func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([
// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan
func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opts *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID))
u, err := addOptions(uri, opt)
u, err := addOptions(uri, opts)
if err != nil {
return nil, nil, err
}
@@ -126,9 +127,9 @@ func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID
// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing
func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opts *ListOptions) ([]*MarketplacePlanAccount, *Response, error) {
uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID))
u, err := addOptions(uri, opt)
u, err := addOptions(uri, opts)
if err != nil {
return nil, nil, err
}
@@ -149,14 +150,15 @@ func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, acc
// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user.
//
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases
func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-subscriptions-for-the-authenticated-user
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-subscriptions-for-the-authenticated-user-stubbed
func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opts *ListOptions) ([]*MarketplacePurchase, *Response, error) {
uri := "user/marketplace_purchases"
if s.Stubbed {
uri = "user/marketplace_purchases/stubbed"
}
u, err := addOptions(uri, opt)
u, err := addOptions(uri, opts)
if err != nil {
return nil, nil, err
}

View File

@@ -41,6 +41,7 @@ const (
ScopeReadGPGKey Scope = "read:gpg_key"
ScopeWriteGPGKey Scope = "write:gpg_key"
ScopeAdminGPGKey Scope = "admin:gpg_key"
ScopeSecurityEvents Scope = "security_events"
)
// AuthorizationsService handles communication with the authorization related
@@ -134,137 +135,6 @@ func (a AuthorizationUpdateRequest) String() string {
return Stringify(a)
}
// List the authorizations for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations
func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) {
u := "authorizations"
u, err := addOptions(u, opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var auths []*Authorization
resp, err := s.client.Do(ctx, req, &auths)
if err != nil {
return nil, resp, err
}
return auths, resp, nil
}
// Get a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization
func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}
// Create a new authorization for the specified OAuth application.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization
func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) {
u := "authorizations"
req, err := s.client.NewRequest("POST", u, auth)
if err != nil {
return nil, nil, err
}
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}
// GetOrCreateForApp creates a new authorization for the specified OAuth
// application, only if an authorization for that application doesnt already
// exist for the user.
//
// If a new token is created, the HTTP status code will be "201 Created", and
// the returned Authorization.Token field will be populated. If an existing
// token is returned, the status code will be "200 OK" and the
// Authorization.Token field will be empty.
//
// clientID is the OAuth Client ID with which to create the token.
//
// GitHub API docs:
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint
func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) {
var u string
if auth.Fingerprint == nil || *auth.Fingerprint == "" {
u = fmt.Sprintf("authorizations/clients/%v", clientID)
} else {
u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint)
}
req, err := s.client.NewRequest("PUT", u, auth)
if err != nil {
return nil, nil, err
}
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}
// Edit a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("PATCH", u, auth)
if err != nil {
return nil, nil, err
}
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}
// Delete a single authorization.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization
func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("authorizations/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Check if an OAuth token is valid for a specific app.
//
// Note that this operation requires the use of BasicAuth, but where the
@@ -273,14 +143,19 @@ func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response
//
// The returned Authorization.User field will be populated.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization
func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) {
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
// GitHub API docs: https://developer.github.com/v3/apps/oauth_applications/#check-a-token
func (s *AuthorizationsService) Check(ctx context.Context, clientID, accessToken string) (*Authorization, *Response, error) {
u := fmt.Sprintf("applications/%v/token", clientID)
req, err := s.client.NewRequest("GET", u, nil)
reqBody := &struct {
AccessToken string `json:"access_token"`
}{AccessToken: accessToken}
req, err := s.client.NewRequest("POST", u, reqBody)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeOAuthAppPreview)
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
@@ -301,14 +176,19 @@ func (s *AuthorizationsService) Check(ctx context.Context, clientID string, toke
//
// The returned Authorization.User field will be populated.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization
func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) {
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
// GitHub API docs: https://developer.github.com/v3/apps/oauth_applications/#reset-a-token
func (s *AuthorizationsService) Reset(ctx context.Context, clientID, accessToken string) (*Authorization, *Response, error) {
u := fmt.Sprintf("applications/%v/token", clientID)
req, err := s.client.NewRequest("POST", u, nil)
reqBody := &struct {
AccessToken string `json:"access_token"`
}{AccessToken: accessToken}
req, err := s.client.NewRequest("PATCH", u, reqBody)
if err != nil {
return nil, nil, err
}
req.Header.Set("Accept", mediaTypeOAuthAppPreview)
a := new(Authorization)
resp, err := s.client.Do(ctx, req, a)
@@ -325,74 +205,40 @@ func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, toke
// username is the OAuth application clientID, and the password is its
// clientSecret. Invalid tokens will return a 404 Not Found.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application
func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) {
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token)
// GitHub API docs: https://developer.github.com/v3/apps/oauth_applications/#delete-an-app-token
func (s *AuthorizationsService) Revoke(ctx context.Context, clientID, accessToken string) (*Response, error) {
u := fmt.Sprintf("applications/%v/token", clientID)
req, err := s.client.NewRequest("DELETE", u, nil)
reqBody := &struct {
AccessToken string `json:"access_token"`
}{AccessToken: accessToken}
req, err := s.client.NewRequest("DELETE", u, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Accept", mediaTypeOAuthAppPreview)
return s.client.Do(ctx, req, nil)
}
// ListGrants lists the set of OAuth applications that have been granted
// access to a user's account. This will return one entry for each application
// that has been granted access to the account, regardless of the number of
// tokens an application has generated for the user.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants
func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions) ([]*Grant, *Response, error) {
u, err := addOptions("applications/grants", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
grants := []*Grant{}
resp, err := s.client.Do(ctx, req, &grants)
if err != nil {
return nil, resp, err
}
return grants, resp, nil
}
// GetGrant gets a single OAuth application grant.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant
func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) {
u := fmt.Sprintf("applications/grants/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
grant := new(Grant)
resp, err := s.client.Do(ctx, req, grant)
if err != nil {
return nil, resp, err
}
return grant, resp, nil
}
// DeleteGrant deletes an OAuth application grant. Deleting an application's
// grant will also delete all OAuth tokens associated with the application for
// the user.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) {
u := fmt.Sprintf("applications/grants/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
// GitHub API docs: https://developer.github.com/v3/apps/oauth_applications/#delete-an-app-authorization
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, clientID, accessToken string) (*Response, error) {
u := fmt.Sprintf("applications/%v/grant", clientID)
reqBody := &struct {
AccessToken string `json:"access_token"`
}{AccessToken: accessToken}
req, err := s.client.NewRequest("DELETE", u, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Accept", mediaTypeOAuthAppPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -8,7 +8,6 @@ package github
import (
"context"
"fmt"
"net/url"
)
// ChecksService provides access to the Checks API in the
@@ -51,9 +50,10 @@ type CheckRunOutput struct {
// CheckRunAnnotation represents an annotation object for a CheckRun output.
type CheckRunAnnotation struct {
Path *string `json:"path,omitempty"`
BlobHRef *string `json:"blob_href,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"`
AnnotationLevel *string `json:"annotation_level,omitempty"`
Message *string `json:"message,omitempty"`
Title *string `json:"title,omitempty"`
@@ -81,6 +81,9 @@ type CheckSuite struct {
App *App `json:"app,omitempty"`
Repository *Repository `json:"repository,omitempty"`
PullRequests []*PullRequest `json:"pull_requests,omitempty"`
// The following fields are only populated by Webhook events.
HeadCommit *Commit `json:"head_commit,omitempty"`
}
func (c CheckRun) String() string {
@@ -93,7 +96,7 @@ func (c CheckSuite) String() string {
// GetCheckRun gets a check-run for a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#get-a-single-check-run
// GitHub API docs: https://developer.github.com/v3/checks/runs/#get-a-check-run
func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
req, err := s.client.NewRequest("GET", u, nil)
@@ -114,7 +117,7 @@ func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, che
// GetCheckSuite gets a single check suite.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#get-a-single-check-suite
// GitHub API docs: https://developer.github.com/v3/checks/suites/#get-a-check-suite
func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*CheckSuite, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v", owner, repo, checkSuiteID)
req, err := s.client.NewRequest("GET", u, nil)
@@ -136,12 +139,11 @@ func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, c
// CreateCheckRunOptions sets up parameters needed to create a CheckRun.
type CreateCheckRunOptions struct {
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.)
HeadBranch string `json:"head_branch"` // The name of the branch to perform a check against. (Required.)
HeadSHA string `json:"head_sha"` // The SHA of the commit. (Required.)
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.)
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.)
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
StartedAt *Timestamp `json:"started_at,omitempty"` // The time that the check run began. (Optional.)
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional)
@@ -158,9 +160,9 @@ type CheckRunAction struct {
// CreateCheckRun creates a check run for repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#create-a-check-run
func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opt CreateCheckRunOptions) (*CheckRun, *Response, error) {
func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opts CreateCheckRunOptions) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs", owner, repo)
req, err := s.client.NewRequest("POST", u, opt)
req, err := s.client.NewRequest("POST", u, opts)
if err != nil {
return nil, nil, err
}
@@ -179,12 +181,11 @@ func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string,
// UpdateCheckRunOptions sets up parameters needed to update a CheckRun.
type UpdateCheckRunOptions struct {
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.)
HeadBranch *string `json:"head_branch,omitempty"` // The name of the branch to perform a check against. (Optional.)
HeadSHA *string `json:"head_sha,omitempty"` // The SHA of the commit. (Optional.)
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.)
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.)
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "skipped", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".)
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.)
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional)
Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.)
@@ -193,9 +194,9 @@ type UpdateCheckRunOptions struct {
// UpdateCheckRun updates a check run for a specific commit in a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#update-a-check-run
func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opt UpdateCheckRunOptions) (*CheckRun, *Response, error) {
func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opts UpdateCheckRunOptions) (*CheckRun, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID)
req, err := s.client.NewRequest("PATCH", u, opt)
req, err := s.client.NewRequest("PATCH", u, opts)
if err != nil {
return nil, nil, err
}
@@ -213,10 +214,10 @@ func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string,
// ListCheckRunAnnotations lists the annotations for a check run.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run
func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opt *ListOptions) ([]*CheckRunAnnotation, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-run-annotations
func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opts *ListOptions) ([]*CheckRunAnnotation, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-runs/%v/annotations", owner, repo, checkRunID)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -254,10 +255,10 @@ type ListCheckRunsResults struct {
// ListCheckRunsForRef lists check runs for a specific ref.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref
func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, url.QueryEscape(ref))
u, err := addOptions(u, opt)
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-git-reference
func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, refURLEscape(ref))
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -281,9 +282,9 @@ func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, re
// ListCheckRunsCheckSuite lists check runs for a check suite.
//
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite
func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opts *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/check-runs", owner, repo, checkSuiteID)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -320,10 +321,10 @@ type ListCheckSuiteResults struct {
// ListCheckSuitesForRef lists check suite for a specific ref.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref
func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, url.QueryEscape(ref))
u, err := addOptions(u, opt)
// GitHub API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-git-reference
func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opts *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, refURLEscape(ref))
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -352,7 +353,7 @@ type AutoTriggerCheck struct {
// CheckSuitePreferenceOptions set options for check suite preferences for a repository.
type CheckSuitePreferenceOptions struct {
PreferenceList *PreferenceList `json:"auto_trigger_checks,omitempty"` // A list of auto trigger checks that can be set for a check suite in a repository.
AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository.
}
// CheckSuitePreferenceResults represents the results of the preference set operation.
@@ -361,17 +362,17 @@ type CheckSuitePreferenceResults struct {
Repository *Repository `json:"repository,omitempty"`
}
// PreferenceList represents a list of auto trigger checks for repository
// PreferenceList represents a list of auto trigger checks for repository
type PreferenceList struct {
AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository.
}
// SetCheckSuitePreferences changes the default automatic flow when creating check suites.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#set-preferences-for-check-suites-on-a-repository
func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opt CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/checks/suites/#update-repository-preferences-for-check-suites
func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opts CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/preferences", owner, repo)
req, err := s.client.NewRequest("PATCH", u, opt)
req, err := s.client.NewRequest("PATCH", u, opts)
if err != nil {
return nil, nil, err
}
@@ -396,9 +397,9 @@ type CreateCheckSuiteOptions struct {
// CreateCheckSuite manually creates a check suite for a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#create-a-check-suite
func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opt CreateCheckSuiteOptions) (*CheckSuite, *Response, error) {
func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opts CreateCheckSuiteOptions) (*CheckSuite, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites", owner, repo)
req, err := s.client.NewRequest("POST", u, opt)
req, err := s.client.NewRequest("POST", u, opts)
if err != nil {
return nil, nil, err
}
@@ -416,7 +417,7 @@ func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string
// ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository.
//
// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite
// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-a-check-suite
func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID)

View File

@@ -0,0 +1,117 @@
// Copyright 2020 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"
"strconv"
"strings"
)
// CodeScanningService handles communication with the code scanning related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/code-scanning/
type CodeScanningService service
type Alert struct {
RuleID *string `json:"rule_id,omitempty"`
RuleSeverity *string `json:"rule_severity,omitempty"`
RuleDescription *string `json:"rule_description,omitempty"`
Tool *string `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"`
}
// ID returns the ID associated with an alert. It is the number at the end of the security alert's URL.
func (a *Alert) ID() int64 {
if a == nil {
return 0
}
s := a.GetHTMLURL()
// Check for an ID to parse at the end of the url
if i := strings.LastIndex(s, "/"); i >= 0 {
s = s[i+1:]
}
// Return the alert ID as a 64-bit integer. Unable to convert or out of range returns 0.
id, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return 0
}
return id
}
// AlertListOptions specifies optional parameters to the CodeScanningService.ListAlerts
// method.
type AlertListOptions struct {
// State of the code scanning alerts to list. Set to closed to list only closed code scanning alerts. Default: open
State string `url:"state,omitempty"`
// Return code scanning alerts for a specific branch reference. The ref must be formatted as heads/<branch name>.
Ref string `url:"ref,omitempty"`
}
// ListAlertsForRepo lists code scanning alerts for a repository.
//
// Lists all open code scanning alerts for the default branch (usually master) and protected branches in a repository.
// You must use an access token with the security_events scope to use this endpoint. GitHub Apps must have the security_events
// read permission to use this endpoint.
//
// GitHub API docs: https://developer.github.com/v3/code-scanning/#list-code-scanning-alerts-for-a-repository
func (s *CodeScanningService) ListAlertsForRepo(ctx context.Context, owner, repo string, opts *AlertListOptions) ([]*Alert, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts", 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 alerts []*Alert
resp, err := s.client.Do(ctx, req, &alerts)
if err != nil {
return nil, resp, err
}
return alerts, resp, nil
}
// GetAlert gets a single code scanning alert for a repository.
//
// You must use an access token with the security_events scope to use this endpoint.
// GitHub Apps must have the security_events read permission to use this endpoint.
//
// The security alert_id is the number at the end of the security alert's URL.
//
// GitHub API docs: https://developer.github.com/v3/code-scanning/#get-a-code-scanning-alert
func (s *CodeScanningService) GetAlert(ctx context.Context, owner, repo string, id int64) (*Alert, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/code-scanning/alerts/%v", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
a := new(Alert)
resp, err := s.client.Do(ctx, req, a)
if err != nil {
return nil, resp, err
}
return a, resp, nil
}

View File

@@ -8,7 +8,7 @@ Package github provides a client for using the GitHub API.
Usage:
import "github.com/google/go-github/v24/github" // with go modules enabled (GO111MODULE=on or outside GOPATH)
import "github.com/google/go-github/v32/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

View File

@@ -40,6 +40,8 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
payload = &CreateEvent{}
case "DeleteEvent":
payload = &DeleteEvent{}
case "DeployKeyEvent":
payload = &DeployKeyEvent{}
case "DeploymentEvent":
payload = &DeploymentEvent{}
case "DeploymentStatusEvent":
@@ -66,12 +68,16 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
payload = &MemberEvent{}
case "MembershipEvent":
payload = &MembershipEvent{}
case "MetaEvent":
payload = &MetaEvent{}
case "MilestoneEvent":
payload = &MilestoneEvent{}
case "OrganizationEvent":
payload = &OrganizationEvent{}
case "OrgBlockEvent":
payload = &OrgBlockEvent{}
case "PackageEvent":
payload = &PackageEvent{}
case "PageBuildEvent":
payload = &PageBuildEvent{}
case "PingEvent":
@@ -96,14 +102,20 @@ func (e *Event) ParsePayload() (payload interface{}, err error) {
payload = &ReleaseEvent{}
case "RepositoryEvent":
payload = &RepositoryEvent{}
case "RepositoryDispatchEvent":
payload = &RepositoryDispatchEvent{}
case "RepositoryVulnerabilityAlertEvent":
payload = &RepositoryVulnerabilityAlertEvent{}
case "StarEvent":
payload = &StarEvent{}
case "StatusEvent":
payload = &StatusEvent{}
case "TeamEvent":
payload = &TeamEvent{}
case "TeamAddEvent":
payload = &TeamAddEvent{}
case "UserEvent":
payload = &UserEvent{}
case "WatchEvent":
payload = &WatchEvent{}
}

View File

@@ -7,19 +7,21 @@
package github
import "encoding/json"
// RequestedAction is included in a CheckRunEvent when a user has invoked an action,
// i.e. when the CheckRunEvent's Action field is "requested_action".
type RequestedAction struct {
Identifier string `json:"identifier"` // The integrator reference of the action requested by the user.
}
// CheckRunEvent is triggered when a check run is "created", "updated", or "re-requested".
// CheckRunEvent is triggered when a check run is "created", "updated", or "rerequested".
// The Webhook event name is "check_run".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checkrunevent
type CheckRunEvent struct {
CheckRun *CheckRun `json:"check_run,omitempty"`
// The action performed. Can be "created", "updated", "rerequested" or "requested_action".
// The action performed. Possible values are: "created", "updated", "rerequested" or "requested_action".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
@@ -32,13 +34,13 @@ type CheckRunEvent struct {
RequestedAction *RequestedAction `json:"requested_action,omitempty"` //
}
// CheckSuiteEvent is triggered when a check suite is "completed", "requested", or "re-requested".
// CheckSuiteEvent is triggered when a check suite is "completed", "requested", or "rerequested".
// The Webhook event name is "check_suite".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checksuiteevent
type CheckSuiteEvent struct {
CheckSuite *CheckSuite `json:"check_suite,omitempty"`
// The action performed. Can be "completed", "requested" or "re-requested".
// The action performed. Possible values are: "completed", "requested" or "rerequested".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
@@ -103,6 +105,19 @@ type DeleteEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// DeployKeyEvent is triggered when a deploy key is added or removed from a repository.
// The Webhook event name is "deploy_key".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploykeyevent
type DeployKeyEvent struct {
// Action is the action that was performed. Possible values are:
// "created" or "deleted".
Action *string `json:"action,omitempty"`
// The deploy key resource.
Key *Key `json:"key,omitempty"`
}
// DeploymentEvent represents a deployment.
// The Webhook event name is "deployment".
//
@@ -153,7 +168,7 @@ type ForkEvent struct {
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#githubappauthorizationevent
type GitHubAppAuthorizationEvent struct {
// The action performed. Can be "revoked".
// The action performed. Possible value is: "revoked".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
@@ -285,14 +300,17 @@ type IssueCommentEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// IssuesEvent is triggered when an issue is assigned, unassigned, labeled,
// unlabeled, opened, closed, or reopened.
// IssuesEvent is triggered when an issue is opened, edited, deleted, transferred,
// pinned, unpinned, closed, reopened, assigned, unassigned, labeled, unlabeled,
// locked, unlocked, milestoned, or demilestoned.
// The Webhook event name is "issues".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent
type IssuesEvent struct {
// Action is the action that was performed. Possible values are: "assigned",
// "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited".
// Action is the action that was performed. Possible values are: "opened",
// "edited", "deleted", "transferred", "pinned", "unpinned", "closed", "reopened",
// "assigned", "unassigned", "labeled", "unlabeled", "locked", "unlocked",
// "milestoned", or "demilestoned".
Action *string `json:"action,omitempty"`
Issue *Issue `json:"issue,omitempty"`
Assignee *User `json:"assignee,omitempty"`
@@ -376,6 +394,23 @@ type MembershipEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// MetaEvent is triggered when the webhook that this event is configured on is deleted.
// This event will only listen for changes to the particular hook the event is installed on.
// Therefore, it must be selected for each hook that you'd like to receive meta events for.
// The Webhook event name is "meta".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#metaevent
type MetaEvent struct {
// Action is the action that was performed. Possible value is: "deleted".
Action *string `json:"action,omitempty"`
// The ID of the modified webhook.
HookID *int64 `json:"hook_id,omitempty"`
// The modified webhook.
// 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"`
}
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted.
// The Webhook event name is "milestone".
//
@@ -394,17 +429,18 @@ type MilestoneEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// OrganizationEvent is triggered when a user is added, removed, or invited to an organization.
// OrganizationEvent is triggered when an organization is deleted and renamed, and when a user is added,
// removed, or invited to an organization.
// Events of this type are not visible in timelines. These events are only used to trigger organization hooks.
// Webhook event name is "organization".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent
type OrganizationEvent struct {
// Action is the action that was performed.
// Can be one of "member_added", "member_removed", or "member_invited".
// Possible values are: "deleted", "renamed", "member_added", "member_removed", or "member_invited".
Action *string `json:"action,omitempty"`
// Invitaion is the invitation for the user or email if the action is "member_invited".
// Invitation is the invitation for the user or email if the action is "member_invited".
Invitation *Invitation `json:"invitation,omitempty"`
// Membership is the membership between the user and the organization.
@@ -432,6 +468,22 @@ type OrgBlockEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// PackageEvent represents activity related to GitHub Packages.
// The Webhook event name is "package".
//
// This event is triggered when a GitHub Package is published or updated.
//
// GitHub API docs: https://developer.github.com/webhooks/event-payloads/#package
type PackageEvent struct {
// Action is the action that was performed.
// Can be "published" or "updated".
Action *string `json:"action,omitempty"`
Package *Package `json:"package,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
}
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
// successful or not.
// The Webhook event name is "page_build".
@@ -527,18 +579,20 @@ type PublicEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// PullRequestEvent is triggered when a pull request is assigned, unassigned,
// labeled, unlabeled, opened, closed, reopened, or synchronized.
// PullRequestEvent is triggered when a pull request is assigned, unassigned, labeled,
// unlabeled, opened, edited, closed, reopened, synchronize, ready_for_review,
// locked, unlocked, a pull request review is requested, or a review request is removed.
// The Webhook event name is "pull_request".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent
type PullRequestEvent struct {
// Action is the action that was performed. Possible values are:
// "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled",
// "opened", "closed", "reopened", "synchronize", "edited".
// If the action is "closed" and the merged key is false,
// the pull request was closed with unmerged commits. If the action is "closed"
// and the merged key is true, the pull request was merged.
// "opened", "edited", "closed", "ready_for_review", "locked", "unlocked", or "reopened".
// If the action is "closed" and the "merged" key is "false", the pull request was closed with unmerged commits.
// If the action is "closed" and the "merged" key is "true", the pull request was merged.
// While webhooks are also triggered when a pull request is synchronized, Events API timelines
// don't include pull request events with the "synchronize" action.
Action *string `json:"action,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Number *int `json:"number,omitempty"`
@@ -549,15 +603,22 @@ type PullRequestEvent struct {
// RequestedReviewer is populated in "review_requested", "review_request_removed" event deliveries.
// A request affecting multiple reviewers at once is split into multiple
// such event deliveries, each with a single, different RequestedReviewer.
RequestedReviewer *User `json:"requested_reviewer,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries.
RequestedReviewer *User `json:"requested_reviewer,omitempty"`
// In the event that a team is requested instead of a user, "requested_team" gets sent in place of
// "requested_user" with the same delivery behavior.
RequestedTeam *Team `json:"requested_team,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries.
// The following field is only present when the webhook is triggered on
// a repository belonging to an organization.
Organization *Organization `json:"organization,omitempty"`
// The following fields are only populated when the Action is "synchronize".
Before *string `json:"before,omitempty"`
After *string `json:"after,omitempty"`
}
// PullRequestReviewEvent is triggered when a review is submitted on a pull
@@ -604,13 +665,13 @@ type PullRequestReviewCommentEvent struct {
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent
type PushEvent struct {
PushID *int64 `json:"push_id,omitempty"`
Head *string `json:"head,omitempty"`
Ref *string `json:"ref,omitempty"`
Size *int `json:"size,omitempty"`
Commits []PushEventCommit `json:"commits,omitempty"`
Before *string `json:"before,omitempty"`
DistinctSize *int `json:"distinct_size,omitempty"`
PushID *int64 `json:"push_id,omitempty"`
Head *string `json:"head,omitempty"`
Ref *string `json:"ref,omitempty"`
Size *int `json:"size,omitempty"`
Commits []*HeadCommit `json:"commits,omitempty"`
Before *string `json:"before,omitempty"`
DistinctSize *int `json:"distinct_size,omitempty"`
// The following fields are only populated by Webhook events.
After *string `json:"after,omitempty"`
@@ -620,7 +681,7 @@ type PushEvent struct {
BaseRef *string `json:"base_ref,omitempty"`
Compare *string `json:"compare,omitempty"`
Repo *PushEventRepository `json:"repository,omitempty"`
HeadCommit *PushEventCommit `json:"head_commit,omitempty"`
HeadCommit *HeadCommit `json:"head_commit,omitempty"`
Pusher *User `json:"pusher,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
@@ -630,8 +691,8 @@ func (p PushEvent) String() string {
return Stringify(p)
}
// PushEventCommit represents a git commit in a GitHub PushEvent.
type PushEventCommit struct {
// HeadCommit represents a git commit in a GitHub PushEvent.
type HeadCommit struct {
Message *string `json:"message,omitempty"`
Author *CommitAuthor `json:"author,omitempty"`
URL *string `json:"url,omitempty"`
@@ -650,7 +711,7 @@ type PushEventCommit struct {
Modified []string `json:"modified,omitempty"`
}
func (p PushEventCommit) String() string {
func (p HeadCommit) String() string {
return Stringify(p)
}
@@ -668,6 +729,7 @@ type PushEventRepository struct {
PushedAt *Timestamp `json:"pushed_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Homepage *string `json:"homepage,omitempty"`
PullsURL *string `json:"pulls_url,omitempty"`
Size *int `json:"size,omitempty"`
StargazersCount *int `json:"stargazers_count,omitempty"`
WatchersCount *int `json:"watchers_count,omitempty"`
@@ -677,6 +739,8 @@ type PushEventRepository struct {
HasWiki *bool `json:"has_wiki,omitempty"`
HasPages *bool `json:"has_pages,omitempty"`
ForksCount *int `json:"forks_count,omitempty"`
Archived *bool `json:"archived,omitempty"`
Disabled *bool `json:"disabled,omitempty"`
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
@@ -697,12 +761,14 @@ type PushEventRepoOwner struct {
Email *string `json:"email,omitempty"`
}
// ReleaseEvent is triggered when a release is published.
// ReleaseEvent is triggered when a release is published, unpublished, created,
// edited, deleted, or prereleased.
// The Webhook event name is "release".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent
type ReleaseEvent struct {
// Action is the action that was performed. Possible value is: "published".
// Action is the action that was performed. Possible values are: "published", "unpublished",
// "created", "edited", "deleted", or "prereleased".
Action *string `json:"action,omitempty"`
Release *RepositoryRelease `json:"release,omitempty"`
@@ -712,7 +778,9 @@ type ReleaseEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// RepositoryEvent is triggered when a repository is created.
// RepositoryEvent is triggered when a repository is created, archived, unarchived,
// renamed, edited, transferred, made public, or made private. Organization hooks are
// also trigerred when a repository is deleted.
// The Webhook event name is "repository".
//
// Events of this type are not visible in timelines, they are only used to
@@ -720,8 +788,9 @@ type ReleaseEvent struct {
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent
type RepositoryEvent struct {
// Action is the action that was performed. Possible values are: "created", "deleted",
// "publicized", "privatized".
// Action is the action that was performed. Possible values are: "created",
// "deleted" (organization hooks only), "archived", "unarchived", "edited", "renamed",
// "transferred", "publicized", or "privatized".
Action *string `json:"action,omitempty"`
Repo *Repository `json:"repository,omitempty"`
@@ -731,11 +800,27 @@ type RepositoryEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// RepositoryDispatchEvent is triggered when a client sends a POST request to the repository dispatch event endpoint.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositorydispatchevent
type RepositoryDispatchEvent struct {
// Action is the event_type that submitted with the repository dispatch payload. Value can be any string.
Action *string `json:"action,omitempty"`
Branch *string `json:"branch,omitempty"`
ClientPayload json.RawMessage `json:"client_payload,omitempty"`
Repo *Repository `json:"repository,omitempty"`
// The following fields are only populated by Webhook events.
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryvulnerabilityalertevent
type RepositoryVulnerabilityAlertEvent struct {
// Action is the action that was performed. This can be: "create", "dismiss", "resolve".
// Action is the action that was performed. Possible values are: "create", "dismiss", "resolve".
Action *string `json:"action,omitempty"`
//The security alert of the vulnerable dependency.
@@ -750,6 +835,21 @@ type RepositoryVulnerabilityAlertEvent struct {
DismissReason *string `json:"dismiss_reason,omitempty"`
DismissedAt *Timestamp `json:"dismissed_at,omitempty"`
} `json:"alert,omitempty"`
//The repository of the vulnerable dependency.
Repository *Repository `json:"repository,omitempty"`
}
// StarEvent is triggered when a star is added or removed from a repository.
// The Webhook event name is "star".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#starevent
type StarEvent struct {
// Action is the action that was performed. Possible values are: "created" or "deleted".
Action *string `json:"action,omitempty"`
// StarredAt is the time the star was created. It will be null for the "deleted" action.
StarredAt *Timestamp `json:"starred_at,omitempty"`
}
// StatusEvent is triggered when the status of a Git commit changes.
@@ -815,6 +915,20 @@ type TeamAddEvent struct {
Installation *Installation `json:"installation,omitempty"`
}
// UserEvent is triggered when a user is created or deleted.
// The Webhook event name is "user".
//
// Only global webhooks can subscribe to this event type.
//
// GitHub API docs: https://developer.github.com/enterprise/v3/activity/events/types/#userevent-enterprise
type UserEvent struct {
User *User `json:"user,omitempty"`
// The action performed. Possible values are: "created" or "deleted".
Action *string `json:"action,omitempty"`
Enterprise *Enterprise `json:"enterprise,omitempty"`
Sender *User `json:"sender,omitempty"`
}
// WatchEvent is related to starring a repository, not watching. See this API
// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/
//

View File

@@ -96,15 +96,16 @@ type GistListOptions struct {
// is authenticated, it will returns all gists for the authenticated
// user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists
func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptions) ([]*Gist, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists-for-a-user
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists-for-the-authenticated-user
func (s *GistsService) List(ctx context.Context, user string, opts *GistListOptions) ([]*Gist, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/gists", user)
} else {
u = "gists"
}
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -125,9 +126,9 @@ func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptio
// ListAll lists all public gists.
//
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) {
u, err := addOptions("gists/public", opt)
// GitHub API docs: https://developer.github.com/v3/gists/#list-public-gists
func (s *GistsService) ListAll(ctx context.Context, opts *GistListOptions) ([]*Gist, *Response, error) {
u, err := addOptions("gists/public", opts)
if err != nil {
return nil, nil, err
}
@@ -148,9 +149,9 @@ func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gi
// ListStarred lists starred gists of authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists
func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) {
u, err := addOptions("gists/starred", opt)
// GitHub API docs: https://developer.github.com/v3/gists/#list-starred-gists
func (s *GistsService) ListStarred(ctx context.Context, opts *GistListOptions) ([]*Gist, *Response, error) {
u, err := addOptions("gists/starred", opts)
if err != nil {
return nil, nil, err
}
@@ -171,7 +172,7 @@ func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([
// Get a single gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-gist
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-gist
func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, error) {
u := fmt.Sprintf("gists/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
@@ -190,7 +191,7 @@ func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, er
// GetRevision gets a specific revision of a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-gist-revision
func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, *Response, error) {
u := fmt.Sprintf("gists/%v/%v", id, sha)
req, err := s.client.NewRequest("GET", u, nil)
@@ -228,7 +229,7 @@ func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response
// Edit a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#edit-a-gist
// GitHub API docs: https://developer.github.com/v3/gists/#update-a-gist
func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, *Response, error) {
u := fmt.Sprintf("gists/%v", id)
req, err := s.client.NewRequest("PATCH", u, gist)
@@ -248,9 +249,9 @@ func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist,
// ListCommits lists commits of a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-commits
func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOptions) ([]*GistCommit, *Response, error) {
func (s *GistsService) ListCommits(ctx context.Context, id string, opts *ListOptions) ([]*GistCommit, *Response, error) {
u := fmt.Sprintf("gists/%v/commits", id)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -341,8 +342,13 @@ func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, e
// ListForks lists forks of a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-forks
func (s *GistsService) ListForks(ctx context.Context, id string) ([]*GistFork, *Response, error) {
func (s *GistsService) ListForks(ctx context.Context, id string, opts *ListOptions) ([]*GistFork, *Response, error) {
u := fmt.Sprintf("gists/%v/forks", 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

View File

@@ -26,10 +26,10 @@ func (g GistComment) String() string {
// ListComments lists all comments for a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist
func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *ListOptions) ([]*GistComment, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-gist-comments
func (s *GistsService) ListComments(ctx context.Context, gistID string, opts *ListOptions) ([]*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments", gistID)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -50,7 +50,7 @@ func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *Lis
// GetComment retrieves a single comment from a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment
// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-gist-comment
func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("GET", u, nil)
@@ -69,7 +69,7 @@ func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID
// CreateComment creates a comment for a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-comment
// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-gist-comment
func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment *GistComment) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments", gistID)
req, err := s.client.NewRequest("POST", u, comment)
@@ -88,7 +88,7 @@ func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment
// EditComment edits an existing gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment
// GitHub API docs: https://developer.github.com/v3/gists/comments/#update-a-gist-comment
func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("PATCH", u, comment)
@@ -107,7 +107,7 @@ func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID
// DeleteComment deletes a gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment
// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-gist-comment
func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("DELETE", u, nil)

View File

@@ -6,9 +6,14 @@
package github
import (
"bytes"
"context"
"errors"
"fmt"
"strings"
"time"
"golang.org/x/crypto/openpgp"
)
// SignatureVerification represents GPG signature verification.
@@ -26,7 +31,7 @@ type Commit struct {
Committer *CommitAuthor `json:"committer,omitempty"`
Message *string `json:"message,omitempty"`
Tree *Tree `json:"tree,omitempty"`
Parents []Commit `json:"parents,omitempty"`
Parents []*Commit `json:"parents,omitempty"`
Stats *CommitStats `json:"stats,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
URL *string `json:"url,omitempty"`
@@ -37,6 +42,11 @@ type Commit struct {
// is only populated for requests that fetch GitHub data like
// Pulls.ListCommits, Repositories.ListCommits, etc.
CommentCount *int `json:"comment_count,omitempty"`
// SigningKey denotes a key to sign the commit with. If not nil this key will
// be used to sign the commit. The private key must be present and already
// decrypted. Ignored if Verification.Signature is defined.
SigningKey *openpgp.Entity `json:"-"`
}
func (c Commit) String() string {
@@ -116,6 +126,13 @@ func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string
if commit.Tree != nil {
body.Tree = commit.Tree.SHA
}
if commit.SigningKey != nil {
signature, err := createSignature(commit.SigningKey, body)
if err != nil {
return nil, nil, err
}
body.Signature = &signature
}
if commit.Verification != nil {
body.Signature = commit.Verification.Signature
}
@@ -133,3 +150,51 @@ func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string
return c, resp, nil
}
func createSignature(signingKey *openpgp.Entity, commit *createCommit) (string, error) {
if signingKey == nil || commit == nil {
return "", errors.New("createSignature: invalid parameters")
}
message, err := createSignatureMessage(commit)
if err != nil {
return "", err
}
writer := new(bytes.Buffer)
reader := bytes.NewReader([]byte(message))
if err := openpgp.ArmoredDetachSign(writer, signingKey, reader, nil); err != nil {
return "", err
}
return writer.String(), nil
}
func createSignatureMessage(commit *createCommit) (string, error) {
if commit == nil || commit.Message == nil || *commit.Message == "" || commit.Author == nil {
return "", errors.New("createSignatureMessage: invalid parameters")
}
var message []string
if commit.Tree != nil {
message = append(message, fmt.Sprintf("tree %s", *commit.Tree))
}
for _, parent := range commit.Parents {
message = append(message, fmt.Sprintf("parent %s", parent))
}
message = append(message, fmt.Sprintf("author %s <%s> %d %s", commit.Author.GetName(), commit.Author.GetEmail(), commit.Author.GetDate().Unix(), commit.Author.GetDate().Format("-0700")))
committer := commit.Committer
if committer == nil {
committer = commit.Author
}
// There needs to be a double newline after committer
message = append(message, fmt.Sprintf("committer %s <%s> %d %s\n", committer.GetName(), committer.GetEmail(), committer.GetDate().Unix(), committer.GetDate().Format("-0700")))
message = append(message, *commit.Message)
return strings.Join(message, "\n"), nil
}

View File

@@ -7,8 +7,6 @@ package github
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
"strings"
@@ -49,16 +47,12 @@ type updateRefRequest struct {
Force *bool `json:"force"`
}
// GetRef fetches a single Reference object for a given Git ref.
// If there is no exact match, GetRef will return an error.
//
// Note: The GitHub API can return multiple matches.
// If you wish to use this functionality please use the GetRefs() method.
// GetRef fetches a single reference in a repository.
//
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference
func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) {
ref = strings.TrimPrefix(ref, "refs/")
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref))
u := fmt.Sprintf("repos/%v/%v/git/ref/%v", owner, repo, refURLEscape(ref))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
@@ -66,80 +60,42 @@ func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref
r := new(Reference)
resp, err := s.client.Do(ctx, req, r)
if _, ok := err.(*json.UnmarshalTypeError); ok {
// Multiple refs, means there wasn't an exact match.
return nil, resp, errors.New("no exact match found for this ref")
} else if err != nil {
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// GetRefs fetches a slice of Reference objects for a given Git ref.
// If there is an exact match, only that ref is returned.
// If there is no exact match, GitHub returns all refs that start with ref.
// If returned error is nil, there will be at least 1 ref returned.
// For example:
//
// "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned.
// "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref.
// "heads/notexist" -> [] // Returns an error.
//
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference
func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) {
ref = strings.TrimPrefix(ref, "refs/")
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref))
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
// refURLEscape escapes every path segment of the given ref. Those must
// not contain escaped "/" - as "%2F" - or github will not recognize it.
func refURLEscape(ref string) string {
parts := strings.Split(ref, "/")
for i, s := range parts {
parts[i] = url.PathEscape(s)
}
var rawJSON json.RawMessage
resp, err := s.client.Do(ctx, req, &rawJSON)
if err != nil {
return nil, resp, err
}
// Prioritize the most common case: a single returned ref.
r := new(Reference)
singleUnmarshalError := json.Unmarshal(rawJSON, r)
if singleUnmarshalError == nil {
return []*Reference{r}, resp, nil
}
// Attempt to unmarshal multiple refs.
var rs []*Reference
multipleUnmarshalError := json.Unmarshal(rawJSON, &rs)
if multipleUnmarshalError == nil {
if len(rs) == 0 {
return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0")
}
return rs, resp, nil
}
return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError)
return strings.Join(parts, "/")
}
// ReferenceListOptions specifies optional parameters to the
// GitService.ListRefs method.
// GitService.ListMatchingRefs method.
type ReferenceListOptions struct {
Type string `url:"-"`
Ref string `url:"-"`
ListOptions
}
// ListRefs lists all refs in a repository.
// ListMatchingRefs lists references in a repository that match a supplied ref.
// Use an empty ref to list all references.
//
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references
func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) {
var u string
if opt != nil && opt.Type != "" {
u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type)
} else {
u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo)
// GitHub API docs: https://developer.github.com/v3/git/refs/#list-matching-references
func (s *GitService) ListMatchingRefs(ctx context.Context, owner, repo string, opts *ReferenceListOptions) ([]*Reference, *Response, error) {
var ref string
if opts != nil {
ref = strings.TrimPrefix(opts.Ref, "refs/")
}
u, err := addOptions(u, opt)
u := fmt.Sprintf("repos/%v/%v/git/matching-refs/%v", owner, repo, refURLEscape(ref))
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -209,7 +165,7 @@ func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, r
// GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference
func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) {
ref = strings.TrimPrefix(ref, "refs/")
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref))
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refURLEscape(ref))
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err

View File

@@ -7,13 +7,14 @@ package github
import (
"context"
"encoding/json"
"fmt"
)
// Tree represents a GitHub tree.
type Tree struct {
SHA *string `json:"sha,omitempty"`
Entries []TreeEntry `json:"tree,omitempty"`
SHA *string `json:"sha,omitempty"`
Entries []*TreeEntry `json:"tree,omitempty"`
// Truncated is true if the number of items in the tree
// exceeded GitHub's maximum limit and the Entries were truncated
@@ -43,6 +44,53 @@ func (t TreeEntry) String() string {
return Stringify(t)
}
// treeEntryWithFileDelete is used internally to delete a file whose
// Content and SHA fields are empty. It does this by removing the "omitempty"
// tag modifier on the SHA field which causes the GitHub API to receive
// {"sha":null} and thereby delete the file.
type treeEntryWithFileDelete struct {
SHA *string `json:"sha"`
Path *string `json:"path,omitempty"`
Mode *string `json:"mode,omitempty"`
Type *string `json:"type,omitempty"`
Size *int `json:"size,omitempty"`
Content *string `json:"content,omitempty"`
URL *string `json:"url,omitempty"`
}
func (t *TreeEntry) MarshalJSON() ([]byte, error) {
if t.SHA == nil && t.Content == nil {
return json.Marshal(struct {
SHA *string `json:"sha"`
Path *string `json:"path,omitempty"`
Mode *string `json:"mode,omitempty"`
Type *string `json:"type,omitempty"`
}{
nil,
t.Path,
t.Mode,
t.Type,
})
}
return json.Marshal(struct {
SHA *string `json:"sha,omitempty"`
Path *string `json:"path,omitempty"`
Mode *string `json:"mode,omitempty"`
Type *string `json:"type,omitempty"`
Size *int `json:"size,omitempty"`
Content *string `json:"content,omitempty"`
URL *string `json:"url,omitempty"`
}{
SHA: t.SHA,
Path: t.Path,
Mode: t.Mode,
Type: t.Type,
Size: t.Size,
Content: t.Content,
URL: t.URL,
})
}
// GetTree fetches the Tree object for a given sha hash from a repository.
//
// GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree
@@ -68,8 +116,8 @@ func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha
// createTree represents the body of a CreateTree request.
type createTree struct {
BaseTree string `json:"base_tree,omitempty"`
Entries []TreeEntry `json:"tree"`
BaseTree string `json:"base_tree,omitempty"`
Entries []interface{} `json:"tree"`
}
// CreateTree creates a new tree in a repository. If both a tree and a nested
@@ -77,12 +125,27 @@ type createTree struct {
// that tree with the new path contents and write a new tree out.
//
// GitHub API docs: https://developer.github.com/v3/git/trees/#create-a-tree
func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) {
func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []*TreeEntry) (*Tree, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo)
newEntries := make([]interface{}, 0, len(entries))
for _, entry := range entries {
if entry.Content == nil && entry.SHA == nil {
newEntries = append(newEntries, treeEntryWithFileDelete{
Path: entry.Path,
Mode: entry.Mode,
Type: entry.Type,
Size: entry.Size,
URL: entry.URL,
})
continue
}
newEntries = append(newEntries, entry)
}
body := &createTree{
BaseTree: baseTree,
Entries: entries,
Entries: newEntries,
}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {

View File

@@ -4,6 +4,7 @@
// license that can be found in the LICENSE file.
//go:generate go run gen-accessors.go
//go:generate go run gen-stringify-test.go
package github
@@ -57,18 +58,12 @@ const (
// https://developer.github.com/changes/2018-10-16-deployments-environments-states-and-auto-inactive-updates/
mediaTypeExpandDeploymentStatusPreview = "application/vnd.github.flash-preview+json"
// https://developer.github.com/changes/2016-02-19-source-import-preview-api/
mediaTypeImportPreview = "application/vnd.github.barred-rock-preview"
// https://developer.github.com/changes/2016-05-12-reactions-api-preview/
mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
// https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/
mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json"
// https://developer.github.com/changes/2016-09-14-projects-api/
mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json"
@@ -90,27 +85,9 @@ const (
// https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/
mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json"
// https://developer.github.com/changes/2017-08-30-preview-nested-teams/
mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json"
// https://developer.github.com/changes/2017-11-09-repository-transfer-api-preview/
mediaTypeRepositoryTransferPreview = "application/vnd.github.nightshade-preview+json"
// https://developer.github.com/changes/2018-01-25-organization-invitation-api-preview/
mediaTypeOrganizationInvitationPreview = "application/vnd.github.dazzler-preview+json"
// https://developer.github.com/changes/2018-03-16-protected-branches-required-approving-reviews/
mediaTypeRequiredApprovingReviewsPreview = "application/vnd.github.luke-cage-preview+json"
// https://developer.github.com/changes/2018-02-22-label-description-search-preview/
mediaTypeLabelDescriptionSearchPreview = "application/vnd.github.symmetra-preview+json"
// https://developer.github.com/changes/2018-02-07-team-discussions-api/
mediaTypeTeamDiscussionsPreview = "application/vnd.github.echo-preview+json"
// https://developer.github.com/changes/2018-03-21-hovercard-api-preview/
mediaTypeHovercardPreview = "application/vnd.github.hagar-preview+json"
// https://developer.github.com/changes/2018-01-10-lock-reason-api-preview/
mediaTypeLockReasonPreview = "application/vnd.github.sailor-v-preview+json"
@@ -129,8 +106,35 @@ const (
// https://developer.github.com/changes/2018-12-18-interactions-preview/
mediaTypeInteractionRestrictionsPreview = "application/vnd.github.sombra-preview+json"
// https://developer.github.com/changes/2019-02-14-draft-pull-requests/
mediaTypeDraftPreview = "application/vnd.github.shadow-cat-preview+json"
// https://developer.github.com/changes/2019-03-14-enabling-disabling-pages/
mediaTypeEnablePagesAPIPreview = "application/vnd.github.switcheroo-preview+json"
// https://developer.github.com/changes/2019-04-24-vulnerability-alerts/
mediaTypeRequiredVulnerabilityAlertsPreview = "application/vnd.github.dorian-preview+json"
// https://developer.github.com/changes/2019-06-04-automated-security-fixes/
mediaTypeRequiredAutomatedSecurityFixesPreview = "application/vnd.github.london-preview+json"
// https://developer.github.com/changes/2019-05-29-update-branch-api/
mediaTypeUpdatePullRequestBranchPreview = "application/vnd.github.lydian-preview+json"
// https://developer.github.com/changes/2019-04-11-pulls-branches-for-commit/
mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json"
// https://developer.github.com/v3/previews/#repository-creation-permissions
mediaTypeMemberAllowedRepoCreationTypePreview = "application/vnd.github.surtur-preview+json"
// https://developer.github.com/v3/previews/#create-and-use-repository-templates
mediaTypeRepositoryTemplatePreview = "application/vnd.github.baptiste-preview+json"
// https://developer.github.com/changes/2019-10-03-multi-line-comments/
mediaTypeMultiLineCommentsPreview = "application/vnd.github.comfort-fade-preview+json"
// https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/
mediaTypeOAuthAppPreview = "application/vnd.github.doctor-strange-preview+json"
// https://developer.github.com/changes/2019-12-03-internal-visibility-changes/
mediaTypeRepositoryVisibilityPreview = "application/vnd.github.nebula-preview+json"
)
// A Client manages communication with the GitHub API.
@@ -155,11 +159,13 @@ type Client struct {
common service // Reuse a single struct instead of allocating one for each service on the heap.
// Services used for talking to different parts of the GitHub API.
Actions *ActionsService
Activity *ActivityService
Admin *AdminService
Apps *AppsService
Authorizations *AuthorizationsService
Checks *ChecksService
CodeScanning *CodeScanningService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
@@ -183,7 +189,7 @@ type service struct {
}
// ListOptions specifies the optional parameters to various List methods that
// support pagination.
// support offset pagination.
type ListOptions struct {
// For paginated result sets, page of results to retrieve.
Page int `url:"page,omitempty"`
@@ -192,6 +198,16 @@ type ListOptions struct {
PerPage int `url:"per_page,omitempty"`
}
// ListCursorOptions specifies the optional parameters to various List methods that
// support cursor pagination.
type ListCursorOptions struct {
// For paginated result sets, page of results to retrieve.
Page string `url:"page,omitempty"`
// For paginated result sets, the number of results to include per page.
PerPage int `url:"per_page,omitempty"`
}
// UploadOptions specifies the parameters to methods that support uploads.
type UploadOptions struct {
Name string `url:"name,omitempty"`
@@ -215,10 +231,10 @@ type RawOptions struct {
Type RawType
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// addOptions adds the parameters in opts as URL query parameters to s. opts
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
v := reflect.ValueOf(opt)
func addOptions(s string, opts interface{}) (string, error) {
v := reflect.ValueOf(opts)
if v.Kind() == reflect.Ptr && v.IsNil() {
return s, nil
}
@@ -228,7 +244,7 @@ func addOptions(s string, opt interface{}) (string, error) {
return s, err
}
qs, err := query.Values(opt)
qs, err := query.Values(opts)
if err != nil {
return s, err
}
@@ -238,23 +254,25 @@ func addOptions(s string, opt interface{}) (string, error) {
}
// NewClient returns a new GitHub API client. If a nil httpClient is
// provided, http.DefaultClient will be used. To use API methods which require
// provided, a new http.Client will be used. To use API methods which require
// authentication, provide an http.Client that will perform the authentication
// for you (such as that provided by the golang.org/x/oauth2 library).
func NewClient(httpClient *http.Client) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
httpClient = &http.Client{}
}
baseURL, _ := url.Parse(defaultBaseURL)
uploadURL, _ := url.Parse(uploadBaseURL)
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
c.common.client = c
c.Actions = (*ActionsService)(&c.common)
c.Activity = (*ActivityService)(&c.common)
c.Admin = (*AdminService)(&c.common)
c.Apps = (*AppsService)(&c.common)
c.Authorizations = (*AuthorizationsService)(&c.common)
c.Checks = (*ChecksService)(&c.common)
c.CodeScanning = (*CodeScanningService)(&c.common)
c.Gists = (*GistsService)(&c.common)
c.Git = (*GitService)(&c.common)
c.Gitignores = (*GitignoresService)(&c.common)
@@ -275,13 +293,18 @@ func NewClient(httpClient *http.Client) *Client {
}
// NewEnterpriseClient returns a new GitHub API client with provided
// base URL and upload URL (often the same URL).
// If either URL does not have a trailing slash, one is added automatically.
// If a nil httpClient is provided, http.DefaultClient will be used.
// base URL and upload URL (often is your GitHub Enterprise hostname).
// If the base URL does not have the suffix "/api/v3/", it will be added automatically.
// If the upload URL does not have the suffix "/api/uploads", it will be added automatically.
// If a nil httpClient is provided, a new http.Client will be used.
//
// Note that NewEnterpriseClient is a convenience helper only;
// its behavior is equivalent to using NewClient, followed by setting
// the BaseURL and UploadURL fields.
//
// Another important thing is that by default, the GitHub Enterprise URL format
// should be http(s)://[hostname]/api/v3/ or you will always receive the 406 status code.
// The upload URL format should be http(s)://[hostname]/api/uploads/.
func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) {
baseEndpoint, err := url.Parse(baseURL)
if err != nil {
@@ -290,6 +313,9 @@ func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*C
if !strings.HasSuffix(baseEndpoint.Path, "/") {
baseEndpoint.Path += "/"
}
if !strings.HasSuffix(baseEndpoint.Path, "/api/v3/") {
baseEndpoint.Path += "api/v3/"
}
uploadEndpoint, err := url.Parse(uploadURL)
if err != nil {
@@ -298,6 +324,9 @@ func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*C
if !strings.HasSuffix(uploadEndpoint.Path, "/") {
uploadEndpoint.Path += "/"
}
if !strings.HasSuffix(uploadEndpoint.Path, "/api/uploads/") {
uploadEndpoint.Path += "api/uploads/"
}
c := NewClient(httpClient)
c.BaseURL = baseEndpoint
@@ -321,7 +350,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
buf = &bytes.Buffer{}
enc := json.NewEncoder(buf)
enc.SetEscapeHTML(false)
err := enc.Encode(body)
@@ -382,12 +411,27 @@ type Response struct {
// results. Any or all of these may be set to the zero value for
// responses that are not part of a paginated set, or for which there
// are no additional pages.
//
// These fields support what is called "offset pagination" and should
// be used with the ListOptions struct.
NextPage int
PrevPage int
FirstPage int
LastPage int
// Additionally, some APIs support "cursor pagination" instead of offset.
// This means that a token points directly to the next record which
// can lead to O(1) performance compared to O(n) performance provided
// by offset pagination.
//
// For APIs that support cursor pagination (such as
// TeamsService.ListIDPGroupsInOrganization), the following field
// will be populated to point to the next page.
//
// To use this token, set ListCursorOptions.Page to this value before
// calling the endpoint again.
NextPageToken string
// Explicitly specify the Rate type so Rate's String() receiver doesn't
// propagate to Response.
Rate Rate
@@ -432,7 +476,9 @@ func (r *Response) populatePageValues() {
for _, segment := range segments[1:] {
switch strings.TrimSpace(segment) {
case `rel="next"`:
r.NextPage, _ = strconv.Atoi(page)
if r.NextPage, err = strconv.Atoi(page); err != nil {
r.NextPageToken = page
}
case `rel="prev"`:
r.PrevPage, _ = strconv.Atoi(page)
case `rel="first"`:
@@ -470,9 +516,12 @@ func parseRate(r *http.Response) Rate {
// first decode it. If rate limit is exceeded and reset time is in the future,
// Do returns *RateLimitError immediately without making a network API call.
//
// The provided ctx must be non-nil. If it is canceled or times out,
// The provided ctx must be non-nil, if it is nil an error is returned. If it is canceled or times out,
// ctx.Err() will be returned.
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
if ctx == nil {
return nil, errors.New("context must be non-nil")
}
req = withContext(ctx, req)
rateLimitCategory := category(req.URL.Path)
@@ -614,7 +663,7 @@ type TwoFactorAuthError ErrorResponse
func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() }
// RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit
// remaining value of 0, and error message starts with "API rate limit exceeded for ".
// remaining value of 0.
type RateLimitError struct {
Rate Rate // Rate specifies last known rate limit for the client
Response *http.Response // HTTP response that caused this error
@@ -624,7 +673,7 @@ type RateLimitError struct {
func (r *RateLimitError) Error() string {
return fmt.Sprintf("%v %v: %d %v %v",
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
r.Response.StatusCode, r.Message, formatRateReset(r.Rate.Reset.Time.Sub(time.Now())))
r.Response.StatusCode, r.Message, formatRateReset(time.Until(r.Rate.Reset.Time)))
}
// AcceptedError occurs when GitHub returns 202 Accepted response with an
@@ -690,6 +739,10 @@ These are the possible validation error codes:
some resources return this (e.g. github.User.CreateKey()), additional
information is set in the Message field of the Error
GitHub error responses structure are often undocumented and inconsistent.
Sometimes error is just a simple string (Issue #540).
In such cases, Message represents an error message as a workaround.
GitHub API docs: https://developer.github.com/v3/#client-errors
*/
type Error struct {
@@ -704,12 +757,19 @@ func (e *Error) Error() string {
e.Code, e.Field, e.Resource)
}
func (e *Error) UnmarshalJSON(data []byte) error {
type aliasError Error // avoid infinite recursion by using type alias.
if err := json.Unmarshal(data, (*aliasError)(e)); err != nil {
return json.Unmarshal(data, &e.Message) // data can be json string.
}
return nil
}
// CheckResponse checks the API response for errors, and returns them if
// present. A response is considered an error if it has a status code outside
// the 200 range or equal to 202 Accepted.
// API error responses are expected to have either no response
// body, or a JSON response body that maps to ErrorResponse. Any other
// response body will be silently ignored.
// API error responses are expected to have response
// body, and a JSON response body that maps to ErrorResponse.
//
// The error type will be *RateLimitError for rate limit exceeded errors,
// *AcceptedError for 202 Accepted status codes,
@@ -726,10 +786,14 @@ func CheckResponse(r *http.Response) error {
if err == nil && data != nil {
json.Unmarshal(data, errorResponse)
}
// Re-populate error response body because GitHub error responses are often
// undocumented and inconsistent.
// Issue #1136, #540.
r.Body = ioutil.NopCloser(bytes.NewBuffer(data))
switch {
case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"):
return (*TwoFactorAuthError)(errorResponse)
case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "):
case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0":
return &RateLimitError{
Rate: parseRate(r),
Response: errorResponse.Response,
@@ -858,6 +922,24 @@ func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error)
return response.Resources, resp, nil
}
func setCredentialsAsHeaders(req *http.Request, id, secret string) *http.Request {
// To set extra headers, we must make a copy of the Request so
// that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper.
//
// Since we are going to modify only req.Header here, we only need a deep copy
// of req.Header.
convertedRequest := new(http.Request)
*convertedRequest = *req
convertedRequest.Header = make(http.Header, len(req.Header))
for k, s := range req.Header {
convertedRequest.Header[k] = append([]string(nil), s...)
}
convertedRequest.SetBasicAuth(id, secret)
return convertedRequest
}
/*
UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls
that need to use a higher rate limit associated with your OAuth application.
@@ -868,8 +950,8 @@ that need to use a higher rate limit associated with your OAuth application.
}
client := github.NewClient(t.Client())
This will append the querystring params client_id=xxx&client_secret=yyy to all
requests.
This will add the client id and secret as a base64-encoded string in the format
ClientID:ClientSecret and apply it as an "Authorization": "Basic" header.
See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for
more information.
@@ -898,22 +980,7 @@ func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*htt
return nil, errors.New("t.ClientSecret is empty")
}
// To set extra querystring params, we must make a copy of the Request so
// that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper.
//
// Since we are going to modify only req.URL here, we only need a deep copy
// of req.URL.
req2 := new(http.Request)
*req2 = *req
req2.URL = new(url.URL)
*req2.URL = *req.URL
q := req2.URL.Query()
q.Set("client_id", t.ClientID)
q.Set("client_secret", t.ClientSecret)
req2.URL.RawQuery = q.Encode()
req2 := setCredentialsAsHeaders(req, t.ClientID, t.ClientSecret)
// Make the HTTP request.
return t.transport().RoundTrip(req2)
}
@@ -947,20 +1014,7 @@ type BasicAuthTransport struct {
// RoundTrip implements the RoundTripper interface.
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
// To set extra headers, we must make a copy of the Request so
// that we don't modify the Request we were given. This is required by the
// specification of http.RoundTripper.
//
// Since we are going to modify only req.Header here, we only need a deep copy
// of req.Header.
req2 := new(http.Request)
*req2 = *req
req2.Header = make(http.Header, len(req.Header))
for k, s := range req.Header {
req2.Header[k] = append([]string(nil), s...)
}
req2.SetBasicAuth(t.Username, t.Password)
req2 := setCredentialsAsHeaders(req, t.Username, t.Password)
if t.OTP != "" {
req2.Header.Set(headerOTP, t.OTP)
}

View File

@@ -29,7 +29,7 @@ func (g Gitignore) String() string {
// List all available Gitignore templates.
//
// GitHub API docs: https://developer.github.com/v3/gitignore/#listing-available-templates
func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error) {
func (s *GitignoresService) List(ctx context.Context) ([]string, *Response, error) {
req, err := s.client.NewRequest("GET", "gitignore/templates", nil)
if err != nil {
return nil, nil, err
@@ -46,8 +46,8 @@ func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error
// Get a Gitignore by name.
//
// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-single-template
func (s GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-gitignore-template
func (s *GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) {
u := fmt.Sprintf("gitignore/templates/%v", name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {

View File

@@ -39,7 +39,7 @@ func (s *InteractionsService) GetRestrictionsForOrg(ctx context.Context, organiz
// in public repositories for the given organization.
// Possible values are: "existing_users", "contributors_only", "collaborators_only".
//
// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#add-or-update-interaction-restrictions-for-an-organization
// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#set-interaction-restrictions-for-an-organization
func (s *InteractionsService) UpdateRestrictionsForOrg(ctx context.Context, organization, limit string) (*InteractionRestriction, *Response, error) {
u := fmt.Sprintf("orgs/%v/interaction-limits", organization)

View File

@@ -39,7 +39,7 @@ func (s *InteractionsService) GetRestrictionsForRepo(ctx context.Context, owner,
// for the given repository.
// Possible values are: "existing_users", "contributors_only", "collaborators_only".
//
// GitHub API docs: https://developer.github.com/v3/interactions/repos/#add-or-update-interaction-restrictions-for-a-repository
// GitHub API docs: https://developer.github.com/v3/interactions/repos/#set-interaction-restrictions-for-a-repository
func (s *InteractionsService) UpdateRestrictionsForRepo(ctx context.Context, owner, repo, limit string) (*InteractionRestriction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo)

View File

@@ -26,36 +26,37 @@ type IssuesService service
// this is an issue, and if PullRequestLinks is not nil, this is a pull request.
// The IsPullRequest helper method can be used to check that.
type Issue struct {
ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Locked *bool `json:"locked,omitempty"`
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Labels []Label `json:"labels,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Comments *int `json:"comments,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedBy *User `json:"closed_by,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
LabelsURL *string `json:"labels_url,omitempty"`
RepositoryURL *string `json:"repository_url,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`
Repository *Repository `json:"repository,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
NodeID *string `json:"node_id,omitempty"`
ID *int64 `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Locked *bool `json:"locked,omitempty"`
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
AuthorAssociation *string `json:"author_association,omitempty"`
User *User `json:"user,omitempty"`
Labels []*Label `json:"labels,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Comments *int `json:"comments,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedBy *User `json:"closed_by,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
LabelsURL *string `json:"labels_url,omitempty"`
RepositoryURL *string `json:"repository_url,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"`
Repository *Repository `json:"repository,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
NodeID *string `json:"node_id,omitempty"`
// TextMatches is only populated from search results that request text matches
// See: search.go and https://developer.github.com/v3/search/#text-match-metadata
TextMatches []TextMatch `json:"text_matches,omitempty"`
TextMatches []*TextMatch `json:"text_matches,omitempty"`
// ActiveLockReason is populated only when LockReason is provided while locking the issue.
// Possible values are: "off-topic", "too heated", "resolved", and "spam".
@@ -128,28 +129,29 @@ type PullRequestLinks struct {
// organization repositories; if false, list only owned and member
// repositories.
//
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-assigned-to-the-authenticated-user
// GitHub API docs: https://developer.github.com/v3/issues/#list-user-account-issues-assigned-to-the-authenticated-user
func (s *IssuesService) List(ctx context.Context, all bool, opts *IssueListOptions) ([]*Issue, *Response, error) {
var u string
if all {
u = "issues"
} else {
u = "user/issues"
}
return s.listIssues(ctx, u, opt)
return s.listIssues(ctx, u, opts)
}
// ListByOrg fetches the issues in the specified organization for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues
func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/issues/#list-organization-issues-assigned-to-the-authenticated-user
func (s *IssuesService) ListByOrg(ctx context.Context, org string, opts *IssueListOptions) ([]*Issue, *Response, error) {
u := fmt.Sprintf("orgs/%v/issues", org)
return s.listIssues(ctx, u, opt)
return s.listIssues(ctx, u, opts)
}
func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) {
u, err := addOptions(u, opt)
func (s *IssuesService) listIssues(ctx context.Context, u string, opts *IssueListOptions) ([]*Issue, *Response, error) {
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -160,7 +162,7 @@ func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueList
}
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var issues []*Issue
@@ -214,10 +216,10 @@ type IssueListByRepoOptions struct {
// ListByRepo lists the issues for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository
func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) {
// GitHub API docs: https://developer.github.com/v3/issues/#list-repository-issues
func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opts *IssueListByRepoOptions) ([]*Issue, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -228,7 +230,7 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin
}
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview}
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeIntegrationPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
var issues []*Issue
@@ -242,7 +244,7 @@ func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo strin
// Get a single issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue
// GitHub API docs: https://developer.github.com/v3/issues/#get-an-issue
func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
@@ -251,7 +253,7 @@ func (s *IssuesService) Get(ctx context.Context, owner string, repo string, numb
}
// TODO: remove custom Accept headers when APIs fully launch.
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview}
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLockReasonPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
issue := new(Issue)
@@ -273,9 +275,6 @@ func (s *IssuesService) Create(ctx context.Context, owner string, repo string, i
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
@@ -287,7 +286,7 @@ func (s *IssuesService) Create(ctx context.Context, owner string, repo string, i
// Edit an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue
// GitHub API docs: https://developer.github.com/v3/issues/#update-an-issue
func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number)
req, err := s.client.NewRequest("PATCH", u, issue)
@@ -295,9 +294,6 @@ func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, num
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview)
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
@@ -319,14 +315,14 @@ type LockIssueOptions struct {
// Lock an issue's conversation.
//
// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue
func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) {
func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opts *LockIssueOptions) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
req, err := s.client.NewRequest("PUT", u, opt)
req, err := s.client.NewRequest("PUT", u, opts)
if err != nil {
return nil, err
}
if opt != nil {
if opts != nil {
req.Header.Set("Accept", mediaTypeLockReasonPreview)
}

View File

@@ -14,9 +14,9 @@ import (
// which issues may be assigned.
//
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#list-assignees
func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) {
func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opts *ListOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo)
u, err := addOptions(u, opt)
u, err := addOptions(u, opts)
if err != nil {
return nil, nil, err
}
@@ -36,7 +36,7 @@ func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, o
// IsAssignee checks if a user is an assignee for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-if-a-user-can-be-assigned
func (s *IssuesService) IsAssignee(ctx context.Context, owner, repo, user string) (bool, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user)
req, err := s.client.NewRequest("GET", u, nil)

Some files were not shown because too many files have changed in this diff Show More