38 Commits

Author SHA1 Message Date
e543d93265 add proper title 2022-02-20 02:44:50 +05:30
45d9fcc84d remove tests and fakes [ci skip] 2022-02-20 02:13:06 +05:30
c857b66f5e update README [skip ci] 2022-02-20 01:35:05 +05:30
259f477b6e atleast basic shit (barely) works now 2022-02-20 01:30:05 +05:30
39205467a1 im (a little) retarded 2022-02-19 21:56:04 +05:30
86c3472723 ci: init 2022-02-19 21:43:22 +05:30
b1b0e4bf2a some more renaming 2022-02-19 21:39:08 +05:30
83153b190b get program to compile (Still a WIP) 2022-02-19 21:34:15 +05:30
03c02caefb remove ProjectFile stuff 2022-02-19 18:09:51 +05:30
67f60bae11 mass renaming of gitlab to gitea 2022-02-19 17:25:38 +05:30
Ed Tan
1bb88c7a05 fix markdown formatting 2021-02-18 12:33:23 -05:00
Ed Tan
c152504641 Update README with link to newer resource 2021-02-18 12:32:44 -05:00
Ed
2f9e38b13b Merge pull request #2 from demilletech/master
Fixed some GitHub-related typos in the README
2019-04-04 10:00:19 -04:00
Eamonn Nugent
9a4f4b57b9 Update README.md 2019-04-04 00:55:24 -04:00
Eamonn Nugent
1d9c9b45ae Making commit-ish optional 2019-04-04 00:55:01 -04:00
Eamonn Nugent
ab8d9ba33a Update README.md 2019-04-04 00:24:39 -04:00
Ed
c9d66cbc88 Check resource should be able to grab multiple tags for the same commit 2018-12-21 15:47:15 -05:00
Ed
fa27800311 Fix check bug where versions were being missed 2018-12-19 18:10:41 -05:00
Ed
9eb0ab0a0f Begin working on tests 2018-12-19 18:00:25 -05:00
Ed
7cab700694 Capitalization 2018-12-19 18:00:10 -05:00
Ed
267cf61ef9 Remove duplicate check in 2018-12-19 17:54:00 -05:00
Ed
43dc7bef8d Remove comment about check's returned version 2018-12-19 17:52:55 -05:00
Ed
724ad443d9 Nil checks for metadata 2018-12-19 10:31:27 -05:00
Ed
6f0e6ea3ca Add SHA to versions 2018-12-18 12:56:09 -05:00
Ed
b747989d43 Rename ID to commitish in versions struct 2018-12-18 00:22:14 -05:00
Ed
7688a7faf8 Fix out bugs 2018-12-18 00:02:07 -05:00
Ed
44f8ef502d Remove all debug logs 2018-12-17 21:18:53 -05:00
Ed
f6b3522b3f Remove debug logs from in to avoid erroring in Concourse 2018-12-17 21:02:28 -05:00
Ed
7ea2024512 Fix in bugs 2018-12-17 20:52:55 -05:00
Ed
1c2dfeddfe Fix markdown link parsing for in 2018-12-17 19:00:43 -05:00
Ed
c7900456b4 Fix check command bugs 2018-12-17 18:41:44 -05:00
Ed
00a9fbf8e1 Update README with WIP notice 2018-12-17 10:29:33 -05:00
Ed
130c7c1c06 Bug fixes and logging 2018-12-16 23:02:46 -05:00
Ed
74430d5d22 Dockerfile updates 2018-12-16 22:29:36 -05:00
Ed
aa5c784d45 Get compile working 2018-12-16 21:38:30 -05:00
Ed
bc89091d9f First draft of check and out 2018-12-16 19:17:03 -05:00
Ed
bf7e6d5e02 Rename github.go to gitlab.go, init go modules 2018-12-12 22:05:31 -05:00
Ed
d91e7afd2c Remove Godeps 2018-12-12 22:04:21 -05:00
307 changed files with 1049 additions and 58878 deletions

View File

@@ -1,23 +1,15 @@
FROM golang:alpine as builder FROM golang:alpine as builder
COPY . /go/src/github.com/concourse/github-release-resource COPY . /src
WORKDIR /src
ENV CGO_ENABLED 0 ENV CGO_ENABLED 0
ENV GOPATH /go/src/github.com/concourse/github-release-resource/Godeps/_workspace:${GOPATH} RUN go get -d ./...
ENV PATH /go/src/github.com/concourse/github-release-resource/Godeps/_workspace/bin:${PATH} RUN go build -o /assets/in ./cmd/in
RUN go build -o /assets/out github.com/concourse/github-release-resource/cmd/out RUN go build -o /assets/out ./cmd/out
RUN go build -o /assets/in github.com/concourse/github-release-resource/cmd/in RUN go build -o /assets/check ./cmd/check
RUN go build -o /assets/check github.com/concourse/github-release-resource/cmd/check
RUN set -e; for pkg in $(go list ./...); do \
go test -o "/tests/$(basename $pkg).test" -c $pkg; \
done
FROM alpine:edge AS resource FROM alpine:latest AS resource
RUN apk add --update bash tzdata ca-certificates RUN apk add --no-cache bash tzdata ca-certificates unzip zip gzip tar
COPY --from=builder /assets /opt/resource COPY --from=builder assets/ /opt/resource/
RUN chmod +x /opt/resource/*
FROM resource AS tests
COPY --from=builder /tests /tests
RUN set -e; for test in /tests/*.test; do \
$test; \
done
FROM resource FROM resource

222
Godeps/Godeps.json generated
View File

@@ -1,222 +0,0 @@
{
"ImportPath": "github.com/concourse/github-release-resource",
"GoVersion": "go1.8",
"GodepVersion": "v79",
"Packages": [
"./...",
"github.com/onsi/ginkgo/ginkgo"
],
"Deps": [
{
"ImportPath": "github.com/cppforlife/go-semi-semantic/version",
"Rev": "6d81a57cf024149eba18224dc3f46b1f79cbb7f3"
},
{
"ImportPath": "github.com/golang/protobuf/proto",
"Rev": "45bba206dd5270d96bac4942dcfe515726613249"
},
{
"ImportPath": "github.com/google/go-github/github",
"Rev": "ebfec748347a9af6793c723f8859afcd906860fb"
},
{
"ImportPath": "github.com/google/go-querystring/query",
"Rev": "9235644dd9e52eeae6fa48efd539fdc351a0af53"
},
{
"ImportPath": "github.com/mitchellh/colorstring",
"Rev": "8631ce90f28644f54aeedcb3e389a85174e067d1"
},
{
"ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/config",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/convert",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/interrupthandler",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/nodot",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testrunner",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testsuite",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/ginkgo/watch",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/codelocation",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/containernode",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/failer",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/leafnodes",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/remote",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/spec",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/specrunner",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/suite",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/testingtproxy",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/writer",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/ginkgo/types",
"Comment": "v1.2.0-55-g5437a97",
"Rev": "5437a97bf824dec14e58d68c56ee36e772670c2e"
},
{
"ImportPath": "github.com/onsi/gomega",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/format",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/ghttp",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/internal/assertion",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/internal/asyncassertion",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/internal/oraclematcher",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/internal/testingtsupport",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/matchers",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/edge",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/node",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/util",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "github.com/onsi/gomega/types",
"Comment": "v1.0-97-gc73e516",
"Rev": "c73e51675ad2455a4515b6213eb7145eaade4824"
},
{
"ImportPath": "golang.org/x/net/context",
"Rev": "f841c39de738b1d0df95b5a7187744f0e03d8112"
},
{
"ImportPath": "golang.org/x/oauth2",
"Rev": "7357e96168422ba1e6e7f21075713b72d58764e7"
},
{
"ImportPath": "golang.org/x/oauth2/internal",
"Rev": "7357e96168422ba1e6e7f21075713b72d58764e7"
},
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "a83829b6f1293c91addabc89d0571c246397bbf4"
}
]
}

5
Godeps/Readme generated
View File

@@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View File

@@ -1,46 +1,29 @@
# GitHub Releases Resource [![Concourse CI](https://ci.weirdnatto.in/api/v1/teams/main/pipelines/gitea-release-resource/badge)](https://ci.weirdnatto.in/teams/main/pipelines/gitea-release-resource) [![Docker](https://img.shields.io/docker/image-size/natto17/gitea-release-resource.svg)](https://hub.docker.com/repository/docker/natto17/gitea-release-resource)
# WIP
Forked from https://github.com/edtan/gitlab-release-resource
*please help ;-;*
Fetches and creates versioned GitHub resources. # Gitea Releases Resource
> If you're seeing rate limits affecting you then please add a token to the source Fetches and creates versioned Gitea resources. GitLab resources are metadata attached to tags. Note that `check` will skip tags that do not have associated resources.
> configuration. This will increase your number of allowed requests.
Note that this is still in development, and is still undergoing changes. It may or may not work properly at the moment, but should hopefully be somewhat more stable soon.
You may want to clean up your uploads folder over time if you re-run a put step with the same inputs, as this will simply re-upload the files under a new hash.
## Source Configuration ## Source Configuration
* `owner`: *Required.* The GitHub user or organization name for the repository
that the releases are in.
* `repository`: *Required.* The repository name that contains the releases. * `repository`: *Required.* The repository name that contains the releases.
* `access_token`: *Optional.* Used for accessing a release in a private-repo * `access_token`: *Required.* Used for accessing a release in a private-repo
during an `in` and pushing a release to a repo during an `out`. The access during an `in` and pushing a release to a repo during an `out`. The access
token you create is only required to have the `repo` or `public_repo` scope. token you create is only required to have the `repo` or `public_repo` scope.
* `github_api_url`: *Optional.* If you use a non-public GitHub deployment then * `gitea_api_url`: *Optional.* If you use a non-public Gitea deployment then
you can set your API URL here. you can set your API URL here.
* `github_uploads_url`: *Optional.* Some GitHub instances have a separate URL
for uploading. If `github_api_url` is set, this value defaults to the same
value, but if you have your own endpoint, this field will override it.
* `insecure`: *Optional. Default `false`.* When set to `true`, concourse will allow * `insecure`: *Optional. Default `false`.* When set to `true`, concourse will allow
insecure connection to your github API. insecure connection to your gitea API.
* `release`: *Optional. Default `true`.* When set to `true`, `put` produces
release and `check` detects releases. If `false`, `put` and `check` will ignore releases.
Note that releases must have semver compliant tags to be detected.
* `pre_release`: *Optional. Default `false`.* When set to `true`, `put` produces
pre-release and `check` detects prereleases. If `false`, only non-prerelease releases
will be detected and published. Note that releases must have semver compliant
tags to be detected.
If `release` and `pre_release` are set to `true`, `put` produces
release and `check` detects prereleases and releases.
* `drafts`: *Optional. Default `false`.* When set to `true`, `put` produces
drafts and `check` only detects drafts. If `false`, only non-draft releases
will be detected and published. Note that releases must have semver compliant
tags to be detected, even if they're drafts.
* `tag_filter`: *Optional.* If set, override default tag filter regular * `tag_filter`: *Optional.* If set, override default tag filter regular
expression of `v?([^v].*)`. If the filter includes a capture group, the capture expression of `v?([^v].*)`. If the filter includes a capture group, the capture
@@ -50,22 +33,20 @@ Fetches and creates versioned GitHub resources.
### Example ### Example
``` yaml ``` yaml
- name: gh-release - name: gl-release
type: github-release type: gitea-release
source: source:
owner: concourse repository: group/project
repository: concourse
access_token: abcdef1234567890 access_token: abcdef1234567890
``` ```
``` yaml ``` yaml
- get: gh-release - get: gl-release
``` ```
``` yaml ``` yaml
- put: gh-release - put: gl-release
params: params:
name: path/to/name/file
tag: path/to/tag/file tag: path/to/tag/file
body: path/to/body/file body: path/to/body/file
globs: globs:
@@ -75,15 +56,15 @@ Fetches and creates versioned GitHub resources.
To get a specific version of a release: To get a specific version of a release:
``` yaml ``` yaml
- get: gh-release - get: gl-release
version: { tag: 'v0.0.1' } version: { tag: 'v0.0.1' }
``` ```
To set a custom tag filter: To set a custom tag filter:
```yaml ```yaml
- name: gh-release - name: gl-release
type: github-release type: gitea-release
source: source:
owner: concourse owner: concourse
repository: concourse repository: concourse
@@ -115,31 +96,25 @@ Also creates the following files:
* `globs`: *Optional.* A list of globs for files that will be downloaded from * `globs`: *Optional.* A list of globs for files that will be downloaded from
the release. If not specified, all assets will be fetched. the release. If not specified, all assets will be fetched.
* `include_source_tarball`: *Optional.* Enables downloading of the source
artifact tarball for the release as `source.tar.gz`. Defaults to `false`.
* `include_source_zip`: *Optional.* Enables downloading of the source
artifact zip for the release as `source.zip`. Defaults to `false`.
### `out`: Publish a release. ### `out`: Publish a release.
Given a name specified in `name`, a body specified in `body`, and the tag to use Given a `commit_sha` and `tag`, this tags the commit and creates a release on Gitea, then uploads the files
specified in `tag`, this creates a release on GitHub then uploads the files
matching the patterns in `globs` to the release. matching the patterns in `globs` to the release.
#### Parameters #### Parameters
* `name`: *Required.* A path to a file containing the name of the release. * `commitish`: *Optional, if tag is not specified.* A path to a file containing the commitish (SHA, tag,
branch name) that the new tag and release should be associated with.
* `tag`: *Required.* A path to a file containing the name of the Git tag to use * `tag`: *Required.* A path to a file containing the name of the Git tag to use
for the release. for the release.
* `title`: *Optional.* A path to a file containing the name of the title
for the release. (Defaults to tag name)
* `tag_prefix`: *Optional.* If specified, the tag read from the file will be * `tag_prefix`: *Optional.* If specified, the tag read from the file will be
prepended with this string. This is useful for adding v in front of version numbers. prepended with this string. This is useful for adding v in front of version numbers.
* `commitish`: *Optional.* A path to a file containing the commitish (SHA, tag,
branch name) that the release should be associated with.
* `body`: *Optional.* A path to a file containing the body text of the release. * `body`: *Optional.* A path to a file containing the body text of the release.
* `globs`: *Optional.* A list of globs for files that will be uploaded alongside * `globs`: *Optional.* A list of globs for files that will be uploaded alongside
@@ -165,7 +140,7 @@ will stop the build.
Run the tests with the following command: Run the tests with the following command:
```sh ```sh
docker build -t github-release-resource . docker build -t gitea-release-resource .
``` ```
### Contributing ### Contributing

View File

@@ -2,69 +2,65 @@ package resource
import ( import (
"sort" "sort"
"strconv"
"github.com/google/go-github/github"
"code.gitea.io/sdk/gitea"
"github.com/cppforlife/go-semi-semantic/version" "github.com/cppforlife/go-semi-semantic/version"
) )
type CheckCommand struct { type CheckCommand struct {
github GitHub gitea Gitea
} }
func NewCheckCommand(github GitHub) *CheckCommand { func NewCheckCommand(gitea Gitea) *CheckCommand {
return &CheckCommand{ return &CheckCommand{
github: github, gitea: gitea,
} }
} }
func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) { func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
releases, err := c.github.ListReleases() var tags []*gitea.Tag
var err error
if (request.Version == Version{}) {
tags, err = c.gitea.ListTags()
} else {
tags, err = c.gitea.ListTagsUntil(request.Version.Tag)
}
if err != nil { if err != nil {
return []Version{}, err return []Version{}, err
} }
if len(releases) == 0 { if len(tags) == 0 {
return []Version{}, nil return []Version{}, nil
} }
var filteredReleases []*github.RepositoryRelease var filteredTags []*gitea.Tag
// TODO: make ListTagsUntil work better with this
versionParser, err := newVersionParser(request.Source.TagFilter) versionParser, err := newVersionParser(request.Source.TagFilter)
if err != nil { if err != nil {
return []Version{}, err return []Version{}, err
} }
for _, release := range releases { for _, tag := range tags {
if request.Source.Drafts != *release.Draft { if _, err := version.NewVersionFromString(versionParser.parse(tag.Name)); err != nil {
continue continue
} }
// Should we skip this release /* if tag.Release == nil {
// a- prerelease condition dont match our source config continue
// b- release condition match prerealse in github since github has true/false to describe release/prerelase }*/
if request.Source.PreRelease != *release.Prerelease && request.Source.Release == *release.Prerelease {
continue
}
if release.TagName == nil { filteredTags = append(filteredTags, tag)
continue
}
if _, err := version.NewVersionFromString(versionParser.parse(*release.TagName)); err != nil {
continue
}
filteredReleases = append(filteredReleases, release)
} }
sort.Slice(filteredReleases, func(i, j int) bool { sort.Slice(filteredTags, func(i, j int) bool {
first, err := version.NewVersionFromString(versionParser.parse(*filteredReleases[i].TagName)) first, err := version.NewVersionFromString(versionParser.parse(filteredTags[i].Name))
if err != nil { if err != nil {
return true return true
} }
second, err := version.NewVersionFromString(versionParser.parse(*filteredReleases[j].TagName)) second, err := version.NewVersionFromString(versionParser.parse(filteredTags[j].Name))
if err != nil { if err != nil {
return false return false
} }
@@ -72,47 +68,48 @@ func (c *CheckCommand) Run(request CheckRequest) ([]Version, error) {
return first.IsLt(second) return first.IsLt(second)
}) })
if len(filteredReleases) == 0 { if len(filteredTags) == 0 {
return []Version{}, nil return []Version{}, nil
} }
latestRelease := filteredReleases[len(filteredReleases)-1] latestTag := filteredTags[len(filteredTags)-1]
latestRelease, err := c.gitea.GetReleaseByTag(latestTag.Name)
if err != nil {
return []Version{}, err
}
if (request.Version == Version{}) { if (request.Version == Version{}) {
return []Version{ return []Version{versionFromRelease(latestRelease)}, nil
versionFromRelease(latestRelease),
}, nil
} }
if *latestRelease.TagName == request.Version.Tag { if latestTag.Name == request.Version.Tag {
return []Version{}, nil return []Version{versionFromRelease(latestRelease)}, nil
} }
upToLatest := false upToLatest := false
reversedVersions := []Version{} nextVersions := []Version{} // contains the requested version and all newer ones
for _, release := range filteredReleases { for _, release := range filteredTags {
if !upToLatest { if !upToLatest {
if *release.Draft || *release.Prerelease { version := release.Name
id := *release.ID upToLatest = request.Version.Tag == version
upToLatest = request.Version.ID == strconv.Itoa(id)
} else {
version := *release.TagName
upToLatest = request.Version.Tag == version
}
} }
if upToLatest { if upToLatest {
reversedVersions = append(reversedVersions, versionFromRelease(release)) nextVersions = append(nextVersions, Version{Tag: release.Name})
} }
} }
if !upToLatest { if !upToLatest {
// current version was removed; start over from latest // current version was removed; start over from latest
reversedVersions = append( release, err := c.gitea.GetReleaseByTag(filteredTags[len(filteredTags)-1].Name)
reversedVersions, if err != nil {
versionFromRelease(filteredReleases[len(filteredReleases)-1]), return []Version{}, err
}
nextVersions = append(
nextVersions,
versionFromRelease(release),
) )
} }
return reversedVersions, nil return nextVersions, nil
} }

View File

@@ -1,464 +0,0 @@
package resource_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/google/go-github/github"
"github.com/concourse/github-release-resource"
"github.com/concourse/github-release-resource/fakes"
)
var _ = Describe("Check Command", func() {
var (
githubClient *fakes.FakeGitHub
command *resource.CheckCommand
returnedReleases []*github.RepositoryRelease
)
BeforeEach(func() {
githubClient = &fakes.FakeGitHub{}
command = resource.NewCheckCommand(githubClient)
returnedReleases = []*github.RepositoryRelease{}
})
JustBeforeEach(func() {
githubClient.ListReleasesReturns(returnedReleases, nil)
})
Context("when this is the first time that the resource has been run", func() {
Context("when there are no releases", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{}
})
It("returns no versions", func() {
versions, err := command.Run(resource.CheckRequest{})
Ω(err).ShouldNot(HaveOccurred())
Ω(versions).Should(BeEmpty())
})
})
Context("when there are releases that get filtered out", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
}
})
It("returns no versions", func() {
versions, err := command.Run(resource.CheckRequest{})
Ω(err).ShouldNot(HaveOccurred())
Ω(versions).Should(BeEmpty())
})
})
Context("when there are releases", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newRepositoryRelease(1, "v0.4.0"),
newRepositoryRelease(2, "0.1.3"),
newRepositoryRelease(3, "v0.1.2"),
}
})
It("outputs the most recent version only", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(HaveLen(1))
Ω(response[0]).Should(Equal(resource.Version{
Tag: "v0.4.0",
}))
})
})
})
Context("when there are prior versions", func() {
Context("when there are no releases", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{}
})
It("returns no versions", func() {
versions, err := command.Run(resource.CheckRequest{})
Ω(err).ShouldNot(HaveOccurred())
Ω(versions).Should(BeEmpty())
})
})
Context("when there are releases", func() {
Context("and there is a custom tag filter", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newRepositoryRelease(1, "package-0.1.4"),
newRepositoryRelease(2, "package-0.4.0"),
newRepositoryRelease(3, "package-0.1.3"),
newRepositoryRelease(4, "package-0.1.2"),
}
})
It("returns all of the versions that are newer", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "package-0.1.3",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "package-0.1.3"},
{Tag: "package-0.1.4"},
{Tag: "package-0.4.0"},
}))
})
})
Context("and the releases do not contain a draft release", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newRepositoryRelease(1, "v0.1.4"),
newRepositoryRelease(2, "0.4.0"),
newRepositoryRelease(3, "v0.1.3"),
newRepositoryRelease(4, "0.1.2"),
}
})
It("returns an empty list if the lastet version has been checked", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "0.4.0",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(BeEmpty())
})
It("returns all of the versions that are newer", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "v0.1.3",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.1.3"},
{Tag: "v0.1.4"},
{Tag: "0.4.0"},
}))
})
It("returns the latest version if the current version is not found", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "v3.4.5",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "0.4.0"},
}))
})
Context("when there are not-quite-semver versions", func() {
BeforeEach(func() {
returnedReleases = append(returnedReleases, newRepositoryRelease(5, "v1"))
returnedReleases = append(returnedReleases, newRepositoryRelease(6, "v0"))
})
It("combines them with the semver versions in a reasonable order", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "v0.1.3",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.1.3"},
{Tag: "v0.1.4"},
{Tag: "0.4.0"},
{Tag: "v1"},
}))
})
})
})
Context("and one of the releases is a draft", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newRepositoryRelease(2, "0.4.0"),
newRepositoryRelease(3, "v0.1.3"),
}
})
It("returns all of the versions that are newer, and not a draft", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{
Tag: "v0.1.3",
},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.1.3"},
{Tag: "0.4.0"},
}))
})
})
Context("when pre releases are allowed and releases are not", func() {
Context("and one of the releases is a final and another is a draft", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newRepositoryRelease(2, "0.4.0"),
newPreReleaseRepositoryRelease(1, "v0.4.1-rc.10"),
newPreReleaseRepositoryRelease(2, "0.4.1-rc.9"),
newPreReleaseRepositoryRelease(3, "v0.4.1-rc.8"),
}
})
It("returns all of the versions that are newer, and only pre relases", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "2"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: false},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "0.4.1-rc.9"},
{Tag: "v0.4.1-rc.10"},
}))
})
It("returns the latest prerelease version if the current version is not found", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "5"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: false},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.4.1-rc.10"},
}))
})
})
})
Context("when releases and pre releases are allowed", func() {
Context("and final release is newer", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newRepositoryRelease(1, "0.3.9"),
newRepositoryRelease(2, "0.4.0"),
newRepositoryRelease(3, "v0.4.2"),
newPreReleaseRepositoryRelease(1, "v0.4.1-rc.10"),
newPreReleaseRepositoryRelease(2, "0.4.1-rc.9"),
newPreReleaseRepositoryRelease(3, "v0.4.2-rc.1"),
}
})
It("returns all of the versions that are newer, and are release and prerealse", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{Tag: "0.4.0"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "0.4.0"},
{Tag: "0.4.1-rc.9"},
{Tag: "v0.4.1-rc.10"},
{Tag: "v0.4.2-rc.1"},
{Tag: "v0.4.2"},
}))
})
It("returns the latest release version if the current version is not found", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "5"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.4.2"},
}))
})
})
Context("and prerelease is newer", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newRepositoryRelease(1, "0.3.9"),
newRepositoryRelease(2, "0.4.0"),
newRepositoryRelease(3, "v0.4.2"),
newPreReleaseRepositoryRelease(1, "v0.4.1-rc.10"),
newPreReleaseRepositoryRelease(2, "0.4.1-rc.9"),
newPreReleaseRepositoryRelease(3, "v0.4.2-rc.1"),
newPreReleaseRepositoryRelease(4, "v0.4.3-rc.1"),
}
})
It("returns all of the versions that are newer, and are release and prerealse", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{Tag: "0.4.0"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "0.4.0"},
{Tag: "0.4.1-rc.9"},
{Tag: "v0.4.1-rc.10"},
{Tag: "v0.4.2-rc.1"},
{Tag: "v0.4.2"},
{Tag: "v0.4.3-rc.1"},
}))
})
It("returns the latest prerelease version if the current version is not found", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "5"},
Source: resource.Source{Drafts: false, PreRelease: true, Release: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{Tag: "v0.4.3-rc.1"},
}))
})
})
})
Context("when draft releases are allowed", func() {
Context("and one of the releases is a final release", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newDraftRepositoryRelease(2, "v0.1.3"),
newDraftRepositoryRelease(3, "v0.1.1"),
newRepositoryRelease(2, "0.4.0"),
}
})
It("returns all of the versions that are newer, and only draft", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "2"},
Source: resource.Source{Drafts: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{ID: "2"},
{ID: "1"},
}))
})
It("returns the latest draft version if the current version is not found", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{ID: "5"},
Source: resource.Source{Drafts: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{ID: "1"},
}))
})
})
Context("and non-of them are semver", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "abc/d"),
newDraftRepositoryRelease(2, "123*4"),
}
})
It("returns all of the releases with semver resources", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{},
Source: resource.Source{Drafts: true},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{}))
})
})
Context("and one of the releases is not a versioned draft release", func() {
BeforeEach(func() {
returnedReleases = []*github.RepositoryRelease{
newDraftRepositoryRelease(1, "v0.1.4"),
newDraftRepositoryRelease(2, ""),
newDraftWithNilTagRepositoryRelease(3),
newDraftRepositoryRelease(4, "asdf@example.com"),
}
})
It("returns all of the releases with semver resources", func() {
command := resource.NewCheckCommand(githubClient)
response, err := command.Run(resource.CheckRequest{
Version: resource.Version{},
Source: resource.Source{Drafts: true, PreRelease: false},
})
Ω(err).ShouldNot(HaveOccurred())
Ω(response).Should(Equal([]resource.Version{
{ID: "1"},
}))
})
})
})
})
})
})

51
ci/pipeline.yml Normal file
View File

@@ -0,0 +1,51 @@
resources:
- name: resource-repo
type: git
icon: git
source:
uri: https://github.com/natto1784/gitea-release-resource.git
branch: master
- name: target-image
type: registry-image
icon: docker
source:
repository: ((docker.user))/gitea-release-resource
tag: latest
username: ((docker.user))
password: ((docker.pass))
jobs:
- name: configure-self
public: true
plan:
- get: resource-repo
trigger: true
- set_pipeline: self
file: resource-repo/ci/pipeline.yml
- name: build-and-push
plan:
- get: resource-repo
trigger: true
passed: [configure-self]
- task: build-image
privileged: true
config:
caches:
- path: cache
platform: linux
image_resource:
type: registry-image
source:
repository: rdclda/concourse-oci-build-task
inputs:
- name: resource-repo
outputs:
- name: image
params:
CONTEXT: resource-repo
run:
path: build
- put: target-image
params:
image: image/image.tar

View File

@@ -4,19 +4,19 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"github.com/concourse/github-release-resource" "github.com/natto1784/gitea-release-resource"
) )
func main() { func main() {
request := resource.NewCheckRequest() request := resource.NewCheckRequest()
inputRequest(&request) inputRequest(&request)
github, err := resource.NewGitHubClient(request.Source) gitea, err := resource.NewGiteaClient(request.Source)
if err != nil { if err != nil {
resource.Fatal("constructing github client", err) resource.Fatal("constructing gitea client", err)
} }
command := resource.NewCheckCommand(github) command := resource.NewCheckCommand(gitea)
response, err := command.Run(request) response, err := command.Run(request)
if err != nil { if err != nil {
resource.Fatal("running command", err) resource.Fatal("running command", err)

View File

@@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"github.com/concourse/github-release-resource" "github.com/natto1784/gitea-release-resource"
) )
func main() { func main() {
@@ -18,12 +18,12 @@ func main() {
destDir := os.Args[1] destDir := os.Args[1]
github, err := resource.NewGitHubClient(request.Source) gitea, err := resource.NewGiteaClient(request.Source)
if err != nil { if err != nil {
resource.Fatal("constructing github client", err) resource.Fatal("constructing gitea client", err)
} }
command := resource.NewInCommand(github, os.Stderr) command := resource.NewInCommand(gitea, os.Stderr)
response, err := command.Run(destDir, request) response, err := command.Run(destDir, request)
if err != nil { if err != nil {
resource.Fatal("running command", err) resource.Fatal("running command", err)

View File

@@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"github.com/concourse/github-release-resource" "github.com/natto1784/gitea-release-resource"
) )
func main() { func main() {
@@ -18,12 +18,12 @@ func main() {
sourceDir := os.Args[1] sourceDir := os.Args[1]
github, err := resource.NewGitHubClient(request.Source) gitea, err := resource.NewGiteaClient(request.Source)
if err != nil { if err != nil {
resource.Fatal("constructing github client", err) resource.Fatal("constructing gitea client", err)
} }
command := resource.NewOutCommand(github, os.Stderr) command := resource.NewOutCommand(gitea, os.Stderr)
response, err := command.Run(sourceDir, request) response, err := command.Run(sourceDir, request)
if err != nil { if err != nil {
resource.Fatal("running command", err) resource.Fatal("running command", err)

View File

@@ -1,511 +0,0 @@
// This file was generated by counterfeiter
package fakes
import (
"io"
"net/url"
"os"
"sync"
"github.com/concourse/github-release-resource"
"github.com/google/go-github/github"
)
type FakeGitHub struct {
ListReleasesStub func() ([]*github.RepositoryRelease, error)
listReleasesMutex sync.RWMutex
listReleasesArgsForCall []struct{}
listReleasesReturns struct {
result1 []*github.RepositoryRelease
result2 error
}
GetReleaseByTagStub func(tag string) (*github.RepositoryRelease, error)
getReleaseByTagMutex sync.RWMutex
getReleaseByTagArgsForCall []struct {
tag string
}
getReleaseByTagReturns struct {
result1 *github.RepositoryRelease
result2 error
}
GetReleaseStub func(id int) (*github.RepositoryRelease, error)
getReleaseMutex sync.RWMutex
getReleaseArgsForCall []struct {
id int
}
getReleaseReturns struct {
result1 *github.RepositoryRelease
result2 error
}
CreateReleaseStub func(release github.RepositoryRelease) (*github.RepositoryRelease, error)
createReleaseMutex sync.RWMutex
createReleaseArgsForCall []struct {
release github.RepositoryRelease
}
createReleaseReturns struct {
result1 *github.RepositoryRelease
result2 error
}
UpdateReleaseStub func(release github.RepositoryRelease) (*github.RepositoryRelease, error)
updateReleaseMutex sync.RWMutex
updateReleaseArgsForCall []struct {
release github.RepositoryRelease
}
updateReleaseReturns struct {
result1 *github.RepositoryRelease
result2 error
}
ListReleaseAssetsStub func(release github.RepositoryRelease) ([]*github.ReleaseAsset, error)
listReleaseAssetsMutex sync.RWMutex
listReleaseAssetsArgsForCall []struct {
release github.RepositoryRelease
}
listReleaseAssetsReturns struct {
result1 []*github.ReleaseAsset
result2 error
}
UploadReleaseAssetStub func(release github.RepositoryRelease, name string, file *os.File) error
uploadReleaseAssetMutex sync.RWMutex
uploadReleaseAssetArgsForCall []struct {
release github.RepositoryRelease
name string
file *os.File
}
uploadReleaseAssetReturns struct {
result1 error
}
DeleteReleaseAssetStub func(asset github.ReleaseAsset) error
deleteReleaseAssetMutex sync.RWMutex
deleteReleaseAssetArgsForCall []struct {
asset github.ReleaseAsset
}
deleteReleaseAssetReturns struct {
result1 error
}
DownloadReleaseAssetStub func(asset github.ReleaseAsset) (io.ReadCloser, error)
downloadReleaseAssetMutex sync.RWMutex
downloadReleaseAssetArgsForCall []struct {
asset github.ReleaseAsset
}
downloadReleaseAssetReturns struct {
result1 io.ReadCloser
result2 error
}
GetTarballLinkStub func(tag string) (*url.URL, error)
getTarballLinkMutex sync.RWMutex
getTarballLinkArgsForCall []struct {
tag string
}
getTarballLinkReturns struct {
result1 *url.URL
result2 error
}
GetZipballLinkStub func(tag string) (*url.URL, error)
getZipballLinkMutex sync.RWMutex
getZipballLinkArgsForCall []struct {
tag string
}
getZipballLinkReturns struct {
result1 *url.URL
result2 error
}
GetRefStub func(tag string) (*github.Reference, error)
getRefMutex sync.RWMutex
getRefArgsForCall []struct {
tag string
}
getRefReturns struct {
result1 *github.Reference
result2 error
}
}
func (fake *FakeGitHub) ListReleases() ([]*github.RepositoryRelease, error) {
fake.listReleasesMutex.Lock()
fake.listReleasesArgsForCall = append(fake.listReleasesArgsForCall, struct{}{})
fake.listReleasesMutex.Unlock()
if fake.ListReleasesStub != nil {
return fake.ListReleasesStub()
} else {
return fake.listReleasesReturns.result1, fake.listReleasesReturns.result2
}
}
func (fake *FakeGitHub) ListReleasesCallCount() int {
fake.listReleasesMutex.RLock()
defer fake.listReleasesMutex.RUnlock()
return len(fake.listReleasesArgsForCall)
}
func (fake *FakeGitHub) ListReleasesReturns(result1 []*github.RepositoryRelease, result2 error) {
fake.ListReleasesStub = nil
fake.listReleasesReturns = struct {
result1 []*github.RepositoryRelease
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) GetReleaseByTag(tag string) (*github.RepositoryRelease, error) {
fake.getReleaseByTagMutex.Lock()
fake.getReleaseByTagArgsForCall = append(fake.getReleaseByTagArgsForCall, struct {
tag string
}{tag})
fake.getReleaseByTagMutex.Unlock()
if fake.GetReleaseByTagStub != nil {
return fake.GetReleaseByTagStub(tag)
} else {
return fake.getReleaseByTagReturns.result1, fake.getReleaseByTagReturns.result2
}
}
func (fake *FakeGitHub) GetReleaseByTagCallCount() int {
fake.getReleaseByTagMutex.RLock()
defer fake.getReleaseByTagMutex.RUnlock()
return len(fake.getReleaseByTagArgsForCall)
}
func (fake *FakeGitHub) GetReleaseByTagArgsForCall(i int) string {
fake.getReleaseByTagMutex.RLock()
defer fake.getReleaseByTagMutex.RUnlock()
return fake.getReleaseByTagArgsForCall[i].tag
}
func (fake *FakeGitHub) GetReleaseByTagReturns(result1 *github.RepositoryRelease, result2 error) {
fake.GetReleaseByTagStub = nil
fake.getReleaseByTagReturns = struct {
result1 *github.RepositoryRelease
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) GetRelease(id int) (*github.RepositoryRelease, error) {
fake.getReleaseMutex.Lock()
fake.getReleaseArgsForCall = append(fake.getReleaseArgsForCall, struct {
id int
}{id})
fake.getReleaseMutex.Unlock()
if fake.GetReleaseStub != nil {
return fake.GetReleaseStub(id)
} else {
return fake.getReleaseReturns.result1, fake.getReleaseReturns.result2
}
}
func (fake *FakeGitHub) GetReleaseCallCount() int {
fake.getReleaseMutex.RLock()
defer fake.getReleaseMutex.RUnlock()
return len(fake.getReleaseArgsForCall)
}
func (fake *FakeGitHub) GetReleaseArgsForCall(i int) int {
fake.getReleaseMutex.RLock()
defer fake.getReleaseMutex.RUnlock()
return fake.getReleaseArgsForCall[i].id
}
func (fake *FakeGitHub) GetReleaseReturns(result1 *github.RepositoryRelease, result2 error) {
fake.GetReleaseStub = nil
fake.getReleaseReturns = struct {
result1 *github.RepositoryRelease
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) CreateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error) {
fake.createReleaseMutex.Lock()
fake.createReleaseArgsForCall = append(fake.createReleaseArgsForCall, struct {
release github.RepositoryRelease
}{release})
fake.createReleaseMutex.Unlock()
if fake.CreateReleaseStub != nil {
return fake.CreateReleaseStub(release)
} else {
return fake.createReleaseReturns.result1, fake.createReleaseReturns.result2
}
}
func (fake *FakeGitHub) CreateReleaseCallCount() int {
fake.createReleaseMutex.RLock()
defer fake.createReleaseMutex.RUnlock()
return len(fake.createReleaseArgsForCall)
}
func (fake *FakeGitHub) CreateReleaseArgsForCall(i int) github.RepositoryRelease {
fake.createReleaseMutex.RLock()
defer fake.createReleaseMutex.RUnlock()
return fake.createReleaseArgsForCall[i].release
}
func (fake *FakeGitHub) CreateReleaseReturns(result1 *github.RepositoryRelease, result2 error) {
fake.CreateReleaseStub = nil
fake.createReleaseReturns = struct {
result1 *github.RepositoryRelease
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) UpdateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error) {
fake.updateReleaseMutex.Lock()
fake.updateReleaseArgsForCall = append(fake.updateReleaseArgsForCall, struct {
release github.RepositoryRelease
}{release})
fake.updateReleaseMutex.Unlock()
if fake.UpdateReleaseStub != nil {
return fake.UpdateReleaseStub(release)
} else {
return fake.updateReleaseReturns.result1, fake.updateReleaseReturns.result2
}
}
func (fake *FakeGitHub) UpdateReleaseCallCount() int {
fake.updateReleaseMutex.RLock()
defer fake.updateReleaseMutex.RUnlock()
return len(fake.updateReleaseArgsForCall)
}
func (fake *FakeGitHub) UpdateReleaseArgsForCall(i int) github.RepositoryRelease {
fake.updateReleaseMutex.RLock()
defer fake.updateReleaseMutex.RUnlock()
return fake.updateReleaseArgsForCall[i].release
}
func (fake *FakeGitHub) UpdateReleaseReturns(result1 *github.RepositoryRelease, result2 error) {
fake.UpdateReleaseStub = nil
fake.updateReleaseReturns = struct {
result1 *github.RepositoryRelease
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) ListReleaseAssets(release github.RepositoryRelease) ([]*github.ReleaseAsset, error) {
fake.listReleaseAssetsMutex.Lock()
fake.listReleaseAssetsArgsForCall = append(fake.listReleaseAssetsArgsForCall, struct {
release github.RepositoryRelease
}{release})
fake.listReleaseAssetsMutex.Unlock()
if fake.ListReleaseAssetsStub != nil {
return fake.ListReleaseAssetsStub(release)
} else {
return fake.listReleaseAssetsReturns.result1, fake.listReleaseAssetsReturns.result2
}
}
func (fake *FakeGitHub) ListReleaseAssetsCallCount() int {
fake.listReleaseAssetsMutex.RLock()
defer fake.listReleaseAssetsMutex.RUnlock()
return len(fake.listReleaseAssetsArgsForCall)
}
func (fake *FakeGitHub) ListReleaseAssetsArgsForCall(i int) github.RepositoryRelease {
fake.listReleaseAssetsMutex.RLock()
defer fake.listReleaseAssetsMutex.RUnlock()
return fake.listReleaseAssetsArgsForCall[i].release
}
func (fake *FakeGitHub) ListReleaseAssetsReturns(result1 []*github.ReleaseAsset, result2 error) {
fake.ListReleaseAssetsStub = nil
fake.listReleaseAssetsReturns = struct {
result1 []*github.ReleaseAsset
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) UploadReleaseAsset(release github.RepositoryRelease, name string, file *os.File) error {
fake.uploadReleaseAssetMutex.Lock()
fake.uploadReleaseAssetArgsForCall = append(fake.uploadReleaseAssetArgsForCall, struct {
release github.RepositoryRelease
name string
file *os.File
}{release, name, file})
fake.uploadReleaseAssetMutex.Unlock()
if fake.UploadReleaseAssetStub != nil {
return fake.UploadReleaseAssetStub(release, name, file)
} else {
return fake.uploadReleaseAssetReturns.result1
}
}
func (fake *FakeGitHub) UploadReleaseAssetCallCount() int {
fake.uploadReleaseAssetMutex.RLock()
defer fake.uploadReleaseAssetMutex.RUnlock()
return len(fake.uploadReleaseAssetArgsForCall)
}
func (fake *FakeGitHub) UploadReleaseAssetArgsForCall(i int) (github.RepositoryRelease, string, *os.File) {
fake.uploadReleaseAssetMutex.RLock()
defer fake.uploadReleaseAssetMutex.RUnlock()
return fake.uploadReleaseAssetArgsForCall[i].release, fake.uploadReleaseAssetArgsForCall[i].name, fake.uploadReleaseAssetArgsForCall[i].file
}
func (fake *FakeGitHub) UploadReleaseAssetReturns(result1 error) {
fake.UploadReleaseAssetStub = nil
fake.uploadReleaseAssetReturns = struct {
result1 error
}{result1}
}
func (fake *FakeGitHub) DeleteReleaseAsset(asset github.ReleaseAsset) error {
fake.deleteReleaseAssetMutex.Lock()
fake.deleteReleaseAssetArgsForCall = append(fake.deleteReleaseAssetArgsForCall, struct {
asset github.ReleaseAsset
}{asset})
fake.deleteReleaseAssetMutex.Unlock()
if fake.DeleteReleaseAssetStub != nil {
return fake.DeleteReleaseAssetStub(asset)
} else {
return fake.deleteReleaseAssetReturns.result1
}
}
func (fake *FakeGitHub) DeleteReleaseAssetCallCount() int {
fake.deleteReleaseAssetMutex.RLock()
defer fake.deleteReleaseAssetMutex.RUnlock()
return len(fake.deleteReleaseAssetArgsForCall)
}
func (fake *FakeGitHub) DeleteReleaseAssetArgsForCall(i int) github.ReleaseAsset {
fake.deleteReleaseAssetMutex.RLock()
defer fake.deleteReleaseAssetMutex.RUnlock()
return fake.deleteReleaseAssetArgsForCall[i].asset
}
func (fake *FakeGitHub) DeleteReleaseAssetReturns(result1 error) {
fake.DeleteReleaseAssetStub = nil
fake.deleteReleaseAssetReturns = struct {
result1 error
}{result1}
}
func (fake *FakeGitHub) DownloadReleaseAsset(asset github.ReleaseAsset) (io.ReadCloser, error) {
fake.downloadReleaseAssetMutex.Lock()
fake.downloadReleaseAssetArgsForCall = append(fake.downloadReleaseAssetArgsForCall, struct {
asset github.ReleaseAsset
}{asset})
fake.downloadReleaseAssetMutex.Unlock()
if fake.DownloadReleaseAssetStub != nil {
return fake.DownloadReleaseAssetStub(asset)
} else {
return fake.downloadReleaseAssetReturns.result1, fake.downloadReleaseAssetReturns.result2
}
}
func (fake *FakeGitHub) DownloadReleaseAssetCallCount() int {
fake.downloadReleaseAssetMutex.RLock()
defer fake.downloadReleaseAssetMutex.RUnlock()
return len(fake.downloadReleaseAssetArgsForCall)
}
func (fake *FakeGitHub) DownloadReleaseAssetArgsForCall(i int) github.ReleaseAsset {
fake.downloadReleaseAssetMutex.RLock()
defer fake.downloadReleaseAssetMutex.RUnlock()
return fake.downloadReleaseAssetArgsForCall[i].asset
}
func (fake *FakeGitHub) DownloadReleaseAssetReturns(result1 io.ReadCloser, result2 error) {
fake.DownloadReleaseAssetStub = nil
fake.downloadReleaseAssetReturns = struct {
result1 io.ReadCloser
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) GetTarballLink(tag string) (*url.URL, error) {
fake.getTarballLinkMutex.Lock()
fake.getTarballLinkArgsForCall = append(fake.getTarballLinkArgsForCall, struct {
tag string
}{tag})
fake.getTarballLinkMutex.Unlock()
if fake.GetTarballLinkStub != nil {
return fake.GetTarballLinkStub(tag)
} else {
return fake.getTarballLinkReturns.result1, fake.getTarballLinkReturns.result2
}
}
func (fake *FakeGitHub) GetTarballLinkCallCount() int {
fake.getTarballLinkMutex.RLock()
defer fake.getTarballLinkMutex.RUnlock()
return len(fake.getTarballLinkArgsForCall)
}
func (fake *FakeGitHub) GetTarballLinkArgsForCall(i int) string {
fake.getTarballLinkMutex.RLock()
defer fake.getTarballLinkMutex.RUnlock()
return fake.getTarballLinkArgsForCall[i].tag
}
func (fake *FakeGitHub) GetTarballLinkReturns(result1 *url.URL, result2 error) {
fake.GetTarballLinkStub = nil
fake.getTarballLinkReturns = struct {
result1 *url.URL
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) GetZipballLink(tag string) (*url.URL, error) {
fake.getZipballLinkMutex.Lock()
fake.getZipballLinkArgsForCall = append(fake.getZipballLinkArgsForCall, struct {
tag string
}{tag})
fake.getZipballLinkMutex.Unlock()
if fake.GetZipballLinkStub != nil {
return fake.GetZipballLinkStub(tag)
} else {
return fake.getZipballLinkReturns.result1, fake.getZipballLinkReturns.result2
}
}
func (fake *FakeGitHub) GetZipballLinkCallCount() int {
fake.getZipballLinkMutex.RLock()
defer fake.getZipballLinkMutex.RUnlock()
return len(fake.getZipballLinkArgsForCall)
}
func (fake *FakeGitHub) GetZipballLinkArgsForCall(i int) string {
fake.getZipballLinkMutex.RLock()
defer fake.getZipballLinkMutex.RUnlock()
return fake.getZipballLinkArgsForCall[i].tag
}
func (fake *FakeGitHub) GetZipballLinkReturns(result1 *url.URL, result2 error) {
fake.GetZipballLinkStub = nil
fake.getZipballLinkReturns = struct {
result1 *url.URL
result2 error
}{result1, result2}
}
func (fake *FakeGitHub) GetRef(tag string) (*github.Reference, error) {
fake.getRefMutex.Lock()
fake.getRefArgsForCall = append(fake.getRefArgsForCall, struct {
tag string
}{tag})
fake.getRefMutex.Unlock()
if fake.GetRefStub != nil {
return fake.GetRefStub(tag)
} else {
return fake.getRefReturns.result1, fake.getRefReturns.result2
}
}
func (fake *FakeGitHub) GetRefCallCount() int {
fake.getRefMutex.RLock()
defer fake.getRefMutex.RUnlock()
return len(fake.getRefArgsForCall)
}
func (fake *FakeGitHub) GetRefArgsForCall(i int) string {
fake.getRefMutex.RLock()
defer fake.getRefMutex.RUnlock()
return fake.getRefArgsForCall[i].tag
}
func (fake *FakeGitHub) GetRefReturns(result1 *github.Reference, result2 error) {
fake.GetRefStub = nil
fake.getRefReturns = struct {
result1 *github.Reference
result2 error
}{result1, result2}
}
var _ resource.GitHub = new(FakeGitHub)

309
gitea.go Normal file
View File

@@ -0,0 +1,309 @@
package resource
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
"golang.org/x/oauth2"
"context"
"code.gitea.io/sdk/gitea"
)
type Gitea interface {
ListTags() ([]*gitea.Tag, error)
ListTagsUntil(tag_name string) ([]*gitea.Tag, error)
GetTag(tag_name string) (*gitea.Tag, error)
CreateTag(tag_name string, ref string) (*gitea.Tag, error)
GetReleaseByTag(tag_name string) (*gitea.Release, error)
CreateRelease(title string, tag_name string, body string) (*gitea.Release, error)
EditRelease(title string, tag_name string, release_id int64, body string) (*gitea.Release, error)
CreateAttachment(filePath string, release_id int64) (*gitea.Attachment, error)
GetAttachment(filePath, destPath string) error
}
type GiteaClient struct {
client *gitea.Client
baseUrl *url.URL
accessToken string
user string
repository string
}
func NewGiteaClient(source Source) (*GiteaClient, error) {
var httpClient = &http.Client{}
var ctx = context.TODO()
baseUrl, err := url.Parse(source.GiteaAPIURL)
if err != nil {
return nil, err
}
if source.Insecure {
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
}
client, err := gitea.NewClient(baseUrl.String(), gitea.SetHTTPClient(httpClient), gitea.SetToken(source.AccessToken))
if err != nil {
return nil, err
}
split := strings.SplitN(source.Repository, "/", 2)
return &GiteaClient{
client: client,
baseUrl: baseUrl,
user: split[0],
repository: split[1],
accessToken: source.AccessToken,
}, nil
}
func (g *GiteaClient) ListTags() ([]*gitea.Tag, error) {
var allTags []*gitea.Tag
opt := gitea.ListRepoTagsOptions{
ListOptions: gitea.ListOptions{
PageSize: 100,
Page: 1,
},
}
for {
tags, _, err := g.client.ListRepoTags(g.user, g.repository, opt)
if len(tags) == 0 {
break
}
if err != nil {
return []*gitea.Tag{}, err
}
allTags = append(allTags, tags...)
opt.Page++
}
return allTags, nil
}
func (g *GiteaClient) ListTagsUntil(tag_name string) ([]*gitea.Tag, error) {
var allTags []*gitea.Tag
pageSize := 100
opt := gitea.ListRepoTagsOptions{
ListOptions: gitea.ListOptions{
PageSize: pageSize,
Page: 1,
},
}
var foundTag *gitea.Tag
for {
tags, _, err := g.client.ListRepoTags(g.user, g.repository, opt)
if len(tags) == 0 {
break
}
if err != nil {
return []*gitea.Tag{}, err
}
skipToNextPage := false
for i, tag := range tags {
if foundTag != nil {
if foundTag.Commit.Created.Format("2006-01-02") == tag.Commit.Created.Format("2006-01-02") {
allTags = append(allTags, tag)
if i == (pageSize - 1) {
skipToNextPage = true
break
} else {
continue
}
} else {
break
}
}
if tag.Name == tag_name {
allTags = append(allTags, tags[:i+1]...)
foundTag = tag
continue
}
}
if skipToNextPage {
opt.Page++
continue
}
if foundTag != nil {
break
}
allTags = append(allTags, tags...)
opt.Page++
}
return allTags, nil
}
func (g *GiteaClient) GetTag(tag_name string) (*gitea.Tag, error) {
tag, res, err := g.client.GetTag(g.user, g.repository, tag_name)
if err != nil {
return &gitea.Tag{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return tag, nil
}
func (g *GiteaClient) CreateTag(ref string, tag_name string) (*gitea.Tag, error) {
opt := gitea.CreateTagOption{
TagName: tag_name,
Message: tag_name,
Target: ref,
}
tag, res, err := g.client.CreateTag(g.user, g.repository, opt)
if err != nil {
return &gitea.Tag{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return tag, nil
}
func (g *GiteaClient) GetReleaseByTag(tag_name string) (*gitea.Release, error) {
release, res, err := g.client.GetReleaseByTag(g.user, g.repository, tag_name)
if res.StatusCode == http.StatusNotFound {
return nil, nil
}
err = res.Body.Close()
if err != nil {
return nil, err
}
if err != nil {
return &gitea.Release{}, err
}
return release, nil
}
func (g *GiteaClient) CreateRelease(title string, tag_name string, body string) (*gitea.Release, error) {
opt := gitea.CreateReleaseOption{
Note: body,
TagName: tag_name,
Title: title,
}
release, res, err := g.client.CreateRelease(g.user, g.repository, opt)
if err != nil {
return &gitea.Release{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
if res.StatusCode == http.StatusConflict {
return nil, errors.New("release already exists")
}
return release, nil
}
func (g *GiteaClient) EditRelease(title string, tag_name string, release_id int64, body string) (*gitea.Release, error) {
opt := gitea.EditReleaseOption{
Note: body,
TagName: tag_name,
Title: title,
}
release, res, err := g.client.EditRelease(g.user, g.repository, release_id, opt)
if err != nil {
return &gitea.Release{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return release, nil
}
func (g *GiteaClient) CreateAttachment(filePath string, release_id int64) (*gitea.Attachment, error) {
f, err := os.Open(filePath)
if err != nil {
return &gitea.Attachment{}, err
}
var file io.Reader
file = f
attachment, _, err := g.client.CreateReleaseAttachment(g.user, g.repository, release_id, file, filepath.Base(filePath))
return attachment, err
}
func (g *GiteaClient) GetAttachment(filePath, destPath string) error {
out, err := os.Create(destPath)
if err != nil {
return err
}
defer out.Close()
filePathRef, err := url.Parse(filePath)
if err != nil {
return err
}
projectFileUrl := g.baseUrl.ResolveReference(filePathRef)
client := &http.Client{}
req, err := http.NewRequest("GET", projectFileUrl.String(), nil)
if err != nil {
return err
}
req.Header.Add("Authorization", "token "+g.accessToken)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to download file `%s`: HTTP status %d", filepath.Base(destPath), resp.StatusCode)
}
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}

272
github.go
View File

@@ -1,272 +0,0 @@
package resource
import (
"crypto/tls"
"errors"
"io"
"net/http"
"net/url"
"os"
"golang.org/x/oauth2"
"context"
"github.com/google/go-github/github"
)
//go:generate counterfeiter . GitHub
type GitHub interface {
ListReleases() ([]*github.RepositoryRelease, error)
GetReleaseByTag(tag string) (*github.RepositoryRelease, error)
GetRelease(id int) (*github.RepositoryRelease, error)
CreateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error)
UpdateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error)
ListReleaseAssets(release github.RepositoryRelease) ([]*github.ReleaseAsset, error)
UploadReleaseAsset(release github.RepositoryRelease, name string, file *os.File) error
DeleteReleaseAsset(asset github.ReleaseAsset) error
DownloadReleaseAsset(asset github.ReleaseAsset) (io.ReadCloser, error)
GetTarballLink(tag string) (*url.URL, error)
GetZipballLink(tag string) (*url.URL, error)
GetRef(tag string) (*github.Reference, error)
}
type GitHubClient struct {
client *github.Client
owner string
repository string
}
func NewGitHubClient(source Source) (*GitHubClient, error) {
var httpClient = &http.Client{}
var ctx = context.TODO()
if source.Insecure {
httpClient.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
}
if source.AccessToken != "" {
var err error
httpClient, err = oauthClient(ctx, source)
if err != nil {
return nil, err
}
}
client := github.NewClient(httpClient)
if source.GitHubAPIURL != "" {
var err error
client.BaseURL, err = url.Parse(source.GitHubAPIURL)
if err != nil {
return nil, err
}
client.UploadURL, err = url.Parse(source.GitHubAPIURL)
if err != nil {
return nil, err
}
}
if source.GitHubUploadsURL != "" {
var err error
client.UploadURL, err = url.Parse(source.GitHubUploadsURL)
if err != nil {
return nil, err
}
}
owner := source.Owner
if source.User != "" {
owner = source.User
}
return &GitHubClient{
client: client,
owner: owner,
repository: source.Repository,
}, nil
}
func (g *GitHubClient) ListReleases() ([]*github.RepositoryRelease, error) {
releases, res, err := g.client.Repositories.ListReleases(context.TODO(), g.owner, g.repository, nil)
if err != nil {
return []*github.RepositoryRelease{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return releases, nil
}
func (g *GitHubClient) GetReleaseByTag(tag string) (*github.RepositoryRelease, error) {
release, res, err := g.client.Repositories.GetReleaseByTag(context.TODO(), g.owner, g.repository, tag)
if err != nil {
return &github.RepositoryRelease{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return release, nil
}
func (g *GitHubClient) GetRelease(id int) (*github.RepositoryRelease, error) {
release, res, err := g.client.Repositories.GetRelease(context.TODO(), g.owner, g.repository, id)
if err != nil {
return &github.RepositoryRelease{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return release, nil
}
func (g *GitHubClient) CreateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error) {
createdRelease, res, err := g.client.Repositories.CreateRelease(context.TODO(), g.owner, g.repository, &release)
if err != nil {
return &github.RepositoryRelease{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return createdRelease, nil
}
func (g *GitHubClient) UpdateRelease(release github.RepositoryRelease) (*github.RepositoryRelease, error) {
if release.ID == nil {
return nil, errors.New("release did not have an ID: has it been saved yet?")
}
updatedRelease, res, err := g.client.Repositories.EditRelease(context.TODO(), g.owner, g.repository, *release.ID, &release)
if err != nil {
return &github.RepositoryRelease{}, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return updatedRelease, nil
}
func (g *GitHubClient) ListReleaseAssets(release github.RepositoryRelease) ([]*github.ReleaseAsset, error) {
assets, res, err := g.client.Repositories.ListReleaseAssets(context.TODO(), g.owner, g.repository, *release.ID, nil)
if err != nil {
return nil, err
}
err = res.Body.Close()
if err != nil {
return nil, err
}
return assets, nil
}
func (g *GitHubClient) UploadReleaseAsset(release github.RepositoryRelease, name string, file *os.File) error {
_, res, err := g.client.Repositories.UploadReleaseAsset(
context.TODO(),
g.owner,
g.repository,
*release.ID,
&github.UploadOptions{
Name: name,
},
file,
)
if err != nil {
return err
}
return res.Body.Close()
}
func (g *GitHubClient) DeleteReleaseAsset(asset github.ReleaseAsset) error {
res, err := g.client.Repositories.DeleteReleaseAsset(context.TODO(), g.owner, g.repository, *asset.ID)
if err != nil {
return err
}
return res.Body.Close()
}
func (g *GitHubClient) DownloadReleaseAsset(asset github.ReleaseAsset) (io.ReadCloser, error) {
res, redir, err := g.client.Repositories.DownloadReleaseAsset(context.TODO(), g.owner, g.repository, *asset.ID)
if err != nil {
return nil, err
}
if redir != "" {
resp, err := http.Get(redir)
if err != nil {
return nil, err
}
return resp.Body, nil
}
return res, err
}
func (g *GitHubClient) GetTarballLink(tag string) (*url.URL, error) {
opt := &github.RepositoryContentGetOptions{Ref: tag}
u, res, err := g.client.Repositories.GetArchiveLink(context.TODO(), g.owner, g.repository, github.Tarball, opt)
if err != nil {
return nil, err
}
res.Body.Close()
return u, nil
}
func (g *GitHubClient) GetZipballLink(tag string) (*url.URL, error) {
opt := &github.RepositoryContentGetOptions{Ref: tag}
u, res, err := g.client.Repositories.GetArchiveLink(context.TODO(), g.owner, g.repository, github.Zipball, opt)
if err != nil {
return nil, err
}
res.Body.Close()
return u, nil
}
func (g *GitHubClient) GetRef(tag string) (*github.Reference, error) {
ref, res, err := g.client.Git.GetRef(context.TODO(), g.owner, g.repository, "tags/"+tag)
if err != nil {
return nil, err
}
res.Body.Close()
return ref, nil
}
func oauthClient(ctx context.Context, source Source) (*http.Client, error) {
ts := oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: source.AccessToken,
})
oauthClient := oauth2.NewClient(ctx, ts)
githubHTTPClient := &http.Client{
Transport: oauthClient.Transport,
}
return githubHTTPClient, nil
}

View File

@@ -1,275 +0,0 @@
package resource_test
import (
"net/http"
. "github.com/concourse/github-release-resource"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
"github.com/google/go-github/github"
)
var _ = Describe("GitHub Client", func() {
var server *ghttp.Server
var client *GitHubClient
var source Source
BeforeEach(func() {
server = ghttp.NewServer()
})
JustBeforeEach(func() {
source.GitHubAPIURL = server.URL()
var err error
client, err = NewGitHubClient(source)
Ω(err).ShouldNot(HaveOccurred())
})
AfterEach(func() {
server.Close()
})
Context("with bad URLs", func() {
BeforeEach(func() {
source.AccessToken = "hello?"
})
It("returns an error if the API URL is bad", func() {
source.GitHubAPIURL = ":"
_, err := NewGitHubClient(source)
Ω(err).Should(HaveOccurred())
})
It("returns an error if the API URL is bad", func() {
source.GitHubUploadsURL = ":"
_, err := NewGitHubClient(source)
Ω(err).Should(HaveOccurred())
})
})
Context("with an OAuth Token", func() {
BeforeEach(func() {
source = Source{
Owner: "concourse",
Repository: "concourse",
AccessToken: "abc123",
}
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/releases"),
ghttp.RespondWith(200, "[]"),
ghttp.VerifyHeaderKV("Authorization", "Bearer abc123"),
),
)
})
It("sends one", func() {
_, err := client.ListReleases()
Ω(err).ShouldNot(HaveOccurred())
})
})
Context("without an OAuth Token", func() {
BeforeEach(func() {
source = Source{
Owner: "concourse",
Repository: "concourse",
}
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/releases"),
ghttp.RespondWith(200, "[]"),
ghttp.VerifyHeader(http.Header{"Authorization": nil}),
),
)
})
It("sends one", func() {
_, err := client.ListReleases()
Ω(err).ShouldNot(HaveOccurred())
})
})
Describe("when the source is configured with the deprecated user field", func() {
BeforeEach(func() {
source = Source{
User: "some-owner",
Repository: "some-repo",
}
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/some-owner/some-repo/releases"),
ghttp.RespondWith(200, "[]"),
),
)
})
It("uses the provided user as the owner", func() {
_, err := client.ListReleases()
Ω(err).ShouldNot(HaveOccurred())
})
})
Describe("GetRelease", func() {
BeforeEach(func() {
source = Source{
Owner: "concourse",
Repository: "concourse",
}
})
Context("When GitHub's rate limit has been exceeded", func() {
BeforeEach(func() {
rateLimitResponse := `{
"message": "API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
"documentation_url": "https://developer.github.com/v3/#rate-limiting"
}`
rateLimitHeaders := http.Header(map[string][]string{
"X-RateLimit-Limit": {"60"},
"X-RateLimit-Remaining": {"0"},
"X-RateLimit-Reset": {"1377013266"},
})
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/releases/20"),
ghttp.RespondWith(403, rateLimitResponse, rateLimitHeaders),
),
)
})
It("Returns an appropriate error", func() {
_, err := client.GetRelease(20)
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)"))
})
})
})
Describe("GetReleaseByTag", func() {
BeforeEach(func() {
source = Source{
Owner: "concourse",
Repository: "concourse",
}
})
Context("When GitHub's rate limit has been exceeded", func() {
BeforeEach(func() {
rateLimitResponse := `{
"message": "API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
"documentation_url": "https://developer.github.com/v3/#rate-limiting"
}`
rateLimitHeaders := http.Header(map[string][]string{
"X-RateLimit-Limit": {"60"},
"X-RateLimit-Remaining": {"0"},
"X-RateLimit-Reset": {"1377013266"},
})
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/releases/tags/some-tag"),
ghttp.RespondWith(403, rateLimitResponse, rateLimitHeaders),
),
)
})
It("Returns an appropriate error", func() {
_, err := client.GetReleaseByTag("some-tag")
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)"))
})
})
Context("When GitHub responds successfully", func() {
BeforeEach(func() {
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/releases/tags/some-tag"),
ghttp.RespondWith(200, `{ "id": 1 }`),
),
)
})
It("Returns a populated github.RepositoryRelease", func() {
expectedRelease := &github.RepositoryRelease{
ID: github.Int(1),
}
release, err := client.GetReleaseByTag("some-tag")
Ω(err).ShouldNot(HaveOccurred())
Expect(release).To(Equal(expectedRelease))
})
})
})
Describe("GetRef", func() {
BeforeEach(func() {
source = Source{
Owner: "concourse",
Repository: "concourse",
}
})
Context("When GitHub's rate limit has been exceeded", func() {
BeforeEach(func() {
rateLimitResponse := `{
"message": "API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)",
"documentation_url": "https://developer.github.com/v3/#rate-limiting"
}`
rateLimitHeaders := http.Header(map[string][]string{
"X-RateLimit-Limit": {"60"},
"X-RateLimit-Remaining": {"0"},
"X-RateLimit-Reset": {"1377013266"},
})
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/git/refs/tags/some-tag"),
ghttp.RespondWith(403, rateLimitResponse, rateLimitHeaders),
),
)
})
It("Returns an appropriate error", func() {
_, err := client.GetRef("some-tag")
Expect(err).ToNot(BeNil())
Expect(err.Error()).To(ContainSubstring("API rate limit exceeded for 127.0.0.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)"))
})
})
Context("When GitHub responds successfully", func() {
BeforeEach(func() {
server.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", "/repos/concourse/concourse/git/refs/tags/some-tag"),
ghttp.RespondWith(200, `{ "ref": "refs/tags/some-tag" }`),
),
)
})
It("Returns a populated github.Reference", func() {
expectedReference := &github.Reference{
Ref: github.String("refs/tags/some-tag"),
}
reference, err := client.GetRef("some-tag")
Ω(err).ShouldNot(HaveOccurred())
Expect(reference).To(Equal(expectedReference))
})
})
})
})

36
go.mod Normal file
View File

@@ -0,0 +1,36 @@
module github.com/natto1784/gitea-release-resource
go 1.17
require (
code.gitea.io/sdk/gitea v0.15.1
github.com/concourse/github-release-resource v1.0.0
github.com/cppforlife/go-semi-semantic v0.0.0-20160921010311-576b6af77ae4
github.com/google/go-github v17.0.0+incompatible
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db
github.com/onsi/ginkgo v1.7.0
github.com/onsi/gomega v1.4.3
github.com/xanzy/go-gitlab v0.12.0
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
)
require (
github.com/fsnotify/fsnotify v1.4.7 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hpcloud/tail v1.0.0 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/kr/pty v1.1.1 // indirect
github.com/kr/text v0.1.0 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.2.2 // indirect
)

424
go.sum Normal file
View File

@@ -0,0 +1,424 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M=
code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/concourse/github-release-resource v1.0.0 h1:jpN453IQGRXNZcyKdmIznnBNXU1p1ALKFDRYq6KblY4=
github.com/concourse/github-release-resource v1.0.0/go.mod h1:fTyLw17ZewAEU8MSqLNKj5cM4MSNhhi2uVHUALGZ1Zo=
github.com/cppforlife/go-semi-semantic v0.0.0-20160921010311-576b6af77ae4 h1:J+ghqo7ZubTzelkjo9hntpTtP/9lUCWH9icEmAW+B+Q=
github.com/cppforlife/go-semi-semantic v0.0.0-20160921010311-576b6af77ae4/go.mod h1:socxpf5+mELPbosI149vWpNlHK6mbfWFxSWOoSndXR8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/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-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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.4.0 h1:aAQzgqIrRKRa7w75CKpbBxYsmUoPjzVm1W59ca1L0J4=
github.com/hashicorp/go-version v1.4.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286 h1:KHyL+3mQOF9sPfs26lsefckcFNDcIZtiACQiECzIUkw=
github.com/mitchellh/colorstring v0.0.0-20150917214807-8631ce90f286/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/xanzy/go-gitlab v0.12.0 h1:rs40DfrKvJoIluarQJcFmOADVMlgFGDMXvnEeQpjqGg=
github.com/xanzy/go-gitlab v0.12.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849 h1:FSqE2GGG7wzsYUsWiQ8MZrvEd1EOyU3NCF0AW3Wtltg=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326 h1:iCzOf0xz39Tstp+Tu/WwyGjUXCk34QhQORRxBeXXTA4=
golang.org/x/net v0.0.0-20181217023233-e147a9138326/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288 h1:JIqe8uIcRBHXDQVvZtHwp80ai3Lw3IJAeJEs55Dc1W0=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6 h1:MXtOG7w2ND9qNCUZSDBGll/SpVIq7ftozR9I8/JGBHY=
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -2,25 +2,28 @@ package resource
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strings"
"github.com/google/go-github/github" "code.gitea.io/sdk/gitea"
) )
type InCommand struct { type InCommand struct {
github GitHub gitea Gitea
writer io.Writer writer io.Writer
} }
func NewInCommand(github GitHub, writer io.Writer) *InCommand { type attachment struct {
Name string
URL string
}
func NewInCommand(gitea Gitea, writer io.Writer) *InCommand {
return &InCommand{ return &InCommand{
github: github, gitea: gitea,
writer: writer, writer: writer,
} }
} }
@@ -31,81 +34,66 @@ func (c *InCommand) Run(destDir string, request InRequest) (InResponse, error) {
return InResponse{}, err return InResponse{}, err
} }
var foundRelease *github.RepositoryRelease var foundTag *gitea.Tag
var commitSHA string
if request.Version.Tag != "" { foundTag, err = c.gitea.GetTag(request.Version.Tag)
foundRelease, err = c.github.GetReleaseByTag(request.Version.Tag) if err != nil {
return InResponse{}, err
}
if foundTag == nil {
return InResponse{}, errors.New("could not find tag")
}
tagPath := filepath.Join(destDir, "tag")
err = ioutil.WriteFile(tagPath, []byte(foundTag.Name), 0644)
if err != nil {
return InResponse{}, err
}
versionParser, err := newVersionParser(request.Source.TagFilter)
if err != nil {
return InResponse{}, err
}
version := versionParser.parse(foundTag.Name)
versionPath := filepath.Join(destDir, "version")
err = ioutil.WriteFile(versionPath, []byte(version), 0644)
if err != nil {
return InResponse{}, err
}
commitPath := filepath.Join(destDir, "commit_sha")
err = ioutil.WriteFile(commitPath, []byte(foundTag.Commit.SHA), 0644)
if err != nil {
return InResponse{}, err
}
var release *gitea.Release
release, err = c.gitea.GetReleaseByTag(request.Version.Tag)
if err != nil {
return InResponse{}, err
}
if release != nil && release.Note != "" {
body := release.Note
bodyPath := filepath.Join(destDir, "body")
err = ioutil.WriteFile(bodyPath, []byte(body), 0644)
if err != nil {
return InResponse{}, err
}
} else { } else {
id, _ := strconv.Atoi(request.Version.ID) return InResponse{}, errors.New("release notes for the tag was empty")
foundRelease, err = c.github.GetRelease(id)
}
if err != nil {
return InResponse{}, err
} }
if foundRelease == nil { for _, attachment := range release.Attachments {
return InResponse{}, errors.New("no releases") path := filepath.Join(destDir, attachment.Name)
}
if foundRelease.TagName != nil && *foundRelease.TagName != "" {
tagPath := filepath.Join(destDir, "tag")
err = ioutil.WriteFile(tagPath, []byte(*foundRelease.TagName), 0644)
if err != nil {
return InResponse{}, err
}
versionParser, err := newVersionParser(request.Source.TagFilter)
if err != nil {
return InResponse{}, err
}
version := versionParser.parse(*foundRelease.TagName)
versionPath := filepath.Join(destDir, "version")
err = ioutil.WriteFile(versionPath, []byte(version), 0644)
if err != nil {
return InResponse{}, err
}
if foundRelease.Draft != nil && !*foundRelease.Draft {
commitPath := filepath.Join(destDir, "commit_sha")
commitSHA, err = c.resolveTagToCommitSHA(*foundRelease.TagName)
if err != nil {
return InResponse{}, err
}
if commitSHA != "" {
err = ioutil.WriteFile(commitPath, []byte(commitSHA), 0644)
if err != nil {
return InResponse{}, err
}
}
}
if foundRelease.Body != nil && *foundRelease.Body != "" {
body := *foundRelease.Body
bodyPath := filepath.Join(destDir, "body")
err = ioutil.WriteFile(bodyPath, []byte(body), 0644)
if err != nil {
return InResponse{}, err
}
}
}
assets, err := c.github.ListReleaseAssets(*foundRelease)
if err != nil {
return InResponse{}, err
}
for _, asset := range assets {
path := filepath.Join(destDir, *asset.Name)
var matchFound bool var matchFound bool
if len(request.Params.Globs) == 0 { if len(request.Params.Globs) == 0 {
matchFound = true matchFound = true
} else { } else {
for _, glob := range request.Params.Globs { for _, glob := range request.Params.Globs {
matches, err := filepath.Match(glob, *asset.Name) matches, err := filepath.Match(glob, attachment.Name)
if err != nil { if err != nil {
return InResponse{}, err return InResponse{}, err
} }
@@ -121,98 +109,42 @@ func (c *InCommand) Run(destDir string, request InRequest) (InResponse, error) {
continue continue
} }
fmt.Fprintf(c.writer, "downloading asset: %s\n", *asset.Name) err := c.gitea.GetAttachment(attachment.DownloadURL, path)
err := c.downloadAsset(asset, path)
if err != nil { if err != nil {
return InResponse{}, err return InResponse{}, err
} }
} }
if request.Params.IncludeSourceTarball {
u, err := c.github.GetTarballLink(request.Version.Tag)
if err != nil {
return InResponse{}, err
}
fmt.Fprintln(c.writer, "downloading source tarball to source.tar.gz")
if err := c.downloadFile(u.String(), filepath.Join(destDir, "source.tar.gz")); err != nil {
return InResponse{}, err
}
}
if request.Params.IncludeSourceZip {
u, err := c.github.GetZipballLink(request.Version.Tag)
if err != nil {
return InResponse{}, err
}
fmt.Fprintln(c.writer, "downloading source zip to source.zip")
if err := c.downloadFile(u.String(), filepath.Join(destDir, "source.zip")); err != nil {
return InResponse{}, err
}
}
return InResponse{ return InResponse{
Version: versionFromRelease(foundRelease), Version: versionFromRelease(release),
Metadata: metadataFromRelease(foundRelease, commitSHA), Metadata: metadataFromRelease(release),
}, nil }, nil
} }
func (c *InCommand) downloadAsset(asset *github.ReleaseAsset, destPath string) error { // This resource stores the attachments as line-separated markdown links.
out, err := os.Create(destPath) func (c *InCommand) getAttachments(releaseBody string) ([]attachment, error) {
if err != nil { var attachments []attachment
return err
}
defer out.Close()
content, err := c.github.DownloadReleaseAsset(*asset) lines := strings.Split(releaseBody, "\n")
if err != nil { for _, line := range lines {
return err nameStart := strings.Index(line, "[")
} nameEnd := strings.Index(line, "]")
defer content.Close() urlStart := strings.Index(line, "(")
urlEnd := strings.Index(line, ")")
if nameStart == -1 || nameEnd == -1 || urlStart == -1 || urlEnd == -1 {
continue
}
nameStart++
urlStart++
attachments = append(attachments, attachment{
Name: line[nameStart:nameEnd],
URL: line[urlStart:urlEnd],
})
_, err = io.Copy(out, content)
if err != nil {
return err
} }
return nil return attachments, nil
}
func (c *InCommand) downloadFile(url, destPath string) error {
out, err := os.Create(destPath)
if err != nil {
return err
}
defer out.Close()
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to download file `%s`: HTTP status %d", filepath.Base(destPath), resp.StatusCode)
}
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
return nil
}
func (c *InCommand) resolveTagToCommitSHA(tag string) (string, error) {
reference, err := c.github.GetRef(tag)
if err != nil {
return "", err
}
if *reference.Object.Type != "commit" {
fmt.Fprintf(c.writer, "could not resolve tag '%s' to commit: returned type is not 'commit' - only lightweight tags are supported\n", tag)
return "", err
}
return *reference.Object.SHA, err
} }

View File

@@ -1,566 +0,0 @@
package resource_test
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/ghttp"
"github.com/google/go-github/github"
"github.com/concourse/github-release-resource"
"github.com/concourse/github-release-resource/fakes"
)
var _ = Describe("In Command", func() {
var (
command *resource.InCommand
githubClient *fakes.FakeGitHub
githubServer *ghttp.Server
inRequest resource.InRequest
inResponse resource.InResponse
inErr error
tmpDir string
destDir string
)
BeforeEach(func() {
var err error
githubClient = &fakes.FakeGitHub{}
githubServer = ghttp.NewServer()
command = resource.NewInCommand(githubClient, ioutil.Discard)
tmpDir, err = ioutil.TempDir("", "github-release")
Ω(err).ShouldNot(HaveOccurred())
destDir = filepath.Join(tmpDir, "destination")
githubClient.DownloadReleaseAssetReturns(ioutil.NopCloser(bytes.NewBufferString("some-content")), nil)
inRequest = resource.InRequest{}
})
AfterEach(func() {
Ω(os.RemoveAll(tmpDir)).Should(Succeed())
})
buildRelease := func(id int, tag string, draft bool) *github.RepositoryRelease {
return &github.RepositoryRelease{
ID: github.Int(id),
TagName: github.String(tag),
HTMLURL: github.String("http://google.com"),
Name: github.String("release-name"),
Body: github.String("*markdown*"),
Draft: github.Bool(draft),
Prerelease: github.Bool(false),
}
}
buildNilTagRelease := func(id int) *github.RepositoryRelease {
return &github.RepositoryRelease{
ID: github.Int(id),
HTMLURL: github.String("http://google.com"),
Name: github.String("release-name"),
Body: github.String("*markdown*"),
Draft: github.Bool(true),
Prerelease: github.Bool(false),
}
}
buildAsset := func(id int, name string) *github.ReleaseAsset {
return &github.ReleaseAsset{
ID: github.Int(id),
Name: &name,
}
}
buildTagRef := func(tagRef, commitSHA string) *github.Reference {
return &github.Reference{
Ref: github.String(tagRef),
URL: github.String("https://example.com"),
Object: &github.GitObject{
Type: github.String("commit"),
SHA: github.String(commitSHA),
URL: github.String("https://example.com"),
},
}
}
Context("when there is a tagged release", func() {
Context("when a present version is specified", func() {
BeforeEach(func() {
githubClient.GetReleaseByTagReturns(buildRelease(1, "v0.35.0", false), nil)
githubClient.GetRefReturns(buildTagRef("v0.35.0", "f28085a4a8f744da83411f5e09fd7b1709149eee"), nil)
githubClient.ListReleaseAssetsReturns([]*github.ReleaseAsset{
buildAsset(0, "example.txt"),
buildAsset(1, "example.rtf"),
buildAsset(2, "example.wtf"),
}, nil)
inRequest.Version = &resource.Version{
Tag: "v0.35.0",
}
})
Context("when valid asset filename globs are given", func() {
BeforeEach(func() {
inRequest.Params = resource.InParams{
Globs: []string{"*.txt", "*.rtf"},
}
})
It("succeeds", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Ω(inErr).ShouldNot(HaveOccurred())
})
It("returns the fetched version", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Ω(inResponse.Version).Should(Equal(resource.Version{Tag: "v0.35.0"}))
})
It("has some sweet metadata", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Ω(inResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "v0.35.0"},
resource.MetadataPair{Name: "commit_sha", Value: "f28085a4a8f744da83411f5e09fd7b1709149eee"},
))
})
It("calls #GetReleastByTag with the correct arguments", func() {
command.Run(destDir, inRequest)
Ω(githubClient.GetReleaseByTagArgsForCall(0)).Should(Equal("v0.35.0"))
})
It("downloads only the files that match the globs", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(githubClient.DownloadReleaseAssetCallCount()).To(Equal(2))
Ω(githubClient.DownloadReleaseAssetArgsForCall(0)).Should(Equal(*buildAsset(0, "example.txt")))
Ω(githubClient.DownloadReleaseAssetArgsForCall(1)).Should(Equal(*buildAsset(1, "example.rtf")))
})
It("does create the body, tag and version files", func() {
inResponse, inErr = command.Run(destDir, inRequest)
contents, err := ioutil.ReadFile(path.Join(destDir, "tag"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("v0.35.0"))
contents, err = ioutil.ReadFile(path.Join(destDir, "version"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("0.35.0"))
contents, err = ioutil.ReadFile(path.Join(destDir, "commit_sha"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("f28085a4a8f744da83411f5e09fd7b1709149eee"))
contents, err = ioutil.ReadFile(path.Join(destDir, "body"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("*markdown*"))
})
Context("when there is a custom tag filter", func() {
BeforeEach(func() {
inRequest.Source = resource.Source{
TagFilter: "package-(.*)",
}
githubClient.GetReleaseByTagReturns(buildRelease(1, "package-0.35.0", false), nil)
githubClient.GetRefReturns(buildTagRef("package-0.35.0", "f28085a4a8f744da83411f5e09fd7b1709149eee"), nil)
inResponse, inErr = command.Run(destDir, inRequest)
})
It("succeeds", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).ToNot(HaveOccurred())
})
It("does create the body, tag and version files", func() {
inResponse, inErr = command.Run(destDir, inRequest)
contents, err := ioutil.ReadFile(path.Join(destDir, "tag"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("package-0.35.0"))
contents, err = ioutil.ReadFile(path.Join(destDir, "version"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("0.35.0"))
})
})
Context("when include_source_tarball is true", func() {
var tarballUrl *url.URL
BeforeEach(func() {
inRequest.Params.IncludeSourceTarball = true
tarballUrl, _ = url.Parse(githubServer.URL())
tarballUrl.Path = "/gimme-a-tarball/"
})
Context("when getting the tarball link succeeds", func() {
BeforeEach(func() {
githubClient.GetTarballLinkReturns(tarballUrl, nil)
})
Context("when downloading the tarball succeeds", func() {
BeforeEach(func() {
githubServer.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", tarballUrl.Path),
ghttp.RespondWith(http.StatusOK, "source-tar-file-contents"),
),
)
})
It("succeeds", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).ToNot(HaveOccurred())
})
It("downloads the source tarball", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(githubServer.ReceivedRequests()).To(HaveLen(1))
})
It("saves the source tarball in the destination directory", func() {
inResponse, inErr = command.Run(destDir, inRequest)
fileContents, err := ioutil.ReadFile(filepath.Join(destDir, "source.tar.gz"))
fContents := string(fileContents)
Expect(err).NotTo(HaveOccurred())
Expect(fContents).To(Equal("source-tar-file-contents"))
})
})
Context("when downloading the tarball fails", func() {
BeforeEach(func() {
githubServer.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", tarballUrl.Path),
ghttp.RespondWith(http.StatusInternalServerError, ""),
),
)
})
It("returns an appropriate error", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).To(MatchError("failed to download file `source.tar.gz`: HTTP status 500"))
})
})
})
Context("when getting the tarball link fails", func() {
disaster := errors.New("oh my")
BeforeEach(func() {
githubClient.GetTarballLinkReturns(nil, disaster)
})
It("returns the error", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).To(Equal(disaster))
})
})
})
Context("when include_source_zip is true", func() {
var zipUrl *url.URL
BeforeEach(func() {
inRequest.Params.IncludeSourceZip = true
zipUrl, _ = url.Parse(githubServer.URL())
zipUrl.Path = "/gimme-a-zip/"
})
Context("when getting the zip link succeeds", func() {
BeforeEach(func() {
githubClient.GetZipballLinkReturns(zipUrl, nil)
})
Context("when downloading the zip succeeds", func() {
BeforeEach(func() {
githubServer.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", zipUrl.Path),
ghttp.RespondWith(http.StatusOK, "source-zip-file-contents"),
),
)
})
It("succeeds", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).ToNot(HaveOccurred())
})
It("downloads the source zip", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(githubServer.ReceivedRequests()).To(HaveLen(1))
})
It("saves the source zip in the destination directory", func() {
inResponse, inErr = command.Run(destDir, inRequest)
fileContents, err := ioutil.ReadFile(filepath.Join(destDir, "source.zip"))
fContents := string(fileContents)
Expect(err).NotTo(HaveOccurred())
Expect(fContents).To(Equal("source-zip-file-contents"))
})
})
Context("when downloading the zip fails", func() {
BeforeEach(func() {
githubServer.AppendHandlers(
ghttp.CombineHandlers(
ghttp.VerifyRequest("GET", zipUrl.Path),
ghttp.RespondWith(http.StatusInternalServerError, ""),
),
)
})
It("returns an appropriate error", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).To(MatchError("failed to download file `source.zip`: HTTP status 500"))
})
})
})
Context("when getting the zip link fails", func() {
disaster := errors.New("oh my")
BeforeEach(func() {
githubClient.GetZipballLinkReturns(nil, disaster)
})
It("returns the error", func() {
inResponse, inErr = command.Run(destDir, inRequest)
Expect(inErr).To(Equal(disaster))
})
})
})
})
Context("when no globs are specified", func() {
BeforeEach(func() {
inRequest.Source = resource.Source{}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("succeeds", func() {
Ω(inErr).ShouldNot(HaveOccurred())
})
It("returns the fetched version", func() {
Ω(inResponse.Version).Should(Equal(resource.Version{Tag: "v0.35.0"}))
})
It("has some sweet metadata", func() {
Ω(inResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "v0.35.0"},
resource.MetadataPair{Name: "commit_sha", Value: "f28085a4a8f744da83411f5e09fd7b1709149eee"},
))
})
It("downloads all of the files", func() {
Ω(githubClient.DownloadReleaseAssetArgsForCall(0)).Should(Equal(*buildAsset(0, "example.txt")))
Ω(githubClient.DownloadReleaseAssetArgsForCall(1)).Should(Equal(*buildAsset(1, "example.rtf")))
Ω(githubClient.DownloadReleaseAssetArgsForCall(2)).Should(Equal(*buildAsset(2, "example.wtf")))
})
})
Context("when downloading an asset fails", func() {
BeforeEach(func() {
githubClient.DownloadReleaseAssetReturns(nil, errors.New("not this time"))
inResponse, inErr = command.Run(destDir, inRequest)
})
It("returns an error", func() {
Ω(inErr).Should(HaveOccurred())
})
})
Context("when listing release assets fails", func() {
disaster := errors.New("nope")
BeforeEach(func() {
githubClient.ListReleaseAssetsReturns(nil, disaster)
inResponse, inErr = command.Run(destDir, inRequest)
})
It("returns the error", func() {
Ω(inErr).Should(Equal(disaster))
})
})
})
})
Context("when no tagged release is present", func() {
BeforeEach(func() {
githubClient.GetReleaseByTagReturns(nil, nil)
inRequest.Version = &resource.Version{
Tag: "v0.40.0",
}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("returns an error", func() {
Ω(inErr).Should(MatchError("no releases"))
})
})
Context("when getting a tagged release fails", func() {
disaster := errors.New("nope")
BeforeEach(func() {
githubClient.GetReleaseByTagReturns(nil, disaster)
inRequest.Version = &resource.Version{
Tag: "some-tag",
}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("returns the error", func() {
Ω(inErr).Should(Equal(disaster))
})
})
Context("when there is a draft release", func() {
Context("which has a tag", func() {
BeforeEach(func() {
githubClient.GetReleaseReturns(buildRelease(1, "v0.35.0", true), nil)
inRequest.Version = &resource.Version{ID: "1"}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("succeeds", func() {
Ω(inErr).ShouldNot(HaveOccurred())
})
It("returns the fetched version", func() {
Ω(inResponse.Version).Should(Equal(resource.Version{ID: "1"}))
})
It("has some sweet metadata", func() {
Ω(inResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "v0.35.0"},
resource.MetadataPair{Name: "draft", Value: "true"},
))
})
It("does create the tag and version files", func() {
contents, err := ioutil.ReadFile(path.Join(destDir, "tag"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("v0.35.0"))
contents, err = ioutil.ReadFile(path.Join(destDir, "version"))
Ω(err).ShouldNot(HaveOccurred())
Ω(string(contents)).Should(Equal("0.35.0"))
})
})
Context("which has an empty tag", func() {
BeforeEach(func() {
githubClient.GetReleaseReturns(buildRelease(1, "", true), nil)
inRequest.Version = &resource.Version{ID: "1"}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("succeeds", func() {
Ω(inErr).ShouldNot(HaveOccurred())
})
It("returns the fetched version", func() {
Ω(inResponse.Version).Should(Equal(resource.Version{ID: "1"}))
})
It("has some sweet metadata", func() {
Ω(inResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: ""},
resource.MetadataPair{Name: "draft", Value: "true"},
))
})
It("does not create the tag and version files", func() {
Ω(path.Join(destDir, "tag")).ShouldNot(BeAnExistingFile())
Ω(path.Join(destDir, "version")).ShouldNot(BeAnExistingFile())
Ω(path.Join(destDir, "commit_sha")).ShouldNot(BeAnExistingFile())
})
})
Context("which has a nil tag", func() {
BeforeEach(func() {
githubClient.GetReleaseReturns(buildNilTagRelease(1), nil)
inRequest.Version = &resource.Version{ID: "1"}
inResponse, inErr = command.Run(destDir, inRequest)
})
It("succeeds", func() {
Ω(inErr).ShouldNot(HaveOccurred())
})
It("returns the fetched version", func() {
Ω(inResponse.Version).Should(Equal(resource.Version{ID: "1"}))
})
It("has some sweet metadata", func() {
Ω(inResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "draft", Value: "true"},
))
})
It("does not create the tag and version files", func() {
Ω(path.Join(destDir, "tag")).ShouldNot(BeAnExistingFile())
Ω(path.Join(destDir, "version")).ShouldNot(BeAnExistingFile())
Ω(path.Join(destDir, "commit_sha")).ShouldNot(BeAnExistingFile())
})
})
})
})

View File

@@ -1,63 +1,31 @@
package resource package resource
import "github.com/google/go-github/github" import "code.gitea.io/sdk/gitea"
func metadataFromRelease(release *github.RepositoryRelease, commitSHA string) []MetadataPair { func metadataFromRelease(release *gitea.Release) []MetadataPair {
metadata := []MetadataPair{} metadata := []MetadataPair{}
if release.Name != nil { if release.TagName != "" {
nameMeta := MetadataPair{ nameMeta := MetadataPair{
Name: "name", Name: "tag",
Value: *release.Name, Value: release.TagName,
}
if release.HTMLURL != nil {
nameMeta.URL = *release.HTMLURL
} }
metadata = append(metadata, nameMeta) metadata = append(metadata, nameMeta)
} }
if release.HTMLURL != nil { if release != nil && release.Note != "" {
metadata = append(metadata, MetadataPair{
Name: "url",
Value: *release.HTMLURL,
})
}
if release.Body != nil {
metadata = append(metadata, MetadataPair{ metadata = append(metadata, MetadataPair{
Name: "body", Name: "body",
Value: *release.Body, Value: release.Note,
Markdown: true, Markdown: true,
}) })
} }
if release.TagName != nil { if release != nil && release.Target != "" {
metadata = append(metadata, MetadataPair{
Name: "tag",
Value: *release.TagName,
})
}
if commitSHA != "" {
metadata = append(metadata, MetadataPair{ metadata = append(metadata, MetadataPair{
Name: "commit_sha", Name: "commit_sha",
Value: commitSHA, Value: release.Target,
})
}
if *release.Draft {
metadata = append(metadata, MetadataPair{
Name: "draft",
Value: "true",
})
}
if *release.Prerelease {
metadata = append(metadata, MetadataPair{
Name: "pre-release",
Value: "true",
}) })
} }
return metadata return metadata

View File

@@ -1,24 +1,22 @@
package resource package resource
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"github.com/google/go-github/github"
) )
type OutCommand struct { type OutCommand struct {
github GitHub gitea Gitea
writer io.Writer writer io.Writer
} }
func NewOutCommand(github GitHub, writer io.Writer) *OutCommand { func NewOutCommand(gitea Gitea, writer io.Writer) *OutCommand {
return &OutCommand{ return &OutCommand{
github: github, gitea: gitea,
writer: writer, writer: writer,
} }
} }
@@ -26,105 +24,39 @@ func NewOutCommand(github GitHub, writer io.Writer) *OutCommand {
func (c *OutCommand) Run(sourceDir string, request OutRequest) (OutResponse, error) { func (c *OutCommand) Run(sourceDir string, request OutRequest) (OutResponse, error) {
params := request.Params params := request.Params
name, err := c.fileContents(filepath.Join(sourceDir, request.Params.NamePath)) tag_name, err := c.fileContents(filepath.Join(sourceDir, request.Params.TagPath))
if err != nil { if err != nil {
return OutResponse{}, err return OutResponse{}, err
} }
tag, err := c.fileContents(filepath.Join(sourceDir, request.Params.TagPath)) tag_name = request.Params.TagPrefix + tag_name
if err != nil {
return OutResponse{}, err title := tag_name
if request.Params.TitlePath != "" {
title, err = c.fileContents(filepath.Join(sourceDir, request.Params.TitlePath))
if err != nil {
return OutResponse{}, err
}
} }
tag = request.Params.TagPrefix + tag body := "Auto-generated from Concourse Gitea Release Resource"
var body string
bodySpecified := false
if request.Params.BodyPath != "" { if request.Params.BodyPath != "" {
bodySpecified = true body, err = c.fileContents(filepath.Join(sourceDir, request.Params.TitlePath))
body, err = c.fileContents(filepath.Join(sourceDir, request.Params.BodyPath))
if err != nil { if err != nil {
return OutResponse{}, err return OutResponse{}, err
} }
} }
targetCommitish := "" release, err := c.gitea.GetReleaseByTag(tag_name)
if request.Params.CommitishPath != "" {
targetCommitish, err = c.fileContents(filepath.Join(sourceDir, request.Params.CommitishPath)) if release == nil {
if err != nil { release, err = c.gitea.CreateRelease(title, tag_name, body)
return OutResponse{}, err
}
}
draft := request.Source.Drafts
prerelease := false
if request.Source.PreRelease == true && request.Source.Release == false {
prerelease = request.Source.PreRelease
}
release := &github.RepositoryRelease{
Name: github.String(name),
TagName: github.String(tag),
Body: github.String(body),
Draft: github.Bool(draft),
Prerelease: github.Bool(prerelease),
TargetCommitish: github.String(targetCommitish),
}
existingReleases, err := c.github.ListReleases()
if err != nil {
return OutResponse{}, err
}
var existingRelease *github.RepositoryRelease
for _, e := range existingReleases {
if e.TagName != nil && *e.TagName == tag {
existingRelease = e
break
}
}
if existingRelease != nil {
releaseAssets, err := c.github.ListReleaseAssets(*existingRelease)
if err != nil {
return OutResponse{}, err
}
existingRelease.Name = github.String(name)
existingRelease.TargetCommitish = github.String(targetCommitish)
existingRelease.Draft = github.Bool(draft)
existingRelease.Prerelease = github.Bool(prerelease)
if bodySpecified {
existingRelease.Body = github.String(body)
} else {
existingRelease.Body = nil
}
for _, asset := range releaseAssets {
fmt.Fprintf(c.writer, "clearing existing asset: %s\n", *asset.Name)
err := c.github.DeleteReleaseAsset(*asset)
if err != nil {
return OutResponse{}, err
}
}
fmt.Fprintf(c.writer, "updating release %s\n", name)
release, err = c.github.UpdateRelease(*existingRelease)
if err != nil {
return OutResponse{}, err
}
} else {
fmt.Fprintf(c.writer, "creating release %s\n", name)
release, err = c.github.CreateRelease(*release)
if err != nil { if err != nil {
return OutResponse{}, err return OutResponse{}, err
} }
} }
// upload files
for _, fileGlob := range params.Globs { for _, fileGlob := range params.Globs {
matches, err := filepath.Glob(filepath.Join(sourceDir, fileGlob)) matches, err := filepath.Glob(filepath.Join(sourceDir, fileGlob))
if err != nil { if err != nil {
@@ -136,16 +68,22 @@ func (c *OutCommand) Run(sourceDir string, request OutRequest) (OutResponse, err
} }
for _, filePath := range matches { for _, filePath := range matches {
err := c.upload(release, filePath) _, err := c.gitea.CreateAttachment(filePath, release.ID)
if err != nil { if err != nil {
return OutResponse{}, err return OutResponse{}, err
} }
} }
} }
// update the release
_, err = c.gitea.EditRelease(title, tag_name, release.ID, body)
if err != nil {
return OutResponse{}, errors.New("could not get saved tag")
}
return OutResponse{ return OutResponse{
Version: versionFromRelease(release), Version: versionFromRelease(release),
Metadata: metadataFromRelease(release, ""), Metadata: metadataFromRelease(release),
}, nil }, nil
} }
@@ -157,45 +95,3 @@ func (c *OutCommand) fileContents(path string) (string, error) {
return strings.TrimSpace(string(contents)), nil return strings.TrimSpace(string(contents)), nil
} }
func (c *OutCommand) upload(release *github.RepositoryRelease, filePath string) error {
fmt.Fprintf(c.writer, "uploading %s\n", filePath)
name := filepath.Base(filePath)
var retryErr error
for i := 0; i < 10; i++ {
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
retryErr = c.github.UploadReleaseAsset(*release, name, file)
if retryErr == nil {
break
}
assets, err := c.github.ListReleaseAssets(*release)
if err != nil {
return err
}
for _, asset := range assets {
if asset.Name != nil && *asset.Name == name {
err = c.github.DeleteReleaseAsset(*asset)
if err != nil {
return err
}
break
}
}
}
if retryErr != nil {
return retryErr
}
return nil
}

View File

@@ -1,589 +0,0 @@
package resource_test
import (
"errors"
"io/ioutil"
"os"
"path/filepath"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/concourse/github-release-resource"
"github.com/concourse/github-release-resource/fakes"
"github.com/google/go-github/github"
)
func file(path, contents string) {
Ω(ioutil.WriteFile(path, []byte(contents), 0644)).Should(Succeed())
}
var _ = Describe("Out Command", func() {
var (
command *resource.OutCommand
githubClient *fakes.FakeGitHub
sourcesDir string
request resource.OutRequest
)
BeforeEach(func() {
var err error
githubClient = &fakes.FakeGitHub{}
command = resource.NewOutCommand(githubClient, ioutil.Discard)
sourcesDir, err = ioutil.TempDir("", "github-release")
Ω(err).ShouldNot(HaveOccurred())
githubClient.CreateReleaseStub = func(gh github.RepositoryRelease) (*github.RepositoryRelease, error) {
createdRel := gh
createdRel.ID = github.Int(112)
createdRel.HTMLURL = github.String("http://google.com")
createdRel.Name = github.String("release-name")
createdRel.Body = github.String("*markdown*")
return &createdRel, nil
}
githubClient.UpdateReleaseStub = func(gh github.RepositoryRelease) (*github.RepositoryRelease, error) {
return &gh, nil
}
})
AfterEach(func() {
Ω(os.RemoveAll(sourcesDir)).Should(Succeed())
})
Context("when the release has already been created", func() {
existingAssets := []github.ReleaseAsset{
{
ID: github.Int(456789),
Name: github.String("unicorns.txt"),
},
{
ID: github.Int(3450798),
Name: github.String("rainbows.txt"),
State: github.String("new"),
},
}
existingReleases := []github.RepositoryRelease{
{
ID: github.Int(1),
Draft: github.Bool(true),
},
{
ID: github.Int(112),
TagName: github.String("some-tag-name"),
Assets: []github.ReleaseAsset{existingAssets[0]},
Draft: github.Bool(false),
},
}
BeforeEach(func() {
githubClient.ListReleasesStub = func() ([]*github.RepositoryRelease, error) {
rels := []*github.RepositoryRelease{}
for _, r := range existingReleases {
c := r
rels = append(rels, &c)
}
return rels, nil
}
githubClient.ListReleaseAssetsStub = func(github.RepositoryRelease) ([]*github.ReleaseAsset, error) {
assets := []*github.ReleaseAsset{}
for _, a := range existingAssets {
c := a
assets = append(assets, &c)
}
return assets, nil
}
namePath := filepath.Join(sourcesDir, "name")
bodyPath := filepath.Join(sourcesDir, "body")
tagPath := filepath.Join(sourcesDir, "tag")
file(namePath, "v0.3.12")
file(bodyPath, "this is a great release")
file(tagPath, "some-tag-name")
request = resource.OutRequest{
Params: resource.OutParams{
NamePath: "name",
BodyPath: "body",
TagPath: "tag",
},
}
})
It("deletes the existing assets", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.ListReleaseAssetsCallCount()).Should(Equal(1))
Ω(githubClient.ListReleaseAssetsArgsForCall(0)).Should(Equal(existingReleases[1]))
Ω(githubClient.DeleteReleaseAssetCallCount()).Should(Equal(2))
Ω(githubClient.DeleteReleaseAssetArgsForCall(0)).Should(Equal(existingAssets[0]))
Ω(githubClient.DeleteReleaseAssetArgsForCall(1)).Should(Equal(existingAssets[1]))
})
Context("when not set as a draft release", func() {
BeforeEach(func() {
request.Source.Drafts = false
})
It("updates the existing release to a non-draft", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UpdateReleaseCallCount()).Should(Equal(1))
updatedRelease := githubClient.UpdateReleaseArgsForCall(0)
Ω(*updatedRelease.Name).Should(Equal("v0.3.12"))
Ω(*updatedRelease.Draft).Should(Equal(false))
})
})
Context("when set as a draft release", func() {
BeforeEach(func() {
request.Source.Drafts = true
})
It("updates the existing release to a draft", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UpdateReleaseCallCount()).Should(Equal(1))
updatedRelease := githubClient.UpdateReleaseArgsForCall(0)
Ω(*updatedRelease.Name).Should(Equal("v0.3.12"))
Ω(*updatedRelease.Draft).Should(Equal(true))
})
})
Context("when a body is not supplied", func() {
BeforeEach(func() {
request.Params.BodyPath = ""
})
It("does not blow away the body", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UpdateReleaseCallCount()).Should(Equal(1))
updatedRelease := githubClient.UpdateReleaseArgsForCall(0)
Ω(*updatedRelease.Name).Should(Equal("v0.3.12"))
Ω(updatedRelease.Body).Should(BeNil())
})
})
Context("when a commitish is not supplied", func() {
It("updates the existing release", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UpdateReleaseCallCount()).Should(Equal(1))
updatedRelease := githubClient.UpdateReleaseArgsForCall(0)
Ω(*updatedRelease.Name).Should(Equal("v0.3.12"))
Ω(*updatedRelease.Body).Should(Equal("this is a great release"))
Ω(updatedRelease.TargetCommitish).Should(Equal(github.String("")))
})
})
Context("when a commitish is supplied", func() {
BeforeEach(func() {
commitishPath := filepath.Join(sourcesDir, "commitish")
file(commitishPath, "1z22f1")
request.Params.CommitishPath = "commitish"
})
It("updates the existing release", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UpdateReleaseCallCount()).Should(Equal(1))
updatedRelease := githubClient.UpdateReleaseArgsForCall(0)
Ω(*updatedRelease.Name).Should(Equal("v0.3.12"))
Ω(*updatedRelease.Body).Should(Equal("this is a great release"))
Ω(updatedRelease.TargetCommitish).Should(Equal(github.String("1z22f1")))
})
})
})
Context("when the release has not already been created", func() {
BeforeEach(func() {
namePath := filepath.Join(sourcesDir, "name")
tagPath := filepath.Join(sourcesDir, "tag")
file(namePath, "v0.3.12")
file(tagPath, "0.3.12")
request = resource.OutRequest{
Params: resource.OutParams{
NamePath: "name",
TagPath: "tag",
},
}
})
Context("with a commitish", func() {
BeforeEach(func() {
commitishPath := filepath.Join(sourcesDir, "commitish")
file(commitishPath, "a2f4a3")
request.Params.CommitishPath = "commitish"
})
It("creates a release on GitHub with the commitish", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(release.TargetCommitish).Should(Equal(github.String("a2f4a3")))
})
})
Context("without a commitish", func() {
It("creates a release on GitHub without the commitish", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
// GitHub treats empty string the same as not suppying the field.
Ω(release.TargetCommitish).Should(Equal(github.String("")))
})
})
Context("with a body", func() {
BeforeEach(func() {
bodyPath := filepath.Join(sourcesDir, "body")
file(bodyPath, "this is a great release")
request.Params.BodyPath = "body"
})
It("creates a release on GitHub", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("0.3.12"))
Ω(*release.Body).Should(Equal("this is a great release"))
})
})
Context("without a body", func() {
It("works", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("0.3.12"))
Ω(*release.Body).Should(Equal(""))
})
})
It("always defaults to non-draft mode", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Draft).Should(Equal(false))
})
Context("when pre-release are set and release are not", func() {
BeforeEach(func() {
bodyPath := filepath.Join(sourcesDir, "body")
file(bodyPath, "this is a great release")
request.Source.Release = false
request.Source.PreRelease = true
})
It("creates a non-draft pre-release in Github", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("0.3.12"))
Ω(*release.Body).Should(Equal(""))
Ω(*release.Draft).Should(Equal(false))
Ω(*release.Prerelease).Should(Equal(true))
})
It("has some sweet metadata", func() {
outResponse, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(outResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "0.3.12"},
resource.MetadataPair{Name: "pre-release", Value: "true"},
))
})
})
Context("when release and pre-release are set", func() {
BeforeEach(func() {
bodyPath := filepath.Join(sourcesDir, "body")
file(bodyPath, "this is a great release")
request.Source.Release = true
request.Source.PreRelease = true
})
It("creates a final release in Github", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("0.3.12"))
Ω(*release.Body).Should(Equal(""))
Ω(*release.Draft).Should(Equal(false))
Ω(*release.Prerelease).Should(Equal(false))
})
It("has some sweet metadata", func() {
outResponse, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(outResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "0.3.12"},
))
})
})
Context("when set as a draft release", func() {
BeforeEach(func() {
bodyPath := filepath.Join(sourcesDir, "body")
file(bodyPath, "this is a great release")
request.Source.Drafts = true
})
It("creates a release on GitHub in draft mode", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("0.3.12"))
Ω(*release.Body).Should(Equal(""))
Ω(*release.Draft).Should(Equal(true))
Ω(*release.Prerelease).Should(Equal(false))
})
It("has some sweet metadata", func() {
outResponse, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(outResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "0.3.12"},
resource.MetadataPair{Name: "draft", Value: "true"},
))
})
})
Context("with file globs", func() {
BeforeEach(func() {
globMatching := filepath.Join(sourcesDir, "great-file.tgz")
globNotMatching := filepath.Join(sourcesDir, "bad-file.txt")
file(globMatching, "matching")
file(globNotMatching, "not matching")
request = resource.OutRequest{
Params: resource.OutParams{
NamePath: "name",
BodyPath: "body",
TagPath: "tag",
Globs: []string{
"*.tgz",
},
},
}
bodyPath := filepath.Join(sourcesDir, "body")
file(bodyPath, "*markdown*")
request.Params.BodyPath = "body"
})
It("uploads matching file globs", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.UploadReleaseAssetCallCount()).Should(Equal(1))
release, name, file := githubClient.UploadReleaseAssetArgsForCall(0)
Ω(*release.ID).Should(Equal(112))
Ω(name).Should(Equal("great-file.tgz"))
Ω(file.Name()).Should(Equal(filepath.Join(sourcesDir, "great-file.tgz")))
})
It("has some sweet metadata", func() {
outResponse, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(outResponse.Metadata).Should(ConsistOf(
resource.MetadataPair{Name: "url", Value: "http://google.com"},
resource.MetadataPair{Name: "name", Value: "release-name", URL: "http://google.com"},
resource.MetadataPair{Name: "body", Value: "*markdown*", Markdown: true},
resource.MetadataPair{Name: "tag", Value: "0.3.12"},
))
})
It("returns an error if a glob is provided that does not match any files", func() {
request.Params.Globs = []string{
"*.tgz",
"*.gif",
}
_, err := command.Run(sourcesDir, request)
Ω(err).Should(HaveOccurred())
Ω(err).Should(MatchError("could not find file that matches glob '*.gif'"))
})
Context("when upload release asset fails", func() {
BeforeEach(func() {
existingAsset := false
githubClient.DeleteReleaseAssetStub = func(github.ReleaseAsset) error {
existingAsset = false
return nil
}
githubClient.ListReleaseAssetsReturns([]*github.ReleaseAsset{
{
ID: github.Int(456789),
Name: github.String("great-file.tgz"),
},
{
ID: github.Int(3450798),
Name: github.String("whatever.tgz"),
},
}, nil)
githubClient.UploadReleaseAssetStub = func(rel github.RepositoryRelease, name string, file *os.File) error {
Expect(ioutil.ReadAll(file)).To(Equal([]byte("matching")))
Expect(existingAsset).To(BeFalse())
existingAsset = true
return errors.New("some-error")
}
})
It("retries 10 times", func() {
_, err := command.Run(sourcesDir, request)
Expect(err).To(Equal(errors.New("some-error")))
Ω(githubClient.UploadReleaseAssetCallCount()).Should(Equal(10))
Ω(githubClient.ListReleaseAssetsCallCount()).Should(Equal(10))
Ω(*githubClient.ListReleaseAssetsArgsForCall(9).ID).Should(Equal(112))
actualRelease, actualName, actualFile := githubClient.UploadReleaseAssetArgsForCall(9)
Ω(*actualRelease.ID).Should(Equal(112))
Ω(actualName).Should(Equal("great-file.tgz"))
Ω(actualFile.Name()).Should(Equal(filepath.Join(sourcesDir, "great-file.tgz")))
Ω(githubClient.DeleteReleaseAssetCallCount()).Should(Equal(10))
actualAsset := githubClient.DeleteReleaseAssetArgsForCall(8)
Expect(*actualAsset.ID).To(Equal(456789))
})
Context("when uploading succeeds on the 5th attempt", func() {
BeforeEach(func() {
results := make(chan error, 6)
results <- errors.New("1")
results <- errors.New("2")
results <- errors.New("3")
results <- errors.New("4")
results <- nil
results <- errors.New("6")
githubClient.UploadReleaseAssetStub = func(github.RepositoryRelease, string, *os.File) error {
return <-results
}
})
It("succeeds", func() {
_, err := command.Run(sourcesDir, request)
Expect(err).ToNot(HaveOccurred())
Ω(githubClient.UploadReleaseAssetCallCount()).Should(Equal(5))
Ω(githubClient.ListReleaseAssetsCallCount()).Should(Equal(4))
Ω(*githubClient.ListReleaseAssetsArgsForCall(3).ID).Should(Equal(112))
actualRelease, actualName, actualFile := githubClient.UploadReleaseAssetArgsForCall(4)
Ω(*actualRelease.ID).Should(Equal(112))
Ω(actualName).Should(Equal("great-file.tgz"))
Ω(actualFile.Name()).Should(Equal(filepath.Join(sourcesDir, "great-file.tgz")))
Ω(githubClient.DeleteReleaseAssetCallCount()).Should(Equal(4))
actualAsset := githubClient.DeleteReleaseAssetArgsForCall(3)
Expect(*actualAsset.ID).To(Equal(456789))
})
})
})
})
Context("when the tag_prefix is set", func() {
BeforeEach(func() {
namePath := filepath.Join(sourcesDir, "name")
tagPath := filepath.Join(sourcesDir, "tag")
file(namePath, "v0.3.12")
file(tagPath, "0.3.12")
request = resource.OutRequest{
Params: resource.OutParams{
NamePath: "name",
TagPath: "tag",
TagPrefix: "version-",
},
}
})
It("appends the TagPrefix onto the TagName", func() {
_, err := command.Run(sourcesDir, request)
Ω(err).ShouldNot(HaveOccurred())
Ω(githubClient.CreateReleaseCallCount()).Should(Equal(1))
release := githubClient.CreateReleaseArgsForCall(0)
Ω(*release.Name).Should(Equal("v0.3.12"))
Ω(*release.TagName).Should(Equal("version-0.3.12"))
})
})
})
})

View File

@@ -1,48 +0,0 @@
package resource_test
import (
"testing"
"github.com/google/go-github/github"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestGithubReleaseResource(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Github Release Resource Suite")
}
func newRepositoryRelease(id int, version string) *github.RepositoryRelease {
return &github.RepositoryRelease{
TagName: github.String(version),
Draft: github.Bool(false),
Prerelease: github.Bool(false),
ID: github.Int(id),
}
}
func newPreReleaseRepositoryRelease(id int, version string) *github.RepositoryRelease {
return &github.RepositoryRelease{
TagName: github.String(version),
Draft: github.Bool(false),
Prerelease: github.Bool(true),
ID: github.Int(id),
}
}
func newDraftRepositoryRelease(id int, version string) *github.RepositoryRelease {
return &github.RepositoryRelease{
TagName: github.String(version),
Draft: github.Bool(true),
Prerelease: github.Bool(false),
ID: github.Int(id),
}
}
func newDraftWithNilTagRepositoryRelease(id int) *github.RepositoryRelease {
return &github.RepositoryRelease{
Draft: github.Bool(true),
Prerelease: github.Bool(false),
ID: github.Int(id),
}
}

View File

@@ -1,19 +1,11 @@
package resource package resource
type Source struct { type Source struct {
Owner string `json:"owner"`
Repository string `json:"repository"` Repository string `json:"repository"`
// Deprecated; use Owner instead GiteaAPIURL string `json:"gitea_api_url"`
User string `json:"user"` AccessToken string `json:"access_token"`
Insecure bool `json:"insecure"`
GitHubAPIURL string `json:"github_api_url"`
GitHubUploadsURL string `json:"github_uploads_url"`
AccessToken string `json:"access_token"`
Drafts bool `json:"drafts"`
PreRelease bool `json:"pre_release"`
Release bool `json:"release"`
Insecure bool `json:"insecure"`
TagFilter string `json:"tag_filter"` TagFilter string `json:"tag_filter"`
} }
@@ -25,19 +17,16 @@ type CheckRequest struct {
func NewCheckRequest() CheckRequest { func NewCheckRequest() CheckRequest {
res := CheckRequest{} res := CheckRequest{}
res.Source.Release = true
return res return res
} }
func NewOutRequest() OutRequest { func NewOutRequest() OutRequest {
res := OutRequest{} res := OutRequest{}
res.Source.Release = true
return res return res
} }
func NewInRequest() InRequest { func NewInRequest() InRequest {
res := InRequest{} res := InRequest{}
res.Source.Release = true
return res return res
} }
@@ -67,6 +56,7 @@ type OutParams struct {
NamePath string `json:"name"` NamePath string `json:"name"`
BodyPath string `json:"body"` BodyPath string `json:"body"`
TagPath string `json:"tag"` TagPath string `json:"tag"`
TitlePath string `json:"title"`
CommitishPath string `json:"commitish"` CommitishPath string `json:"commitish"`
TagPrefix string `json:"tag_prefix"` TagPrefix string `json:"tag_prefix"`
@@ -79,8 +69,8 @@ type OutResponse struct {
} }
type Version struct { type Version struct {
Tag string `json:"tag,omitempty"` Tag string `json:"tag,omitempty"`
ID string `json:"id,omitempty"` CommitSHA string `json:"commit_sha,omitempty"`
} }
type MetadataPair struct { type MetadataPair struct {

View File

@@ -1,2 +0,0 @@
#!/bin/bash
godep save ./... github.com/onsi/ginkgo/ginkgo

View File

@@ -1,20 +0,0 @@
Copyright (c) 2014 Dmitriy Kalinin
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,51 +0,0 @@
package version
import (
"errors"
"regexp"
"strconv"
)
var (
verSegCompIntRegexp = regexp.MustCompile(`\A[0-9]+\z`)
)
type VerSegCompInt struct{ I int }
func NewVerSegCompIntFromString(piece string) (VerSegCompInt, bool, error) {
if !verSegCompIntRegexp.MatchString(piece) {
return VerSegCompInt{}, false, nil
}
i, err := strconv.Atoi(piece)
if err != nil {
return VerSegCompInt{}, true, err
}
return VerSegCompInt{i}, true, nil
}
func (i VerSegCompInt) Validate() error {
if i.I < 0 {
return errors.New("Expected integer component to be greater than or equal to 0")
}
return nil
}
func (i VerSegCompInt) Compare(other VerSegComp) int {
otherTyped := other.(VerSegCompInt)
switch {
case i.I < otherTyped.I:
return -1
case i.I == otherTyped.I:
return 0
case i.I > otherTyped.I:
return 1
}
panic("unreachable")
}
func (i VerSegCompInt) String() string { return i.AsString() }
func (i VerSegCompInt) AsString() string { return strconv.Itoa(i.I) }

View File

@@ -1,45 +0,0 @@
package version
import (
"errors"
"regexp"
)
var (
verSegCompStrRegexp = regexp.MustCompile(`\A[0-9A-Za-z_\-]+\z`)
)
type VerSegCompStr struct{ S string }
func NewVerSegCompStrFromString(piece string) (VerSegCompStr, bool) {
if !verSegCompStrRegexp.MatchString(piece) {
return VerSegCompStr{}, false
}
return VerSegCompStr{piece}, true
}
func (s VerSegCompStr) Validate() error {
if len(s.S) == 0 {
return errors.New("Expected string component to be non-empty")
}
return nil
}
func (s VerSegCompStr) Compare(other VerSegComp) int {
otherTyped := other.(VerSegCompStr)
switch {
case s.S < otherTyped.S:
return -1
case s.S == otherTyped.S:
return 0
case s.S > otherTyped.S:
return 1
}
panic("unreachable")
}
func (s VerSegCompStr) String() string { return s.AsString() }
func (s VerSegCompStr) AsString() string { return s.S }

View File

@@ -1,192 +0,0 @@
package version
import (
"errors"
"fmt"
"regexp"
)
var (
versionRegexp = matchingRegexp{regexp.MustCompile(`\A(?P<release>[0-9A-Za-z_\.]+)(\-(?P<pre_release>[0-9A-Za-z_\-\.]+))?(\+(?P<post_release>[0-9A-Za-z_\-\.]+))?\z`)}
)
type matchingRegexp struct {
*regexp.Regexp
}
type Version struct {
Release, PreRelease, PostRelease VersionSegment
Segments []VersionSegment
}
func MustNewVersionFromString(v string) Version {
ver, err := NewVersionFromString(v)
if err != nil {
panic(fmt.Sprintf("Invalid version '%s': %s", v, err))
}
return ver
}
func NewVersionFromString(v string) (Version, error) {
var err error
if len(v) == 0 {
return Version{}, errors.New("Expected version to be non-empty string")
}
captures := versionRegexp.FindStringSubmatchMap(v)
if len(captures) == 0 {
errMsg := fmt.Sprintf("Expected version '%s' to match version format", v)
return Version{}, errors.New(errMsg)
}
release := VersionSegment{}
preRelease := VersionSegment{}
postRelease := VersionSegment{}
if releaseStr, ok := captures["release"]; ok {
release, err = NewVersionSegmentFromString(releaseStr)
if err != nil {
return Version{}, err
}
}
if preReleaseStr, ok := captures["pre_release"]; ok {
preRelease, err = NewVersionSegmentFromString(preReleaseStr)
if err != nil {
return Version{}, err
}
}
if postReleaseStr, ok := captures["post_release"]; ok {
postRelease, err = NewVersionSegmentFromString(postReleaseStr)
if err != nil {
return Version{}, err
}
}
return NewVersion(release, preRelease, postRelease)
}
func NewVersion(release, preRelease, postRelease VersionSegment) (Version, error) {
if release.Empty() {
return Version{}, errors.New("Expected to non-empty release segment for constructing version")
}
version := Version{
Release: release,
PreRelease: preRelease,
PostRelease: postRelease,
Segments: []VersionSegment{release, preRelease, postRelease},
}
return version, nil
}
func (v Version) IncrementRelease() (Version, error) {
incRelease, err := v.Release.Increment()
if err != nil {
return Version{}, err
}
return NewVersion(incRelease, VersionSegment{}, VersionSegment{})
}
func (v Version) IncrementPostRelease(defaultPostRelease VersionSegment) (Version, error) {
var newPostRelease VersionSegment
var err error
if defaultPostRelease.Empty() {
return Version{}, errors.New("Expected default post relase to be non-empty")
}
if v.PostRelease.Empty() {
newPostRelease = defaultPostRelease.Copy()
} else {
newPostRelease, err = v.PostRelease.Increment()
if err != nil {
return Version{}, err
}
}
return NewVersion(v.Release.Copy(), v.PreRelease.Copy(), newPostRelease)
}
func (v Version) Empty() bool { return len(v.Segments) == 0 }
func (v Version) String() string { return v.AsString() }
func (v Version) AsString() string {
result := v.Release.AsString()
if !v.PreRelease.Empty() {
result += "-" + v.PreRelease.AsString()
}
if !v.PostRelease.Empty() {
result += "+" + v.PostRelease.AsString()
}
return result
}
func (v Version) Compare(other Version) int {
result := v.Release.Compare(other.Release)
if result != 0 {
return result
}
if !v.PreRelease.Empty() || !other.PreRelease.Empty() {
if v.PreRelease.Empty() {
return 1
}
if other.PreRelease.Empty() {
return -1
}
result = v.PreRelease.Compare(other.PreRelease)
if result != 0 {
return result
}
}
if !v.PostRelease.Empty() || !other.PostRelease.Empty() {
if v.PostRelease.Empty() {
return -1
}
if other.PostRelease.Empty() {
return 1
}
result = v.PostRelease.Compare(other.PostRelease)
if result != 0 {
return result
}
}
return 0
}
func (v Version) IsEq(other Version) bool { return v.Compare(other) == 0 }
func (v Version) IsGt(other Version) bool { return v.Compare(other) == 1 }
func (v Version) IsLt(other Version) bool { return v.Compare(other) == -1 }
func (r *matchingRegexp) FindStringSubmatchMap(s string) map[string]string {
captures := map[string]string{}
match := r.FindStringSubmatch(s)
if match == nil {
return captures
}
for i, name := range r.SubexpNames() {
// 0 is a whole regex
if i == 0 || name == "" || match[i] == "" {
continue
}
captures[name] = match[i]
}
return captures
}

View File

@@ -1,182 +0,0 @@
package version
import (
"errors"
"fmt"
"strings"
)
type VerSegComp interface {
Validate() error
// Compare should panic if incompatible interface is given
Compare(VerSegComp) int
AsString() string
}
type VersionSegment struct {
Components []VerSegComp
}
func MustNewVersionSegmentFromString(v string) VersionSegment {
verSeg, err := NewVersionSegmentFromString(v)
if err != nil {
panic(fmt.Sprintf("Invalid version segment '%s': %s", v, err))
}
return verSeg
}
func NewVersionSegmentFromString(v string) (VersionSegment, error) {
pieces := strings.Split(v, ".")
components := []VerSegComp{}
for _, p := range pieces {
i, matchedI, err := NewVerSegCompIntFromString(p)
if err != nil {
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be a parseable integer: %s", p, v, err)
return VersionSegment{}, errors.New(errMsg)
}
if matchedI {
components = append(components, i)
} else if s, matched := NewVerSegCompStrFromString(p); matched {
components = append(components, s)
} else {
errMsg := fmt.Sprintf("Expected component '%s' from version segment '%s' to be either an integer or a formatted string", p, v)
return VersionSegment{}, errors.New(errMsg)
}
}
return VersionSegment{components}, nil
}
func NewVersionSegment(components []VerSegComp) (VersionSegment, error) {
if len(components) == 0 {
return VersionSegment{}, errors.New("Expected version segment to be build from at least one component")
}
for _, c := range components {
err := c.Validate()
if err != nil {
return VersionSegment{}, err
}
}
return VersionSegment{components}, nil
}
func (s VersionSegment) Increment() (VersionSegment, error) {
if len(s.Components) == 0 {
errMsg := "Expected version segment to have at least one component to be incremented"
return VersionSegment{}, errors.New(errMsg)
}
lastComp := s.Components[len(s.Components)-1]
lastCompInt, isInt := lastComp.(VerSegCompInt)
if !isInt {
errMsg := fmt.Sprintf("Expected version segment '%s' to have last component '%s' to be an integer", s, lastComp)
return VersionSegment{}, errors.New(errMsg)
}
copiedComponents := make([]VerSegComp, len(s.Components))
copy(copiedComponents, s.Components)
copiedComponents[len(copiedComponents)-1] = VerSegCompInt{I: lastCompInt.I + 1}
return NewVersionSegment(copiedComponents)
}
func (s VersionSegment) Copy() VersionSegment {
// Don't use constructor; assuming that original components are valid
copiedComponents := make([]VerSegComp, len(s.Components))
copy(copiedComponents, s.Components)
return VersionSegment{copiedComponents}
}
func (s VersionSegment) Empty() bool { return len(s.Components) == 0 }
func (s VersionSegment) String() string { return s.AsString() }
func (s VersionSegment) AsString() string {
result := ""
for i, c := range s.Components {
result += c.AsString()
if i < len(s.Components)-1 {
result += "."
}
}
return result
}
func (s VersionSegment) Compare(other VersionSegment) int {
a := s.Components
b := other.Components
if len(a) > len(b) {
comparison := s.compareArrays(a[0:len(b)], b)
if comparison != 0 {
return comparison
}
if !s.isAllZeros(a[len(b):len(a)]) {
return 1
}
return 0
}
if len(a) < len(b) {
comparison := s.compareArrays(a, b[0:len(a)])
if comparison != 0 {
return comparison
}
if !s.isAllZeros(b[len(a):len(b)]) {
return -1
}
return 0
}
return s.compareArrays(a, b)
}
func (s VersionSegment) IsEq(other VersionSegment) bool { return s.Compare(other) == 0 }
func (s VersionSegment) IsGt(other VersionSegment) bool { return s.Compare(other) == 1 }
func (s VersionSegment) IsLt(other VersionSegment) bool { return s.Compare(other) == -1 }
// compareArrays compares 2 equally sized a & b
func (s VersionSegment) compareArrays(a, b []VerSegComp) int {
for i, v1 := range a {
v2 := b[i]
_, v1IsStr := v1.(VerSegCompStr)
_, v1IsInt := v1.(VerSegCompInt)
_, v2IsStr := v2.(VerSegCompStr)
_, v2IsInt := v2.(VerSegCompInt)
if v1IsStr && v2IsInt {
return 1
} else if v1IsInt && v2IsStr {
return -1
}
comparison := v1.Compare(v2)
if comparison != 0 {
return comparison
}
}
return 0
}
func (s VersionSegment) isAllZeros(a []VerSegComp) bool {
for _, v := range a {
vTyped, ok := v.(VerSegCompInt)
if !ok || vTyped.I != 0 {
return false
}
}
return true
}

View File

@@ -1,7 +0,0 @@
package version
type AscSorting []Version
func (s AscSorting) Len() int { return len(s) }
func (s AscSorting) Less(i, j int) bool { return s[i].IsLt(s[j]) }
func (s AscSorting) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

View File

@@ -1,31 +0,0 @@
Go support for Protocol Buffers - Google's data interchange format
Copyright 2010 The Go Authors. All rights reserved.
https://github.com/golang/protobuf
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,43 +0,0 @@
# Go support for Protocol Buffers - Google's data interchange format
#
# Copyright 2010 The Go Authors. All rights reserved.
# https://github.com/golang/protobuf
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
install:
go install
test: install generate-test-pbs
go test
generate-test-pbs:
make install
make -C testdata
protoc --go_out=Mtestdata/test.proto=github.com/golang/protobuf/proto/testdata:. proto3_proto/proto3.proto
make

View File

@@ -1,223 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2011 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Protocol buffer deep copy and merge.
// TODO: RawMessage.
package proto
import (
"log"
"reflect"
"strings"
)
// Clone returns a deep copy of a protocol buffer.
func Clone(pb Message) Message {
in := reflect.ValueOf(pb)
if in.IsNil() {
return pb
}
out := reflect.New(in.Type().Elem())
// out is empty so a merge is a deep copy.
mergeStruct(out.Elem(), in.Elem())
return out.Interface().(Message)
}
// Merge merges src into dst.
// Required and optional fields that are set in src will be set to that value in dst.
// Elements of repeated fields will be appended.
// Merge panics if src and dst are not the same type, or if dst is nil.
func Merge(dst, src Message) {
in := reflect.ValueOf(src)
out := reflect.ValueOf(dst)
if out.IsNil() {
panic("proto: nil destination")
}
if in.Type() != out.Type() {
// Explicit test prior to mergeStruct so that mistyped nils will fail
panic("proto: type mismatch")
}
if in.IsNil() {
// Merging nil into non-nil is a quiet no-op
return
}
mergeStruct(out.Elem(), in.Elem())
}
func mergeStruct(out, in reflect.Value) {
sprop := GetProperties(in.Type())
for i := 0; i < in.NumField(); i++ {
f := in.Type().Field(i)
if strings.HasPrefix(f.Name, "XXX_") {
continue
}
mergeAny(out.Field(i), in.Field(i), false, sprop.Prop[i])
}
if emIn, ok := in.Addr().Interface().(extendableProto); ok {
emOut := out.Addr().Interface().(extendableProto)
mergeExtension(emOut.ExtensionMap(), emIn.ExtensionMap())
}
uf := in.FieldByName("XXX_unrecognized")
if !uf.IsValid() {
return
}
uin := uf.Bytes()
if len(uin) > 0 {
out.FieldByName("XXX_unrecognized").SetBytes(append([]byte(nil), uin...))
}
}
// mergeAny performs a merge between two values of the same type.
// viaPtr indicates whether the values were indirected through a pointer (implying proto2).
// prop is set if this is a struct field (it may be nil).
func mergeAny(out, in reflect.Value, viaPtr bool, prop *Properties) {
if in.Type() == protoMessageType {
if !in.IsNil() {
if out.IsNil() {
out.Set(reflect.ValueOf(Clone(in.Interface().(Message))))
} else {
Merge(out.Interface().(Message), in.Interface().(Message))
}
}
return
}
switch in.Kind() {
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
reflect.String, reflect.Uint32, reflect.Uint64:
if !viaPtr && isProto3Zero(in) {
return
}
out.Set(in)
case reflect.Interface:
// Probably a oneof field; copy non-nil values.
if in.IsNil() {
return
}
// Allocate destination if it is not set, or set to a different type.
// Otherwise we will merge as normal.
if out.IsNil() || out.Elem().Type() != in.Elem().Type() {
out.Set(reflect.New(in.Elem().Elem().Type())) // interface -> *T -> T -> new(T)
}
mergeAny(out.Elem(), in.Elem(), false, nil)
case reflect.Map:
if in.Len() == 0 {
return
}
if out.IsNil() {
out.Set(reflect.MakeMap(in.Type()))
}
// For maps with value types of *T or []byte we need to deep copy each value.
elemKind := in.Type().Elem().Kind()
for _, key := range in.MapKeys() {
var val reflect.Value
switch elemKind {
case reflect.Ptr:
val = reflect.New(in.Type().Elem().Elem())
mergeAny(val, in.MapIndex(key), false, nil)
case reflect.Slice:
val = in.MapIndex(key)
val = reflect.ValueOf(append([]byte{}, val.Bytes()...))
default:
val = in.MapIndex(key)
}
out.SetMapIndex(key, val)
}
case reflect.Ptr:
if in.IsNil() {
return
}
if out.IsNil() {
out.Set(reflect.New(in.Elem().Type()))
}
mergeAny(out.Elem(), in.Elem(), true, nil)
case reflect.Slice:
if in.IsNil() {
return
}
if in.Type().Elem().Kind() == reflect.Uint8 {
// []byte is a scalar bytes field, not a repeated field.
// Edge case: if this is in a proto3 message, a zero length
// bytes field is considered the zero value, and should not
// be merged.
if prop != nil && prop.proto3 && in.Len() == 0 {
return
}
// Make a deep copy.
// Append to []byte{} instead of []byte(nil) so that we never end up
// with a nil result.
out.SetBytes(append([]byte{}, in.Bytes()...))
return
}
n := in.Len()
if out.IsNil() {
out.Set(reflect.MakeSlice(in.Type(), 0, n))
}
switch in.Type().Elem().Kind() {
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64,
reflect.String, reflect.Uint32, reflect.Uint64:
out.Set(reflect.AppendSlice(out, in))
default:
for i := 0; i < n; i++ {
x := reflect.Indirect(reflect.New(in.Type().Elem()))
mergeAny(x, in.Index(i), false, nil)
out.Set(reflect.Append(out, x))
}
}
case reflect.Struct:
mergeStruct(out, in)
default:
// unknown type, so not a protocol buffer
log.Printf("proto: don't know how to copy %v", in)
}
}
func mergeExtension(out, in map[int32]Extension) {
for extNum, eIn := range in {
eOut := Extension{desc: eIn.desc}
if eIn.value != nil {
v := reflect.New(reflect.TypeOf(eIn.value)).Elem()
mergeAny(v, reflect.ValueOf(eIn.value), false, nil)
eOut.value = v.Interface()
}
if eIn.enc != nil {
eOut.enc = make([]byte, len(eIn.enc))
copy(eOut.enc, eIn.enc)
}
out[extNum] = eOut
}
}

View File

@@ -1,867 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
/*
* Routines for decoding protocol buffer data to construct in-memory representations.
*/
import (
"errors"
"fmt"
"io"
"os"
"reflect"
)
// errOverflow is returned when an integer is too large to be represented.
var errOverflow = errors.New("proto: integer overflow")
// ErrInternalBadWireType is returned by generated code when an incorrect
// wire type is encountered. It does not get returned to user code.
var ErrInternalBadWireType = errors.New("proto: internal error: bad wiretype for oneof")
// The fundamental decoders that interpret bytes on the wire.
// Those that take integer types all return uint64 and are
// therefore of type valueDecoder.
// DecodeVarint reads a varint-encoded integer from the slice.
// It returns the integer and the number of bytes consumed, or
// zero if there is not enough.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func DecodeVarint(buf []byte) (x uint64, n int) {
// x, n already 0
for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) {
return 0, 0
}
b := uint64(buf[n])
n++
x |= (b & 0x7F) << shift
if (b & 0x80) == 0 {
return x, n
}
}
// The number is too large to represent in a 64-bit value.
return 0, 0
}
// DecodeVarint reads a varint-encoded integer from the Buffer.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func (p *Buffer) DecodeVarint() (x uint64, err error) {
// x, err already 0
i := p.index
l := len(p.buf)
for shift := uint(0); shift < 64; shift += 7 {
if i >= l {
err = io.ErrUnexpectedEOF
return
}
b := p.buf[i]
i++
x |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
p.index = i
return
}
}
// The number is too large to represent in a 64-bit value.
err = errOverflow
return
}
// DecodeFixed64 reads a 64-bit integer from the Buffer.
// This is the format for the
// fixed64, sfixed64, and double protocol buffer types.
func (p *Buffer) DecodeFixed64() (x uint64, err error) {
// x, err already 0
i := p.index + 8
if i < 0 || i > len(p.buf) {
err = io.ErrUnexpectedEOF
return
}
p.index = i
x = uint64(p.buf[i-8])
x |= uint64(p.buf[i-7]) << 8
x |= uint64(p.buf[i-6]) << 16
x |= uint64(p.buf[i-5]) << 24
x |= uint64(p.buf[i-4]) << 32
x |= uint64(p.buf[i-3]) << 40
x |= uint64(p.buf[i-2]) << 48
x |= uint64(p.buf[i-1]) << 56
return
}
// DecodeFixed32 reads a 32-bit integer from the Buffer.
// This is the format for the
// fixed32, sfixed32, and float protocol buffer types.
func (p *Buffer) DecodeFixed32() (x uint64, err error) {
// x, err already 0
i := p.index + 4
if i < 0 || i > len(p.buf) {
err = io.ErrUnexpectedEOF
return
}
p.index = i
x = uint64(p.buf[i-4])
x |= uint64(p.buf[i-3]) << 8
x |= uint64(p.buf[i-2]) << 16
x |= uint64(p.buf[i-1]) << 24
return
}
// DecodeZigzag64 reads a zigzag-encoded 64-bit integer
// from the Buffer.
// This is the format used for the sint64 protocol buffer type.
func (p *Buffer) DecodeZigzag64() (x uint64, err error) {
x, err = p.DecodeVarint()
if err != nil {
return
}
x = (x >> 1) ^ uint64((int64(x&1)<<63)>>63)
return
}
// DecodeZigzag32 reads a zigzag-encoded 32-bit integer
// from the Buffer.
// This is the format used for the sint32 protocol buffer type.
func (p *Buffer) DecodeZigzag32() (x uint64, err error) {
x, err = p.DecodeVarint()
if err != nil {
return
}
x = uint64((uint32(x) >> 1) ^ uint32((int32(x&1)<<31)>>31))
return
}
// These are not ValueDecoders: they produce an array of bytes or a string.
// bytes, embedded messages
// DecodeRawBytes reads a count-delimited byte buffer from the Buffer.
// This is the format used for the bytes protocol buffer
// type and for embedded messages.
func (p *Buffer) DecodeRawBytes(alloc bool) (buf []byte, err error) {
n, err := p.DecodeVarint()
if err != nil {
return nil, err
}
nb := int(n)
if nb < 0 {
return nil, fmt.Errorf("proto: bad byte length %d", nb)
}
end := p.index + nb
if end < p.index || end > len(p.buf) {
return nil, io.ErrUnexpectedEOF
}
if !alloc {
// todo: check if can get more uses of alloc=false
buf = p.buf[p.index:end]
p.index += nb
return
}
buf = make([]byte, nb)
copy(buf, p.buf[p.index:])
p.index += nb
return
}
// DecodeStringBytes reads an encoded string from the Buffer.
// This is the format used for the proto2 string type.
func (p *Buffer) DecodeStringBytes() (s string, err error) {
buf, err := p.DecodeRawBytes(false)
if err != nil {
return
}
return string(buf), nil
}
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
// If the protocol buffer has extensions, and the field matches, add it as an extension.
// Otherwise, if the XXX_unrecognized field exists, append the skipped data there.
func (o *Buffer) skipAndSave(t reflect.Type, tag, wire int, base structPointer, unrecField field) error {
oi := o.index
err := o.skip(t, tag, wire)
if err != nil {
return err
}
if !unrecField.IsValid() {
return nil
}
ptr := structPointer_Bytes(base, unrecField)
// Add the skipped field to struct field
obuf := o.buf
o.buf = *ptr
o.EncodeVarint(uint64(tag<<3 | wire))
*ptr = append(o.buf, obuf[oi:o.index]...)
o.buf = obuf
return nil
}
// Skip the next item in the buffer. Its wire type is decoded and presented as an argument.
func (o *Buffer) skip(t reflect.Type, tag, wire int) error {
var u uint64
var err error
switch wire {
case WireVarint:
_, err = o.DecodeVarint()
case WireFixed64:
_, err = o.DecodeFixed64()
case WireBytes:
_, err = o.DecodeRawBytes(false)
case WireFixed32:
_, err = o.DecodeFixed32()
case WireStartGroup:
for {
u, err = o.DecodeVarint()
if err != nil {
break
}
fwire := int(u & 0x7)
if fwire == WireEndGroup {
break
}
ftag := int(u >> 3)
err = o.skip(t, ftag, fwire)
if err != nil {
break
}
}
default:
err = fmt.Errorf("proto: can't skip unknown wire type %d for %s", wire, t)
}
return err
}
// Unmarshaler is the interface representing objects that can
// unmarshal themselves. The method should reset the receiver before
// decoding starts. The argument points to data that may be
// overwritten, so implementations should not keep references to the
// buffer.
type Unmarshaler interface {
Unmarshal([]byte) error
}
// Unmarshal parses the protocol buffer representation in buf and places the
// decoded result in pb. If the struct underlying pb does not match
// the data in buf, the results can be unpredictable.
//
// Unmarshal resets pb before starting to unmarshal, so any
// existing data in pb is always removed. Use UnmarshalMerge
// to preserve and append to existing data.
func Unmarshal(buf []byte, pb Message) error {
pb.Reset()
return UnmarshalMerge(buf, pb)
}
// UnmarshalMerge parses the protocol buffer representation in buf and
// writes the decoded result to pb. If the struct underlying pb does not match
// the data in buf, the results can be unpredictable.
//
// UnmarshalMerge merges into existing data in pb.
// Most code should use Unmarshal instead.
func UnmarshalMerge(buf []byte, pb Message) error {
// If the object can unmarshal itself, let it.
if u, ok := pb.(Unmarshaler); ok {
return u.Unmarshal(buf)
}
return NewBuffer(buf).Unmarshal(pb)
}
// DecodeMessage reads a count-delimited message from the Buffer.
func (p *Buffer) DecodeMessage(pb Message) error {
enc, err := p.DecodeRawBytes(false)
if err != nil {
return err
}
return NewBuffer(enc).Unmarshal(pb)
}
// DecodeGroup reads a tag-delimited group from the Buffer.
func (p *Buffer) DecodeGroup(pb Message) error {
typ, base, err := getbase(pb)
if err != nil {
return err
}
return p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), true, base)
}
// Unmarshal parses the protocol buffer representation in the
// Buffer and places the decoded result in pb. If the struct
// underlying pb does not match the data in the buffer, the results can be
// unpredictable.
func (p *Buffer) Unmarshal(pb Message) error {
// If the object can unmarshal itself, let it.
if u, ok := pb.(Unmarshaler); ok {
err := u.Unmarshal(p.buf[p.index:])
p.index = len(p.buf)
return err
}
typ, base, err := getbase(pb)
if err != nil {
return err
}
err = p.unmarshalType(typ.Elem(), GetProperties(typ.Elem()), false, base)
if collectStats {
stats.Decode++
}
return err
}
// unmarshalType does the work of unmarshaling a structure.
func (o *Buffer) unmarshalType(st reflect.Type, prop *StructProperties, is_group bool, base structPointer) error {
var state errorState
required, reqFields := prop.reqCount, uint64(0)
var err error
for err == nil && o.index < len(o.buf) {
oi := o.index
var u uint64
u, err = o.DecodeVarint()
if err != nil {
break
}
wire := int(u & 0x7)
if wire == WireEndGroup {
if is_group {
return nil // input is satisfied
}
return fmt.Errorf("proto: %s: wiretype end group for non-group", st)
}
tag := int(u >> 3)
if tag <= 0 {
return fmt.Errorf("proto: %s: illegal tag %d (wire type %d)", st, tag, wire)
}
fieldnum, ok := prop.decoderTags.get(tag)
if !ok {
// Maybe it's an extension?
if prop.extendable {
if e := structPointer_Interface(base, st).(extendableProto); isExtensionField(e, int32(tag)) {
if err = o.skip(st, tag, wire); err == nil {
ext := e.ExtensionMap()[int32(tag)] // may be missing
ext.enc = append(ext.enc, o.buf[oi:o.index]...)
e.ExtensionMap()[int32(tag)] = ext
}
continue
}
}
// Maybe it's a oneof?
if prop.oneofUnmarshaler != nil {
m := structPointer_Interface(base, st).(Message)
// First return value indicates whether tag is a oneof field.
ok, err = prop.oneofUnmarshaler(m, tag, wire, o)
if err == ErrInternalBadWireType {
// Map the error to something more descriptive.
// Do the formatting here to save generated code space.
err = fmt.Errorf("bad wiretype for oneof field in %T", m)
}
if ok {
continue
}
}
err = o.skipAndSave(st, tag, wire, base, prop.unrecField)
continue
}
p := prop.Prop[fieldnum]
if p.dec == nil {
fmt.Fprintf(os.Stderr, "proto: no protobuf decoder for %s.%s\n", st, st.Field(fieldnum).Name)
continue
}
dec := p.dec
if wire != WireStartGroup && wire != p.WireType {
if wire == WireBytes && p.packedDec != nil {
// a packable field
dec = p.packedDec
} else {
err = fmt.Errorf("proto: bad wiretype for field %s.%s: got wiretype %d, want %d", st, st.Field(fieldnum).Name, wire, p.WireType)
continue
}
}
decErr := dec(o, p, base)
if decErr != nil && !state.shouldContinue(decErr, p) {
err = decErr
}
if err == nil && p.Required {
// Successfully decoded a required field.
if tag <= 64 {
// use bitmap for fields 1-64 to catch field reuse.
var mask uint64 = 1 << uint64(tag-1)
if reqFields&mask == 0 {
// new required field
reqFields |= mask
required--
}
} else {
// This is imprecise. It can be fooled by a required field
// with a tag > 64 that is encoded twice; that's very rare.
// A fully correct implementation would require allocating
// a data structure, which we would like to avoid.
required--
}
}
}
if err == nil {
if is_group {
return io.ErrUnexpectedEOF
}
if state.err != nil {
return state.err
}
if required > 0 {
// Not enough information to determine the exact field. If we use extra
// CPU, we could determine the field only if the missing required field
// has a tag <= 64 and we check reqFields.
return &RequiredNotSetError{"{Unknown}"}
}
}
return err
}
// Individual type decoders
// For each,
// u is the decoded value,
// v is a pointer to the field (pointer) in the struct
// Sizes of the pools to allocate inside the Buffer.
// The goal is modest amortization and allocation
// on at least 16-byte boundaries.
const (
boolPoolSize = 16
uint32PoolSize = 8
uint64PoolSize = 4
)
// Decode a bool.
func (o *Buffer) dec_bool(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
if len(o.bools) == 0 {
o.bools = make([]bool, boolPoolSize)
}
o.bools[0] = u != 0
*structPointer_Bool(base, p.field) = &o.bools[0]
o.bools = o.bools[1:]
return nil
}
func (o *Buffer) dec_proto3_bool(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
*structPointer_BoolVal(base, p.field) = u != 0
return nil
}
// Decode an int32.
func (o *Buffer) dec_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word32_Set(structPointer_Word32(base, p.field), o, uint32(u))
return nil
}
func (o *Buffer) dec_proto3_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word32Val_Set(structPointer_Word32Val(base, p.field), uint32(u))
return nil
}
// Decode an int64.
func (o *Buffer) dec_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word64_Set(structPointer_Word64(base, p.field), o, u)
return nil
}
func (o *Buffer) dec_proto3_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
word64Val_Set(structPointer_Word64Val(base, p.field), o, u)
return nil
}
// Decode a string.
func (o *Buffer) dec_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
*structPointer_String(base, p.field) = &s
return nil
}
func (o *Buffer) dec_proto3_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
*structPointer_StringVal(base, p.field) = s
return nil
}
// Decode a slice of bytes ([]byte).
func (o *Buffer) dec_slice_byte(p *Properties, base structPointer) error {
b, err := o.DecodeRawBytes(true)
if err != nil {
return err
}
*structPointer_Bytes(base, p.field) = b
return nil
}
// Decode a slice of bools ([]bool).
func (o *Buffer) dec_slice_bool(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
v := structPointer_BoolSlice(base, p.field)
*v = append(*v, u != 0)
return nil
}
// Decode a slice of bools ([]bool) in packed format.
func (o *Buffer) dec_slice_packed_bool(p *Properties, base structPointer) error {
v := structPointer_BoolSlice(base, p.field)
nn, err := o.DecodeVarint()
if err != nil {
return err
}
nb := int(nn) // number of bytes of encoded bools
fin := o.index + nb
if fin < o.index {
return errOverflow
}
y := *v
for o.index < fin {
u, err := p.valDec(o)
if err != nil {
return err
}
y = append(y, u != 0)
}
*v = y
return nil
}
// Decode a slice of int32s ([]int32).
func (o *Buffer) dec_slice_int32(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
structPointer_Word32Slice(base, p.field).Append(uint32(u))
return nil
}
// Decode a slice of int32s ([]int32) in packed format.
func (o *Buffer) dec_slice_packed_int32(p *Properties, base structPointer) error {
v := structPointer_Word32Slice(base, p.field)
nn, err := o.DecodeVarint()
if err != nil {
return err
}
nb := int(nn) // number of bytes of encoded int32s
fin := o.index + nb
if fin < o.index {
return errOverflow
}
for o.index < fin {
u, err := p.valDec(o)
if err != nil {
return err
}
v.Append(uint32(u))
}
return nil
}
// Decode a slice of int64s ([]int64).
func (o *Buffer) dec_slice_int64(p *Properties, base structPointer) error {
u, err := p.valDec(o)
if err != nil {
return err
}
structPointer_Word64Slice(base, p.field).Append(u)
return nil
}
// Decode a slice of int64s ([]int64) in packed format.
func (o *Buffer) dec_slice_packed_int64(p *Properties, base structPointer) error {
v := structPointer_Word64Slice(base, p.field)
nn, err := o.DecodeVarint()
if err != nil {
return err
}
nb := int(nn) // number of bytes of encoded int64s
fin := o.index + nb
if fin < o.index {
return errOverflow
}
for o.index < fin {
u, err := p.valDec(o)
if err != nil {
return err
}
v.Append(u)
}
return nil
}
// Decode a slice of strings ([]string).
func (o *Buffer) dec_slice_string(p *Properties, base structPointer) error {
s, err := o.DecodeStringBytes()
if err != nil {
return err
}
v := structPointer_StringSlice(base, p.field)
*v = append(*v, s)
return nil
}
// Decode a slice of slice of bytes ([][]byte).
func (o *Buffer) dec_slice_slice_byte(p *Properties, base structPointer) error {
b, err := o.DecodeRawBytes(true)
if err != nil {
return err
}
v := structPointer_BytesSlice(base, p.field)
*v = append(*v, b)
return nil
}
// Decode a map field.
func (o *Buffer) dec_new_map(p *Properties, base structPointer) error {
raw, err := o.DecodeRawBytes(false)
if err != nil {
return err
}
oi := o.index // index at the end of this map entry
o.index -= len(raw) // move buffer back to start of map entry
mptr := structPointer_NewAt(base, p.field, p.mtype) // *map[K]V
if mptr.Elem().IsNil() {
mptr.Elem().Set(reflect.MakeMap(mptr.Type().Elem()))
}
v := mptr.Elem() // map[K]V
// Prepare addressable doubly-indirect placeholders for the key and value types.
// See enc_new_map for why.
keyptr := reflect.New(reflect.PtrTo(p.mtype.Key())).Elem() // addressable *K
keybase := toStructPointer(keyptr.Addr()) // **K
var valbase structPointer
var valptr reflect.Value
switch p.mtype.Elem().Kind() {
case reflect.Slice:
// []byte
var dummy []byte
valptr = reflect.ValueOf(&dummy) // *[]byte
valbase = toStructPointer(valptr) // *[]byte
case reflect.Ptr:
// message; valptr is **Msg; need to allocate the intermediate pointer
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
valptr.Set(reflect.New(valptr.Type().Elem()))
valbase = toStructPointer(valptr)
default:
// everything else
valptr = reflect.New(reflect.PtrTo(p.mtype.Elem())).Elem() // addressable *V
valbase = toStructPointer(valptr.Addr()) // **V
}
// Decode.
// This parses a restricted wire format, namely the encoding of a message
// with two fields. See enc_new_map for the format.
for o.index < oi {
// tagcode for key and value properties are always a single byte
// because they have tags 1 and 2.
tagcode := o.buf[o.index]
o.index++
switch tagcode {
case p.mkeyprop.tagcode[0]:
if err := p.mkeyprop.dec(o, p.mkeyprop, keybase); err != nil {
return err
}
case p.mvalprop.tagcode[0]:
if err := p.mvalprop.dec(o, p.mvalprop, valbase); err != nil {
return err
}
default:
// TODO: Should we silently skip this instead?
return fmt.Errorf("proto: bad map data tag %d", raw[0])
}
}
keyelem, valelem := keyptr.Elem(), valptr.Elem()
if !keyelem.IsValid() || !valelem.IsValid() {
// We did not decode the key or the value in the map entry.
// Either way, it's an invalid map entry.
return fmt.Errorf("proto: bad map data: missing key/val")
}
v.SetMapIndex(keyelem, valelem)
return nil
}
// Decode a group.
func (o *Buffer) dec_struct_group(p *Properties, base structPointer) error {
bas := structPointer_GetStructPointer(base, p.field)
if structPointer_IsNil(bas) {
// allocate new nested message
bas = toStructPointer(reflect.New(p.stype))
structPointer_SetStructPointer(base, p.field, bas)
}
return o.unmarshalType(p.stype, p.sprop, true, bas)
}
// Decode an embedded message.
func (o *Buffer) dec_struct_message(p *Properties, base structPointer) (err error) {
raw, e := o.DecodeRawBytes(false)
if e != nil {
return e
}
bas := structPointer_GetStructPointer(base, p.field)
if structPointer_IsNil(bas) {
// allocate new nested message
bas = toStructPointer(reflect.New(p.stype))
structPointer_SetStructPointer(base, p.field, bas)
}
// If the object can unmarshal itself, let it.
if p.isUnmarshaler {
iv := structPointer_Interface(bas, p.stype)
return iv.(Unmarshaler).Unmarshal(raw)
}
obuf := o.buf
oi := o.index
o.buf = raw
o.index = 0
err = o.unmarshalType(p.stype, p.sprop, false, bas)
o.buf = obuf
o.index = oi
return err
}
// Decode a slice of embedded messages.
func (o *Buffer) dec_slice_struct_message(p *Properties, base structPointer) error {
return o.dec_slice_struct(p, false, base)
}
// Decode a slice of embedded groups.
func (o *Buffer) dec_slice_struct_group(p *Properties, base structPointer) error {
return o.dec_slice_struct(p, true, base)
}
// Decode a slice of structs ([]*struct).
func (o *Buffer) dec_slice_struct(p *Properties, is_group bool, base structPointer) error {
v := reflect.New(p.stype)
bas := toStructPointer(v)
structPointer_StructPointerSlice(base, p.field).Append(bas)
if is_group {
err := o.unmarshalType(p.stype, p.sprop, is_group, bas)
return err
}
raw, err := o.DecodeRawBytes(false)
if err != nil {
return err
}
// If the object can unmarshal itself, let it.
if p.isUnmarshaler {
iv := v.Interface()
return iv.(Unmarshaler).Unmarshal(raw)
}
obuf := o.buf
oi := o.index
o.buf = raw
o.index = 0
err = o.unmarshalType(p.stype, p.sprop, is_group, bas)
o.buf = obuf
o.index = oi
return err
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,276 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2011 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Protocol buffer comparison.
package proto
import (
"bytes"
"log"
"reflect"
"strings"
)
/*
Equal returns true iff protocol buffers a and b are equal.
The arguments must both be pointers to protocol buffer structs.
Equality is defined in this way:
- Two messages are equal iff they are the same type,
corresponding fields are equal, unknown field sets
are equal, and extensions sets are equal.
- Two set scalar fields are equal iff their values are equal.
If the fields are of a floating-point type, remember that
NaN != x for all x, including NaN. If the message is defined
in a proto3 .proto file, fields are not "set"; specifically,
zero length proto3 "bytes" fields are equal (nil == {}).
- Two repeated fields are equal iff their lengths are the same,
and their corresponding elements are equal (a "bytes" field,
although represented by []byte, is not a repeated field)
- Two unset fields are equal.
- Two unknown field sets are equal if their current
encoded state is equal.
- Two extension sets are equal iff they have corresponding
elements that are pairwise equal.
- Every other combination of things are not equal.
The return value is undefined if a and b are not protocol buffers.
*/
func Equal(a, b Message) bool {
if a == nil || b == nil {
return a == b
}
v1, v2 := reflect.ValueOf(a), reflect.ValueOf(b)
if v1.Type() != v2.Type() {
return false
}
if v1.Kind() == reflect.Ptr {
if v1.IsNil() {
return v2.IsNil()
}
if v2.IsNil() {
return false
}
v1, v2 = v1.Elem(), v2.Elem()
}
if v1.Kind() != reflect.Struct {
return false
}
return equalStruct(v1, v2)
}
// v1 and v2 are known to have the same type.
func equalStruct(v1, v2 reflect.Value) bool {
sprop := GetProperties(v1.Type())
for i := 0; i < v1.NumField(); i++ {
f := v1.Type().Field(i)
if strings.HasPrefix(f.Name, "XXX_") {
continue
}
f1, f2 := v1.Field(i), v2.Field(i)
if f.Type.Kind() == reflect.Ptr {
if n1, n2 := f1.IsNil(), f2.IsNil(); n1 && n2 {
// both unset
continue
} else if n1 != n2 {
// set/unset mismatch
return false
}
b1, ok := f1.Interface().(raw)
if ok {
b2 := f2.Interface().(raw)
// RawMessage
if !bytes.Equal(b1.Bytes(), b2.Bytes()) {
return false
}
continue
}
f1, f2 = f1.Elem(), f2.Elem()
}
if !equalAny(f1, f2, sprop.Prop[i]) {
return false
}
}
if em1 := v1.FieldByName("XXX_extensions"); em1.IsValid() {
em2 := v2.FieldByName("XXX_extensions")
if !equalExtensions(v1.Type(), em1.Interface().(map[int32]Extension), em2.Interface().(map[int32]Extension)) {
return false
}
}
uf := v1.FieldByName("XXX_unrecognized")
if !uf.IsValid() {
return true
}
u1 := uf.Bytes()
u2 := v2.FieldByName("XXX_unrecognized").Bytes()
if !bytes.Equal(u1, u2) {
return false
}
return true
}
// v1 and v2 are known to have the same type.
// prop may be nil.
func equalAny(v1, v2 reflect.Value, prop *Properties) bool {
if v1.Type() == protoMessageType {
m1, _ := v1.Interface().(Message)
m2, _ := v2.Interface().(Message)
return Equal(m1, m2)
}
switch v1.Kind() {
case reflect.Bool:
return v1.Bool() == v2.Bool()
case reflect.Float32, reflect.Float64:
return v1.Float() == v2.Float()
case reflect.Int32, reflect.Int64:
return v1.Int() == v2.Int()
case reflect.Interface:
// Probably a oneof field; compare the inner values.
n1, n2 := v1.IsNil(), v2.IsNil()
if n1 || n2 {
return n1 == n2
}
e1, e2 := v1.Elem(), v2.Elem()
if e1.Type() != e2.Type() {
return false
}
return equalAny(e1, e2, nil)
case reflect.Map:
if v1.Len() != v2.Len() {
return false
}
for _, key := range v1.MapKeys() {
val2 := v2.MapIndex(key)
if !val2.IsValid() {
// This key was not found in the second map.
return false
}
if !equalAny(v1.MapIndex(key), val2, nil) {
return false
}
}
return true
case reflect.Ptr:
return equalAny(v1.Elem(), v2.Elem(), prop)
case reflect.Slice:
if v1.Type().Elem().Kind() == reflect.Uint8 {
// short circuit: []byte
// Edge case: if this is in a proto3 message, a zero length
// bytes field is considered the zero value.
if prop != nil && prop.proto3 && v1.Len() == 0 && v2.Len() == 0 {
return true
}
if v1.IsNil() != v2.IsNil() {
return false
}
return bytes.Equal(v1.Interface().([]byte), v2.Interface().([]byte))
}
if v1.Len() != v2.Len() {
return false
}
for i := 0; i < v1.Len(); i++ {
if !equalAny(v1.Index(i), v2.Index(i), prop) {
return false
}
}
return true
case reflect.String:
return v1.Interface().(string) == v2.Interface().(string)
case reflect.Struct:
return equalStruct(v1, v2)
case reflect.Uint32, reflect.Uint64:
return v1.Uint() == v2.Uint()
}
// unknown type, so not a protocol buffer
log.Printf("proto: don't know how to compare %v", v1)
return false
}
// base is the struct type that the extensions are based on.
// em1 and em2 are extension maps.
func equalExtensions(base reflect.Type, em1, em2 map[int32]Extension) bool {
if len(em1) != len(em2) {
return false
}
for extNum, e1 := range em1 {
e2, ok := em2[extNum]
if !ok {
return false
}
m1, m2 := e1.value, e2.value
if m1 != nil && m2 != nil {
// Both are unencoded.
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
return false
}
continue
}
// At least one is encoded. To do a semantically correct comparison
// we need to unmarshal them first.
var desc *ExtensionDesc
if m := extensionMaps[base]; m != nil {
desc = m[extNum]
}
if desc == nil {
log.Printf("proto: don't know how to compare extension %d of %v", extNum, base)
continue
}
var err error
if m1 == nil {
m1, err = decodeExtension(e1.enc, desc)
}
if m2 == nil && err == nil {
m2, err = decodeExtension(e2.enc, desc)
}
if err != nil {
// The encoded form is invalid.
log.Printf("proto: badly encoded extension %d of %v: %v", extNum, base, err)
return false
}
if !equalAny(reflect.ValueOf(m1), reflect.ValueOf(m2), nil) {
return false
}
}
return true
}

View File

@@ -1,399 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
/*
* Types and routines for supporting protocol buffer extensions.
*/
import (
"errors"
"fmt"
"reflect"
"strconv"
"sync"
)
// ErrMissingExtension is the error returned by GetExtension if the named extension is not in the message.
var ErrMissingExtension = errors.New("proto: missing extension")
// ExtensionRange represents a range of message extensions for a protocol buffer.
// Used in code generated by the protocol compiler.
type ExtensionRange struct {
Start, End int32 // both inclusive
}
// extendableProto is an interface implemented by any protocol buffer that may be extended.
type extendableProto interface {
Message
ExtensionRangeArray() []ExtensionRange
ExtensionMap() map[int32]Extension
}
var extendableProtoType = reflect.TypeOf((*extendableProto)(nil)).Elem()
// ExtensionDesc represents an extension specification.
// Used in generated code from the protocol compiler.
type ExtensionDesc struct {
ExtendedType Message // nil pointer to the type that is being extended
ExtensionType interface{} // nil pointer to the extension type
Field int32 // field number
Name string // fully-qualified name of extension, for text formatting
Tag string // protobuf tag style
}
func (ed *ExtensionDesc) repeated() bool {
t := reflect.TypeOf(ed.ExtensionType)
return t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
}
// Extension represents an extension in a message.
type Extension struct {
// When an extension is stored in a message using SetExtension
// only desc and value are set. When the message is marshaled
// enc will be set to the encoded form of the message.
//
// When a message is unmarshaled and contains extensions, each
// extension will have only enc set. When such an extension is
// accessed using GetExtension (or GetExtensions) desc and value
// will be set.
desc *ExtensionDesc
value interface{}
enc []byte
}
// SetRawExtension is for testing only.
func SetRawExtension(base extendableProto, id int32, b []byte) {
base.ExtensionMap()[id] = Extension{enc: b}
}
// isExtensionField returns true iff the given field number is in an extension range.
func isExtensionField(pb extendableProto, field int32) bool {
for _, er := range pb.ExtensionRangeArray() {
if er.Start <= field && field <= er.End {
return true
}
}
return false
}
// checkExtensionTypes checks that the given extension is valid for pb.
func checkExtensionTypes(pb extendableProto, extension *ExtensionDesc) error {
// Check the extended type.
if a, b := reflect.TypeOf(pb), reflect.TypeOf(extension.ExtendedType); a != b {
return errors.New("proto: bad extended type; " + b.String() + " does not extend " + a.String())
}
// Check the range.
if !isExtensionField(pb, extension.Field) {
return errors.New("proto: bad extension number; not in declared ranges")
}
return nil
}
// extPropKey is sufficient to uniquely identify an extension.
type extPropKey struct {
base reflect.Type
field int32
}
var extProp = struct {
sync.RWMutex
m map[extPropKey]*Properties
}{
m: make(map[extPropKey]*Properties),
}
func extensionProperties(ed *ExtensionDesc) *Properties {
key := extPropKey{base: reflect.TypeOf(ed.ExtendedType), field: ed.Field}
extProp.RLock()
if prop, ok := extProp.m[key]; ok {
extProp.RUnlock()
return prop
}
extProp.RUnlock()
extProp.Lock()
defer extProp.Unlock()
// Check again.
if prop, ok := extProp.m[key]; ok {
return prop
}
prop := new(Properties)
prop.Init(reflect.TypeOf(ed.ExtensionType), "unknown_name", ed.Tag, nil)
extProp.m[key] = prop
return prop
}
// encodeExtensionMap encodes any unmarshaled (unencoded) extensions in m.
func encodeExtensionMap(m map[int32]Extension) error {
for k, e := range m {
if e.value == nil || e.desc == nil {
// Extension is only in its encoded form.
continue
}
// We don't skip extensions that have an encoded form set,
// because the extension value may have been mutated after
// the last time this function was called.
et := reflect.TypeOf(e.desc.ExtensionType)
props := extensionProperties(e.desc)
p := NewBuffer(nil)
// If e.value has type T, the encoder expects a *struct{ X T }.
// Pass a *T with a zero field and hope it all works out.
x := reflect.New(et)
x.Elem().Set(reflect.ValueOf(e.value))
if err := props.enc(p, props, toStructPointer(x)); err != nil {
return err
}
e.enc = p.buf
m[k] = e
}
return nil
}
func sizeExtensionMap(m map[int32]Extension) (n int) {
for _, e := range m {
if e.value == nil || e.desc == nil {
// Extension is only in its encoded form.
n += len(e.enc)
continue
}
// We don't skip extensions that have an encoded form set,
// because the extension value may have been mutated after
// the last time this function was called.
et := reflect.TypeOf(e.desc.ExtensionType)
props := extensionProperties(e.desc)
// If e.value has type T, the encoder expects a *struct{ X T }.
// Pass a *T with a zero field and hope it all works out.
x := reflect.New(et)
x.Elem().Set(reflect.ValueOf(e.value))
n += props.size(props, toStructPointer(x))
}
return
}
// HasExtension returns whether the given extension is present in pb.
func HasExtension(pb extendableProto, extension *ExtensionDesc) bool {
// TODO: Check types, field numbers, etc.?
_, ok := pb.ExtensionMap()[extension.Field]
return ok
}
// ClearExtension removes the given extension from pb.
func ClearExtension(pb extendableProto, extension *ExtensionDesc) {
// TODO: Check types, field numbers, etc.?
delete(pb.ExtensionMap(), extension.Field)
}
// GetExtension parses and returns the given extension of pb.
// If the extension is not present and has no default value it returns ErrMissingExtension.
func GetExtension(pb extendableProto, extension *ExtensionDesc) (interface{}, error) {
if err := checkExtensionTypes(pb, extension); err != nil {
return nil, err
}
emap := pb.ExtensionMap()
e, ok := emap[extension.Field]
if !ok {
// defaultExtensionValue returns the default value or
// ErrMissingExtension if there is no default.
return defaultExtensionValue(extension)
}
if e.value != nil {
// Already decoded. Check the descriptor, though.
if e.desc != extension {
// This shouldn't happen. If it does, it means that
// GetExtension was called twice with two different
// descriptors with the same field number.
return nil, errors.New("proto: descriptor conflict")
}
return e.value, nil
}
v, err := decodeExtension(e.enc, extension)
if err != nil {
return nil, err
}
// Remember the decoded version and drop the encoded version.
// That way it is safe to mutate what we return.
e.value = v
e.desc = extension
e.enc = nil
emap[extension.Field] = e
return e.value, nil
}
// defaultExtensionValue returns the default value for extension.
// If no default for an extension is defined ErrMissingExtension is returned.
func defaultExtensionValue(extension *ExtensionDesc) (interface{}, error) {
t := reflect.TypeOf(extension.ExtensionType)
props := extensionProperties(extension)
sf, _, err := fieldDefault(t, props)
if err != nil {
return nil, err
}
if sf == nil || sf.value == nil {
// There is no default value.
return nil, ErrMissingExtension
}
if t.Kind() != reflect.Ptr {
// We do not need to return a Ptr, we can directly return sf.value.
return sf.value, nil
}
// We need to return an interface{} that is a pointer to sf.value.
value := reflect.New(t).Elem()
value.Set(reflect.New(value.Type().Elem()))
if sf.kind == reflect.Int32 {
// We may have an int32 or an enum, but the underlying data is int32.
// Since we can't set an int32 into a non int32 reflect.value directly
// set it as a int32.
value.Elem().SetInt(int64(sf.value.(int32)))
} else {
value.Elem().Set(reflect.ValueOf(sf.value))
}
return value.Interface(), nil
}
// decodeExtension decodes an extension encoded in b.
func decodeExtension(b []byte, extension *ExtensionDesc) (interface{}, error) {
o := NewBuffer(b)
t := reflect.TypeOf(extension.ExtensionType)
props := extensionProperties(extension)
// t is a pointer to a struct, pointer to basic type or a slice.
// Allocate a "field" to store the pointer/slice itself; the
// pointer/slice will be stored here. We pass
// the address of this field to props.dec.
// This passes a zero field and a *t and lets props.dec
// interpret it as a *struct{ x t }.
value := reflect.New(t).Elem()
for {
// Discard wire type and field number varint. It isn't needed.
if _, err := o.DecodeVarint(); err != nil {
return nil, err
}
if err := props.dec(o, props, toStructPointer(value.Addr())); err != nil {
return nil, err
}
if o.index >= len(o.buf) {
break
}
}
return value.Interface(), nil
}
// GetExtensions returns a slice of the extensions present in pb that are also listed in es.
// The returned slice has the same length as es; missing extensions will appear as nil elements.
func GetExtensions(pb Message, es []*ExtensionDesc) (extensions []interface{}, err error) {
epb, ok := pb.(extendableProto)
if !ok {
err = errors.New("proto: not an extendable proto")
return
}
extensions = make([]interface{}, len(es))
for i, e := range es {
extensions[i], err = GetExtension(epb, e)
if err == ErrMissingExtension {
err = nil
}
if err != nil {
return
}
}
return
}
// SetExtension sets the specified extension of pb to the specified value.
func SetExtension(pb extendableProto, extension *ExtensionDesc, value interface{}) error {
if err := checkExtensionTypes(pb, extension); err != nil {
return err
}
typ := reflect.TypeOf(extension.ExtensionType)
if typ != reflect.TypeOf(value) {
return errors.New("proto: bad extension value type")
}
// nil extension values need to be caught early, because the
// encoder can't distinguish an ErrNil due to a nil extension
// from an ErrNil due to a missing field. Extensions are
// always optional, so the encoder would just swallow the error
// and drop all the extensions from the encoded message.
if reflect.ValueOf(value).IsNil() {
return fmt.Errorf("proto: SetExtension called with nil value of type %T", value)
}
pb.ExtensionMap()[extension.Field] = Extension{desc: extension, value: value}
return nil
}
// A global registry of extensions.
// The generated code will register the generated descriptors by calling RegisterExtension.
var extensionMaps = make(map[reflect.Type]map[int32]*ExtensionDesc)
// RegisterExtension is called from the generated code.
func RegisterExtension(desc *ExtensionDesc) {
st := reflect.TypeOf(desc.ExtendedType).Elem()
m := extensionMaps[st]
if m == nil {
m = make(map[int32]*ExtensionDesc)
extensionMaps[st] = m
}
if _, ok := m[desc.Field]; ok {
panic("proto: duplicate extension registered: " + st.String() + " " + strconv.Itoa(int(desc.Field)))
}
m[desc.Field] = desc
}
// RegisteredExtensions returns a map of the registered extensions of a
// protocol buffer struct, indexed by the extension number.
// The argument pb should be a nil pointer to the struct type.
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
return extensionMaps[reflect.TypeOf(pb).Elem()]
}

View File

@@ -1,894 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
Package proto converts data structures to and from the wire format of
protocol buffers. It works in concert with the Go source code generated
for .proto files by the protocol compiler.
A summary of the properties of the protocol buffer interface
for a protocol buffer variable v:
- Names are turned from camel_case to CamelCase for export.
- There are no methods on v to set fields; just treat
them as structure fields.
- There are getters that return a field's value if set,
and return the field's default value if unset.
The getters work even if the receiver is a nil message.
- The zero value for a struct is its correct initialization state.
All desired fields must be set before marshaling.
- A Reset() method will restore a protobuf struct to its zero state.
- Non-repeated fields are pointers to the values; nil means unset.
That is, optional or required field int32 f becomes F *int32.
- Repeated fields are slices.
- Helper functions are available to aid the setting of fields.
msg.Foo = proto.String("hello") // set field
- Constants are defined to hold the default values of all fields that
have them. They have the form Default_StructName_FieldName.
Because the getter methods handle defaulted values,
direct use of these constants should be rare.
- Enums are given type names and maps from names to values.
Enum values are prefixed by the enclosing message's name, or by the
enum's type name if it is a top-level enum. Enum types have a String
method, and a Enum method to assist in message construction.
- Nested messages, groups and enums have type names prefixed with the name of
the surrounding message type.
- Extensions are given descriptor names that start with E_,
followed by an underscore-delimited list of the nested messages
that contain it (if any) followed by the CamelCased name of the
extension field itself. HasExtension, ClearExtension, GetExtension
and SetExtension are functions for manipulating extensions.
- Oneof field sets are given a single field in their message,
with distinguished wrapper types for each possible field value.
- Marshal and Unmarshal are functions to encode and decode the wire format.
When the .proto file specifies `syntax="proto3"`, there are some differences:
- Non-repeated fields of non-message type are values instead of pointers.
- Getters are only generated for message and oneof fields.
- Enum types do not get an Enum method.
The simplest way to describe this is to see an example.
Given file test.proto, containing
package example;
enum FOO { X = 17; }
message Test {
required string label = 1;
optional int32 type = 2 [default=77];
repeated int64 reps = 3;
optional group OptionalGroup = 4 {
required string RequiredField = 5;
}
oneof union {
int32 number = 6;
string name = 7;
}
}
The resulting file, test.pb.go, is:
package example
import proto "github.com/golang/protobuf/proto"
import math "math"
type FOO int32
const (
FOO_X FOO = 17
)
var FOO_name = map[int32]string{
17: "X",
}
var FOO_value = map[string]int32{
"X": 17,
}
func (x FOO) Enum() *FOO {
p := new(FOO)
*p = x
return p
}
func (x FOO) String() string {
return proto.EnumName(FOO_name, int32(x))
}
func (x *FOO) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(FOO_value, data)
if err != nil {
return err
}
*x = FOO(value)
return nil
}
type Test struct {
Label *string `protobuf:"bytes,1,req,name=label" json:"label,omitempty"`
Type *int32 `protobuf:"varint,2,opt,name=type,def=77" json:"type,omitempty"`
Reps []int64 `protobuf:"varint,3,rep,name=reps" json:"reps,omitempty"`
Optionalgroup *Test_OptionalGroup `protobuf:"group,4,opt,name=OptionalGroup" json:"optionalgroup,omitempty"`
// Types that are valid to be assigned to Union:
// *Test_Number
// *Test_Name
Union isTest_Union `protobuf_oneof:"union"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Test) Reset() { *m = Test{} }
func (m *Test) String() string { return proto.CompactTextString(m) }
func (*Test) ProtoMessage() {}
type isTest_Union interface {
isTest_Union()
}
type Test_Number struct {
Number int32 `protobuf:"varint,6,opt,name=number"`
}
type Test_Name struct {
Name string `protobuf:"bytes,7,opt,name=name"`
}
func (*Test_Number) isTest_Union() {}
func (*Test_Name) isTest_Union() {}
func (m *Test) GetUnion() isTest_Union {
if m != nil {
return m.Union
}
return nil
}
const Default_Test_Type int32 = 77
func (m *Test) GetLabel() string {
if m != nil && m.Label != nil {
return *m.Label
}
return ""
}
func (m *Test) GetType() int32 {
if m != nil && m.Type != nil {
return *m.Type
}
return Default_Test_Type
}
func (m *Test) GetOptionalgroup() *Test_OptionalGroup {
if m != nil {
return m.Optionalgroup
}
return nil
}
type Test_OptionalGroup struct {
RequiredField *string `protobuf:"bytes,5,req" json:"RequiredField,omitempty"`
}
func (m *Test_OptionalGroup) Reset() { *m = Test_OptionalGroup{} }
func (m *Test_OptionalGroup) String() string { return proto.CompactTextString(m) }
func (m *Test_OptionalGroup) GetRequiredField() string {
if m != nil && m.RequiredField != nil {
return *m.RequiredField
}
return ""
}
func (m *Test) GetNumber() int32 {
if x, ok := m.GetUnion().(*Test_Number); ok {
return x.Number
}
return 0
}
func (m *Test) GetName() string {
if x, ok := m.GetUnion().(*Test_Name); ok {
return x.Name
}
return ""
}
func init() {
proto.RegisterEnum("example.FOO", FOO_name, FOO_value)
}
To create and play with a Test object:
package main
import (
"log"
"github.com/golang/protobuf/proto"
pb "./example.pb"
)
func main() {
test := &pb.Test{
Label: proto.String("hello"),
Type: proto.Int32(17),
Reps: []int64{1, 2, 3},
Optionalgroup: &pb.Test_OptionalGroup{
RequiredField: proto.String("good bye"),
},
Union: &pb.Test_Name{"fred"},
}
data, err := proto.Marshal(test)
if err != nil {
log.Fatal("marshaling error: ", err)
}
newTest := &pb.Test{}
err = proto.Unmarshal(data, newTest)
if err != nil {
log.Fatal("unmarshaling error: ", err)
}
// Now test and newTest contain the same data.
if test.GetLabel() != newTest.GetLabel() {
log.Fatalf("data mismatch %q != %q", test.GetLabel(), newTest.GetLabel())
}
// Use a type switch to determine which oneof was set.
switch u := test.Union.(type) {
case *pb.Test_Number: // u.Number contains the number.
case *pb.Test_Name: // u.Name contains the string.
}
// etc.
}
*/
package proto
import (
"encoding/json"
"fmt"
"log"
"reflect"
"sort"
"strconv"
"sync"
)
// Message is implemented by generated protocol buffer messages.
type Message interface {
Reset()
String() string
ProtoMessage()
}
// Stats records allocation details about the protocol buffer encoders
// and decoders. Useful for tuning the library itself.
type Stats struct {
Emalloc uint64 // mallocs in encode
Dmalloc uint64 // mallocs in decode
Encode uint64 // number of encodes
Decode uint64 // number of decodes
Chit uint64 // number of cache hits
Cmiss uint64 // number of cache misses
Size uint64 // number of sizes
}
// Set to true to enable stats collection.
const collectStats = false
var stats Stats
// GetStats returns a copy of the global Stats structure.
func GetStats() Stats { return stats }
// A Buffer is a buffer manager for marshaling and unmarshaling
// protocol buffers. It may be reused between invocations to
// reduce memory usage. It is not necessary to use a Buffer;
// the global functions Marshal and Unmarshal create a
// temporary Buffer and are fine for most applications.
type Buffer struct {
buf []byte // encode/decode byte stream
index int // write point
// pools of basic types to amortize allocation.
bools []bool
uint32s []uint32
uint64s []uint64
// extra pools, only used with pointer_reflect.go
int32s []int32
int64s []int64
float32s []float32
float64s []float64
}
// NewBuffer allocates a new Buffer and initializes its internal data to
// the contents of the argument slice.
func NewBuffer(e []byte) *Buffer {
return &Buffer{buf: e}
}
// Reset resets the Buffer, ready for marshaling a new protocol buffer.
func (p *Buffer) Reset() {
p.buf = p.buf[0:0] // for reading/writing
p.index = 0 // for reading
}
// SetBuf replaces the internal buffer with the slice,
// ready for unmarshaling the contents of the slice.
func (p *Buffer) SetBuf(s []byte) {
p.buf = s
p.index = 0
}
// Bytes returns the contents of the Buffer.
func (p *Buffer) Bytes() []byte { return p.buf }
/*
* Helper routines for simplifying the creation of optional fields of basic type.
*/
// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool {
return &v
}
// Int32 is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it.
func Int32(v int32) *int32 {
return &v
}
// Int is a helper routine that allocates a new int32 value
// to store v and returns a pointer to it, but unlike Int32
// its argument value is an int.
func Int(v int) *int32 {
p := new(int32)
*p = int32(v)
return p
}
// Int64 is a helper routine that allocates a new int64 value
// to store v and returns a pointer to it.
func Int64(v int64) *int64 {
return &v
}
// Float32 is a helper routine that allocates a new float32 value
// to store v and returns a pointer to it.
func Float32(v float32) *float32 {
return &v
}
// Float64 is a helper routine that allocates a new float64 value
// to store v and returns a pointer to it.
func Float64(v float64) *float64 {
return &v
}
// Uint32 is a helper routine that allocates a new uint32 value
// to store v and returns a pointer to it.
func Uint32(v uint32) *uint32 {
return &v
}
// Uint64 is a helper routine that allocates a new uint64 value
// to store v and returns a pointer to it.
func Uint64(v uint64) *uint64 {
return &v
}
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
return &v
}
// EnumName is a helper function to simplify printing protocol buffer enums
// by name. Given an enum map and a value, it returns a useful string.
func EnumName(m map[int32]string, v int32) string {
s, ok := m[v]
if ok {
return s
}
return strconv.Itoa(int(v))
}
// UnmarshalJSONEnum is a helper function to simplify recovering enum int values
// from their JSON-encoded representation. Given a map from the enum's symbolic
// names to its int values, and a byte buffer containing the JSON-encoded
// value, it returns an int32 that can be cast to the enum type by the caller.
//
// The function can deal with both JSON representations, numeric and symbolic.
func UnmarshalJSONEnum(m map[string]int32, data []byte, enumName string) (int32, error) {
if data[0] == '"' {
// New style: enums are strings.
var repr string
if err := json.Unmarshal(data, &repr); err != nil {
return -1, err
}
val, ok := m[repr]
if !ok {
return 0, fmt.Errorf("unrecognized enum %s value %q", enumName, repr)
}
return val, nil
}
// Old style: enums are ints.
var val int32
if err := json.Unmarshal(data, &val); err != nil {
return 0, fmt.Errorf("cannot unmarshal %#q into enum %s", data, enumName)
}
return val, nil
}
// DebugPrint dumps the encoded data in b in a debugging format with a header
// including the string s. Used in testing but made available for general debugging.
func (p *Buffer) DebugPrint(s string, b []byte) {
var u uint64
obuf := p.buf
index := p.index
p.buf = b
p.index = 0
depth := 0
fmt.Printf("\n--- %s ---\n", s)
out:
for {
for i := 0; i < depth; i++ {
fmt.Print(" ")
}
index := p.index
if index == len(p.buf) {
break
}
op, err := p.DecodeVarint()
if err != nil {
fmt.Printf("%3d: fetching op err %v\n", index, err)
break out
}
tag := op >> 3
wire := op & 7
switch wire {
default:
fmt.Printf("%3d: t=%3d unknown wire=%d\n",
index, tag, wire)
break out
case WireBytes:
var r []byte
r, err = p.DecodeRawBytes(false)
if err != nil {
break out
}
fmt.Printf("%3d: t=%3d bytes [%d]", index, tag, len(r))
if len(r) <= 6 {
for i := 0; i < len(r); i++ {
fmt.Printf(" %.2x", r[i])
}
} else {
for i := 0; i < 3; i++ {
fmt.Printf(" %.2x", r[i])
}
fmt.Printf(" ..")
for i := len(r) - 3; i < len(r); i++ {
fmt.Printf(" %.2x", r[i])
}
}
fmt.Printf("\n")
case WireFixed32:
u, err = p.DecodeFixed32()
if err != nil {
fmt.Printf("%3d: t=%3d fix32 err %v\n", index, tag, err)
break out
}
fmt.Printf("%3d: t=%3d fix32 %d\n", index, tag, u)
case WireFixed64:
u, err = p.DecodeFixed64()
if err != nil {
fmt.Printf("%3d: t=%3d fix64 err %v\n", index, tag, err)
break out
}
fmt.Printf("%3d: t=%3d fix64 %d\n", index, tag, u)
case WireVarint:
u, err = p.DecodeVarint()
if err != nil {
fmt.Printf("%3d: t=%3d varint err %v\n", index, tag, err)
break out
}
fmt.Printf("%3d: t=%3d varint %d\n", index, tag, u)
case WireStartGroup:
fmt.Printf("%3d: t=%3d start\n", index, tag)
depth++
case WireEndGroup:
depth--
fmt.Printf("%3d: t=%3d end\n", index, tag)
}
}
if depth != 0 {
fmt.Printf("%3d: start-end not balanced %d\n", p.index, depth)
}
fmt.Printf("\n")
p.buf = obuf
p.index = index
}
// SetDefaults sets unset protocol buffer fields to their default values.
// It only modifies fields that are both unset and have defined defaults.
// It recursively sets default values in any non-nil sub-messages.
func SetDefaults(pb Message) {
setDefaults(reflect.ValueOf(pb), true, false)
}
// v is a pointer to a struct.
func setDefaults(v reflect.Value, recur, zeros bool) {
v = v.Elem()
defaultMu.RLock()
dm, ok := defaults[v.Type()]
defaultMu.RUnlock()
if !ok {
dm = buildDefaultMessage(v.Type())
defaultMu.Lock()
defaults[v.Type()] = dm
defaultMu.Unlock()
}
for _, sf := range dm.scalars {
f := v.Field(sf.index)
if !f.IsNil() {
// field already set
continue
}
dv := sf.value
if dv == nil && !zeros {
// no explicit default, and don't want to set zeros
continue
}
fptr := f.Addr().Interface() // **T
// TODO: Consider batching the allocations we do here.
switch sf.kind {
case reflect.Bool:
b := new(bool)
if dv != nil {
*b = dv.(bool)
}
*(fptr.(**bool)) = b
case reflect.Float32:
f := new(float32)
if dv != nil {
*f = dv.(float32)
}
*(fptr.(**float32)) = f
case reflect.Float64:
f := new(float64)
if dv != nil {
*f = dv.(float64)
}
*(fptr.(**float64)) = f
case reflect.Int32:
// might be an enum
if ft := f.Type(); ft != int32PtrType {
// enum
f.Set(reflect.New(ft.Elem()))
if dv != nil {
f.Elem().SetInt(int64(dv.(int32)))
}
} else {
// int32 field
i := new(int32)
if dv != nil {
*i = dv.(int32)
}
*(fptr.(**int32)) = i
}
case reflect.Int64:
i := new(int64)
if dv != nil {
*i = dv.(int64)
}
*(fptr.(**int64)) = i
case reflect.String:
s := new(string)
if dv != nil {
*s = dv.(string)
}
*(fptr.(**string)) = s
case reflect.Uint8:
// exceptional case: []byte
var b []byte
if dv != nil {
db := dv.([]byte)
b = make([]byte, len(db))
copy(b, db)
} else {
b = []byte{}
}
*(fptr.(*[]byte)) = b
case reflect.Uint32:
u := new(uint32)
if dv != nil {
*u = dv.(uint32)
}
*(fptr.(**uint32)) = u
case reflect.Uint64:
u := new(uint64)
if dv != nil {
*u = dv.(uint64)
}
*(fptr.(**uint64)) = u
default:
log.Printf("proto: can't set default for field %v (sf.kind=%v)", f, sf.kind)
}
}
for _, ni := range dm.nested {
f := v.Field(ni)
// f is *T or []*T or map[T]*T
switch f.Kind() {
case reflect.Ptr:
if f.IsNil() {
continue
}
setDefaults(f, recur, zeros)
case reflect.Slice:
for i := 0; i < f.Len(); i++ {
e := f.Index(i)
if e.IsNil() {
continue
}
setDefaults(e, recur, zeros)
}
case reflect.Map:
for _, k := range f.MapKeys() {
e := f.MapIndex(k)
if e.IsNil() {
continue
}
setDefaults(e, recur, zeros)
}
}
}
}
var (
// defaults maps a protocol buffer struct type to a slice of the fields,
// with its scalar fields set to their proto-declared non-zero default values.
defaultMu sync.RWMutex
defaults = make(map[reflect.Type]defaultMessage)
int32PtrType = reflect.TypeOf((*int32)(nil))
)
// defaultMessage represents information about the default values of a message.
type defaultMessage struct {
scalars []scalarField
nested []int // struct field index of nested messages
}
type scalarField struct {
index int // struct field index
kind reflect.Kind // element type (the T in *T or []T)
value interface{} // the proto-declared default value, or nil
}
// t is a struct type.
func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
sprop := GetProperties(t)
for _, prop := range sprop.Prop {
fi, ok := sprop.decoderTags.get(prop.Tag)
if !ok {
// XXX_unrecognized
continue
}
ft := t.Field(fi).Type
sf, nested, err := fieldDefault(ft, prop)
switch {
case err != nil:
log.Print(err)
case nested:
dm.nested = append(dm.nested, fi)
case sf != nil:
sf.index = fi
dm.scalars = append(dm.scalars, *sf)
}
}
return dm
}
// fieldDefault returns the scalarField for field type ft.
// sf will be nil if the field can not have a default.
// nestedMessage will be true if this is a nested message.
// Note that sf.index is not set on return.
func fieldDefault(ft reflect.Type, prop *Properties) (sf *scalarField, nestedMessage bool, err error) {
var canHaveDefault bool
switch ft.Kind() {
case reflect.Ptr:
if ft.Elem().Kind() == reflect.Struct {
nestedMessage = true
} else {
canHaveDefault = true // proto2 scalar field
}
case reflect.Slice:
switch ft.Elem().Kind() {
case reflect.Ptr:
nestedMessage = true // repeated message
case reflect.Uint8:
canHaveDefault = true // bytes field
}
case reflect.Map:
if ft.Elem().Kind() == reflect.Ptr {
nestedMessage = true // map with message values
}
}
if !canHaveDefault {
if nestedMessage {
return nil, true, nil
}
return nil, false, nil
}
// We now know that ft is a pointer or slice.
sf = &scalarField{kind: ft.Elem().Kind()}
// scalar fields without defaults
if !prop.HasDefault {
return sf, false, nil
}
// a scalar field: either *T or []byte
switch ft.Elem().Kind() {
case reflect.Bool:
x, err := strconv.ParseBool(prop.Default)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default bool %q: %v", prop.Default, err)
}
sf.value = x
case reflect.Float32:
x, err := strconv.ParseFloat(prop.Default, 32)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default float32 %q: %v", prop.Default, err)
}
sf.value = float32(x)
case reflect.Float64:
x, err := strconv.ParseFloat(prop.Default, 64)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default float64 %q: %v", prop.Default, err)
}
sf.value = x
case reflect.Int32:
x, err := strconv.ParseInt(prop.Default, 10, 32)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default int32 %q: %v", prop.Default, err)
}
sf.value = int32(x)
case reflect.Int64:
x, err := strconv.ParseInt(prop.Default, 10, 64)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default int64 %q: %v", prop.Default, err)
}
sf.value = x
case reflect.String:
sf.value = prop.Default
case reflect.Uint8:
// []byte (not *uint8)
sf.value = []byte(prop.Default)
case reflect.Uint32:
x, err := strconv.ParseUint(prop.Default, 10, 32)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default uint32 %q: %v", prop.Default, err)
}
sf.value = uint32(x)
case reflect.Uint64:
x, err := strconv.ParseUint(prop.Default, 10, 64)
if err != nil {
return nil, false, fmt.Errorf("proto: bad default uint64 %q: %v", prop.Default, err)
}
sf.value = x
default:
return nil, false, fmt.Errorf("proto: unhandled def kind %v", ft.Elem().Kind())
}
return sf, false, nil
}
// Map fields may have key types of non-float scalars, strings and enums.
// The easiest way to sort them in some deterministic order is to use fmt.
// If this turns out to be inefficient we can always consider other options,
// such as doing a Schwartzian transform.
func mapKeys(vs []reflect.Value) sort.Interface {
s := mapKeySorter{
vs: vs,
// default Less function: textual comparison
less: func(a, b reflect.Value) bool {
return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
},
}
// Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
// numeric keys are sorted numerically.
if len(vs) == 0 {
return s
}
switch vs[0].Kind() {
case reflect.Int32, reflect.Int64:
s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
case reflect.Uint32, reflect.Uint64:
s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
}
return s
}
type mapKeySorter struct {
vs []reflect.Value
less func(a, b reflect.Value) bool
}
func (s mapKeySorter) Len() int { return len(s.vs) }
func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
func (s mapKeySorter) Less(i, j int) bool {
return s.less(s.vs[i], s.vs[j])
}
// isProto3Zero reports whether v is a zero proto3 value.
func isProto3Zero(v reflect.Value) bool {
switch v.Kind() {
case reflect.Bool:
return !v.Bool()
case reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint32, reflect.Uint64:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.String:
return v.String() == ""
}
return false
}
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
// to assert that that code is compatible with this version of the proto package.
const ProtoPackageIsVersion1 = true

View File

@@ -1,280 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
/*
* Support for message sets.
*/
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"reflect"
"sort"
)
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
// A message type ID is required for storing a protocol buffer in a message set.
var errNoMessageTypeID = errors.New("proto does not have a message type ID")
// The first two types (_MessageSet_Item and messageSet)
// model what the protocol compiler produces for the following protocol message:
// message MessageSet {
// repeated group Item = 1 {
// required int32 type_id = 2;
// required string message = 3;
// };
// }
// That is the MessageSet wire format. We can't use a proto to generate these
// because that would introduce a circular dependency between it and this package.
type _MessageSet_Item struct {
TypeId *int32 `protobuf:"varint,2,req,name=type_id"`
Message []byte `protobuf:"bytes,3,req,name=message"`
}
type messageSet struct {
Item []*_MessageSet_Item `protobuf:"group,1,rep"`
XXX_unrecognized []byte
// TODO: caching?
}
// Make sure messageSet is a Message.
var _ Message = (*messageSet)(nil)
// messageTypeIder is an interface satisfied by a protocol buffer type
// that may be stored in a MessageSet.
type messageTypeIder interface {
MessageTypeId() int32
}
func (ms *messageSet) find(pb Message) *_MessageSet_Item {
mti, ok := pb.(messageTypeIder)
if !ok {
return nil
}
id := mti.MessageTypeId()
for _, item := range ms.Item {
if *item.TypeId == id {
return item
}
}
return nil
}
func (ms *messageSet) Has(pb Message) bool {
if ms.find(pb) != nil {
return true
}
return false
}
func (ms *messageSet) Unmarshal(pb Message) error {
if item := ms.find(pb); item != nil {
return Unmarshal(item.Message, pb)
}
if _, ok := pb.(messageTypeIder); !ok {
return errNoMessageTypeID
}
return nil // TODO: return error instead?
}
func (ms *messageSet) Marshal(pb Message) error {
msg, err := Marshal(pb)
if err != nil {
return err
}
if item := ms.find(pb); item != nil {
// reuse existing item
item.Message = msg
return nil
}
mti, ok := pb.(messageTypeIder)
if !ok {
return errNoMessageTypeID
}
mtid := mti.MessageTypeId()
ms.Item = append(ms.Item, &_MessageSet_Item{
TypeId: &mtid,
Message: msg,
})
return nil
}
func (ms *messageSet) Reset() { *ms = messageSet{} }
func (ms *messageSet) String() string { return CompactTextString(ms) }
func (*messageSet) ProtoMessage() {}
// Support for the message_set_wire_format message option.
func skipVarint(buf []byte) []byte {
i := 0
for ; buf[i]&0x80 != 0; i++ {
}
return buf[i+1:]
}
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSet(m map[int32]Extension) ([]byte, error) {
if err := encodeExtensionMap(m); err != nil {
return nil, err
}
// Sort extension IDs to provide a deterministic encoding.
// See also enc_map in encode.go.
ids := make([]int, 0, len(m))
for id := range m {
ids = append(ids, int(id))
}
sort.Ints(ids)
ms := &messageSet{Item: make([]*_MessageSet_Item, 0, len(m))}
for _, id := range ids {
e := m[int32(id)]
// Remove the wire type and field number varint, as well as the length varint.
msg := skipVarint(skipVarint(e.enc))
ms.Item = append(ms.Item, &_MessageSet_Item{
TypeId: Int32(int32(id)),
Message: msg,
})
}
return Marshal(ms)
}
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
// It is called by generated Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSet(buf []byte, m map[int32]Extension) error {
ms := new(messageSet)
if err := Unmarshal(buf, ms); err != nil {
return err
}
for _, item := range ms.Item {
id := *item.TypeId
msg := item.Message
// Restore wire type and field number varint, plus length varint.
// Be careful to preserve duplicate items.
b := EncodeVarint(uint64(id)<<3 | WireBytes)
if ext, ok := m[id]; ok {
// Existing data; rip off the tag and length varint
// so we join the new data correctly.
// We can assume that ext.enc is set because we are unmarshaling.
o := ext.enc[len(b):] // skip wire type and field number
_, n := DecodeVarint(o) // calculate length of length varint
o = o[n:] // skip length varint
msg = append(o, msg...) // join old data and new data
}
b = append(b, EncodeVarint(uint64(len(msg)))...)
b = append(b, msg...)
m[id] = Extension{enc: b}
}
return nil
}
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func MarshalMessageSetJSON(m map[int32]Extension) ([]byte, error) {
var b bytes.Buffer
b.WriteByte('{')
// Process the map in key order for deterministic output.
ids := make([]int32, 0, len(m))
for id := range m {
ids = append(ids, id)
}
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
for i, id := range ids {
ext := m[id]
if i > 0 {
b.WriteByte(',')
}
msd, ok := messageSetMap[id]
if !ok {
// Unknown type; we can't render it, so skip it.
continue
}
fmt.Fprintf(&b, `"[%s]":`, msd.name)
x := ext.value
if x == nil {
x = reflect.New(msd.t.Elem()).Interface()
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
return nil, err
}
}
d, err := json.Marshal(x)
if err != nil {
return nil, err
}
b.Write(d)
}
b.WriteByte('}')
return b.Bytes(), nil
}
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
func UnmarshalMessageSetJSON(buf []byte, m map[int32]Extension) error {
// Common-case fast path.
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
return nil
}
// This is fairly tricky, and it's not clear that it is needed.
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
}
// A global registry of types that can be used in a MessageSet.
var messageSetMap = make(map[int32]messageSetDesc)
type messageSetDesc struct {
t reflect.Type // pointer to struct
name string
}
// RegisterMessageSetType is called from the generated code.
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
messageSetMap[fieldNum] = messageSetDesc{
t: reflect.TypeOf(m),
name: name,
}
}

View File

@@ -1,479 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2012 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build appengine
// This file contains an implementation of proto field accesses using package reflect.
// It is slower than the code in pointer_unsafe.go but it avoids package unsafe and can
// be used on App Engine.
package proto
import (
"math"
"reflect"
)
// A structPointer is a pointer to a struct.
type structPointer struct {
v reflect.Value
}
// toStructPointer returns a structPointer equivalent to the given reflect value.
// The reflect value must itself be a pointer to a struct.
func toStructPointer(v reflect.Value) structPointer {
return structPointer{v}
}
// IsNil reports whether p is nil.
func structPointer_IsNil(p structPointer) bool {
return p.v.IsNil()
}
// Interface returns the struct pointer as an interface value.
func structPointer_Interface(p structPointer, _ reflect.Type) interface{} {
return p.v.Interface()
}
// A field identifies a field in a struct, accessible from a structPointer.
// In this implementation, a field is identified by the sequence of field indices
// passed to reflect's FieldByIndex.
type field []int
// toField returns a field equivalent to the given reflect field.
func toField(f *reflect.StructField) field {
return f.Index
}
// invalidField is an invalid field identifier.
var invalidField = field(nil)
// IsValid reports whether the field identifier is valid.
func (f field) IsValid() bool { return f != nil }
// field returns the given field in the struct as a reflect value.
func structPointer_field(p structPointer, f field) reflect.Value {
// Special case: an extension map entry with a value of type T
// passes a *T to the struct-handling code with a zero field,
// expecting that it will be treated as equivalent to *struct{ X T },
// which has the same memory layout. We have to handle that case
// specially, because reflect will panic if we call FieldByIndex on a
// non-struct.
if f == nil {
return p.v.Elem()
}
return p.v.Elem().FieldByIndex(f)
}
// ifield returns the given field in the struct as an interface value.
func structPointer_ifield(p structPointer, f field) interface{} {
return structPointer_field(p, f).Addr().Interface()
}
// Bytes returns the address of a []byte field in the struct.
func structPointer_Bytes(p structPointer, f field) *[]byte {
return structPointer_ifield(p, f).(*[]byte)
}
// BytesSlice returns the address of a [][]byte field in the struct.
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
return structPointer_ifield(p, f).(*[][]byte)
}
// Bool returns the address of a *bool field in the struct.
func structPointer_Bool(p structPointer, f field) **bool {
return structPointer_ifield(p, f).(**bool)
}
// BoolVal returns the address of a bool field in the struct.
func structPointer_BoolVal(p structPointer, f field) *bool {
return structPointer_ifield(p, f).(*bool)
}
// BoolSlice returns the address of a []bool field in the struct.
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
return structPointer_ifield(p, f).(*[]bool)
}
// String returns the address of a *string field in the struct.
func structPointer_String(p structPointer, f field) **string {
return structPointer_ifield(p, f).(**string)
}
// StringVal returns the address of a string field in the struct.
func structPointer_StringVal(p structPointer, f field) *string {
return structPointer_ifield(p, f).(*string)
}
// StringSlice returns the address of a []string field in the struct.
func structPointer_StringSlice(p structPointer, f field) *[]string {
return structPointer_ifield(p, f).(*[]string)
}
// ExtMap returns the address of an extension map field in the struct.
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return structPointer_ifield(p, f).(*map[int32]Extension)
}
// NewAt returns the reflect.Value for a pointer to a field in the struct.
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
return structPointer_field(p, f).Addr()
}
// SetStructPointer writes a *struct field in the struct.
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
structPointer_field(p, f).Set(q.v)
}
// GetStructPointer reads a *struct field in the struct.
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
return structPointer{structPointer_field(p, f)}
}
// StructPointerSlice the address of a []*struct field in the struct.
func structPointer_StructPointerSlice(p structPointer, f field) structPointerSlice {
return structPointerSlice{structPointer_field(p, f)}
}
// A structPointerSlice represents the address of a slice of pointers to structs
// (themselves messages or groups). That is, v.Type() is *[]*struct{...}.
type structPointerSlice struct {
v reflect.Value
}
func (p structPointerSlice) Len() int { return p.v.Len() }
func (p structPointerSlice) Index(i int) structPointer { return structPointer{p.v.Index(i)} }
func (p structPointerSlice) Append(q structPointer) {
p.v.Set(reflect.Append(p.v, q.v))
}
var (
int32Type = reflect.TypeOf(int32(0))
uint32Type = reflect.TypeOf(uint32(0))
float32Type = reflect.TypeOf(float32(0))
int64Type = reflect.TypeOf(int64(0))
uint64Type = reflect.TypeOf(uint64(0))
float64Type = reflect.TypeOf(float64(0))
)
// A word32 represents a field of type *int32, *uint32, *float32, or *enum.
// That is, v.Type() is *int32, *uint32, *float32, or *enum and v is assignable.
type word32 struct {
v reflect.Value
}
// IsNil reports whether p is nil.
func word32_IsNil(p word32) bool {
return p.v.IsNil()
}
// Set sets p to point at a newly allocated word with bits set to x.
func word32_Set(p word32, o *Buffer, x uint32) {
t := p.v.Type().Elem()
switch t {
case int32Type:
if len(o.int32s) == 0 {
o.int32s = make([]int32, uint32PoolSize)
}
o.int32s[0] = int32(x)
p.v.Set(reflect.ValueOf(&o.int32s[0]))
o.int32s = o.int32s[1:]
return
case uint32Type:
if len(o.uint32s) == 0 {
o.uint32s = make([]uint32, uint32PoolSize)
}
o.uint32s[0] = x
p.v.Set(reflect.ValueOf(&o.uint32s[0]))
o.uint32s = o.uint32s[1:]
return
case float32Type:
if len(o.float32s) == 0 {
o.float32s = make([]float32, uint32PoolSize)
}
o.float32s[0] = math.Float32frombits(x)
p.v.Set(reflect.ValueOf(&o.float32s[0]))
o.float32s = o.float32s[1:]
return
}
// must be enum
p.v.Set(reflect.New(t))
p.v.Elem().SetInt(int64(int32(x)))
}
// Get gets the bits pointed at by p, as a uint32.
func word32_Get(p word32) uint32 {
elem := p.v.Elem()
switch elem.Kind() {
case reflect.Int32:
return uint32(elem.Int())
case reflect.Uint32:
return uint32(elem.Uint())
case reflect.Float32:
return math.Float32bits(float32(elem.Float()))
}
panic("unreachable")
}
// Word32 returns a reference to a *int32, *uint32, *float32, or *enum field in the struct.
func structPointer_Word32(p structPointer, f field) word32 {
return word32{structPointer_field(p, f)}
}
// A word32Val represents a field of type int32, uint32, float32, or enum.
// That is, v.Type() is int32, uint32, float32, or enum and v is assignable.
type word32Val struct {
v reflect.Value
}
// Set sets *p to x.
func word32Val_Set(p word32Val, x uint32) {
switch p.v.Type() {
case int32Type:
p.v.SetInt(int64(x))
return
case uint32Type:
p.v.SetUint(uint64(x))
return
case float32Type:
p.v.SetFloat(float64(math.Float32frombits(x)))
return
}
// must be enum
p.v.SetInt(int64(int32(x)))
}
// Get gets the bits pointed at by p, as a uint32.
func word32Val_Get(p word32Val) uint32 {
elem := p.v
switch elem.Kind() {
case reflect.Int32:
return uint32(elem.Int())
case reflect.Uint32:
return uint32(elem.Uint())
case reflect.Float32:
return math.Float32bits(float32(elem.Float()))
}
panic("unreachable")
}
// Word32Val returns a reference to a int32, uint32, float32, or enum field in the struct.
func structPointer_Word32Val(p structPointer, f field) word32Val {
return word32Val{structPointer_field(p, f)}
}
// A word32Slice is a slice of 32-bit values.
// That is, v.Type() is []int32, []uint32, []float32, or []enum.
type word32Slice struct {
v reflect.Value
}
func (p word32Slice) Append(x uint32) {
n, m := p.v.Len(), p.v.Cap()
if n < m {
p.v.SetLen(n + 1)
} else {
t := p.v.Type().Elem()
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
}
elem := p.v.Index(n)
switch elem.Kind() {
case reflect.Int32:
elem.SetInt(int64(int32(x)))
case reflect.Uint32:
elem.SetUint(uint64(x))
case reflect.Float32:
elem.SetFloat(float64(math.Float32frombits(x)))
}
}
func (p word32Slice) Len() int {
return p.v.Len()
}
func (p word32Slice) Index(i int) uint32 {
elem := p.v.Index(i)
switch elem.Kind() {
case reflect.Int32:
return uint32(elem.Int())
case reflect.Uint32:
return uint32(elem.Uint())
case reflect.Float32:
return math.Float32bits(float32(elem.Float()))
}
panic("unreachable")
}
// Word32Slice returns a reference to a []int32, []uint32, []float32, or []enum field in the struct.
func structPointer_Word32Slice(p structPointer, f field) word32Slice {
return word32Slice{structPointer_field(p, f)}
}
// word64 is like word32 but for 64-bit values.
type word64 struct {
v reflect.Value
}
func word64_Set(p word64, o *Buffer, x uint64) {
t := p.v.Type().Elem()
switch t {
case int64Type:
if len(o.int64s) == 0 {
o.int64s = make([]int64, uint64PoolSize)
}
o.int64s[0] = int64(x)
p.v.Set(reflect.ValueOf(&o.int64s[0]))
o.int64s = o.int64s[1:]
return
case uint64Type:
if len(o.uint64s) == 0 {
o.uint64s = make([]uint64, uint64PoolSize)
}
o.uint64s[0] = x
p.v.Set(reflect.ValueOf(&o.uint64s[0]))
o.uint64s = o.uint64s[1:]
return
case float64Type:
if len(o.float64s) == 0 {
o.float64s = make([]float64, uint64PoolSize)
}
o.float64s[0] = math.Float64frombits(x)
p.v.Set(reflect.ValueOf(&o.float64s[0]))
o.float64s = o.float64s[1:]
return
}
panic("unreachable")
}
func word64_IsNil(p word64) bool {
return p.v.IsNil()
}
func word64_Get(p word64) uint64 {
elem := p.v.Elem()
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return elem.Uint()
case reflect.Float64:
return math.Float64bits(elem.Float())
}
panic("unreachable")
}
func structPointer_Word64(p structPointer, f field) word64 {
return word64{structPointer_field(p, f)}
}
// word64Val is like word32Val but for 64-bit values.
type word64Val struct {
v reflect.Value
}
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
switch p.v.Type() {
case int64Type:
p.v.SetInt(int64(x))
return
case uint64Type:
p.v.SetUint(x)
return
case float64Type:
p.v.SetFloat(math.Float64frombits(x))
return
}
panic("unreachable")
}
func word64Val_Get(p word64Val) uint64 {
elem := p.v
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return elem.Uint()
case reflect.Float64:
return math.Float64bits(elem.Float())
}
panic("unreachable")
}
func structPointer_Word64Val(p structPointer, f field) word64Val {
return word64Val{structPointer_field(p, f)}
}
type word64Slice struct {
v reflect.Value
}
func (p word64Slice) Append(x uint64) {
n, m := p.v.Len(), p.v.Cap()
if n < m {
p.v.SetLen(n + 1)
} else {
t := p.v.Type().Elem()
p.v.Set(reflect.Append(p.v, reflect.Zero(t)))
}
elem := p.v.Index(n)
switch elem.Kind() {
case reflect.Int64:
elem.SetInt(int64(int64(x)))
case reflect.Uint64:
elem.SetUint(uint64(x))
case reflect.Float64:
elem.SetFloat(float64(math.Float64frombits(x)))
}
}
func (p word64Slice) Len() int {
return p.v.Len()
}
func (p word64Slice) Index(i int) uint64 {
elem := p.v.Index(i)
switch elem.Kind() {
case reflect.Int64:
return uint64(elem.Int())
case reflect.Uint64:
return uint64(elem.Uint())
case reflect.Float64:
return math.Float64bits(float64(elem.Float()))
}
panic("unreachable")
}
func structPointer_Word64Slice(p structPointer, f field) word64Slice {
return word64Slice{structPointer_field(p, f)}
}

View File

@@ -1,266 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2012 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// +build !appengine
// This file contains the implementation of the proto field accesses using package unsafe.
package proto
import (
"reflect"
"unsafe"
)
// NOTE: These type_Foo functions would more idiomatically be methods,
// but Go does not allow methods on pointer types, and we must preserve
// some pointer type for the garbage collector. We use these
// funcs with clunky names as our poor approximation to methods.
//
// An alternative would be
// type structPointer struct { p unsafe.Pointer }
// but that does not registerize as well.
// A structPointer is a pointer to a struct.
type structPointer unsafe.Pointer
// toStructPointer returns a structPointer equivalent to the given reflect value.
func toStructPointer(v reflect.Value) structPointer {
return structPointer(unsafe.Pointer(v.Pointer()))
}
// IsNil reports whether p is nil.
func structPointer_IsNil(p structPointer) bool {
return p == nil
}
// Interface returns the struct pointer, assumed to have element type t,
// as an interface value.
func structPointer_Interface(p structPointer, t reflect.Type) interface{} {
return reflect.NewAt(t, unsafe.Pointer(p)).Interface()
}
// A field identifies a field in a struct, accessible from a structPointer.
// In this implementation, a field is identified by its byte offset from the start of the struct.
type field uintptr
// toField returns a field equivalent to the given reflect field.
func toField(f *reflect.StructField) field {
return field(f.Offset)
}
// invalidField is an invalid field identifier.
const invalidField = ^field(0)
// IsValid reports whether the field identifier is valid.
func (f field) IsValid() bool {
return f != ^field(0)
}
// Bytes returns the address of a []byte field in the struct.
func structPointer_Bytes(p structPointer, f field) *[]byte {
return (*[]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// BytesSlice returns the address of a [][]byte field in the struct.
func structPointer_BytesSlice(p structPointer, f field) *[][]byte {
return (*[][]byte)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// Bool returns the address of a *bool field in the struct.
func structPointer_Bool(p structPointer, f field) **bool {
return (**bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// BoolVal returns the address of a bool field in the struct.
func structPointer_BoolVal(p structPointer, f field) *bool {
return (*bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// BoolSlice returns the address of a []bool field in the struct.
func structPointer_BoolSlice(p structPointer, f field) *[]bool {
return (*[]bool)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// String returns the address of a *string field in the struct.
func structPointer_String(p structPointer, f field) **string {
return (**string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// StringVal returns the address of a string field in the struct.
func structPointer_StringVal(p structPointer, f field) *string {
return (*string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// StringSlice returns the address of a []string field in the struct.
func structPointer_StringSlice(p structPointer, f field) *[]string {
return (*[]string)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// ExtMap returns the address of an extension map field in the struct.
func structPointer_ExtMap(p structPointer, f field) *map[int32]Extension {
return (*map[int32]Extension)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// NewAt returns the reflect.Value for a pointer to a field in the struct.
func structPointer_NewAt(p structPointer, f field, typ reflect.Type) reflect.Value {
return reflect.NewAt(typ, unsafe.Pointer(uintptr(p)+uintptr(f)))
}
// SetStructPointer writes a *struct field in the struct.
func structPointer_SetStructPointer(p structPointer, f field, q structPointer) {
*(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f))) = q
}
// GetStructPointer reads a *struct field in the struct.
func structPointer_GetStructPointer(p structPointer, f field) structPointer {
return *(*structPointer)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// StructPointerSlice the address of a []*struct field in the struct.
func structPointer_StructPointerSlice(p structPointer, f field) *structPointerSlice {
return (*structPointerSlice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// A structPointerSlice represents a slice of pointers to structs (themselves submessages or groups).
type structPointerSlice []structPointer
func (v *structPointerSlice) Len() int { return len(*v) }
func (v *structPointerSlice) Index(i int) structPointer { return (*v)[i] }
func (v *structPointerSlice) Append(p structPointer) { *v = append(*v, p) }
// A word32 is the address of a "pointer to 32-bit value" field.
type word32 **uint32
// IsNil reports whether *v is nil.
func word32_IsNil(p word32) bool {
return *p == nil
}
// Set sets *v to point at a newly allocated word set to x.
func word32_Set(p word32, o *Buffer, x uint32) {
if len(o.uint32s) == 0 {
o.uint32s = make([]uint32, uint32PoolSize)
}
o.uint32s[0] = x
*p = &o.uint32s[0]
o.uint32s = o.uint32s[1:]
}
// Get gets the value pointed at by *v.
func word32_Get(p word32) uint32 {
return **p
}
// Word32 returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
func structPointer_Word32(p structPointer, f field) word32 {
return word32((**uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// A word32Val is the address of a 32-bit value field.
type word32Val *uint32
// Set sets *p to x.
func word32Val_Set(p word32Val, x uint32) {
*p = x
}
// Get gets the value pointed at by p.
func word32Val_Get(p word32Val) uint32 {
return *p
}
// Word32Val returns the address of a *int32, *uint32, *float32, or *enum field in the struct.
func structPointer_Word32Val(p structPointer, f field) word32Val {
return word32Val((*uint32)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// A word32Slice is a slice of 32-bit values.
type word32Slice []uint32
func (v *word32Slice) Append(x uint32) { *v = append(*v, x) }
func (v *word32Slice) Len() int { return len(*v) }
func (v *word32Slice) Index(i int) uint32 { return (*v)[i] }
// Word32Slice returns the address of a []int32, []uint32, []float32, or []enum field in the struct.
func structPointer_Word32Slice(p structPointer, f field) *word32Slice {
return (*word32Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}
// word64 is like word32 but for 64-bit values.
type word64 **uint64
func word64_Set(p word64, o *Buffer, x uint64) {
if len(o.uint64s) == 0 {
o.uint64s = make([]uint64, uint64PoolSize)
}
o.uint64s[0] = x
*p = &o.uint64s[0]
o.uint64s = o.uint64s[1:]
}
func word64_IsNil(p word64) bool {
return *p == nil
}
func word64_Get(p word64) uint64 {
return **p
}
func structPointer_Word64(p structPointer, f field) word64 {
return word64((**uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// word64Val is like word32Val but for 64-bit values.
type word64Val *uint64
func word64Val_Set(p word64Val, o *Buffer, x uint64) {
*p = x
}
func word64Val_Get(p word64Val) uint64 {
return *p
}
func structPointer_Word64Val(p structPointer, f field) word64Val {
return word64Val((*uint64)(unsafe.Pointer(uintptr(p) + uintptr(f))))
}
// word64Slice is like word32Slice but for 64-bit values.
type word64Slice []uint64
func (v *word64Slice) Append(x uint64) { *v = append(*v, x) }
func (v *word64Slice) Len() int { return len(*v) }
func (v *word64Slice) Index(i int) uint64 { return (*v)[i] }
func structPointer_Word64Slice(p structPointer, f field) *word64Slice {
return (*word64Slice)(unsafe.Pointer(uintptr(p) + uintptr(f)))
}

View File

@@ -1,842 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
/*
* Routines for encoding data into the wire format for protocol buffers.
*/
import (
"fmt"
"log"
"os"
"reflect"
"sort"
"strconv"
"strings"
"sync"
)
const debug bool = false
// Constants that identify the encoding of a value on the wire.
const (
WireVarint = 0
WireFixed64 = 1
WireBytes = 2
WireStartGroup = 3
WireEndGroup = 4
WireFixed32 = 5
)
const startSize = 10 // initial slice/string sizes
// Encoders are defined in encode.go
// An encoder outputs the full representation of a field, including its
// tag and encoder type.
type encoder func(p *Buffer, prop *Properties, base structPointer) error
// A valueEncoder encodes a single integer in a particular encoding.
type valueEncoder func(o *Buffer, x uint64) error
// Sizers are defined in encode.go
// A sizer returns the encoded size of a field, including its tag and encoder
// type.
type sizer func(prop *Properties, base structPointer) int
// A valueSizer returns the encoded size of a single integer in a particular
// encoding.
type valueSizer func(x uint64) int
// Decoders are defined in decode.go
// A decoder creates a value from its wire representation.
// Unrecognized subelements are saved in unrec.
type decoder func(p *Buffer, prop *Properties, base structPointer) error
// A valueDecoder decodes a single integer in a particular encoding.
type valueDecoder func(o *Buffer) (x uint64, err error)
// A oneofMarshaler does the marshaling for all oneof fields in a message.
type oneofMarshaler func(Message, *Buffer) error
// A oneofUnmarshaler does the unmarshaling for a oneof field in a message.
type oneofUnmarshaler func(Message, int, int, *Buffer) (bool, error)
// A oneofSizer does the sizing for all oneof fields in a message.
type oneofSizer func(Message) int
// tagMap is an optimization over map[int]int for typical protocol buffer
// use-cases. Encoded protocol buffers are often in tag order with small tag
// numbers.
type tagMap struct {
fastTags []int
slowTags map[int]int
}
// tagMapFastLimit is the upper bound on the tag number that will be stored in
// the tagMap slice rather than its map.
const tagMapFastLimit = 1024
func (p *tagMap) get(t int) (int, bool) {
if t > 0 && t < tagMapFastLimit {
if t >= len(p.fastTags) {
return 0, false
}
fi := p.fastTags[t]
return fi, fi >= 0
}
fi, ok := p.slowTags[t]
return fi, ok
}
func (p *tagMap) put(t int, fi int) {
if t > 0 && t < tagMapFastLimit {
for len(p.fastTags) < t+1 {
p.fastTags = append(p.fastTags, -1)
}
p.fastTags[t] = fi
return
}
if p.slowTags == nil {
p.slowTags = make(map[int]int)
}
p.slowTags[t] = fi
}
// StructProperties represents properties for all the fields of a struct.
// decoderTags and decoderOrigNames should only be used by the decoder.
type StructProperties struct {
Prop []*Properties // properties for each field
reqCount int // required count
decoderTags tagMap // map from proto tag to struct field number
decoderOrigNames map[string]int // map from original name to struct field number
order []int // list of struct field numbers in tag order
unrecField field // field id of the XXX_unrecognized []byte field
extendable bool // is this an extendable proto
oneofMarshaler oneofMarshaler
oneofUnmarshaler oneofUnmarshaler
oneofSizer oneofSizer
stype reflect.Type
// OneofTypes contains information about the oneof fields in this message.
// It is keyed by the original name of a field.
OneofTypes map[string]*OneofProperties
}
// OneofProperties represents information about a specific field in a oneof.
type OneofProperties struct {
Type reflect.Type // pointer to generated struct type for this oneof field
Field int // struct field number of the containing oneof in the message
Prop *Properties
}
// Implement the sorting interface so we can sort the fields in tag order, as recommended by the spec.
// See encode.go, (*Buffer).enc_struct.
func (sp *StructProperties) Len() int { return len(sp.order) }
func (sp *StructProperties) Less(i, j int) bool {
return sp.Prop[sp.order[i]].Tag < sp.Prop[sp.order[j]].Tag
}
func (sp *StructProperties) Swap(i, j int) { sp.order[i], sp.order[j] = sp.order[j], sp.order[i] }
// Properties represents the protocol-specific behavior of a single struct field.
type Properties struct {
Name string // name of the field, for error messages
OrigName string // original name before protocol compiler (always set)
Wire string
WireType int
Tag int
Required bool
Optional bool
Repeated bool
Packed bool // relevant for repeated primitives only
Enum string // set for enum types only
proto3 bool // whether this is known to be a proto3 field; set for []byte only
oneof bool // whether this is a oneof field
Default string // default value
HasDefault bool // whether an explicit default was provided
def_uint64 uint64
enc encoder
valEnc valueEncoder // set for bool and numeric types only
field field
tagcode []byte // encoding of EncodeVarint((Tag<<3)|WireType)
tagbuf [8]byte
stype reflect.Type // set for struct types only
sprop *StructProperties // set for struct types only
isMarshaler bool
isUnmarshaler bool
mtype reflect.Type // set for map types only
mkeyprop *Properties // set for map types only
mvalprop *Properties // set for map types only
size sizer
valSize valueSizer // set for bool and numeric types only
dec decoder
valDec valueDecoder // set for bool and numeric types only
// If this is a packable field, this will be the decoder for the packed version of the field.
packedDec decoder
}
// String formats the properties in the protobuf struct field tag style.
func (p *Properties) String() string {
s := p.Wire
s = ","
s += strconv.Itoa(p.Tag)
if p.Required {
s += ",req"
}
if p.Optional {
s += ",opt"
}
if p.Repeated {
s += ",rep"
}
if p.Packed {
s += ",packed"
}
if p.OrigName != p.Name {
s += ",name=" + p.OrigName
}
if p.proto3 {
s += ",proto3"
}
if p.oneof {
s += ",oneof"
}
if len(p.Enum) > 0 {
s += ",enum=" + p.Enum
}
if p.HasDefault {
s += ",def=" + p.Default
}
return s
}
// Parse populates p by parsing a string in the protobuf struct field tag style.
func (p *Properties) Parse(s string) {
// "bytes,49,opt,name=foo,def=hello!"
fields := strings.Split(s, ",") // breaks def=, but handled below.
if len(fields) < 2 {
fmt.Fprintf(os.Stderr, "proto: tag has too few fields: %q\n", s)
return
}
p.Wire = fields[0]
switch p.Wire {
case "varint":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeVarint
p.valDec = (*Buffer).DecodeVarint
p.valSize = sizeVarint
case "fixed32":
p.WireType = WireFixed32
p.valEnc = (*Buffer).EncodeFixed32
p.valDec = (*Buffer).DecodeFixed32
p.valSize = sizeFixed32
case "fixed64":
p.WireType = WireFixed64
p.valEnc = (*Buffer).EncodeFixed64
p.valDec = (*Buffer).DecodeFixed64
p.valSize = sizeFixed64
case "zigzag32":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeZigzag32
p.valDec = (*Buffer).DecodeZigzag32
p.valSize = sizeZigzag32
case "zigzag64":
p.WireType = WireVarint
p.valEnc = (*Buffer).EncodeZigzag64
p.valDec = (*Buffer).DecodeZigzag64
p.valSize = sizeZigzag64
case "bytes", "group":
p.WireType = WireBytes
// no numeric converter for non-numeric types
default:
fmt.Fprintf(os.Stderr, "proto: tag has unknown wire type: %q\n", s)
return
}
var err error
p.Tag, err = strconv.Atoi(fields[1])
if err != nil {
return
}
for i := 2; i < len(fields); i++ {
f := fields[i]
switch {
case f == "req":
p.Required = true
case f == "opt":
p.Optional = true
case f == "rep":
p.Repeated = true
case f == "packed":
p.Packed = true
case strings.HasPrefix(f, "name="):
p.OrigName = f[5:]
case strings.HasPrefix(f, "enum="):
p.Enum = f[5:]
case f == "proto3":
p.proto3 = true
case f == "oneof":
p.oneof = true
case strings.HasPrefix(f, "def="):
p.HasDefault = true
p.Default = f[4:] // rest of string
if i+1 < len(fields) {
// Commas aren't escaped, and def is always last.
p.Default += "," + strings.Join(fields[i+1:], ",")
break
}
}
}
}
func logNoSliceEnc(t1, t2 reflect.Type) {
fmt.Fprintf(os.Stderr, "proto: no slice oenc for %T = []%T\n", t1, t2)
}
var protoMessageType = reflect.TypeOf((*Message)(nil)).Elem()
// Initialize the fields for encoding and decoding.
func (p *Properties) setEncAndDec(typ reflect.Type, f *reflect.StructField, lockGetProp bool) {
p.enc = nil
p.dec = nil
p.size = nil
switch t1 := typ; t1.Kind() {
default:
fmt.Fprintf(os.Stderr, "proto: no coders for %v\n", t1)
// proto3 scalar types
case reflect.Bool:
p.enc = (*Buffer).enc_proto3_bool
p.dec = (*Buffer).dec_proto3_bool
p.size = size_proto3_bool
case reflect.Int32:
p.enc = (*Buffer).enc_proto3_int32
p.dec = (*Buffer).dec_proto3_int32
p.size = size_proto3_int32
case reflect.Uint32:
p.enc = (*Buffer).enc_proto3_uint32
p.dec = (*Buffer).dec_proto3_int32 // can reuse
p.size = size_proto3_uint32
case reflect.Int64, reflect.Uint64:
p.enc = (*Buffer).enc_proto3_int64
p.dec = (*Buffer).dec_proto3_int64
p.size = size_proto3_int64
case reflect.Float32:
p.enc = (*Buffer).enc_proto3_uint32 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int32
p.size = size_proto3_uint32
case reflect.Float64:
p.enc = (*Buffer).enc_proto3_int64 // can just treat them as bits
p.dec = (*Buffer).dec_proto3_int64
p.size = size_proto3_int64
case reflect.String:
p.enc = (*Buffer).enc_proto3_string
p.dec = (*Buffer).dec_proto3_string
p.size = size_proto3_string
case reflect.Ptr:
switch t2 := t1.Elem(); t2.Kind() {
default:
fmt.Fprintf(os.Stderr, "proto: no encoder function for %v -> %v\n", t1, t2)
break
case reflect.Bool:
p.enc = (*Buffer).enc_bool
p.dec = (*Buffer).dec_bool
p.size = size_bool
case reflect.Int32:
p.enc = (*Buffer).enc_int32
p.dec = (*Buffer).dec_int32
p.size = size_int32
case reflect.Uint32:
p.enc = (*Buffer).enc_uint32
p.dec = (*Buffer).dec_int32 // can reuse
p.size = size_uint32
case reflect.Int64, reflect.Uint64:
p.enc = (*Buffer).enc_int64
p.dec = (*Buffer).dec_int64
p.size = size_int64
case reflect.Float32:
p.enc = (*Buffer).enc_uint32 // can just treat them as bits
p.dec = (*Buffer).dec_int32
p.size = size_uint32
case reflect.Float64:
p.enc = (*Buffer).enc_int64 // can just treat them as bits
p.dec = (*Buffer).dec_int64
p.size = size_int64
case reflect.String:
p.enc = (*Buffer).enc_string
p.dec = (*Buffer).dec_string
p.size = size_string
case reflect.Struct:
p.stype = t1.Elem()
p.isMarshaler = isMarshaler(t1)
p.isUnmarshaler = isUnmarshaler(t1)
if p.Wire == "bytes" {
p.enc = (*Buffer).enc_struct_message
p.dec = (*Buffer).dec_struct_message
p.size = size_struct_message
} else {
p.enc = (*Buffer).enc_struct_group
p.dec = (*Buffer).dec_struct_group
p.size = size_struct_group
}
}
case reflect.Slice:
switch t2 := t1.Elem(); t2.Kind() {
default:
logNoSliceEnc(t1, t2)
break
case reflect.Bool:
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_bool
p.size = size_slice_packed_bool
} else {
p.enc = (*Buffer).enc_slice_bool
p.size = size_slice_bool
}
p.dec = (*Buffer).dec_slice_bool
p.packedDec = (*Buffer).dec_slice_packed_bool
case reflect.Int32:
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_int32
p.size = size_slice_packed_int32
} else {
p.enc = (*Buffer).enc_slice_int32
p.size = size_slice_int32
}
p.dec = (*Buffer).dec_slice_int32
p.packedDec = (*Buffer).dec_slice_packed_int32
case reflect.Uint32:
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_uint32
p.size = size_slice_packed_uint32
} else {
p.enc = (*Buffer).enc_slice_uint32
p.size = size_slice_uint32
}
p.dec = (*Buffer).dec_slice_int32
p.packedDec = (*Buffer).dec_slice_packed_int32
case reflect.Int64, reflect.Uint64:
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_int64
p.size = size_slice_packed_int64
} else {
p.enc = (*Buffer).enc_slice_int64
p.size = size_slice_int64
}
p.dec = (*Buffer).dec_slice_int64
p.packedDec = (*Buffer).dec_slice_packed_int64
case reflect.Uint8:
p.enc = (*Buffer).enc_slice_byte
p.dec = (*Buffer).dec_slice_byte
p.size = size_slice_byte
// This is a []byte, which is either a bytes field,
// or the value of a map field. In the latter case,
// we always encode an empty []byte, so we should not
// use the proto3 enc/size funcs.
// f == nil iff this is the key/value of a map field.
if p.proto3 && f != nil {
p.enc = (*Buffer).enc_proto3_slice_byte
p.size = size_proto3_slice_byte
}
case reflect.Float32, reflect.Float64:
switch t2.Bits() {
case 32:
// can just treat them as bits
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_uint32
p.size = size_slice_packed_uint32
} else {
p.enc = (*Buffer).enc_slice_uint32
p.size = size_slice_uint32
}
p.dec = (*Buffer).dec_slice_int32
p.packedDec = (*Buffer).dec_slice_packed_int32
case 64:
// can just treat them as bits
if p.Packed {
p.enc = (*Buffer).enc_slice_packed_int64
p.size = size_slice_packed_int64
} else {
p.enc = (*Buffer).enc_slice_int64
p.size = size_slice_int64
}
p.dec = (*Buffer).dec_slice_int64
p.packedDec = (*Buffer).dec_slice_packed_int64
default:
logNoSliceEnc(t1, t2)
break
}
case reflect.String:
p.enc = (*Buffer).enc_slice_string
p.dec = (*Buffer).dec_slice_string
p.size = size_slice_string
case reflect.Ptr:
switch t3 := t2.Elem(); t3.Kind() {
default:
fmt.Fprintf(os.Stderr, "proto: no ptr oenc for %T -> %T -> %T\n", t1, t2, t3)
break
case reflect.Struct:
p.stype = t2.Elem()
p.isMarshaler = isMarshaler(t2)
p.isUnmarshaler = isUnmarshaler(t2)
if p.Wire == "bytes" {
p.enc = (*Buffer).enc_slice_struct_message
p.dec = (*Buffer).dec_slice_struct_message
p.size = size_slice_struct_message
} else {
p.enc = (*Buffer).enc_slice_struct_group
p.dec = (*Buffer).dec_slice_struct_group
p.size = size_slice_struct_group
}
}
case reflect.Slice:
switch t2.Elem().Kind() {
default:
fmt.Fprintf(os.Stderr, "proto: no slice elem oenc for %T -> %T -> %T\n", t1, t2, t2.Elem())
break
case reflect.Uint8:
p.enc = (*Buffer).enc_slice_slice_byte
p.dec = (*Buffer).dec_slice_slice_byte
p.size = size_slice_slice_byte
}
}
case reflect.Map:
p.enc = (*Buffer).enc_new_map
p.dec = (*Buffer).dec_new_map
p.size = size_new_map
p.mtype = t1
p.mkeyprop = &Properties{}
p.mkeyprop.init(reflect.PtrTo(p.mtype.Key()), "Key", f.Tag.Get("protobuf_key"), nil, lockGetProp)
p.mvalprop = &Properties{}
vtype := p.mtype.Elem()
if vtype.Kind() != reflect.Ptr && vtype.Kind() != reflect.Slice {
// The value type is not a message (*T) or bytes ([]byte),
// so we need encoders for the pointer to this type.
vtype = reflect.PtrTo(vtype)
}
p.mvalprop.init(vtype, "Value", f.Tag.Get("protobuf_val"), nil, lockGetProp)
}
// precalculate tag code
wire := p.WireType
if p.Packed {
wire = WireBytes
}
x := uint32(p.Tag)<<3 | uint32(wire)
i := 0
for i = 0; x > 127; i++ {
p.tagbuf[i] = 0x80 | uint8(x&0x7F)
x >>= 7
}
p.tagbuf[i] = uint8(x)
p.tagcode = p.tagbuf[0 : i+1]
if p.stype != nil {
if lockGetProp {
p.sprop = GetProperties(p.stype)
} else {
p.sprop = getPropertiesLocked(p.stype)
}
}
}
var (
marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem()
unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem()
)
// isMarshaler reports whether type t implements Marshaler.
func isMarshaler(t reflect.Type) bool {
// We're checking for (likely) pointer-receiver methods
// so if t is not a pointer, something is very wrong.
// The calls above only invoke isMarshaler on pointer types.
if t.Kind() != reflect.Ptr {
panic("proto: misuse of isMarshaler")
}
return t.Implements(marshalerType)
}
// isUnmarshaler reports whether type t implements Unmarshaler.
func isUnmarshaler(t reflect.Type) bool {
// We're checking for (likely) pointer-receiver methods
// so if t is not a pointer, something is very wrong.
// The calls above only invoke isUnmarshaler on pointer types.
if t.Kind() != reflect.Ptr {
panic("proto: misuse of isUnmarshaler")
}
return t.Implements(unmarshalerType)
}
// Init populates the properties from a protocol buffer struct tag.
func (p *Properties) Init(typ reflect.Type, name, tag string, f *reflect.StructField) {
p.init(typ, name, tag, f, true)
}
func (p *Properties) init(typ reflect.Type, name, tag string, f *reflect.StructField, lockGetProp bool) {
// "bytes,49,opt,def=hello!"
p.Name = name
p.OrigName = name
if f != nil {
p.field = toField(f)
}
if tag == "" {
return
}
p.Parse(tag)
p.setEncAndDec(typ, f, lockGetProp)
}
var (
propertiesMu sync.RWMutex
propertiesMap = make(map[reflect.Type]*StructProperties)
)
// GetProperties returns the list of properties for the type represented by t.
// t must represent a generated struct type of a protocol message.
func GetProperties(t reflect.Type) *StructProperties {
if t.Kind() != reflect.Struct {
panic("proto: type must have kind struct")
}
// Most calls to GetProperties in a long-running program will be
// retrieving details for types we have seen before.
propertiesMu.RLock()
sprop, ok := propertiesMap[t]
propertiesMu.RUnlock()
if ok {
if collectStats {
stats.Chit++
}
return sprop
}
propertiesMu.Lock()
sprop = getPropertiesLocked(t)
propertiesMu.Unlock()
return sprop
}
// getPropertiesLocked requires that propertiesMu is held.
func getPropertiesLocked(t reflect.Type) *StructProperties {
if prop, ok := propertiesMap[t]; ok {
if collectStats {
stats.Chit++
}
return prop
}
if collectStats {
stats.Cmiss++
}
prop := new(StructProperties)
// in case of recursive protos, fill this in now.
propertiesMap[t] = prop
// build properties
prop.extendable = reflect.PtrTo(t).Implements(extendableProtoType)
prop.unrecField = invalidField
prop.Prop = make([]*Properties, t.NumField())
prop.order = make([]int, t.NumField())
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
p := new(Properties)
name := f.Name
p.init(f.Type, name, f.Tag.Get("protobuf"), &f, false)
if f.Name == "XXX_extensions" { // special case
p.enc = (*Buffer).enc_map
p.dec = nil // not needed
p.size = size_map
}
if f.Name == "XXX_unrecognized" { // special case
prop.unrecField = toField(&f)
}
oneof := f.Tag.Get("protobuf_oneof") != "" // special case
prop.Prop[i] = p
prop.order[i] = i
if debug {
print(i, " ", f.Name, " ", t.String(), " ")
if p.Tag > 0 {
print(p.String())
}
print("\n")
}
if p.enc == nil && !strings.HasPrefix(f.Name, "XXX_") && !oneof {
fmt.Fprintln(os.Stderr, "proto: no encoder for", f.Name, f.Type.String(), "[GetProperties]")
}
}
// Re-order prop.order.
sort.Sort(prop)
type oneofMessage interface {
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
}
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
var oots []interface{}
prop.oneofMarshaler, prop.oneofUnmarshaler, prop.oneofSizer, oots = om.XXX_OneofFuncs()
prop.stype = t
// Interpret oneof metadata.
prop.OneofTypes = make(map[string]*OneofProperties)
for _, oot := range oots {
oop := &OneofProperties{
Type: reflect.ValueOf(oot).Type(), // *T
Prop: new(Properties),
}
sft := oop.Type.Elem().Field(0)
oop.Prop.Name = sft.Name
oop.Prop.Parse(sft.Tag.Get("protobuf"))
// There will be exactly one interface field that
// this new value is assignable to.
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Type.Kind() != reflect.Interface {
continue
}
if !oop.Type.AssignableTo(f.Type) {
continue
}
oop.Field = i
break
}
prop.OneofTypes[oop.Prop.OrigName] = oop
}
}
// build required counts
// build tags
reqCount := 0
prop.decoderOrigNames = make(map[string]int)
for i, p := range prop.Prop {
if strings.HasPrefix(p.Name, "XXX_") {
// Internal fields should not appear in tags/origNames maps.
// They are handled specially when encoding and decoding.
continue
}
if p.Required {
reqCount++
}
prop.decoderTags.put(p.Tag, i)
prop.decoderOrigNames[p.OrigName] = i
}
prop.reqCount = reqCount
return prop
}
// Return the Properties object for the x[0]'th field of the structure.
func propByIndex(t reflect.Type, x []int) *Properties {
if len(x) != 1 {
fmt.Fprintf(os.Stderr, "proto: field index dimension %d (not 1) for type %s\n", len(x), t)
return nil
}
prop := GetProperties(t)
return prop.Prop[x[0]]
}
// Get the address and type of a pointer to a struct from an interface.
func getbase(pb Message) (t reflect.Type, b structPointer, err error) {
if pb == nil {
err = ErrNil
return
}
// get the reflect type of the pointer to the struct.
t = reflect.TypeOf(pb)
// get the address of the struct.
value := reflect.ValueOf(pb)
b = toStructPointer(value)
return
}
// A global registry of enum types.
// The generated code will register the generated maps by calling RegisterEnum.
var enumValueMaps = make(map[string]map[string]int32)
// RegisterEnum is called from the generated code to install the enum descriptor
// maps into the global table to aid parsing text format protocol buffers.
func RegisterEnum(typeName string, unusedNameMap map[int32]string, valueMap map[string]int32) {
if _, ok := enumValueMaps[typeName]; ok {
panic("proto: duplicate enum registered: " + typeName)
}
enumValueMaps[typeName] = valueMap
}
// EnumValueMap returns the mapping from names to integers of the
// enum type enumType, or a nil if not found.
func EnumValueMap(enumType string) map[string]int32 {
return enumValueMaps[enumType]
}
// A registry of all linked message types.
// The string is a fully-qualified proto name ("pkg.Message").
var (
protoTypes = make(map[string]reflect.Type)
revProtoTypes = make(map[reflect.Type]string)
)
// RegisterType is called from generated code and maps from the fully qualified
// proto name to the type (pointer to struct) of the protocol buffer.
func RegisterType(x Message, name string) {
if _, ok := protoTypes[name]; ok {
// TODO: Some day, make this a panic.
log.Printf("proto: duplicate proto type registered: %s", name)
return
}
t := reflect.TypeOf(x)
protoTypes[name] = t
revProtoTypes[t] = name
}
// MessageName returns the fully-qualified proto name for the given message type.
func MessageName(x Message) string { return revProtoTypes[reflect.TypeOf(x)] }
// MessageType returns the message type (pointer to struct) for a named message.
func MessageType(name string) reflect.Type { return protoTypes[name] }

View File

@@ -1,751 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
// Functions for writing the text protocol buffer format.
import (
"bufio"
"bytes"
"encoding"
"errors"
"fmt"
"io"
"log"
"math"
"reflect"
"sort"
"strings"
)
var (
newline = []byte("\n")
spaces = []byte(" ")
gtNewline = []byte(">\n")
endBraceNewline = []byte("}\n")
backslashN = []byte{'\\', 'n'}
backslashR = []byte{'\\', 'r'}
backslashT = []byte{'\\', 't'}
backslashDQ = []byte{'\\', '"'}
backslashBS = []byte{'\\', '\\'}
posInf = []byte("inf")
negInf = []byte("-inf")
nan = []byte("nan")
)
type writer interface {
io.Writer
WriteByte(byte) error
}
// textWriter is an io.Writer that tracks its indentation level.
type textWriter struct {
ind int
complete bool // if the current position is a complete line
compact bool // whether to write out as a one-liner
w writer
}
func (w *textWriter) WriteString(s string) (n int, err error) {
if !strings.Contains(s, "\n") {
if !w.compact && w.complete {
w.writeIndent()
}
w.complete = false
return io.WriteString(w.w, s)
}
// WriteString is typically called without newlines, so this
// codepath and its copy are rare. We copy to avoid
// duplicating all of Write's logic here.
return w.Write([]byte(s))
}
func (w *textWriter) Write(p []byte) (n int, err error) {
newlines := bytes.Count(p, newline)
if newlines == 0 {
if !w.compact && w.complete {
w.writeIndent()
}
n, err = w.w.Write(p)
w.complete = false
return n, err
}
frags := bytes.SplitN(p, newline, newlines+1)
if w.compact {
for i, frag := range frags {
if i > 0 {
if err := w.w.WriteByte(' '); err != nil {
return n, err
}
n++
}
nn, err := w.w.Write(frag)
n += nn
if err != nil {
return n, err
}
}
return n, nil
}
for i, frag := range frags {
if w.complete {
w.writeIndent()
}
nn, err := w.w.Write(frag)
n += nn
if err != nil {
return n, err
}
if i+1 < len(frags) {
if err := w.w.WriteByte('\n'); err != nil {
return n, err
}
n++
}
}
w.complete = len(frags[len(frags)-1]) == 0
return n, nil
}
func (w *textWriter) WriteByte(c byte) error {
if w.compact && c == '\n' {
c = ' '
}
if !w.compact && w.complete {
w.writeIndent()
}
err := w.w.WriteByte(c)
w.complete = c == '\n'
return err
}
func (w *textWriter) indent() { w.ind++ }
func (w *textWriter) unindent() {
if w.ind == 0 {
log.Printf("proto: textWriter unindented too far")
return
}
w.ind--
}
func writeName(w *textWriter, props *Properties) error {
if _, err := w.WriteString(props.OrigName); err != nil {
return err
}
if props.Wire != "group" {
return w.WriteByte(':')
}
return nil
}
// raw is the interface satisfied by RawMessage.
type raw interface {
Bytes() []byte
}
func writeStruct(w *textWriter, sv reflect.Value) error {
st := sv.Type()
sprops := GetProperties(st)
for i := 0; i < sv.NumField(); i++ {
fv := sv.Field(i)
props := sprops.Prop[i]
name := st.Field(i).Name
if strings.HasPrefix(name, "XXX_") {
// There are two XXX_ fields:
// XXX_unrecognized []byte
// XXX_extensions map[int32]proto.Extension
// The first is handled here;
// the second is handled at the bottom of this function.
if name == "XXX_unrecognized" && !fv.IsNil() {
if err := writeUnknownStruct(w, fv.Interface().([]byte)); err != nil {
return err
}
}
continue
}
if fv.Kind() == reflect.Ptr && fv.IsNil() {
// Field not filled in. This could be an optional field or
// a required field that wasn't filled in. Either way, there
// isn't anything we can show for it.
continue
}
if fv.Kind() == reflect.Slice && fv.IsNil() {
// Repeated field that is empty, or a bytes field that is unused.
continue
}
if props.Repeated && fv.Kind() == reflect.Slice {
// Repeated field.
for j := 0; j < fv.Len(); j++ {
if err := writeName(w, props); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
v := fv.Index(j)
if v.Kind() == reflect.Ptr && v.IsNil() {
// A nil message in a repeated field is not valid,
// but we can handle that more gracefully than panicking.
if _, err := w.Write([]byte("<nil>\n")); err != nil {
return err
}
continue
}
if err := writeAny(w, v, props); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
continue
}
if fv.Kind() == reflect.Map {
// Map fields are rendered as a repeated struct with key/value fields.
keys := fv.MapKeys()
sort.Sort(mapKeys(keys))
for _, key := range keys {
val := fv.MapIndex(key)
if err := writeName(w, props); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
// open struct
if err := w.WriteByte('<'); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte('\n'); err != nil {
return err
}
}
w.indent()
// key
if _, err := w.WriteString("key:"); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if err := writeAny(w, key, props.mkeyprop); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
// nil values aren't legal, but we can avoid panicking because of them.
if val.Kind() != reflect.Ptr || !val.IsNil() {
// value
if _, err := w.WriteString("value:"); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if err := writeAny(w, val, props.mvalprop); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
// close struct
w.unindent()
if err := w.WriteByte('>'); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
continue
}
if props.proto3 && fv.Kind() == reflect.Slice && fv.Len() == 0 {
// empty bytes field
continue
}
if fv.Kind() != reflect.Ptr && fv.Kind() != reflect.Slice {
// proto3 non-repeated scalar field; skip if zero value
if isProto3Zero(fv) {
continue
}
}
if fv.Kind() == reflect.Interface {
// Check if it is a oneof.
if st.Field(i).Tag.Get("protobuf_oneof") != "" {
// fv is nil, or holds a pointer to generated struct.
// That generated struct has exactly one field,
// which has a protobuf struct tag.
if fv.IsNil() {
continue
}
inner := fv.Elem().Elem() // interface -> *T -> T
tag := inner.Type().Field(0).Tag.Get("protobuf")
props = new(Properties) // Overwrite the outer props var, but not its pointee.
props.Parse(tag)
// Write the value in the oneof, not the oneof itself.
fv = inner.Field(0)
// Special case to cope with malformed messages gracefully:
// If the value in the oneof is a nil pointer, don't panic
// in writeAny.
if fv.Kind() == reflect.Ptr && fv.IsNil() {
// Use errors.New so writeAny won't render quotes.
msg := errors.New("/* nil */")
fv = reflect.ValueOf(&msg).Elem()
}
}
}
if err := writeName(w, props); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if b, ok := fv.Interface().(raw); ok {
if err := writeRaw(w, b.Bytes()); err != nil {
return err
}
continue
}
// Enums have a String method, so writeAny will work fine.
if err := writeAny(w, fv, props); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
}
// Extensions (the XXX_extensions field).
pv := sv.Addr()
if pv.Type().Implements(extendableProtoType) {
if err := writeExtensions(w, pv); err != nil {
return err
}
}
return nil
}
// writeRaw writes an uninterpreted raw message.
func writeRaw(w *textWriter, b []byte) error {
if err := w.WriteByte('<'); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte('\n'); err != nil {
return err
}
}
w.indent()
if err := writeUnknownStruct(w, b); err != nil {
return err
}
w.unindent()
if err := w.WriteByte('>'); err != nil {
return err
}
return nil
}
// writeAny writes an arbitrary field.
func writeAny(w *textWriter, v reflect.Value, props *Properties) error {
v = reflect.Indirect(v)
// Floats have special cases.
if v.Kind() == reflect.Float32 || v.Kind() == reflect.Float64 {
x := v.Float()
var b []byte
switch {
case math.IsInf(x, 1):
b = posInf
case math.IsInf(x, -1):
b = negInf
case math.IsNaN(x):
b = nan
}
if b != nil {
_, err := w.Write(b)
return err
}
// Other values are handled below.
}
// We don't attempt to serialise every possible value type; only those
// that can occur in protocol buffers.
switch v.Kind() {
case reflect.Slice:
// Should only be a []byte; repeated fields are handled in writeStruct.
if err := writeString(w, string(v.Interface().([]byte))); err != nil {
return err
}
case reflect.String:
if err := writeString(w, v.String()); err != nil {
return err
}
case reflect.Struct:
// Required/optional group/message.
var bra, ket byte = '<', '>'
if props != nil && props.Wire == "group" {
bra, ket = '{', '}'
}
if err := w.WriteByte(bra); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte('\n'); err != nil {
return err
}
}
w.indent()
if tm, ok := v.Interface().(encoding.TextMarshaler); ok {
text, err := tm.MarshalText()
if err != nil {
return err
}
if _, err = w.Write(text); err != nil {
return err
}
} else if err := writeStruct(w, v); err != nil {
return err
}
w.unindent()
if err := w.WriteByte(ket); err != nil {
return err
}
default:
_, err := fmt.Fprint(w, v.Interface())
return err
}
return nil
}
// equivalent to C's isprint.
func isprint(c byte) bool {
return c >= 0x20 && c < 0x7f
}
// writeString writes a string in the protocol buffer text format.
// It is similar to strconv.Quote except we don't use Go escape sequences,
// we treat the string as a byte sequence, and we use octal escapes.
// These differences are to maintain interoperability with the other
// languages' implementations of the text format.
func writeString(w *textWriter, s string) error {
// use WriteByte here to get any needed indent
if err := w.WriteByte('"'); err != nil {
return err
}
// Loop over the bytes, not the runes.
for i := 0; i < len(s); i++ {
var err error
// Divergence from C++: we don't escape apostrophes.
// There's no need to escape them, and the C++ parser
// copes with a naked apostrophe.
switch c := s[i]; c {
case '\n':
_, err = w.w.Write(backslashN)
case '\r':
_, err = w.w.Write(backslashR)
case '\t':
_, err = w.w.Write(backslashT)
case '"':
_, err = w.w.Write(backslashDQ)
case '\\':
_, err = w.w.Write(backslashBS)
default:
if isprint(c) {
err = w.w.WriteByte(c)
} else {
_, err = fmt.Fprintf(w.w, "\\%03o", c)
}
}
if err != nil {
return err
}
}
return w.WriteByte('"')
}
func writeUnknownStruct(w *textWriter, data []byte) (err error) {
if !w.compact {
if _, err := fmt.Fprintf(w, "/* %d unknown bytes */\n", len(data)); err != nil {
return err
}
}
b := NewBuffer(data)
for b.index < len(b.buf) {
x, err := b.DecodeVarint()
if err != nil {
_, err := fmt.Fprintf(w, "/* %v */\n", err)
return err
}
wire, tag := x&7, x>>3
if wire == WireEndGroup {
w.unindent()
if _, err := w.Write(endBraceNewline); err != nil {
return err
}
continue
}
if _, err := fmt.Fprint(w, tag); err != nil {
return err
}
if wire != WireStartGroup {
if err := w.WriteByte(':'); err != nil {
return err
}
}
if !w.compact || wire == WireStartGroup {
if err := w.WriteByte(' '); err != nil {
return err
}
}
switch wire {
case WireBytes:
buf, e := b.DecodeRawBytes(false)
if e == nil {
_, err = fmt.Fprintf(w, "%q", buf)
} else {
_, err = fmt.Fprintf(w, "/* %v */", e)
}
case WireFixed32:
x, err = b.DecodeFixed32()
err = writeUnknownInt(w, x, err)
case WireFixed64:
x, err = b.DecodeFixed64()
err = writeUnknownInt(w, x, err)
case WireStartGroup:
err = w.WriteByte('{')
w.indent()
case WireVarint:
x, err = b.DecodeVarint()
err = writeUnknownInt(w, x, err)
default:
_, err = fmt.Fprintf(w, "/* unknown wire type %d */", wire)
}
if err != nil {
return err
}
if err = w.WriteByte('\n'); err != nil {
return err
}
}
return nil
}
func writeUnknownInt(w *textWriter, x uint64, err error) error {
if err == nil {
_, err = fmt.Fprint(w, x)
} else {
_, err = fmt.Fprintf(w, "/* %v */", err)
}
return err
}
type int32Slice []int32
func (s int32Slice) Len() int { return len(s) }
func (s int32Slice) Less(i, j int) bool { return s[i] < s[j] }
func (s int32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// writeExtensions writes all the extensions in pv.
// pv is assumed to be a pointer to a protocol message struct that is extendable.
func writeExtensions(w *textWriter, pv reflect.Value) error {
emap := extensionMaps[pv.Type().Elem()]
ep := pv.Interface().(extendableProto)
// Order the extensions by ID.
// This isn't strictly necessary, but it will give us
// canonical output, which will also make testing easier.
m := ep.ExtensionMap()
ids := make([]int32, 0, len(m))
for id := range m {
ids = append(ids, id)
}
sort.Sort(int32Slice(ids))
for _, extNum := range ids {
ext := m[extNum]
var desc *ExtensionDesc
if emap != nil {
desc = emap[extNum]
}
if desc == nil {
// Unknown extension.
if err := writeUnknownStruct(w, ext.enc); err != nil {
return err
}
continue
}
pb, err := GetExtension(ep, desc)
if err != nil {
return fmt.Errorf("failed getting extension: %v", err)
}
// Repeated extensions will appear as a slice.
if !desc.repeated() {
if err := writeExtension(w, desc.Name, pb); err != nil {
return err
}
} else {
v := reflect.ValueOf(pb)
for i := 0; i < v.Len(); i++ {
if err := writeExtension(w, desc.Name, v.Index(i).Interface()); err != nil {
return err
}
}
}
}
return nil
}
func writeExtension(w *textWriter, name string, pb interface{}) error {
if _, err := fmt.Fprintf(w, "[%s]:", name); err != nil {
return err
}
if !w.compact {
if err := w.WriteByte(' '); err != nil {
return err
}
}
if err := writeAny(w, reflect.ValueOf(pb), nil); err != nil {
return err
}
if err := w.WriteByte('\n'); err != nil {
return err
}
return nil
}
func (w *textWriter) writeIndent() {
if !w.complete {
return
}
remain := w.ind * 2
for remain > 0 {
n := remain
if n > len(spaces) {
n = len(spaces)
}
w.w.Write(spaces[:n])
remain -= n
}
w.complete = false
}
func marshalText(w io.Writer, pb Message, compact bool) error {
val := reflect.ValueOf(pb)
if pb == nil || val.IsNil() {
w.Write([]byte("<nil>"))
return nil
}
var bw *bufio.Writer
ww, ok := w.(writer)
if !ok {
bw = bufio.NewWriter(w)
ww = bw
}
aw := &textWriter{
w: ww,
complete: true,
compact: compact,
}
if tm, ok := pb.(encoding.TextMarshaler); ok {
text, err := tm.MarshalText()
if err != nil {
return err
}
if _, err = aw.Write(text); err != nil {
return err
}
if bw != nil {
return bw.Flush()
}
return nil
}
// Dereference the received pointer so we don't have outer < and >.
v := reflect.Indirect(val)
if err := writeStruct(aw, v); err != nil {
return err
}
if bw != nil {
return bw.Flush()
}
return nil
}
// MarshalText writes a given protocol buffer in text format.
// The only errors returned are from w.
func MarshalText(w io.Writer, pb Message) error {
return marshalText(w, pb, false)
}
// MarshalTextString is the same as MarshalText, but returns the string directly.
func MarshalTextString(pb Message) string {
var buf bytes.Buffer
marshalText(&buf, pb, false)
return buf.String()
}
// CompactText writes a given protocol buffer in compact text format (one line).
func CompactText(w io.Writer, pb Message) error { return marshalText(w, pb, true) }
// CompactTextString is the same as CompactText, but returns the string directly.
func CompactTextString(pb Message) string {
var buf bytes.Buffer
marshalText(&buf, pb, true)
return buf.String()
}

View File

@@ -1,798 +0,0 @@
// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2010 The Go Authors. All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package proto
// Functions for parsing the Text protocol buffer format.
// TODO: message sets.
import (
"encoding"
"errors"
"fmt"
"reflect"
"strconv"
"strings"
"unicode/utf8"
)
type ParseError struct {
Message string
Line int // 1-based line number
Offset int // 0-based byte offset from start of input
}
func (p *ParseError) Error() string {
if p.Line == 1 {
// show offset only for first line
return fmt.Sprintf("line 1.%d: %v", p.Offset, p.Message)
}
return fmt.Sprintf("line %d: %v", p.Line, p.Message)
}
type token struct {
value string
err *ParseError
line int // line number
offset int // byte number from start of input, not start of line
unquoted string // the unquoted version of value, if it was a quoted string
}
func (t *token) String() string {
if t.err == nil {
return fmt.Sprintf("%q (line=%d, offset=%d)", t.value, t.line, t.offset)
}
return fmt.Sprintf("parse error: %v", t.err)
}
type textParser struct {
s string // remaining input
done bool // whether the parsing is finished (success or error)
backed bool // whether back() was called
offset, line int
cur token
}
func newTextParser(s string) *textParser {
p := new(textParser)
p.s = s
p.line = 1
p.cur.line = 1
return p
}
func (p *textParser) errorf(format string, a ...interface{}) *ParseError {
pe := &ParseError{fmt.Sprintf(format, a...), p.cur.line, p.cur.offset}
p.cur.err = pe
p.done = true
return pe
}
// Numbers and identifiers are matched by [-+._A-Za-z0-9]
func isIdentOrNumberChar(c byte) bool {
switch {
case 'A' <= c && c <= 'Z', 'a' <= c && c <= 'z':
return true
case '0' <= c && c <= '9':
return true
}
switch c {
case '-', '+', '.', '_':
return true
}
return false
}
func isWhitespace(c byte) bool {
switch c {
case ' ', '\t', '\n', '\r':
return true
}
return false
}
func (p *textParser) skipWhitespace() {
i := 0
for i < len(p.s) && (isWhitespace(p.s[i]) || p.s[i] == '#') {
if p.s[i] == '#' {
// comment; skip to end of line or input
for i < len(p.s) && p.s[i] != '\n' {
i++
}
if i == len(p.s) {
break
}
}
if p.s[i] == '\n' {
p.line++
}
i++
}
p.offset += i
p.s = p.s[i:len(p.s)]
if len(p.s) == 0 {
p.done = true
}
}
func (p *textParser) advance() {
// Skip whitespace
p.skipWhitespace()
if p.done {
return
}
// Start of non-whitespace
p.cur.err = nil
p.cur.offset, p.cur.line = p.offset, p.line
p.cur.unquoted = ""
switch p.s[0] {
case '<', '>', '{', '}', ':', '[', ']', ';', ',':
// Single symbol
p.cur.value, p.s = p.s[0:1], p.s[1:len(p.s)]
case '"', '\'':
// Quoted string
i := 1
for i < len(p.s) && p.s[i] != p.s[0] && p.s[i] != '\n' {
if p.s[i] == '\\' && i+1 < len(p.s) {
// skip escaped char
i++
}
i++
}
if i >= len(p.s) || p.s[i] != p.s[0] {
p.errorf("unmatched quote")
return
}
unq, err := unquoteC(p.s[1:i], rune(p.s[0]))
if err != nil {
p.errorf("invalid quoted string %s: %v", p.s[0:i+1], err)
return
}
p.cur.value, p.s = p.s[0:i+1], p.s[i+1:len(p.s)]
p.cur.unquoted = unq
default:
i := 0
for i < len(p.s) && isIdentOrNumberChar(p.s[i]) {
i++
}
if i == 0 {
p.errorf("unexpected byte %#x", p.s[0])
return
}
p.cur.value, p.s = p.s[0:i], p.s[i:len(p.s)]
}
p.offset += len(p.cur.value)
}
var (
errBadUTF8 = errors.New("proto: bad UTF-8")
errBadHex = errors.New("proto: bad hexadecimal")
)
func unquoteC(s string, quote rune) (string, error) {
// This is based on C++'s tokenizer.cc.
// Despite its name, this is *not* parsing C syntax.
// For instance, "\0" is an invalid quoted string.
// Avoid allocation in trivial cases.
simple := true
for _, r := range s {
if r == '\\' || r == quote {
simple = false
break
}
}
if simple {
return s, nil
}
buf := make([]byte, 0, 3*len(s)/2)
for len(s) > 0 {
r, n := utf8.DecodeRuneInString(s)
if r == utf8.RuneError && n == 1 {
return "", errBadUTF8
}
s = s[n:]
if r != '\\' {
if r < utf8.RuneSelf {
buf = append(buf, byte(r))
} else {
buf = append(buf, string(r)...)
}
continue
}
ch, tail, err := unescape(s)
if err != nil {
return "", err
}
buf = append(buf, ch...)
s = tail
}
return string(buf), nil
}
func unescape(s string) (ch string, tail string, err error) {
r, n := utf8.DecodeRuneInString(s)
if r == utf8.RuneError && n == 1 {
return "", "", errBadUTF8
}
s = s[n:]
switch r {
case 'a':
return "\a", s, nil
case 'b':
return "\b", s, nil
case 'f':
return "\f", s, nil
case 'n':
return "\n", s, nil
case 'r':
return "\r", s, nil
case 't':
return "\t", s, nil
case 'v':
return "\v", s, nil
case '?':
return "?", s, nil // trigraph workaround
case '\'', '"', '\\':
return string(r), s, nil
case '0', '1', '2', '3', '4', '5', '6', '7', 'x', 'X':
if len(s) < 2 {
return "", "", fmt.Errorf(`\%c requires 2 following digits`, r)
}
base := 8
ss := s[:2]
s = s[2:]
if r == 'x' || r == 'X' {
base = 16
} else {
ss = string(r) + ss
}
i, err := strconv.ParseUint(ss, base, 8)
if err != nil {
return "", "", err
}
return string([]byte{byte(i)}), s, nil
case 'u', 'U':
n := 4
if r == 'U' {
n = 8
}
if len(s) < n {
return "", "", fmt.Errorf(`\%c requires %d digits`, r, n)
}
bs := make([]byte, n/2)
for i := 0; i < n; i += 2 {
a, ok1 := unhex(s[i])
b, ok2 := unhex(s[i+1])
if !ok1 || !ok2 {
return "", "", errBadHex
}
bs[i/2] = a<<4 | b
}
s = s[n:]
return string(bs), s, nil
}
return "", "", fmt.Errorf(`unknown escape \%c`, r)
}
// Adapted from src/pkg/strconv/quote.go.
func unhex(b byte) (v byte, ok bool) {
switch {
case '0' <= b && b <= '9':
return b - '0', true
case 'a' <= b && b <= 'f':
return b - 'a' + 10, true
case 'A' <= b && b <= 'F':
return b - 'A' + 10, true
}
return 0, false
}
// Back off the parser by one token. Can only be done between calls to next().
// It makes the next advance() a no-op.
func (p *textParser) back() { p.backed = true }
// Advances the parser and returns the new current token.
func (p *textParser) next() *token {
if p.backed || p.done {
p.backed = false
return &p.cur
}
p.advance()
if p.done {
p.cur.value = ""
} else if len(p.cur.value) > 0 && p.cur.value[0] == '"' {
// Look for multiple quoted strings separated by whitespace,
// and concatenate them.
cat := p.cur
for {
p.skipWhitespace()
if p.done || p.s[0] != '"' {
break
}
p.advance()
if p.cur.err != nil {
return &p.cur
}
cat.value += " " + p.cur.value
cat.unquoted += p.cur.unquoted
}
p.done = false // parser may have seen EOF, but we want to return cat
p.cur = cat
}
return &p.cur
}
func (p *textParser) consumeToken(s string) error {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value != s {
p.back()
return p.errorf("expected %q, found %q", s, tok.value)
}
return nil
}
// Return a RequiredNotSetError indicating which required field was not set.
func (p *textParser) missingRequiredFieldError(sv reflect.Value) *RequiredNotSetError {
st := sv.Type()
sprops := GetProperties(st)
for i := 0; i < st.NumField(); i++ {
if !isNil(sv.Field(i)) {
continue
}
props := sprops.Prop[i]
if props.Required {
return &RequiredNotSetError{fmt.Sprintf("%v.%v", st, props.OrigName)}
}
}
return &RequiredNotSetError{fmt.Sprintf("%v.<unknown field name>", st)} // should not happen
}
// Returns the index in the struct for the named field, as well as the parsed tag properties.
func structFieldByName(sprops *StructProperties, name string) (int, *Properties, bool) {
i, ok := sprops.decoderOrigNames[name]
if ok {
return i, sprops.Prop[i], true
}
return -1, nil, false
}
// Consume a ':' from the input stream (if the next token is a colon),
// returning an error if a colon is needed but not present.
func (p *textParser) checkForColon(props *Properties, typ reflect.Type) *ParseError {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value != ":" {
// Colon is optional when the field is a group or message.
needColon := true
switch props.Wire {
case "group":
needColon = false
case "bytes":
// A "bytes" field is either a message, a string, or a repeated field;
// those three become *T, *string and []T respectively, so we can check for
// this field being a pointer to a non-string.
if typ.Kind() == reflect.Ptr {
// *T or *string
if typ.Elem().Kind() == reflect.String {
break
}
} else if typ.Kind() == reflect.Slice {
// []T or []*T
if typ.Elem().Kind() != reflect.Ptr {
break
}
} else if typ.Kind() == reflect.String {
// The proto3 exception is for a string field,
// which requires a colon.
break
}
needColon = false
}
if needColon {
return p.errorf("expected ':', found %q", tok.value)
}
p.back()
}
return nil
}
func (p *textParser) readStruct(sv reflect.Value, terminator string) error {
st := sv.Type()
sprops := GetProperties(st)
reqCount := sprops.reqCount
var reqFieldErr error
fieldSet := make(map[string]bool)
// A struct is a sequence of "name: value", terminated by one of
// '>' or '}', or the end of the input. A name may also be
// "[extension]".
for {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value == terminator {
break
}
if tok.value == "[" {
// Looks like an extension.
//
// TODO: Check whether we need to handle
// namespace rooted names (e.g. ".something.Foo").
tok = p.next()
if tok.err != nil {
return tok.err
}
var desc *ExtensionDesc
// This could be faster, but it's functional.
// TODO: Do something smarter than a linear scan.
for _, d := range RegisteredExtensions(reflect.New(st).Interface().(Message)) {
if d.Name == tok.value {
desc = d
break
}
}
if desc == nil {
return p.errorf("unrecognized extension %q", tok.value)
}
// Check the extension terminator.
tok = p.next()
if tok.err != nil {
return tok.err
}
if tok.value != "]" {
return p.errorf("unrecognized extension terminator %q", tok.value)
}
props := &Properties{}
props.Parse(desc.Tag)
typ := reflect.TypeOf(desc.ExtensionType)
if err := p.checkForColon(props, typ); err != nil {
return err
}
rep := desc.repeated()
// Read the extension structure, and set it in
// the value we're constructing.
var ext reflect.Value
if !rep {
ext = reflect.New(typ).Elem()
} else {
ext = reflect.New(typ.Elem()).Elem()
}
if err := p.readAny(ext, props); err != nil {
if _, ok := err.(*RequiredNotSetError); !ok {
return err
}
reqFieldErr = err
}
ep := sv.Addr().Interface().(extendableProto)
if !rep {
SetExtension(ep, desc, ext.Interface())
} else {
old, err := GetExtension(ep, desc)
var sl reflect.Value
if err == nil {
sl = reflect.ValueOf(old) // existing slice
} else {
sl = reflect.MakeSlice(typ, 0, 1)
}
sl = reflect.Append(sl, ext)
SetExtension(ep, desc, sl.Interface())
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
continue
}
// This is a normal, non-extension field.
name := tok.value
var dst reflect.Value
fi, props, ok := structFieldByName(sprops, name)
if ok {
dst = sv.Field(fi)
} else if oop, ok := sprops.OneofTypes[name]; ok {
// It is a oneof.
props = oop.Prop
nv := reflect.New(oop.Type.Elem())
dst = nv.Elem().Field(0)
sv.Field(oop.Field).Set(nv)
}
if !dst.IsValid() {
return p.errorf("unknown field name %q in %v", name, st)
}
if dst.Kind() == reflect.Map {
// Consume any colon.
if err := p.checkForColon(props, dst.Type()); err != nil {
return err
}
// Construct the map if it doesn't already exist.
if dst.IsNil() {
dst.Set(reflect.MakeMap(dst.Type()))
}
key := reflect.New(dst.Type().Key()).Elem()
val := reflect.New(dst.Type().Elem()).Elem()
// The map entry should be this sequence of tokens:
// < key : KEY value : VALUE >
// Technically the "key" and "value" could come in any order,
// but in practice they won't.
tok := p.next()
var terminator string
switch tok.value {
case "<":
terminator = ">"
case "{":
terminator = "}"
default:
return p.errorf("expected '{' or '<', found %q", tok.value)
}
if err := p.consumeToken("key"); err != nil {
return err
}
if err := p.consumeToken(":"); err != nil {
return err
}
if err := p.readAny(key, props.mkeyprop); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
if err := p.consumeToken("value"); err != nil {
return err
}
if err := p.checkForColon(props.mvalprop, dst.Type().Elem()); err != nil {
return err
}
if err := p.readAny(val, props.mvalprop); err != nil {
return err
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
if err := p.consumeToken(terminator); err != nil {
return err
}
dst.SetMapIndex(key, val)
continue
}
// Check that it's not already set if it's not a repeated field.
if !props.Repeated && fieldSet[name] {
return p.errorf("non-repeated field %q was repeated", name)
}
if err := p.checkForColon(props, dst.Type()); err != nil {
return err
}
// Parse into the field.
fieldSet[name] = true
if err := p.readAny(dst, props); err != nil {
if _, ok := err.(*RequiredNotSetError); !ok {
return err
}
reqFieldErr = err
} else if props.Required {
reqCount--
}
if err := p.consumeOptionalSeparator(); err != nil {
return err
}
}
if reqCount > 0 {
return p.missingRequiredFieldError(sv)
}
return reqFieldErr
}
// consumeOptionalSeparator consumes an optional semicolon or comma.
// It is used in readStruct to provide backward compatibility.
func (p *textParser) consumeOptionalSeparator() error {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value != ";" && tok.value != "," {
p.back()
}
return nil
}
func (p *textParser) readAny(v reflect.Value, props *Properties) error {
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value == "" {
return p.errorf("unexpected EOF")
}
switch fv := v; fv.Kind() {
case reflect.Slice:
at := v.Type()
if at.Elem().Kind() == reflect.Uint8 {
// Special case for []byte
if tok.value[0] != '"' && tok.value[0] != '\'' {
// Deliberately written out here, as the error after
// this switch statement would write "invalid []byte: ...",
// which is not as user-friendly.
return p.errorf("invalid string: %v", tok.value)
}
bytes := []byte(tok.unquoted)
fv.Set(reflect.ValueOf(bytes))
return nil
}
// Repeated field.
if tok.value == "[" {
// Repeated field with list notation, like [1,2,3].
for {
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
err := p.readAny(fv.Index(fv.Len()-1), props)
if err != nil {
return err
}
tok := p.next()
if tok.err != nil {
return tok.err
}
if tok.value == "]" {
break
}
if tok.value != "," {
return p.errorf("Expected ']' or ',' found %q", tok.value)
}
}
return nil
}
// One value of the repeated field.
p.back()
fv.Set(reflect.Append(fv, reflect.New(at.Elem()).Elem()))
return p.readAny(fv.Index(fv.Len()-1), props)
case reflect.Bool:
// Either "true", "false", 1 or 0.
switch tok.value {
case "true", "1":
fv.SetBool(true)
return nil
case "false", "0":
fv.SetBool(false)
return nil
}
case reflect.Float32, reflect.Float64:
v := tok.value
// Ignore 'f' for compatibility with output generated by C++, but don't
// remove 'f' when the value is "-inf" or "inf".
if strings.HasSuffix(v, "f") && tok.value != "-inf" && tok.value != "inf" {
v = v[:len(v)-1]
}
if f, err := strconv.ParseFloat(v, fv.Type().Bits()); err == nil {
fv.SetFloat(f)
return nil
}
case reflect.Int32:
if x, err := strconv.ParseInt(tok.value, 0, 32); err == nil {
fv.SetInt(x)
return nil
}
if len(props.Enum) == 0 {
break
}
m, ok := enumValueMaps[props.Enum]
if !ok {
break
}
x, ok := m[tok.value]
if !ok {
break
}
fv.SetInt(int64(x))
return nil
case reflect.Int64:
if x, err := strconv.ParseInt(tok.value, 0, 64); err == nil {
fv.SetInt(x)
return nil
}
case reflect.Ptr:
// A basic field (indirected through pointer), or a repeated message/group
p.back()
fv.Set(reflect.New(fv.Type().Elem()))
return p.readAny(fv.Elem(), props)
case reflect.String:
if tok.value[0] == '"' || tok.value[0] == '\'' {
fv.SetString(tok.unquoted)
return nil
}
case reflect.Struct:
var terminator string
switch tok.value {
case "{":
terminator = "}"
case "<":
terminator = ">"
default:
return p.errorf("expected '{' or '<', found %q", tok.value)
}
// TODO: Handle nested messages which implement encoding.TextUnmarshaler.
return p.readStruct(fv, terminator)
case reflect.Uint32:
if x, err := strconv.ParseUint(tok.value, 0, 32); err == nil {
fv.SetUint(uint64(x))
return nil
}
case reflect.Uint64:
if x, err := strconv.ParseUint(tok.value, 0, 64); err == nil {
fv.SetUint(x)
return nil
}
}
return p.errorf("invalid %v: %v", v.Type(), tok.value)
}
// UnmarshalText reads a protocol buffer in Text format. UnmarshalText resets pb
// before starting to unmarshal, so any existing data in pb is always removed.
// If a required field is not set and no other error occurs,
// UnmarshalText returns *RequiredNotSetError.
func UnmarshalText(s string, pb Message) error {
if um, ok := pb.(encoding.TextUnmarshaler); ok {
err := um.UnmarshalText([]byte(s))
return err
}
pb.Reset()
v := reflect.ValueOf(pb)
if pe := newTextParser(s).readStruct(v.Elem(), ""); pe != nil {
return pe
}
return nil
}

View File

@@ -1,137 +0,0 @@
# This is the official list of go-github authors for copyright purposes.
#
# This does not necessarily list everyone who has contributed code, since in
# some cases, their employer may be the copyright holder. To see the full list
# of contributors, see the revision history in source control or
# https://github.com/google/go-github/graphs/contributors.
#
# Authors who wish to be recognized in this file should add themselves (or
# their employer, as appropriate).
Abhinav Gupta <mail@abhinavg.net>
Ainsley Chong <ainsley.chong@gmail.com>
Akeda Bagus <akeda@x-team.com>
Alec Thomas <alec@swapoff.org>
Alex Bramley <a.bramley@gmail.com>
Alexander Harkness <me@bearbin.net>
Amey Sakhadeo <me@ameyms.com>
Andreas Garnæs <https://github.com/andreas>
Andrew Ryabchun <aryabchun@mail.ua>
Andy Hume <andyhume@gmail.com>
Andy Lindeman <andy@lindeman.io>
Anshuman Bhartiya <anshuman.bhartiya@gmail.com>
Arıl Bozoluk <arilbozoluk@hotmail.com>
Austin Dizzy <dizzy@wow.com>
Beshr Kayali <beshrkayali@gmail.com>
Beyang Liu <beyang.liu@gmail.com>
Billy Lynch <wlynch92@gmail.com>
Björn Häuser <b.haeuser@rebuy.de>
Brad Harris <bmharris@gmail.com>
Bradley Falzon <brad@teambrad.net>
Brian Egizi <brian@mojotech.com>
Bryan Boreham <bryan@weave.works>
Cami Diez <diezcami@gmail.com>
Carlos Alexandro Becker <caarlos0@gmail.com>
chandresh-pancholi <chandreshpancholi007@gmail.com>
Charlie Yan <charlieyan08@gmail.com>
Chris Roche <chris@vsco.co>
Chris Schaefer <chris@dtzq.com>
Colin Misare <github.com/cmisare>
Craig Peterson <cpeterson@stackoverflow.com>
Cristian Maglie <c.maglie@bug.st>
Daehyeok Mun <daehyeok@gmail.com>
Daniel Leavitt <daniel.leavitt@gmail.com>
Dave Du Cros <davidducros@gmail.com>
Dave Henderson <dhenderson@gmail.com>
David Deng <daviddengcn@gmail.com>
Diego Lapiduz <diego.lapiduz@cfpb.gov>
Dmitri Shuralyov <shurcooL@gmail.com>
dmnlk <seikima2demon@gmail.com>
Don Petersen <don@donpetersen.net>
Doug Turner <doug.turner@gmail.com>
erwinvaneyk <erwinvaneyk@gmail.com>
Fabrice <fabrice.vaillant@student.ecp.fr>
Filippo Valsorda <hi@filippo.io>
Florian Forster <ff@octo.it>
Francis <hello@francismakes.com>
Fredrik Jönsson <fredrik.jonsson@izettle.com>
Garrett Squire <garrettsquire@gmail.com>
Georgy Buranov <gburanov@gmail.com>
Gnahz <p@oath.pl>
Google Inc.
griffin_stewie <panterathefamilyguy@gmail.com>
Guz Alexander <kalimatas@gmail.com>
Hanno Hecker <hanno.hecker@zalando.de>
Hari haran <hariharan.uno@gmail.com>
Huy Tr <kingbazoka@gmail.com>
huydx <doxuanhuy@gmail.com>
i2bskn <i2bskn@gmail.com>
Isao Jonas <isao.jonas@gmail.com>
isqua <isqua@isqua.ru>
Jameel Haffejee <RC1140@republiccommandos.co.za>
Jihoon Chung <j.c@navercorp.com>
Joe Tsai <joetsai@digital-static.net>
John Engelman <john.r.engelman@gmail.com>
Juan Basso <jrbasso@gmail.com>
Julien Rostand <jrostand@users.noreply.github.com>
Justin Abrahms <justin@abrah.ms>
jzhoucliqr <jzhou@cliqr.com>
Keita Urashima <ursm@ursm.jp>
Kevin Burke <kev@inburke.com>
Konrad Malawski <konrad.malawski@project13.pl>
Kookheon Kwon <kucuny@gmail.com>
Krzysztof Kowalczyk <kkowalczyk@gmail.com>
kyokomi <kyoko1220adword@gmail.com>
Lucas Alcantara <lucasalcantaraf@gmail.com>
Luke Evers <me@lukevers.com>
Luke Roberts <email@luke-roberts.co.uk>
Luke Young <luke@hydrantlabs.org>
Maksim Zhylinski <uzzable@gmail.com>
Martin-Louis Bright <mlbright@gmail.com>
Mat Geist <matgeist@gmail.com>
Matt Brender <mjbrender@gmail.com>
Matt Landis <landis.matt@gmail.com>
Maxime Bury <maxime.bury@gmail.com>
Michael Tiller <michael.tiller@gmail.com>
Michał Glapa <michal.glapa@gmail.com>
Nathan VanBenschoten <nvanbenschoten@gmail.com>
Neil O'Toole <neilotoole@apache.org>
Nick Miyake <nmiyake@palantir.com>
Nick Spragg <nick.spragg@bbc.co.uk>
Ondřej Kupka <ondra.cap@gmail.com>
Panagiotis Moustafellos <pmoust@gmail.com>
Parker Moore <parkrmoore@gmail.com>
Pierre Carrier <pierre@meteor.com>
Piotr Zurek <p.zurek@gmail.com>
Quinn Slack <qslack@qslack.com>
Rackspace US, Inc.
rc1140 <jameel@republiccommandos.co.za>
Red Hat, Inc.
Rob Figueiredo <robfig@yext.com>
Ronak Jain <ronakjain@outlook.in>
Ruben Vereecken <rubenvereecken@gmail.com>
Ryan Lower <rpjlower@gmail.com>
Sahil Dua <sahildua2305@gmail.com>
saisi <saisi@users.noreply.github.com>
Sander van Harmelen <svanharmelen@schubergphilis.com>
Sean Wang <sean@decrypted.org>
Sevki <s@sevki.org>
Shawn Catanzarite <me@shawncatz.com>
Shawn Smith <shawnpsmith@gmail.com>
sona-tar <sona.zip@gmail.com>
SoundCloud, Ltd.
Stian Eikeland <stian@eikeland.se>
Thomas Bruyelle <thomas.bruyelle@gmail.com>
Timothée Peignier <timothee.peignier@tryphon.org>
Trey Tacon <ttacon@gmail.com>
ttacon <ttacon@gmail.com>
Varadarajan Aravamudhan <varadaraajan@gmail.com>
Victor Castell <victor@victorcastell.com>
Victor Vrantchan <vrancean+github@gmail.com>
Will Maier <wcmaier@gmail.com>
William Bailey <mail@williambailey.org.uk>
Yann Malet <yann.malet@gmail.com>
Yannick Utard <yannickutard@gmail.com>
Yicheng Qin <qycqycqycqycqyc@gmail.com>
Yumikiyo Osanai <yumios.art@gmail.com>
Zach Latta <zach@zachlatta.com>

View File

@@ -1,341 +0,0 @@
Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
Some documentation is taken from the GitHub Developer site
<https://developer.github.com/>, which is available under the following Creative
Commons Attribution 3.0 License. This applies only to the go-github source
code and would not apply to any compiled binaries.
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the Work and
other pre-existing works, such as a translation, adaptation,
derivative work, arrangement of music or other alterations of a
literary or artistic work, or phonogram or performance and includes
cinematographic adaptations or any other form in which the Work may be
recast, transformed, or adapted including in any form recognizably
derived from the original, except that a work that constitutes a
Collection will not be considered an Adaptation for the purpose of
this License. For the avoidance of doubt, where the Work is a musical
work, performance or phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be considered an
Adaptation for the purpose of this License.
b. "Collection" means a collection of literary or artistic works, such as
encyclopedias and anthologies, or performances, phonograms or
broadcasts, or other works or subject matter other than works listed
in Section 1(f) below, which, by reason of the selection and
arrangement of their contents, constitute intellectual creations, in
which the Work is included in its entirety in unmodified form along
with one or more other contributions, each constituting separate and
independent works in themselves, which together are assembled into a
collective whole. A work that constitutes a Collection will not be
considered an Adaptation (as defined above) for the purposes of this
License.
c. "Distribute" means to make available to the public the original and
copies of the Work or Adaptation, as appropriate, through sale or
other transfer of ownership.
d. "Licensor" means the individual, individuals, entity or entities that
offer(s) the Work under the terms of this License.
e. "Original Author" means, in the case of a literary or artistic work,
the individual, individuals, entity or entities who created the Work
or if no individual or entity can be identified, the publisher; and in
addition (i) in the case of a performance the actors, singers,
musicians, dancers, and other persons who act, sing, deliver, declaim,
play in, interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the producer
being the person or legal entity who first fixes the sounds of a
performance or other sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.
f. "Work" means the literary and/or artistic work offered under the terms
of this License including without limitation any production in the
literary, scientific and artistic domain, whatever may be the mode or
form of its expression including digital form, such as a book,
pamphlet and other writing; a lecture, address, sermon or other work
of the same nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a musical
composition with or without words; a cinematographic work to which are
assimilated works expressed by a process analogous to cinematography;
a work of drawing, painting, architecture, sculpture, engraving or
lithography; a photographic work to which are assimilated works
expressed by a process analogous to photography; a work of applied
art; an illustration, map, plan, sketch or three-dimensional work
relative to geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of data to the
extent it is protected as a copyrightable work; or a work performed by
a variety or circus performer to the extent it is not otherwise
considered a literary or artistic work.
g. "You" means an individual or entity exercising rights under this
License who has not previously violated the terms of this License with
respect to the Work, or who has received express permission from the
Licensor to exercise rights under this License despite a previous
violation.
h. "Publicly Perform" means to perform public recitations of the Work and
to communicate to the public those public recitations, by any means or
process, including by wire or wireless means or public digital
performances; to make available to the public Works in such a way that
members of the public may access these Works from a place and at a
place individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of the
performances of the Work, including by public digital performance; to
broadcast and rebroadcast the Work by any means including signs,
sounds or images.
i. "Reproduce" means to make copies of the Work by any means including
without limitation by sound or visual recordings and the right of
fixation and reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or other electronic
medium.
2. Fair Dealing Rights. Nothing in this License is intended to reduce,
limit, or restrict any uses free from copyright or rights arising from
limitations or exceptions that are provided for in connection with the
copyright protection under copyright law or other applicable laws.
3. License Grant. Subject to the terms and conditions of this License,
Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
perpetual (for the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:
a. to Reproduce the Work, to incorporate the Work into one or more
Collections, and to Reproduce the Work as incorporated in the
Collections;
b. to create and Reproduce Adaptations provided that any such Adaptation,
including any translation in any medium, takes reasonable steps to
clearly label, demarcate or otherwise identify that changes were made
to the original Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or a
modification could indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as incorporated
in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme cannot be waived, the Licensor
reserves the exclusive right to collect such royalties for any
exercise by You of the rights granted under this License;
ii. Waivable Compulsory License Schemes. In those jurisdictions in
which the right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the Licensor waives the
exclusive right to collect such royalties for any exercise by You
of the rights granted under this License; and,
iii. Voluntary License Schemes. The Licensor waives the right to
collect royalties, whether individually or, in the event that the
Licensor is a member of a collecting society that administers
voluntary licensing schemes, via that society, from any exercise
by You of the rights granted under this License.
The above rights may be exercised in all media and formats whether now
known or hereafter devised. The above rights include the right to make
such modifications as are technically necessary to exercise the rights in
other media and formats. Subject to Section 8(f), all rights not expressly
granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is expressly made
subject to and limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under the terms
of this License. You must include a copy of, or the Uniform Resource
Identifier (URI) for, this License with every copy of the Work You
Distribute or Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or the ability of
the recipient of the Work to exercise the rights granted to that
recipient under the terms of the License. You may not sublicense the
Work. You must keep intact all notices that refer to this License and
to the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or Publicly
Perform the Work, You may not impose any effective technological
measures on the Work that restrict the ability of a recipient of the
Work from You to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to the Work as
incorporated in a Collection, but this does not require the Collection
apart from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the Collection any credit
as required by Section 4(b), as requested. If You create an
Adaptation, upon notice from any Licensor You must, to the extent
practicable, remove from the Adaptation any credit as required by
Section 4(b), as requested.
b. If You Distribute, or Publicly Perform the Work or any Adaptations or
Collections, You must, unless a request has been made pursuant to
Section 4(a), keep intact all copyright notices for the Work and
provide, reasonable to the medium or means You are utilizing: (i) the
name of the Original Author (or pseudonym, if applicable) if supplied,
and/or if the Original Author and/or Licensor designate another party
or parties (e.g., a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's copyright notice,
terms of service or by other reasonable means, the name of such party
or parties; (ii) the title of the Work if supplied; (iii) to the
extent reasonably practicable, the URI, if any, that Licensor
specifies to be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information for the Work;
and (iv) , consistent with Section 3(b), in the case of an Adaptation,
a credit identifying the use of the Work in the Adaptation (e.g.,
"French translation of the Work by Original Author," or "Screenplay
based on original Work by Original Author"). The credit required by
this Section 4 (b) may be implemented in any reasonable manner;
provided, however, that in the case of a Adaptation or Collection, at
a minimum such credit will appear, if a credit for all contributing
authors of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits for the
other contributing authors. For the avoidance of doubt, You may only
use the credit required by this Section for the purpose of attribution
in the manner set out above and, by exercising Your rights under this
License, You may not implicitly or explicitly assert or imply any
connection with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of You or Your
use of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or Attribution
Parties.
c. Except as otherwise agreed in writing by the Licensor or as may be
otherwise permitted by applicable law, if You Reproduce, Distribute or
Publicly Perform the Work either by itself or as part of any
Adaptations or Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work which would be
prejudicial to the Original Author's honor or reputation. Licensor
agrees that in those jurisdictions (e.g. Japan), in which any exercise
of the right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion, mutilation,
modification or other derogatory action prejudicial to the Original
Author's honor and reputation, the Licensor will waive or not assert,
as appropriate, this Section, to the fullest extent permitted by the
applicable national law, to enable You to reasonably exercise Your
right under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this License.
Individuals or entities who have received Adaptations or Collections
from You under this License, however, will not have their licenses
terminated provided such individuals or entities remain in full
compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
survive any termination of this License.
b. Subject to the above terms and conditions, the license granted here is
perpetual (for the duration of the applicable copyright in the Work).
Notwithstanding the above, Licensor reserves the right to release the
Work under different license terms or to stop distributing the Work at
any time; provided, however that any such election will not serve to
withdraw this License (or any other license that has been, or is
required to be, granted under the terms of this License), and this
License will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a Collection,
the Licensor offers to the recipient a license to the Work on the same
terms and conditions as the license granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
offers to the recipient a license to the original Work on the same
terms and conditions as the license granted to You under this License.
c. If any provision of this License is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this License, and without further action
by the parties to this agreement, such provision shall be reformed to
the minimum extent necessary to make such provision valid and
enforceable.
d. No term or provision of this License shall be deemed waived and no
breach consented to unless such waiver or consent shall be in writing
and signed by the party to be charged with such waiver or consent.
e. This License constitutes the entire agreement between the parties with
respect to the Work licensed here. There are no understandings,
agreements or representations with respect to the Work not specified
here. Licensor shall not be bound by any additional provisions that
may appear in any communication from You. This License may not be
modified without the mutual written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced, in this
License were drafted utilizing the terminology of the Berne Convention
for the Protection of Literary and Artistic Works (as amended on
September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
and the Universal Copyright Convention (as revised on July 24, 1971).
These rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be enforced
according to the corresponding provisions of the implementation of
those treaty provisions in the applicable national law. If the
standard suite of rights granted under applicable copyright law
includes additional rights not granted under this License, such
additional rights are deemed to be included in the License; this
License is not intended to restrict the license of any rights under
applicable law.
Creative Commons Notice
Creative Commons is not a party to this License, and makes no warranty
whatsoever in connection with the Work. Creative Commons will not be
liable to You or any party on any legal theory for any damages
whatsoever, including without limitation any general, special,
incidental or consequential damages arising in connection to this
license. Notwithstanding the foregoing two (2) sentences, if Creative
Commons has expressly identified itself as the Licensor hereunder, it
shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of doubt,
this trademark restriction does not form part of this License.
Creative Commons may be contacted at http://creativecommons.org/.

View File

@@ -1,69 +0,0 @@
// Copyright 2013 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"
// ActivityService handles communication with the activity related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/activity/
type ActivityService service
// FeedLink represents a link to a related resource.
type FeedLink struct {
HRef *string `json:"href,omitempty"`
Type *string `json:"type,omitempty"`
}
// Feeds represents timeline resources in Atom format.
type Feeds struct {
TimelineURL *string `json:"timeline_url,omitempty"`
UserURL *string `json:"user_url,omitempty"`
CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"`
CurrentUserURL *string `json:"current_user_url,omitempty"`
CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"`
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"`
} `json:"_links,omitempty"`
}
// ListFeeds lists all the feeds available to the authenticated user.
//
// GitHub provides several timeline resources in Atom format:
// Timeline: The GitHub global public timeline
// User: The public timeline for any user, using URI template
// Current user public: The public timeline for the authenticated user
// Current user: The private timeline for the authenticated user
// Current user actor: The private timeline for activity created by the
// authenticated user
// Current user organizations: The private timeline for the organizations
// the authenticated user is a member of.
//
// Note: Private feeds are only returned when authenticating via Basic Auth
// since current feed URIs use the older, non revocable auth tokens.
func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) {
req, err := s.client.NewRequest("GET", "feeds", nil)
if err != nil {
return nil, nil, err
}
f := &Feeds{}
resp, err := s.client.Do(ctx, req, f)
if err != nil {
return nil, resp, err
}
return f, resp, nil
}

View File

@@ -1,320 +0,0 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"encoding/json"
"fmt"
"time"
)
// Event represents a GitHub event.
type Event struct {
Type *string `json:"type,omitempty"`
Public *bool `json:"public"`
RawPayload *json.RawMessage `json:"payload,omitempty"`
Repo *Repository `json:"repo,omitempty"`
Actor *User `json:"actor,omitempty"`
Org *Organization `json:"org,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
ID *string `json:"id,omitempty"`
}
func (e Event) String() string {
return Stringify(e)
}
// ParsePayload parses the event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) ParsePayload() (payload interface{}, err error) {
switch *e.Type {
case "CommitCommentEvent":
payload = &CommitCommentEvent{}
case "CreateEvent":
payload = &CreateEvent{}
case "DeleteEvent":
payload = &DeleteEvent{}
case "DeploymentEvent":
payload = &DeploymentEvent{}
case "DeploymentStatusEvent":
payload = &DeploymentStatusEvent{}
case "ForkEvent":
payload = &ForkEvent{}
case "GollumEvent":
payload = &GollumEvent{}
case "IntegrationInstallationEvent":
payload = &IntegrationInstallationEvent{}
case "IntegrationInstallationRepositoriesEvent":
payload = &IntegrationInstallationRepositoriesEvent{}
case "IssueCommentEvent":
payload = &IssueCommentEvent{}
case "IssuesEvent":
payload = &IssuesEvent{}
case "LabelEvent":
payload = &LabelEvent{}
case "MemberEvent":
payload = &MemberEvent{}
case "MembershipEvent":
payload = &MembershipEvent{}
case "MilestoneEvent":
payload = &MilestoneEvent{}
case "OrganizationEvent":
payload = &OrganizationEvent{}
case "OrgBlockEvent":
payload = &OrgBlockEvent{}
case "PageBuildEvent":
payload = &PageBuildEvent{}
case "PingEvent":
payload = &PingEvent{}
case "ProjectEvent":
payload = &ProjectEvent{}
case "ProjectCardEvent":
payload = &ProjectCardEvent{}
case "ProjectColumnEvent":
payload = &ProjectColumnEvent{}
case "PublicEvent":
payload = &PublicEvent{}
case "PullRequestEvent":
payload = &PullRequestEvent{}
case "PullRequestReviewEvent":
payload = &PullRequestReviewEvent{}
case "PullRequestReviewCommentEvent":
payload = &PullRequestReviewCommentEvent{}
case "PushEvent":
payload = &PushEvent{}
case "ReleaseEvent":
payload = &ReleaseEvent{}
case "RepositoryEvent":
payload = &RepositoryEvent{}
case "StatusEvent":
payload = &StatusEvent{}
case "TeamAddEvent":
payload = &TeamAddEvent{}
case "WatchEvent":
payload = &WatchEvent{}
}
err = json.Unmarshal(*e.RawPayload, &payload)
return payload, err
}
// Payload returns the parsed event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
//
// Deprecated: Use ParsePayload instead, which returns an error
// rather than panics if JSON unmarshaling raw payload fails.
func (e *Event) Payload() (payload interface{}) {
var err error
payload, err = e.ParsePayload()
if err != nil {
panic(err)
}
return payload
}
// 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)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
u := fmt.Sprintf("repos/%v/%v/events", owner, repo)
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 events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
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 events []*IssueEvent
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
u := fmt.Sprintf("networks/%v/%v/events", owner, repo)
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 events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
u := fmt.Sprintf("orgs/%v/events", 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
}
var events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
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)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
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)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// 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) {
u := fmt.Sprintf("users/%v/events/orgs/%v", user, 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
}
var events []*Event
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}

View File

@@ -1,223 +0,0 @@
// Copyright 2014 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"
)
// Notification identifies a GitHub notification for a user.
type Notification struct {
ID *string `json:"id,omitempty"`
Repository *Repository `json:"repository,omitempty"`
Subject *NotificationSubject `json:"subject,omitempty"`
// Reason identifies the event that triggered the notification.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons
Reason *string `json:"reason,omitempty"`
Unread *bool `json:"unread,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
LastReadAt *time.Time `json:"last_read_at,omitempty"`
URL *string `json:"url,omitempty"`
}
// NotificationSubject identifies the subject of a notification.
type NotificationSubject struct {
Title *string `json:"title,omitempty"`
URL *string `json:"url,omitempty"`
LatestCommentURL *string `json:"latest_comment_url,omitempty"`
Type *string `json:"type,omitempty"`
}
// NotificationListOptions specifies the optional parameters to the
// ActivityService.ListNotifications method.
type NotificationListOptions struct {
All bool `url:"all,omitempty"`
Participating bool `url:"participating,omitempty"`
Since time.Time `url:"since,omitempty"`
Before time.Time `url:"before,omitempty"`
ListOptions
}
// 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) {
u := fmt.Sprintf("notifications")
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 notifications []*Notification
resp, err := s.client.Do(ctx, req, &notifications)
if err != nil {
return nil, resp, err
}
return notifications, resp, nil
}
// 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) {
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo)
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 notifications []*Notification
resp, err := s.client.Do(ctx, req, &notifications)
if err != nil {
return nil, resp, err
}
return notifications, resp, nil
}
type markReadOptions struct {
LastReadAt time.Time `json:"last_read_at,omitempty"`
}
// MarkNotificationsRead marks all notifications up to lastRead as read.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read
func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) {
opts := &markReadOptions{
LastReadAt: lastRead,
}
req, err := s.client.NewRequest("PUT", "notifications", opts)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// 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
func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) {
opts := &markReadOptions{
LastReadAt: lastRead,
}
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo)
req, err := s.client.NewRequest("PUT", u, opts)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// GetThread gets the specified notification thread.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread
func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) {
u := fmt.Sprintf("notifications/threads/%v", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
notification := new(Notification)
resp, err := s.client.Do(ctx, req, notification)
if err != nil {
return nil, resp, err
}
return notification, resp, nil
}
// MarkThreadRead marks the specified thread as read.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read
func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) {
u := fmt.Sprintf("notifications/threads/%v", id)
req, err := s.client.NewRequest("PATCH", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// 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
func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) {
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
sub := new(Subscription)
resp, err := s.client.Do(ctx, req, sub)
if err != nil {
return nil, resp, err
}
return sub, resp, nil
}
// SetThreadSubscription sets the subscription for the specified thread for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription
func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) {
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
req, err := s.client.NewRequest("PUT", u, subscription)
if err != nil {
return nil, nil, err
}
sub := new(Subscription)
resp, err := s.client.Do(ctx, req, sub)
if err != nil {
return nil, resp, err
}
return sub, resp, nil
}
// DeleteThreadSubscription deletes the subscription for the specified thread
// for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription
func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) {
u := fmt.Sprintf("notifications/threads/%v/subscription", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,135 +0,0 @@
// Copyright 2013 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"
)
// StarredRepository is returned by ListStarred.
type StarredRepository struct {
StarredAt *Timestamp `json:"starred_at,omitempty"`
Repository *Repository `json:"repo,omitempty"`
}
// Stargazer represents a user that has starred a repository.
type Stargazer struct {
StarredAt *Timestamp `json:"starred_at,omitempty"`
User *User `json:"user,omitempty"`
}
// 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) {
u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo)
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", mediaTypeStarringPreview)
var stargazers []*Stargazer
resp, err := s.client.Do(ctx, req, &stargazers)
if err != nil {
return nil, resp, err
}
return stargazers, resp, nil
}
// ActivityListStarredOptions specifies the optional parameters to the
// ActivityService.ListStarred method.
type ActivityListStarredOptions struct {
// How to sort the repository list. Possible values are: created, updated,
// pushed, full_name. Default is "full_name".
Sort string `url:"sort,omitempty"`
// Direction in which to sort repositories. Possible values are: asc, desc.
// Default is "asc" when sort is "full_name", otherwise default is "desc".
Direction string `url:"direction,omitempty"`
ListOptions
}
// 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) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/starred", user)
} else {
u = "user/starred"
}
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", mediaTypeStarringPreview)
var repos []*StarredRepository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// 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
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)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
starred, err := parseBoolResponse(err)
return starred, resp, err
}
// Star a repository as the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository
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)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Unstar a repository as the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository
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)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,146 +0,0 @@
// Copyright 2014 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"
)
// Subscription identifies a repository or thread subscription.
type Subscription struct {
Subscribed *bool `json:"subscribed,omitempty"`
Ignored *bool `json:"ignored,omitempty"`
Reason *string `json:"reason,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
URL *string `json:"url,omitempty"`
// only populated for repository subscriptions
RepositoryURL *string `json:"repository_url,omitempty"`
// only populated for thread subscriptions
ThreadURL *string `json:"thread_url,omitempty"`
}
// 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) {
u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo)
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 watchers []*User
resp, err := s.client.Do(ctx, req, &watchers)
if err != nil {
return nil, resp, err
}
return watchers, resp, nil
}
// 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) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/subscriptions", user)
} else {
u = "user/subscriptions"
}
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 watched []*Repository
resp, err := s.client.Do(ctx, req, &watched)
if err != nil {
return nil, resp, err
}
return watched, resp, nil
}
// GetRepositorySubscription returns the subscription for the specified
// repository for the authenticated user. If the authenticated user is not
// watching the repository, a nil Subscription is returned.
//
// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription
func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
sub := new(Subscription)
resp, err := s.client.Do(ctx, req, sub)
if err != nil {
// if it's just a 404, don't return that as an error
_, err = parseBoolResponse(err)
return nil, resp, err
}
return sub, resp, nil
}
// SetRepositorySubscription sets the subscription for the specified repository
// for the authenticated user.
//
// To watch a repository, set subscription.Subscribed to true.
// To ignore notifications made within a repository, set subscription.Ignored to true.
// To stop watching a repository, use DeleteRepositorySubscription.
//
// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription
func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
req, err := s.client.NewRequest("PUT", u, subscription)
if err != nil {
return nil, nil, err
}
sub := new(Subscription)
resp, err := s.client.Do(ctx, req, sub)
if err != nil {
return nil, resp, err
}
return sub, resp, nil
}
// DeleteRepositorySubscription deletes the subscription for the specified
// repository for the authenticated user.
//
// This is used to stop watching a repository. To control whether or not to
// receive notifications from a repository, use SetRepositorySubscription.
//
// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription
func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) {
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,101 +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"
)
// AdminService handles communication with the admin related methods of the
// GitHub API. These API routes are normally only accessible for GitHub
// Enterprise installations.
//
// GitHub API docs: https://developer.github.com/v3/enterprise/
type AdminService service
// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group.
type TeamLDAPMapping struct {
ID *int `json:"id,omitempty"`
LDAPDN *string `json:"ldap_dn,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
Slug *string `json:"slug,omitempty"`
Description *string `json:"description,omitempty"`
Privacy *string `json:"privacy,omitempty"`
Permission *string `json:"permission,omitempty"`
MembersURL *string `json:"members_url,omitempty"`
RepositoriesURL *string `json:"repositories_url,omitempty"`
}
func (m TeamLDAPMapping) String() string {
return Stringify(m)
}
// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user.
type UserLDAPMapping struct {
ID *int `json:"id,omitempty"`
LDAPDN *string `json:"ldap_dn,omitempty"`
Login *string `json:"login,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
GravatarID *string `json:"gravatar_id,omitempty"`
Type *string `json:"type,omitempty"`
SiteAdmin *bool `json:"site_admin,omitempty"`
URL *string `json:"url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
FollowingURL *string `json:"following_url,omitempty"`
FollowersURL *string `json:"followers_url,omitempty"`
GistsURL *string `json:"gists_url,omitempty"`
OrganizationsURL *string `json:"organizations_url,omitempty"`
ReceivedEventsURL *string `json:"received_events_url,omitempty"`
ReposURL *string `json:"repos_url,omitempty"`
StarredURL *string `json:"starred_url,omitempty"`
SubscriptionsURL *string `json:"subscriptions_url,omitempty"`
}
func (m UserLDAPMapping) 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
func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) {
u := fmt.Sprintf("admin/ldap/users/%v/mapping", user)
req, err := s.client.NewRequest("PATCH", u, mapping)
if err != nil {
return nil, nil, err
}
m := new(UserLDAPMapping)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group.
//
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team
func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) {
u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team)
req, err := s.client.NewRequest("PATCH", u, mapping)
if err != nil {
return nil, nil, err
}
m := new(TeamLDAPMapping)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}

View File

@@ -1,435 +0,0 @@
// Copyright 2015 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"
)
// Scope models a GitHub authorization scope.
//
// GitHub API docs: https://developer.github.com/v3/oauth/#scopes
type Scope string
// This is the set of scopes for GitHub API V3
const (
ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact?
ScopeUser Scope = "user"
ScopeUserEmail Scope = "user:email"
ScopeUserFollow Scope = "user:follow"
ScopePublicRepo Scope = "public_repo"
ScopeRepo Scope = "repo"
ScopeRepoDeployment Scope = "repo_deployment"
ScopeRepoStatus Scope = "repo:status"
ScopeDeleteRepo Scope = "delete_repo"
ScopeNotifications Scope = "notifications"
ScopeGist Scope = "gist"
ScopeReadRepoHook Scope = "read:repo_hook"
ScopeWriteRepoHook Scope = "write:repo_hook"
ScopeAdminRepoHook Scope = "admin:repo_hook"
ScopeAdminOrgHook Scope = "admin:org_hook"
ScopeReadOrg Scope = "read:org"
ScopeWriteOrg Scope = "write:org"
ScopeAdminOrg Scope = "admin:org"
ScopeReadPublicKey Scope = "read:public_key"
ScopeWritePublicKey Scope = "write:public_key"
ScopeAdminPublicKey Scope = "admin:public_key"
ScopeReadGPGKey Scope = "read:gpg_key"
ScopeWriteGPGKey Scope = "write:gpg_key"
ScopeAdminGPGKey Scope = "admin:gpg_key"
)
// AuthorizationsService handles communication with the authorization related
// methods of the GitHub API.
//
// This service requires HTTP Basic Authentication; it cannot be accessed using
// an OAuth token.
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/
type AuthorizationsService service
// Authorization represents an individual GitHub authorization.
type Authorization struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Scopes []Scope `json:"scopes,omitempty"`
Token *string `json:"token,omitempty"`
TokenLastEight *string `json:"token_last_eight,omitempty"`
HashedToken *string `json:"hashed_token,omitempty"`
App *AuthorizationApp `json:"app,omitempty"`
Note *string `json:"note,omitempty"`
NoteURL *string `json:"note_url,omitempty"`
UpdateAt *Timestamp `json:"updated_at,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
Fingerprint *string `json:"fingerprint,omitempty"`
// User is only populated by the Check and Reset methods.
User *User `json:"user,omitempty"`
}
func (a Authorization) String() string {
return Stringify(a)
}
// AuthorizationApp represents an individual GitHub app (in the context of authorization).
type AuthorizationApp struct {
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
ClientID *string `json:"client_id,omitempty"`
}
func (a AuthorizationApp) String() string {
return Stringify(a)
}
// Grant represents an OAuth application that has been granted access to an account.
type Grant struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
App *AuthorizationApp `json:"app,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Scopes []string `json:"scopes,omitempty"`
}
func (g Grant) String() string {
return Stringify(g)
}
// AuthorizationRequest represents a request to create an authorization.
type AuthorizationRequest struct {
Scopes []Scope `json:"scopes,omitempty"`
Note *string `json:"note,omitempty"`
NoteURL *string `json:"note_url,omitempty"`
ClientID *string `json:"client_id,omitempty"`
ClientSecret *string `json:"client_secret,omitempty"`
Fingerprint *string `json:"fingerprint,omitempty"`
}
func (a AuthorizationRequest) String() string {
return Stringify(a)
}
// AuthorizationUpdateRequest represents a request to update an authorization.
//
// Note that for any one update, you must only provide one of the "scopes"
// fields. That is, you may provide only one of "Scopes", or "AddScopes", or
// "RemoveScopes".
//
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization
type AuthorizationUpdateRequest struct {
Scopes []string `json:"scopes,omitempty"`
AddScopes []string `json:"add_scopes,omitempty"`
RemoveScopes []string `json:"remove_scopes,omitempty"`
Note *string `json:"note,omitempty"`
NoteURL *string `json:"note_url,omitempty"`
Fingerprint *string `json:"fingerprint,omitempty"`
}
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 int) (*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 int, 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 int) (*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
// username is the OAuth application clientID, and the password is its
// clientSecret. Invalid tokens will return a 404 Not Found.
//
// 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)
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
}
// Reset is used to reset a valid OAuth token without end user involvement.
// Applications must save the "token" property in the response, because changes
// take effect immediately.
//
// Note that this operation requires the use of BasicAuth, but where the
// username is the OAuth application clientID, and the password is its
// clientSecret. Invalid tokens will return a 404 Not Found.
//
// 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)
req, err := s.client.NewRequest("POST", 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
}
// Revoke an authorization for an application.
//
// Note that this operation requires the use of BasicAuth, but where the
// 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)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
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 int) (*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 int) (*Response, error) {
u := fmt.Sprintf("applications/grants/%d", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// CreateImpersonation creates an impersonation OAuth token.
//
// This requires admin permissions. With the returned Authorization.Token
// you can e.g. create or delete a user's public SSH key. NOTE: creating a
// new token automatically revokes an existing one.
//
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#create-an-impersonation-oauth-token
func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) {
u := fmt.Sprintf("admin/users/%v/authorizations", username)
req, err := s.client.NewRequest("POST", u, authReq)
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
}
// DeleteImpersonation deletes an impersonation OAuth token.
//
// NOTE: there can be only one at a time.
//
// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#delete-an-impersonation-oauth-token
func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) {
u := fmt.Sprintf("admin/users/%v/authorizations", username)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,171 +0,0 @@
// Copyright 2013 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 provides a client for using the GitHub API.
Usage:
import "github.com/google/go-github/github"
Construct a new GitHub client, then use the various services on the client to
access different parts of the GitHub API. For example:
client := github.NewClient(nil)
// list all organizations for user "willnorris"
orgs, _, err := client.Organizations.List(ctx, "willnorris", nil)
Some API methods have optional parameters that can be passed. For example:
client := github.NewClient(nil)
// list public repositories for org "github"
opt := &github.RepositoryListByOrgOptions{Type: "public"}
repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt)
The services of a client divide the API into logical chunks and correspond to
the structure of the GitHub API documentation at
https://developer.github.com/v3/.
Authentication
The go-github library does not directly handle authentication. Instead, when
creating a new client, pass an http.Client that can handle authentication for
you. The easiest and recommended way to do this is using the golang.org/x/oauth2
library, but you can always use any other library that provides an http.Client.
If you have an OAuth2 access token (for example, a personal API token), you can
use it with the oauth2 library using:
import "golang.org/x/oauth2"
func main() {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: "... your access token ..."},
)
tc := oauth2.NewClient(ctx, ts)
client := github.NewClient(tc)
// list all repositories for the authenticated user
repos, _, err := client.Repositories.List(ctx, "", nil)
}
Note that when using an authenticated Client, all calls made by the client will
include the specified OAuth token. Therefore, authenticated clients should
almost never be shared between different users.
See the oauth2 docs for complete instructions on using that library.
For API methods that require HTTP Basic Authentication, use the
BasicAuthTransport.
Rate Limiting
GitHub imposes a rate limit on all API clients. Unauthenticated clients are
limited to 60 requests per hour, while authenticated clients can make up to
5,000 requests per hour. To receive the higher rate limit when making calls
that are not issued on behalf of a user, use the
UnauthenticatedRateLimitedTransport.
The returned Response.Rate value contains the rate limit information
from the most recent API call. If a recent enough response isn't
available, you can use RateLimits to fetch the most up-to-date rate
limit data for the client.
To detect an API rate limit error, you can check if its type is *github.RateLimitError:
repos, _, err := client.Repositories.List(ctx, "", nil)
if _, ok := err.(*github.RateLimitError); ok {
log.Println("hit rate limit")
}
Learn more about GitHub rate limiting at
https://developer.github.com/v3/#rate-limiting.
Accepted Status
Some endpoints may return a 202 Accepted status code, meaning that the
information required is not yet ready and was scheduled to be gathered on
the GitHub side. Methods known to behave like this are documented specifying
this behavior.
To detect this condition of error, you can check if its type is
*github.AcceptedError:
stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo)
if _, ok := err.(*github.AcceptedError); ok {
log.Println("scheduled on GitHub side")
}
Conditional Requests
The GitHub API has good support for conditional requests which will help
prevent you from burning through your rate limit, as well as help speed up your
application. go-github does not handle conditional requests directly, but is
instead designed to work with a caching http.Transport. We recommend using
https://github.com/gregjones/httpcache for that.
Learn more about GitHub conditional requests at
https://developer.github.com/v3/#conditional-requests.
Creating and Updating Resources
All structs for GitHub resources use pointer values for all non-repeated fields.
This allows distinguishing between unset fields and those set to a zero-value.
Helper functions have been provided to easily create these pointers for string,
bool, and int values. For example:
// create a new private repository named "foo"
repo := &github.Repository{
Name: github.String("foo"),
Private: github.Bool(true),
}
client.Repositories.Create(ctx, "", repo)
Users who have worked with protocol buffers should find this pattern familiar.
Pagination
All requests for resource collections (repos, pull requests, issues, etc.)
support pagination. Pagination options are described in the
github.ListOptions struct and passed to the list methods directly or as an
embedded type of a more specific list options struct (for example
github.PullRequestListOptions). Pages information is available via the
github.Response struct.
client := github.NewClient(nil)
opt := &github.RepositoryListByOrgOptions{
ListOptions: github.ListOptions{PerPage: 10},
}
// get all pages of results
var allRepos []*github.Repository
for {
repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt)
if err != nil {
return err
}
allRepos = append(allRepos, repos...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
Google App Engine
Go on App Engine Classic (which as of this writing uses Go 1.6) can not use
the "context" import and still relies on "golang.org/x/net/context".
As a result, if you wish to continue to use "go-github" on App Engine Classic,
you will need to rewrite all the "context" imports using the following command:
gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go
See "with_appengine.go" for more details.
*/
package github

View File

@@ -1,688 +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.
// These event types are shared between the Events API and used as Webhook payloads.
package github
// CommitCommentEvent is triggered when a commit comment is created.
// The Webhook event name is "commit_comment".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent
type CommitCommentEvent struct {
Comment *RepositoryComment `json:"comment,omitempty"`
// The following fields are only populated by Webhook events.
Action *string `json:"action,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// CreateEvent represents a created repository, branch, or tag.
// The Webhook event name is "create".
//
// Note: webhooks will not receive this event for created repositories.
// Additionally, webhooks will not receive this event for tags if more
// than three tags are pushed at once.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent
type CreateEvent struct {
Ref *string `json:"ref,omitempty"`
// RefType is the object that was created. Possible values are: "repository", "branch", "tag".
RefType *string `json:"ref_type,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
Description *string `json:"description,omitempty"`
// The following fields are only populated by Webhook events.
PusherType *string `json:"pusher_type,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// DeleteEvent represents a deleted branch or tag.
// The Webhook event name is "delete".
//
// Note: webhooks will not receive this event for tags if more than three tags
// are deleted at once.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent
type DeleteEvent struct {
Ref *string `json:"ref,omitempty"`
// RefType is the object that was deleted. Possible values are: "branch", "tag".
RefType *string `json:"ref_type,omitempty"`
// The following fields are only populated by Webhook events.
PusherType *string `json:"pusher_type,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// DeploymentEvent represents a deployment.
// The Webhook event name is "deployment".
//
// Events of this type are not visible in timelines, they are only used to trigger hooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent
type DeploymentEvent struct {
Deployment *Deployment `json:"deployment,omitempty"`
Repo *Repository `json:"repository,omitempty"`
// The following fields are only populated by Webhook events.
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// DeploymentStatusEvent represents a deployment status.
// The Webhook event name is "deployment_status".
//
// Events of this type are not visible in timelines, they are only used to trigger hooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent
type DeploymentStatusEvent struct {
Deployment *Deployment `json:"deployment,omitempty"`
DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"`
Repo *Repository `json:"repository,omitempty"`
// The following fields are only populated by Webhook events.
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// ForkEvent is triggered when a user forks a repository.
// The Webhook event name is "fork".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent
type ForkEvent struct {
// Forkee is the created repository.
Forkee *Repository `json:"forkee,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// Page represents a single Wiki page.
type Page struct {
PageName *string `json:"page_name,omitempty"`
Title *string `json:"title,omitempty"`
Summary *string `json:"summary,omitempty"`
Action *string `json:"action,omitempty"`
SHA *string `json:"sha,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
}
// GollumEvent is triggered when a Wiki page is created or updated.
// The Webhook event name is "gollum".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent
type GollumEvent struct {
Pages []*Page `json:"pages,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// EditChange represents the changes when an issue, pull request, or comment has
// been edited.
type EditChange struct {
Title *struct {
From *string `json:"from,omitempty"`
} `json:"title,omitempty"`
Body *struct {
From *string `json:"from,omitempty"`
} `json:"body,omitempty"`
}
// ProjectChange represents the changes when a project has been edited.
type ProjectChange struct {
Name *struct {
From *string `json:"from,omitempty"`
} `json:"name,omitempty"`
Body *struct {
From *string `json:"from,omitempty"`
} `json:"body,omitempty"`
}
// ProjectCardChange represents the changes when a project card has been edited.
type ProjectCardChange struct {
Note *struct {
From *string `json:"from,omitempty"`
} `json:"note,omitempty"`
}
// ProjectColumnChange represents the changes when a project column has been edited.
type ProjectColumnChange struct {
Name *struct {
From *string `json:"from,omitempty"`
} `json:"name,omitempty"`
}
// IntegrationInstallationEvent is triggered when an integration is created or deleted.
// The Webhook event name is "integration_installation".
//
// GitHub API docs: https://developer.github.com/early-access/integrations/webhooks/#integrationinstallationevent
type IntegrationInstallationEvent struct {
// The action that was performed. Possible values for an "integration_installation"
// event are: "created", "deleted".
Action *string `json:"action,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// IntegrationInstallationRepositoriesEvent is triggered when an integration repository
// is added or removed. The Webhook event name is "integration_installation_repositories".
//
// GitHub API docs: https://developer.github.com/early-access/integrations/webhooks/#integrationinstallationrepositoriesevent
type IntegrationInstallationRepositoriesEvent struct {
// The action that was performed. Possible values for an "integration_installation_repositories"
// event are: "added", "removed".
Action *string `json:"action,omitempty"`
RepositoriesAdded []*Repository `json:"repositories_added,omitempty"`
RepositoriesRemoved []*Repository `json:"repositories_removed,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// IssueCommentEvent is triggered when an issue comment is created on an issue
// or pull request.
// The Webhook event name is "issue_comment".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent
type IssueCommentEvent struct {
// Action is the action that was performed on the comment.
// Possible values are: "created", "edited", "deleted".
Action *string `json:"action,omitempty"`
Issue *Issue `json:"issue,omitempty"`
Comment *IssueComment `json:"comment,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// IssuesEvent is triggered when an issue is assigned, unassigned, labeled,
// unlabeled, opened, closed, or reopened.
// 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 *string `json:"action,omitempty"`
Issue *Issue `json:"issue,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Label *Label `json:"label,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// LabelEvent is triggered when a repository's label is created, edited, or deleted.
// The Webhook event name is "label"
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent
type LabelEvent struct {
// Action is the action that was performed. Possible values are:
// "created", "edited", "deleted"
Action *string `json:"action,omitempty"`
Label *Label `json:"label,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// MemberEvent is triggered when a user is added as a collaborator to a repository.
// The Webhook event name is "member".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent
type MemberEvent struct {
// Action is the action that was performed. Possible value is: "added".
Action *string `json:"action,omitempty"`
Member *User `json:"member,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// MembershipEvent is triggered when a user is added or removed from a team.
// The Webhook event name is "membership".
//
// Events of this type are not visible in timelines, they are only used to
// trigger organization webhooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent
type MembershipEvent struct {
// Action is the action that was performed. Possible values are: "added", "removed".
Action *string `json:"action,omitempty"`
// Scope is the scope of the membership. Possible value is: "team".
Scope *string `json:"scope,omitempty"`
Member *User `json:"member,omitempty"`
Team *Team `json:"team,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"`
}
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted.
// The Webhook event name is "milestone".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent
type MilestoneEvent struct {
// Action is the action that was performed. Possible values are:
// "created", "closed", "opened", "edited", "deleted"
Action *string `json:"action,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Org *Organization `json:"organization,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// OrganizationEvent is triggered 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".
Action *string `json:"action,omitempty"`
// Invitaion 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.
// Not present when the action is "member_invited".
Membership *Membership `json:"membership,omitempty"`
Organization *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// OrgBlockEvent is triggered when an organization blocks or unblocks a user.
// The Webhook event name is "org_block".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#orgblockevent
type OrgBlockEvent struct {
// Action is the action that was performed.
// Can be "blocked" or "unblocked".
Action *string `json:"action,omitempty"`
BlockedUser *User `json:"blocked_user,omitempty"`
Organization *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
// The following fields are only populated by Webhook events.
Installation *Installation `json:"installation,omitempty"`
}
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether
// successful or not.
// The Webhook event name is "page_build".
//
// This event is triggered on push to a GitHub Pages enabled branch (gh-pages
// for project pages, master for user and organization pages).
//
// Events of this type are not visible in timelines, they are only used to trigger hooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent
type PageBuildEvent struct {
Build *PagesBuild `json:"build,omitempty"`
// The following fields are only populated by Webhook events.
ID *int `json:"id,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// PingEvent is triggered when a Webhook is added to GitHub.
//
// GitHub API docs: https://developer.github.com/webhooks/#ping-event
type PingEvent struct {
// Random string of GitHub zen.
Zen *string `json:"zen,omitempty"`
// The ID of the webhook that triggered the ping.
HookID *int `json:"hook_id,omitempty"`
// The webhook configuration.
Hook *Hook `json:"hook,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// ProjectEvent is triggered when project is created, modified or deleted.
// The webhook event name is "project".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent
type ProjectEvent struct {
Action *string `json:"action,omitempty"`
Changes *ProjectChange `json:"changes,omitempty"`
Project *Project `json:"project,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted.
// The webhook event name is "project_card".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent
type ProjectCardEvent struct {
Action *string `json:"action,omitempty"`
Changes *ProjectCardChange `json:"changes,omitempty"`
AfterID *int `json:"after_id,omitempty"`
ProjectCard *ProjectCard `json:"project_card,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted.
// The webhook event name is "project_column".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent
type ProjectColumnEvent struct {
Action *string `json:"action,omitempty"`
Changes *ProjectColumnChange `json:"changes,omitempty"`
AfterID *int `json:"after_id,omitempty"`
ProjectColumn *ProjectColumn `json:"project_column,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Org *Organization `json:"organization,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// PublicEvent is triggered when a private repository is open sourced.
// According to GitHub: "Without a doubt: the best GitHub event."
// The Webhook event name is "public".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent
type PublicEvent struct {
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// PullRequestEvent is triggered when a pull request is assigned, unassigned,
// labeled, unlabeled, opened, closed, reopened, or synchronized.
// 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", "labeled", "unlabeled", "opened", "closed", or "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.
Action *string `json:"action,omitempty"`
Number *int `json:"number,omitempty"`
PullRequest *PullRequest `json:"pull_request,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// PullRequestReviewEvent is triggered when a review is submitted on a pull
// request.
// The Webhook event name is "pull_request_review".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent
type PullRequestReviewEvent struct {
// Action is always "submitted".
Action *string `json:"action,omitempty"`
Review *PullRequestReview `json:"review,omitempty"`
PullRequest *PullRequest `json:"pull_request,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
// The following field is only present when the webhook is triggered on
// a repository belonging to an organization.
Organization *Organization `json:"organization,omitempty"`
}
// PullRequestReviewCommentEvent is triggered when a comment is created on a
// portion of the unified diff of a pull request.
// The Webhook event name is "pull_request_review_comment".
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent
type PullRequestReviewCommentEvent struct {
// Action is the action that was performed on the comment.
// Possible values are: "created", "edited", "deleted".
Action *string `json:"action,omitempty"`
PullRequest *PullRequest `json:"pull_request,omitempty"`
Comment *PullRequestComment `json:"comment,omitempty"`
// The following fields are only populated by Webhook events.
Changes *EditChange `json:"changes,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// PushEvent represents a git push to a GitHub repository.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent
type PushEvent struct {
PushID *int `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"`
// The following fields are only populated by Webhook events.
After *string `json:"after,omitempty"`
Created *bool `json:"created,omitempty"`
Deleted *bool `json:"deleted,omitempty"`
Forced *bool `json:"forced,omitempty"`
BaseRef *string `json:"base_ref,omitempty"`
Compare *string `json:"compare,omitempty"`
Repo *PushEventRepository `json:"repository,omitempty"`
HeadCommit *PushEventCommit `json:"head_commit,omitempty"`
Pusher *User `json:"pusher,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
func (p PushEvent) String() string {
return Stringify(p)
}
// PushEventCommit represents a git commit in a GitHub PushEvent.
type PushEventCommit struct {
Message *string `json:"message,omitempty"`
Author *CommitAuthor `json:"author,omitempty"`
URL *string `json:"url,omitempty"`
Distinct *bool `json:"distinct,omitempty"`
// The following fields are only populated by Events API.
SHA *string `json:"sha,omitempty"`
// The following fields are only populated by Webhook events.
ID *string `json:"id,omitempty"`
TreeID *string `json:"tree_id,omitempty"`
Timestamp *Timestamp `json:"timestamp,omitempty"`
Committer *CommitAuthor `json:"committer,omitempty"`
Added []string `json:"added,omitempty"`
Removed []string `json:"removed,omitempty"`
Modified []string `json:"modified,omitempty"`
}
func (p PushEventCommit) String() string {
return Stringify(p)
}
// PushEventRepository represents the repo object in a PushEvent payload.
type PushEventRepository struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
FullName *string `json:"full_name,omitempty"`
Owner *PushEventRepoOwner `json:"owner,omitempty"`
Private *bool `json:"private,omitempty"`
Description *string `json:"description,omitempty"`
Fork *bool `json:"fork,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
PushedAt *Timestamp `json:"pushed_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Homepage *string `json:"homepage,omitempty"`
Size *int `json:"size,omitempty"`
StargazersCount *int `json:"stargazers_count,omitempty"`
WatchersCount *int `json:"watchers_count,omitempty"`
Language *string `json:"language,omitempty"`
HasIssues *bool `json:"has_issues,omitempty"`
HasDownloads *bool `json:"has_downloads,omitempty"`
HasWiki *bool `json:"has_wiki,omitempty"`
HasPages *bool `json:"has_pages,omitempty"`
ForksCount *int `json:"forks_count,omitempty"`
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
Organization *string `json:"organization,omitempty"`
URL *string `json:"url,omitempty"`
ArchiveURL *string `json:"archive_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
StatusesURL *string `json:"statuses_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
SSHURL *string `json:"ssh_url,omitempty"`
CloneURL *string `json:"clone_url,omitempty"`
SVNURL *string `json:"svn_url,omitempty"`
}
// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload.
type PushEventRepoOwner struct {
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
}
// ReleaseEvent is triggered when a release is published.
// 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 *string `json:"action,omitempty"`
Release *RepositoryRelease `json:"release,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// RepositoryEvent is triggered when a repository is created.
// The Webhook event name is "repository".
//
// Events of this type are not visible in timelines, they are only used to
// trigger organization webhooks.
//
// 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 *string `json:"action,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"`
}
// StatusEvent is triggered when the status of a Git commit changes.
// The Webhook event name is "status".
//
// Events of this type are not visible in timelines, they are only used to
// trigger hooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent
type StatusEvent struct {
SHA *string `json:"sha,omitempty"`
// State is the new state. Possible values are: "pending", "success", "failure", "error".
State *string `json:"state,omitempty"`
Description *string `json:"description,omitempty"`
TargetURL *string `json:"target_url,omitempty"`
Branches []*Branch `json:"branches,omitempty"`
// The following fields are only populated by Webhook events.
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Context *string `json:"context,omitempty"`
Commit *RepositoryCommit `json:"commit,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}
// TeamAddEvent is triggered when a repository is added to a team.
// The Webhook event name is "team_add".
//
// Events of this type are not visible in timelines. These events are only used
// to trigger hooks.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent
type TeamAddEvent struct {
Team *Team `json:"team,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"`
}
// 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/
//
// The events actor is the user who starred a repository, and the events
// repository is the repository that was starred.
//
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent
type WatchEvent struct {
// Action is the action that was performed. Possible value is: "started".
Action *string `json:"action,omitempty"`
// The following fields are only populated by Webhook events.
Repo *Repository `json:"repository,omitempty"`
Sender *User `json:"sender,omitempty"`
Installation *Installation `json:"installation,omitempty"`
}

View File

@@ -1,299 +0,0 @@
// Copyright 2017 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.
// +build ignore
// gen-accessors generates accessor methods for structs with pointer fields.
//
// It is meant to be used by the go-github authors in conjunction with the
// go generate tool before sending a commit to GitHub.
package main
import (
"bytes"
"flag"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"io/ioutil"
"log"
"os"
"sort"
"strings"
"text/template"
"time"
)
const (
fileSuffix = "-accessors.go"
)
var (
verbose = flag.Bool("v", false, "Print verbose log messages")
sourceTmpl = template.Must(template.New("source").Parse(source))
// blacklist lists which "struct.method" combos to not generate.
blacklist = map[string]bool{
"RepositoryContent.GetContent": true,
"Client.GetBaseURL": true,
"Client.GetUploadURL": true,
"ErrorResponse.GetResponse": true,
"RateLimitError.GetResponse": true,
"AbuseRateLimitError.GetResponse": true,
}
)
func logf(fmt string, args ...interface{}) {
if *verbose {
log.Printf(fmt, args...)
}
}
func main() {
flag.Parse()
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0)
if err != nil {
log.Fatal(err)
return
}
for pkgName, pkg := range pkgs {
t := &templateData{
filename: pkgName + fileSuffix,
Year: time.Now().Year(),
Package: pkgName,
Imports: map[string]string{},
}
for filename, f := range pkg.Files {
logf("Processing %v...", filename)
if err := t.processAST(f); err != nil {
log.Fatal(err)
}
}
if err := t.dump(); err != nil {
log.Fatal(err)
}
}
logf("Done.")
}
func (t *templateData) processAST(f *ast.File) error {
for _, decl := range f.Decls {
gd, ok := decl.(*ast.GenDecl)
if !ok {
continue
}
for _, spec := range gd.Specs {
ts, ok := spec.(*ast.TypeSpec)
if !ok {
continue
}
st, ok := ts.Type.(*ast.StructType)
if !ok {
continue
}
for _, field := range st.Fields.List {
se, ok := field.Type.(*ast.StarExpr)
if len(field.Names) == 0 || !ok {
continue
}
fieldName := field.Names[0]
if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklist[key] {
logf("Method %v blacklisted; skipping.", key)
continue
}
switch x := se.X.(type) {
case *ast.ArrayType:
t.addArrayType(x, ts.Name.String(), fieldName.String())
case *ast.Ident:
t.addIdent(x, ts.Name.String(), fieldName.String())
case *ast.MapType:
t.addMapType(x, ts.Name.String(), fieldName.String())
case *ast.SelectorExpr:
t.addSelectorExpr(x, ts.Name.String(), fieldName.String())
default:
logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x)
}
}
}
}
return nil
}
func sourceFilter(fi os.FileInfo) bool {
return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix)
}
func (t *templateData) dump() error {
if len(t.Getters) == 0 {
logf("No getters for %v; skipping.", t.filename)
return nil
}
// Sort getters by ReceiverType.FieldName
sort.Sort(byName(t.Getters))
var buf bytes.Buffer
if err := sourceTmpl.Execute(&buf, t); err != nil {
return err
}
clean, err := format.Source(buf.Bytes())
if err != nil {
return err
}
logf("Writing %v...", t.filename)
return ioutil.WriteFile(t.filename, clean, 0644)
}
func newGetter(receiverType, fieldName, fieldType, zeroValue string) *getter {
return &getter{
sortVal: strings.ToLower(receiverType) + "." + strings.ToLower(fieldName),
ReceiverVar: strings.ToLower(receiverType[:1]),
ReceiverType: receiverType,
FieldName: fieldName,
FieldType: fieldType,
ZeroValue: zeroValue,
}
}
func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) {
var eltType string
switch elt := x.Elt.(type) {
case *ast.Ident:
eltType = elt.String()
default:
logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt)
return
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil"))
}
func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) {
var zeroValue string
switch x.String() {
case "int":
zeroValue = "0"
case "string":
zeroValue = `""`
case "bool":
zeroValue = "false"
case "Timestamp":
zeroValue = "Timestamp{}"
default: // other structs handled by their receivers directly.
return
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue))
}
func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) {
var keyType string
switch key := x.Key.(type) {
case *ast.Ident:
keyType = key.String()
default:
logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key)
return
}
var valueType string
switch value := x.Value.(type) {
case *ast.Ident:
valueType = value.String()
default:
logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value)
return
}
fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType)
zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType)
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue))
}
func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) {
if strings.ToLower(fieldName[:1]) == fieldName[:1] { // non-exported field
return
}
var xX string
if xx, ok := x.X.(*ast.Ident); ok {
xX = xx.String()
}
switch xX {
case "time", "json":
if xX == "json" {
t.Imports["encoding/json"] = "encoding/json"
} else {
t.Imports[xX] = xX
}
fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name)
zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name)
if xX == "time" && x.Sel.Name == "Duration" {
zeroValue = "0"
}
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue))
default:
logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x)
}
}
type templateData struct {
filename string
Year int
Package string
Imports map[string]string
Getters []*getter
}
type getter struct {
sortVal string // lower-case version of "ReceiverType.FieldName"
ReceiverVar string // the one-letter variable name to match the ReceiverType
ReceiverType string
FieldName string
FieldType string
ZeroValue string
}
type byName []*getter
func (b byName) Len() int { return len(b) }
func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal }
func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
const source = `// Copyright {{.Year}} 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.
// Code generated by gen-accessors; DO NOT EDIT.
package {{.Package}}
{{with .Imports}}
import (
{{- range . -}}
"{{.}}"
{{end -}}
)
{{end}}
{{range .Getters}}
// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise.
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} {
if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil {
return {{.ZeroValue}}
}
return *{{.ReceiverVar}}.{{.FieldName}}
}
{{end}}
`

View File

@@ -1,351 +0,0 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"fmt"
"time"
)
// GistsService handles communication with the Gist related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/gists/
type GistsService service
// Gist represents a GitHub's gist.
type Gist struct {
ID *string `json:"id,omitempty"`
Description *string `json:"description,omitempty"`
Public *bool `json:"public,omitempty"`
Owner *User `json:"owner,omitempty"`
Files map[GistFilename]GistFile `json:"files,omitempty"`
Comments *int `json:"comments,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
GitPullURL *string `json:"git_pull_url,omitempty"`
GitPushURL *string `json:"git_push_url,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
}
func (g Gist) String() string {
return Stringify(g)
}
// GistFilename represents filename on a gist.
type GistFilename string
// GistFile represents a file on a gist.
type GistFile struct {
Size *int `json:"size,omitempty"`
Filename *string `json:"filename,omitempty"`
Language *string `json:"language,omitempty"`
Type *string `json:"type,omitempty"`
RawURL *string `json:"raw_url,omitempty"`
Content *string `json:"content,omitempty"`
}
func (g GistFile) String() string {
return Stringify(g)
}
// GistCommit represents a commit on a gist.
type GistCommit struct {
URL *string `json:"url,omitempty"`
Version *string `json:"version,omitempty"`
User *User `json:"user,omitempty"`
ChangeStatus *CommitStats `json:"change_status,omitempty"`
CommittedAt *Timestamp `json:"committed_at,omitempty"`
}
func (gc GistCommit) String() string {
return Stringify(gc)
}
// GistFork represents a fork of a gist.
type GistFork struct {
URL *string `json:"url,omitempty"`
User *User `json:"user,omitempty"`
ID *string `json:"id,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
func (gf GistFork) String() string {
return Stringify(gf)
}
// GistListOptions specifies the optional parameters to the
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods.
type GistListOptions struct {
// Since filters Gists by time.
Since time.Time `url:"since,omitempty"`
ListOptions
}
// List gists for a user. Passing the empty string will list
// all public gists if called anonymously. However, if the call
// 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) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/gists", user)
} else {
u = "gists"
}
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 gists []*Gist
resp, err := s.client.Do(ctx, req, &gists)
if err != nil {
return nil, resp, err
}
return gists, resp, nil
}
// 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)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var gists []*Gist
resp, err := s.client.Do(ctx, req, &gists)
if err != nil {
return nil, resp, err
}
return gists, resp, nil
}
// 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)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var gists []*Gist
resp, err := s.client.Do(ctx, req, &gists)
if err != nil {
return nil, resp, err
}
return gists, resp, nil
}
// Get a single gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-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)
if err != nil {
return nil, nil, err
}
gist := new(Gist)
resp, err := s.client.Do(ctx, req, gist)
if err != nil {
return nil, resp, err
}
return gist, resp, nil
}
// GetRevision gets a specific revision of a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist
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)
if err != nil {
return nil, nil, err
}
gist := new(Gist)
resp, err := s.client.Do(ctx, req, gist)
if err != nil {
return nil, resp, err
}
return gist, resp, nil
}
// Create a gist for authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#create-a-gist
func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response, error) {
u := "gists"
req, err := s.client.NewRequest("POST", u, gist)
if err != nil {
return nil, nil, err
}
g := new(Gist)
resp, err := s.client.Do(ctx, req, g)
if err != nil {
return nil, resp, err
}
return g, resp, nil
}
// Edit a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#edit-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)
if err != nil {
return nil, nil, err
}
g := new(Gist)
resp, err := s.client.Do(ctx, req, g)
if err != nil {
return nil, resp, err
}
return g, resp, nil
}
// 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) {
u := fmt.Sprintf("gists/%v/commits", id)
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 gistCommits []*GistCommit
resp, err := s.client.Do(ctx, req, &gistCommits)
if err != nil {
return nil, resp, err
}
return gistCommits, resp, nil
}
// Delete a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#delete-a-gist
func (s *GistsService) Delete(ctx context.Context, id string) (*Response, error) {
u := fmt.Sprintf("gists/%v", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Star a gist on behalf of authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#star-a-gist
func (s *GistsService) Star(ctx context.Context, id string) (*Response, error) {
u := fmt.Sprintf("gists/%v/star", id)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Unstar a gist on a behalf of authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#unstar-a-gist
func (s *GistsService) Unstar(ctx context.Context, id string) (*Response, error) {
u := fmt.Sprintf("gists/%v/star", id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// IsStarred checks if a gist is starred by authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/gists/#check-if-a-gist-is-starred
func (s *GistsService) IsStarred(ctx context.Context, id string) (bool, *Response, error) {
u := fmt.Sprintf("gists/%v/star", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
starred, err := parseBoolResponse(err)
return starred, resp, err
}
// Fork a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/#fork-a-gist
func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, error) {
u := fmt.Sprintf("gists/%v/forks", id)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, nil, err
}
g := new(Gist)
resp, err := s.client.Do(ctx, req, g)
if err != nil {
return nil, resp, err
}
return g, resp, nil
}
// 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) {
u := fmt.Sprintf("gists/%v/forks", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var gistForks []*GistFork
resp, err := s.client.Do(ctx, req, &gistForks)
if err != nil {
return nil, resp, err
}
return gistForks, resp, nil
}

View File

@@ -1,119 +0,0 @@
// Copyright 2013 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"
)
// GistComment represents a Gist comment.
type GistComment struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
}
func (g GistComment) String() string {
return Stringify(g)
}
// 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) {
u := fmt.Sprintf("gists/%v/comments", gistID)
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 comments []*GistComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// GetComment retrieves a single comment from a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment
func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
c := new(GistComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// CreateComment creates a comment for a gist.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-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)
if err != nil {
return nil, nil, err
}
c := new(GistComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// EditComment edits an existing gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment
func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int, comment *GistComment) (*GistComment, *Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
}
c := new(GistComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// DeleteComment deletes a gist comment.
//
// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment
func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int) (*Response, error) {
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

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

View File

@@ -1,50 +0,0 @@
// Copyright 2013 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"
)
// Blob represents a blob object.
type Blob struct {
Content *string `json:"content,omitempty"`
Encoding *string `json:"encoding,omitempty"`
SHA *string `json:"sha,omitempty"`
Size *int `json:"size,omitempty"`
URL *string `json:"url,omitempty"`
}
// GetBlob fetchs a blob from a repo given a SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob
func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
blob := new(Blob)
resp, err := s.client.Do(ctx, req, blob)
return blob, resp, err
}
// CreateBlob creates a blob object.
//
// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob
func (s *GitService) CreateBlob(ctx context.Context, owner string, repo string, blob *Blob) (*Blob, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo)
req, err := s.client.NewRequest("POST", u, blob)
if err != nil {
return nil, nil, err
}
t := new(Blob)
resp, err := s.client.Do(ctx, req, t)
return t, resp, err
}

View File

@@ -1,133 +0,0 @@
// Copyright 2013 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"
)
// SignatureVerification represents GPG signature verification.
type SignatureVerification struct {
Verified *bool `json:"verified,omitempty"`
Reason *string `json:"reason,omitempty"`
Signature *string `json:"signature,omitempty"`
Payload *string `json:"payload,omitempty"`
}
// Commit represents a GitHub commit.
type Commit struct {
SHA *string `json:"sha,omitempty"`
Author *CommitAuthor `json:"author,omitempty"`
Committer *CommitAuthor `json:"committer,omitempty"`
Message *string `json:"message,omitempty"`
Tree *Tree `json:"tree,omitempty"`
Parents []Commit `json:"parents,omitempty"`
Stats *CommitStats `json:"stats,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
URL *string `json:"url,omitempty"`
Verification *SignatureVerification `json:"verification,omitempty"`
// CommentCount is the number of GitHub comments on the commit. This
// is only populated for requests that fetch GitHub data like
// Pulls.ListCommits, Repositories.ListCommits, etc.
CommentCount *int `json:"comment_count,omitempty"`
}
func (c Commit) String() string {
return Stringify(c)
}
// CommitAuthor represents the author or committer of a commit. The commit
// author may not correspond to a GitHub User.
type CommitAuthor struct {
Date *time.Time `json:"date,omitempty"`
Name *string `json:"name,omitempty"`
Email *string `json:"email,omitempty"`
// The following fields are only populated by Webhook events.
Login *string `json:"username,omitempty"` // Renamed for go-github consistency.
}
func (c CommitAuthor) String() string {
return Stringify(c)
}
// GetCommit fetchs the Commit object for a given SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit
func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha)
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", mediaTypeGitSigningPreview)
c := new(Commit)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// createCommit represents the body of a CreateCommit request.
type createCommit struct {
Author *CommitAuthor `json:"author,omitempty"`
Committer *CommitAuthor `json:"committer,omitempty"`
Message *string `json:"message,omitempty"`
Tree *string `json:"tree,omitempty"`
Parents []string `json:"parents,omitempty"`
}
// CreateCommit creates a new commit in a repository.
// commit must not be nil.
//
// The commit.Committer is optional and will be filled with the commit.Author
// data if omitted. If the commit.Author is omitted, it will be filled in with
// the authenticated users information and the current date.
//
// GitHub API docs: https://developer.github.com/v3/git/commits/#create-a-commit
func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) {
if commit == nil {
return nil, nil, fmt.Errorf("commit must be provided")
}
u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo)
parents := make([]string, len(commit.Parents))
for i, parent := range commit.Parents {
parents[i] = *parent.SHA
}
body := &createCommit{
Author: commit.Author,
Committer: commit.Committer,
Message: commit.Message,
Parents: parents,
}
if commit.Tree != nil {
body.Tree = commit.Tree.SHA
}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
c := new(Commit)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}

View File

@@ -1,217 +0,0 @@
// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package github
import (
"context"
"encoding/json"
"errors"
"fmt"
"strings"
)
// Reference represents a GitHub reference.
type Reference struct {
Ref *string `json:"ref"`
URL *string `json:"url"`
Object *GitObject `json:"object"`
}
func (r Reference) String() string {
return Stringify(r)
}
// GitObject represents a Git object.
type GitObject struct {
Type *string `json:"type"`
SHA *string `json:"sha"`
URL *string `json:"url"`
}
func (o GitObject) String() string {
return Stringify(o)
}
// createRefRequest represents the payload for creating a reference.
type createRefRequest struct {
Ref *string `json:"ref"`
SHA *string `json:"sha"`
}
// updateRefRequest represents the payload for updating a reference.
type updateRefRequest struct {
SHA *string `json:"sha"`
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.
//
// 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, ref)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
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 {
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, ref)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
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)
}
// ReferenceListOptions specifies optional parameters to the
// GitService.ListRefs method.
type ReferenceListOptions struct {
Type string `url:"-"`
ListOptions
}
// ListRefs lists all refs in a repository.
//
// 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)
}
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 rs []*Reference
resp, err := s.client.Do(ctx, req, &rs)
if err != nil {
return nil, resp, err
}
return rs, resp, nil
}
// CreateRef creates a new ref in a repository.
//
// GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference
func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo)
req, err := s.client.NewRequest("POST", u, &createRefRequest{
// back-compat with previous behavior that didn't require 'refs/' prefix
Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")),
SHA: ref.Object.SHA,
})
if err != nil {
return nil, nil, err
}
r := new(Reference)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// UpdateRef updates an existing ref in a repository.
//
// GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference
func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) {
refPath := strings.TrimPrefix(*ref.Ref, "refs/")
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath)
req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{
SHA: ref.Object.SHA,
Force: &force,
})
if err != nil {
return nil, nil, err
}
r := new(Reference)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// DeleteRef deletes a ref from a repository.
//
// 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, ref)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,78 +0,0 @@
// Copyright 2013 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"
)
// Tag represents a tag object.
type Tag struct {
Tag *string `json:"tag,omitempty"`
SHA *string `json:"sha,omitempty"`
URL *string `json:"url,omitempty"`
Message *string `json:"message,omitempty"`
Tagger *CommitAuthor `json:"tagger,omitempty"`
Object *GitObject `json:"object,omitempty"`
Verification *SignatureVerification `json:"verification,omitempty"`
}
// createTagRequest represents the body of a CreateTag request. This is mostly
// identical to Tag with the exception that the object SHA and Type are
// top-level fields, rather than being nested inside a JSON object.
type createTagRequest struct {
Tag *string `json:"tag,omitempty"`
Message *string `json:"message,omitempty"`
Object *string `json:"object,omitempty"`
Type *string `json:"type,omitempty"`
Tagger *CommitAuthor `json:"tagger,omitempty"`
}
// GetTag fetchs a tag from a repo given a SHA.
//
// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag
func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha)
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", mediaTypeGitSigningPreview)
tag := new(Tag)
resp, err := s.client.Do(ctx, req, tag)
return tag, resp, err
}
// CreateTag creates a tag object.
//
// GitHub API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object
func (s *GitService) CreateTag(ctx context.Context, owner string, repo string, tag *Tag) (*Tag, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo)
// convert Tag into a createTagRequest
tagRequest := &createTagRequest{
Tag: tag.Tag,
Message: tag.Message,
Tagger: tag.Tagger,
}
if tag.Object != nil {
tagRequest.Object = tag.Object.SHA
tagRequest.Type = tag.Object.Type
}
req, err := s.client.NewRequest("POST", u, tagRequest)
if err != nil {
return nil, nil, err
}
t := new(Tag)
resp, err := s.client.Do(ctx, req, t)
return t, resp, err
}

View File

@@ -1,93 +0,0 @@
// Copyright 2013 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"
)
// Tree represents a GitHub tree.
type Tree struct {
SHA *string `json:"sha,omitempty"`
Entries []TreeEntry `json:"tree,omitempty"`
}
func (t Tree) String() string {
return Stringify(t)
}
// TreeEntry represents the contents of a tree structure. TreeEntry can
// represent either a blob, a commit (in the case of a submodule), or another
// tree.
type TreeEntry 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"`
}
func (t TreeEntry) String() string {
return Stringify(t)
}
// 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
func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha)
if recursive {
u += "?recursive=1"
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
t := new(Tree)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// createTree represents the body of a CreateTree request.
type createTree struct {
BaseTree string `json:"base_tree,omitempty"`
Entries []TreeEntry `json:"tree"`
}
// CreateTree creates a new tree in a repository. If both a tree and a nested
// path modifying that tree are specified, it will overwrite the contents of
// 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) {
u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo)
body := &createTree{
BaseTree: baseTree,
Entries: entries,
}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
t := new(Tree)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,890 +0,0 @@
// Copyright 2013 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.
//go:generate go run gen-accessors.go
package github
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"sync"
"time"
"github.com/google/go-querystring/query"
)
const (
libraryVersion = "7"
defaultBaseURL = "https://api.github.com/"
uploadBaseURL = "https://uploads.github.com/"
userAgent = "go-github/" + libraryVersion
headerRateLimit = "X-RateLimit-Limit"
headerRateRemaining = "X-RateLimit-Remaining"
headerRateReset = "X-RateLimit-Reset"
headerOTP = "X-GitHub-OTP"
mediaTypeV3 = "application/vnd.github.v3+json"
defaultMediaType = "application/octet-stream"
mediaTypeV3SHA = "application/vnd.github.v3.sha"
mediaTypeV3Diff = "application/vnd.github.v3.diff"
mediaTypeV3Patch = "application/vnd.github.v3.patch"
mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json"
// Media Type values to access preview APIs
// https://developer.github.com/changes/2015-03-09-licenses-api/
mediaTypeLicensesPreview = "application/vnd.github.drax-preview+json"
// https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/
mediaTypeStarringPreview = "application/vnd.github.v3.star+json"
// https://developer.github.com/changes/2015-11-11-protected-branches-api/
mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json"
// https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/
mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json"
// https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/
mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-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-04-01-squash-api-preview/
// https://developer.github.com/changes/2016-09-26-pull-request-merge-api-update/
mediaTypeSquashPreview = "application/vnd.github.polaris-preview+json"
// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/
mediaTypeGitSigningPreview = "application/vnd.github.cryptographer-preview+json"
// https://developer.github.com/changes/2016-05-23-timeline-preview-api/
mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
// https://developer.github.com/changes/2016-06-14-repository-invitations/
mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-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"
// https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/
mediaTypeIntegrationPreview = "application/vnd.github.machine-man-preview+json"
// https://developer.github.com/changes/2017-01-05-commit-search-api/
mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json"
// https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/
mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json"
// https://developer.github.com/changes/2017-02-09-community-health/
mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json"
)
// A Client manages communication with the GitHub API.
type Client struct {
clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func.
client *http.Client // HTTP client used to communicate with the API.
// Base URL for API requests. Defaults to the public GitHub API, but can be
// set to a domain endpoint to use with GitHub Enterprise. BaseURL should
// always be specified with a trailing slash.
BaseURL *url.URL
// Base URL for uploading files.
UploadURL *url.URL
// User agent used when communicating with the GitHub API.
UserAgent string
rateMu sync.Mutex
rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls.
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.
Activity *ActivityService
Admin *AdminService
Authorizations *AuthorizationsService
Gists *GistsService
Git *GitService
Gitignores *GitignoresService
Integrations *IntegrationsService
Issues *IssuesService
Organizations *OrganizationsService
Projects *ProjectsService
PullRequests *PullRequestsService
Repositories *RepositoriesService
Search *SearchService
Users *UsersService
Licenses *LicensesService
Migrations *MigrationService
Reactions *ReactionsService
}
type service struct {
client *Client
}
// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
// For paginated result sets, page of results to retrieve.
Page int `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"`
}
// RawType represents type of raw format of a request instead of JSON.
type RawType uint8
const (
// Diff format.
Diff RawType = 1 + iota
// Patch format.
Patch
)
// RawOptions specifies parameters when user wants to get raw format of
// a response instead of JSON.
type RawOptions struct {
Type RawType
}
// addOptions adds the parameters in opt as URL query parameters to s. opt
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
v := reflect.ValueOf(opt)
if v.Kind() == reflect.Ptr && v.IsNil() {
return s, nil
}
u, err := url.Parse(s)
if err != nil {
return s, err
}
qs, err := query.Values(opt)
if err != nil {
return s, err
}
u.RawQuery = qs.Encode()
return u.String(), nil
}
// NewClient returns a new GitHub API client. If a nil httpClient is
// provided, http.DefaultClient 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
}
baseURL, _ := url.Parse(defaultBaseURL)
uploadURL, _ := url.Parse(uploadBaseURL)
c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
c.common.client = c
c.Activity = (*ActivityService)(&c.common)
c.Admin = (*AdminService)(&c.common)
c.Authorizations = (*AuthorizationsService)(&c.common)
c.Gists = (*GistsService)(&c.common)
c.Git = (*GitService)(&c.common)
c.Gitignores = (*GitignoresService)(&c.common)
c.Integrations = (*IntegrationsService)(&c.common)
c.Issues = (*IssuesService)(&c.common)
c.Licenses = (*LicensesService)(&c.common)
c.Migrations = (*MigrationService)(&c.common)
c.Organizations = (*OrganizationsService)(&c.common)
c.Projects = (*ProjectsService)(&c.common)
c.PullRequests = (*PullRequestsService)(&c.common)
c.Reactions = (*ReactionsService)(&c.common)
c.Repositories = (*RepositoriesService)(&c.common)
c.Search = (*SearchService)(&c.common)
c.Users = (*UsersService)(&c.common)
return c
}
// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash. If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u := c.BaseURL.ResolveReference(rel)
var buf io.ReadWriter
if body != nil {
buf = new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(body)
if err != nil {
return nil, err
}
}
req, err := http.NewRequest(method, u.String(), buf)
if err != nil {
return nil, err
}
if body != nil {
req.Header.Set("Content-Type", "application/json")
}
req.Header.Set("Accept", mediaTypeV3)
if c.UserAgent != "" {
req.Header.Set("User-Agent", c.UserAgent)
}
return req, nil
}
// NewUploadRequest creates an upload request. A relative URL can be provided in
// urlStr, in which case it is resolved relative to the UploadURL of the Client.
// Relative URLs should always be specified without a preceding slash.
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) {
rel, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
u := c.UploadURL.ResolveReference(rel)
req, err := http.NewRequest("POST", u.String(), reader)
if err != nil {
return nil, err
}
req.ContentLength = size
if mediaType == "" {
mediaType = defaultMediaType
}
req.Header.Set("Content-Type", mediaType)
req.Header.Set("Accept", mediaTypeV3)
req.Header.Set("User-Agent", c.UserAgent)
return req, nil
}
// Response is a GitHub API response. This wraps the standard http.Response
// returned from GitHub and provides convenient access to things like
// pagination links.
type Response struct {
*http.Response
// These fields provide the page values for paginating through a set of
// 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.
NextPage int
PrevPage int
FirstPage int
LastPage int
Rate
}
// newResponse creates a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
response := &Response{Response: r}
response.populatePageValues()
response.Rate = parseRate(r)
return response
}
// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Response.
func (r *Response) populatePageValues() {
if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
for _, link := range strings.Split(links[0], ",") {
segments := strings.Split(strings.TrimSpace(link), ";")
// link must at least have href and rel
if len(segments) < 2 {
continue
}
// ensure href is properly formatted
if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
continue
}
// try to pull out page parameter
url, err := url.Parse(segments[0][1 : len(segments[0])-1])
if err != nil {
continue
}
page := url.Query().Get("page")
if page == "" {
continue
}
for _, segment := range segments[1:] {
switch strings.TrimSpace(segment) {
case `rel="next"`:
r.NextPage, _ = strconv.Atoi(page)
case `rel="prev"`:
r.PrevPage, _ = strconv.Atoi(page)
case `rel="first"`:
r.FirstPage, _ = strconv.Atoi(page)
case `rel="last"`:
r.LastPage, _ = strconv.Atoi(page)
}
}
}
}
}
// parseRate parses the rate related headers.
func parseRate(r *http.Response) Rate {
var rate Rate
if limit := r.Header.Get(headerRateLimit); limit != "" {
rate.Limit, _ = strconv.Atoi(limit)
}
if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
rate.Remaining, _ = strconv.Atoi(remaining)
}
if reset := r.Header.Get(headerRateReset); reset != "" {
if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
rate.Reset = Timestamp{time.Unix(v, 0)}
}
}
return rate
}
// Do sends an API request and returns the API response. The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred. If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// 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,
// ctx.Err() will be returned.
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
ctx, req = withContext(ctx, req)
rateLimitCategory := category(req.URL.Path)
// If we've hit rate limit, don't make further requests before Reset time.
if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil {
return &Response{
Response: err.Response,
Rate: err.Rate,
}, err
}
resp, err := c.client.Do(req)
if err != nil {
// If we got an error, and the context has been canceled,
// the context's error is probably more useful.
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
// If the error type is *url.Error, sanitize its URL before returning.
if e, ok := err.(*url.Error); ok {
if url, err := url.Parse(e.URL); err == nil {
e.URL = sanitizeURL(url).String()
return nil, e
}
}
return nil, err
}
defer func() {
// Drain up to 512 bytes and close the body to let the Transport reuse the connection
io.CopyN(ioutil.Discard, resp.Body, 512)
resp.Body.Close()
}()
response := newResponse(resp)
c.rateMu.Lock()
c.rateLimits[rateLimitCategory] = response.Rate
c.rateMu.Unlock()
err = CheckResponse(resp)
if err != nil {
// even though there was an error, we still return the response
// in case the caller wants to inspect it further
return response, err
}
if v != nil {
if w, ok := v.(io.Writer); ok {
io.Copy(w, resp.Body)
} else {
err = json.NewDecoder(resp.Body).Decode(v)
if err == io.EOF {
err = nil // ignore EOF errors caused by empty response body
}
}
}
return response, err
}
// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from
// current client state in order to quickly check if *RateLimitError can be immediately returned
// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily.
// Otherwise it returns nil, and Client.Do should proceed normally.
func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError {
c.rateMu.Lock()
rate := c.rateLimits[rateLimitCategory]
c.rateMu.Unlock()
if !rate.Reset.Time.IsZero() && rate.Remaining == 0 && time.Now().Before(rate.Reset.Time) {
// Create a fake response.
resp := &http.Response{
Status: http.StatusText(http.StatusForbidden),
StatusCode: http.StatusForbidden,
Request: req,
Header: make(http.Header),
Body: ioutil.NopCloser(strings.NewReader("")),
}
return &RateLimitError{
Rate: rate,
Response: resp,
Message: fmt.Sprintf("API rate limit of %v still exceeded until %v, not making remote request.", rate.Limit, rate.Reset.Time),
}
}
return nil
}
/*
An ErrorResponse reports one or more errors caused by an API request.
GitHub API docs: https://developer.github.com/v3/#client-errors
*/
type ErrorResponse struct {
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // error message
Errors []Error `json:"errors"` // more detail on individual errors
// Block is only populated on certain types of errors such as code 451.
// See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/
// for more information.
Block *struct {
Reason string `json:"reason,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
} `json:"block,omitempty"`
// Most errors will also include a documentation_url field pointing
// to some content that might help you resolve the error, see
// https://developer.github.com/v3/#client-errors
DocumentationURL string `json:"documentation_url,omitempty"`
}
func (r *ErrorResponse) Error() string {
return fmt.Sprintf("%v %v: %d %v %+v",
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
r.Response.StatusCode, r.Message, r.Errors)
}
// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user
// that has two-factor authentication enabled. The request can be reattempted
// by providing a one-time password in the request.
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 ".
type RateLimitError struct {
Rate Rate // Rate specifies last known rate limit for the client
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // error message
}
func (r *RateLimitError) Error() string {
return fmt.Sprintf("%v %v: %d %v; rate reset in %v",
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
r.Response.StatusCode, r.Message, r.Rate.Reset.Time.Sub(time.Now()))
}
// AcceptedError occurs when GitHub returns 202 Accepted response with an
// empty body, which means a job was scheduled on the GitHub side to process
// the information needed and cache it.
// Technically, 202 Accepted is not a real error, it's just used to
// indicate that results are not ready yet, but should be available soon.
// The request can be repeated after some time.
type AcceptedError struct{}
func (*AcceptedError) Error() string {
return "job scheduled on GitHub side; try again later"
}
// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the
// "documentation_url" field value equal to "https://developer.github.com/v3#abuse-rate-limits".
type AbuseRateLimitError struct {
Response *http.Response // HTTP response that caused this error
Message string `json:"message"` // error message
// RetryAfter is provided with some abuse rate limit errors. If present,
// it is the amount of time that the client should wait before retrying.
// Otherwise, the client should try again later (after an unspecified amount of time).
RetryAfter *time.Duration
}
func (r *AbuseRateLimitError) Error() string {
return fmt.Sprintf("%v %v: %d %v",
r.Response.Request.Method, sanitizeURL(r.Response.Request.URL),
r.Response.StatusCode, r.Message)
}
// sanitizeURL redacts the client_secret parameter from the URL which may be
// exposed to the user.
func sanitizeURL(uri *url.URL) *url.URL {
if uri == nil {
return nil
}
params := uri.Query()
if len(params.Get("client_secret")) > 0 {
params.Set("client_secret", "REDACTED")
uri.RawQuery = params.Encode()
}
return uri
}
/*
An Error reports more details on an individual error in an ErrorResponse.
These are the possible validation error codes:
missing:
resource does not exist
missing_field:
a required field on a resource has not been set
invalid:
the formatting of a field is invalid
already_exists:
another resource has the same valid as this field
custom:
some resources return this (e.g. github.User.CreateKey()), additional
information is set in the Message field of the Error
GitHub API docs: https://developer.github.com/v3/#client-errors
*/
type Error struct {
Resource string `json:"resource"` // resource on which the error occurred
Field string `json:"field"` // field on which the error occurred
Code string `json:"code"` // validation error code
Message string `json:"message"` // Message describing the error. Errors with Code == "custom" will always have this set.
}
func (e *Error) Error() string {
return fmt.Sprintf("%v error caused by %v field on %v resource",
e.Code, e.Field, e.Resource)
}
// 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.
//
// The error type will be *RateLimitError for rate limit exceeded errors,
// *AcceptedError for 202 Accepted status codes,
// and *TwoFactorAuthError for two-factor authentication errors.
func CheckResponse(r *http.Response) error {
if r.StatusCode == http.StatusAccepted {
return &AcceptedError{}
}
if c := r.StatusCode; 200 <= c && c <= 299 {
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := ioutil.ReadAll(r.Body)
if err == nil && data != nil {
json.Unmarshal(data, errorResponse)
}
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 "):
return &RateLimitError{
Rate: parseRate(r),
Response: errorResponse.Response,
Message: errorResponse.Message,
}
case r.StatusCode == http.StatusForbidden && errorResponse.DocumentationURL == "https://developer.github.com/v3#abuse-rate-limits":
abuseRateLimitError := &AbuseRateLimitError{
Response: errorResponse.Response,
Message: errorResponse.Message,
}
if v := r.Header["Retry-After"]; len(v) > 0 {
// According to GitHub support, the "Retry-After" header value will be
// an integer which represents the number of seconds that one should
// wait before resuming making requests.
retryAfterSeconds, _ := strconv.ParseInt(v[0], 10, 64) // Error handling is noop.
retryAfter := time.Duration(retryAfterSeconds) * time.Second
abuseRateLimitError.RetryAfter = &retryAfter
}
return abuseRateLimitError
default:
return errorResponse
}
}
// parseBoolResponse determines the boolean result from a GitHub API response.
// Several GitHub API methods return boolean responses indicated by the HTTP
// status code in the response (true indicated by a 204, false indicated by a
// 404). This helper function will determine that result and hide the 404
// error if present. Any other error will be returned through as-is.
func parseBoolResponse(err error) (bool, error) {
if err == nil {
return true, nil
}
if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound {
// Simply false. In this one case, we do not pass the error through.
return false, nil
}
// some other real error occurred
return false, err
}
// Rate represents the rate limit for the current client.
type Rate struct {
// The number of requests per hour the client is currently limited to.
Limit int `json:"limit"`
// The number of remaining requests the client can make this hour.
Remaining int `json:"remaining"`
// The time at which the current rate limit will reset.
Reset Timestamp `json:"reset"`
}
func (r Rate) String() string {
return Stringify(r)
}
// RateLimits represents the rate limits for the current client.
type RateLimits struct {
// The rate limit for non-search API requests. Unauthenticated
// requests are limited to 60 per hour. Authenticated requests are
// limited to 5,000 per hour.
//
// GitHub API docs: https://developer.github.com/v3/#rate-limiting
Core *Rate `json:"core"`
// The rate limit for search API requests. Unauthenticated requests
// are limited to 10 requests per minutes. Authenticated requests are
// limited to 30 per minute.
//
// GitHub API docs: https://developer.github.com/v3/search/#rate-limit
Search *Rate `json:"search"`
}
func (r RateLimits) String() string {
return Stringify(r)
}
type rateLimitCategory uint8
const (
coreCategory rateLimitCategory = iota
searchCategory
categories // An array of this length will be able to contain all rate limit categories.
)
// category returns the rate limit category of the endpoint, determined by Request.URL.Path.
func category(path string) rateLimitCategory {
switch {
default:
return coreCategory
case strings.HasPrefix(path, "/search/"):
return searchCategory
}
}
// RateLimits returns the rate limits for the current client.
func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error) {
req, err := c.NewRequest("GET", "rate_limit", nil)
if err != nil {
return nil, nil, err
}
response := new(struct {
Resources *RateLimits `json:"resources"`
})
resp, err := c.Do(ctx, req, response)
if err != nil {
return nil, nil, err
}
if response.Resources != nil {
c.rateMu.Lock()
if response.Resources.Core != nil {
c.rateLimits[coreCategory] = *response.Resources.Core
}
if response.Resources.Search != nil {
c.rateLimits[searchCategory] = *response.Resources.Search
}
c.rateMu.Unlock()
}
return response.Resources, resp, nil
}
/*
UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls
that need to use a higher rate limit associated with your OAuth application.
t := &github.UnauthenticatedRateLimitedTransport{
ClientID: "your app's client ID",
ClientSecret: "your app's client secret",
}
client := github.NewClient(t.Client())
This will append the querystring params client_id=xxx&client_secret=yyy to all
requests.
See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for
more information.
*/
type UnauthenticatedRateLimitedTransport struct {
// ClientID is the GitHub OAuth client ID of the current application, which
// can be found by selecting its entry in the list at
// https://github.com/settings/applications.
ClientID string
// ClientSecret is the GitHub OAuth client secret of the current
// application.
ClientSecret string
// Transport is the underlying HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
Transport http.RoundTripper
}
// RoundTrip implements the RoundTripper interface.
func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) {
if t.ClientID == "" {
return nil, errors.New("t.ClientID is empty")
}
if t.ClientSecret == "" {
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.
req = cloneRequest(req)
q := req.URL.Query()
q.Set("client_id", t.ClientID)
q.Set("client_secret", t.ClientSecret)
req.URL.RawQuery = q.Encode()
// Make the HTTP request.
return t.transport().RoundTrip(req)
}
// Client returns an *http.Client that makes requests which are subject to the
// rate limit of your OAuth application.
func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
// BasicAuthTransport is an http.RoundTripper that authenticates all requests
// using HTTP Basic Authentication with the provided username and password. It
// additionally supports users who have two-factor authentication enabled on
// their GitHub account.
type BasicAuthTransport struct {
Username string // GitHub username
Password string // GitHub password
OTP string // one-time password for users with two-factor auth enabled
// Transport is the underlying HTTP transport to use when making requests.
// It will default to http.DefaultTransport if nil.
Transport http.RoundTripper
}
// RoundTrip implements the RoundTripper interface.
func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req = cloneRequest(req) // per RoundTrip contract
req.SetBasicAuth(t.Username, t.Password)
if t.OTP != "" {
req.Header.Set(headerOTP, t.OTP)
}
return t.transport().RoundTrip(req)
}
// Client returns an *http.Client that makes requests that are authenticated
// using HTTP Basic Authentication.
func (t *BasicAuthTransport) Client() *http.Client {
return &http.Client{Transport: t}
}
func (t *BasicAuthTransport) transport() http.RoundTripper {
if t.Transport != nil {
return t.Transport
}
return http.DefaultTransport
}
// cloneRequest returns a clone of the provided *http.Request. The clone is a
// shallow copy of the struct and its Header map.
func cloneRequest(r *http.Request) *http.Request {
// shallow copy of the struct
r2 := new(http.Request)
*r2 = *r
// deep copy of the Header
r2.Header = make(http.Header, len(r.Header))
for k, s := range r.Header {
r2.Header[k] = append([]string(nil), s...)
}
return r2
}
// Bool is a helper routine that allocates a new bool value
// to store v and returns a pointer to it.
func Bool(v bool) *bool { return &v }
// Int is a helper routine that allocates a new int value
// to store v and returns a pointer to it.
func Int(v int) *int { return &v }
// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string { return &v }

View File

@@ -1,64 +0,0 @@
// Copyright 2013 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"
)
// GitignoresService provides access to the gitignore related functions in the
// GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/gitignore/
type GitignoresService service
// Gitignore represents a .gitignore file as returned by the GitHub API.
type Gitignore struct {
Name *string `json:"name,omitempty"`
Source *string `json:"source,omitempty"`
}
func (g Gitignore) String() string {
return Stringify(g)
}
// 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) {
req, err := s.client.NewRequest("GET", "gitignore/templates", nil)
if err != nil {
return nil, nil, err
}
var availableTemplates []string
resp, err := s.client.Do(ctx, req, &availableTemplates)
if err != nil {
return nil, resp, err
}
return availableTemplates, resp, nil
}
// 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) {
u := fmt.Sprintf("gitignore/templates/%v", name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
gitignore := new(Gitignore)
resp, err := s.client.Do(ctx, req, gitignore)
if err != nil {
return nil, resp, err
}
return gitignore, resp, nil
}

View File

@@ -1,40 +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"
// IntegrationsService provides access to the installation related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/integrations/
type IntegrationsService service
// ListInstallations lists the installations that the current integration has.
//
// GitHub API docs: https://developer.github.com/v3/integrations/#find-installations
func (s *IntegrationsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) {
u, err := addOptions("integration/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
}

View File

@@ -1,49 +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"
// Installation represents a GitHub integration installation.
type Installation struct {
ID *int `json:"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"`
}
func (i Installation) String() string {
return Stringify(i)
}
// ListRepos lists the repositories that the current installation has access to.
//
// GitHub API docs: https://developer.github.com/v3/integrations/installations/#list-repositories
func (s *IntegrationsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) {
u, err := addOptions("installation/repositories", 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 r struct {
Repositories []*Repository `json:"repositories"`
}
resp, err := s.client.Do(ctx, req, &r)
if err != nil {
return nil, resp, err
}
return r.Repositories, resp, nil
}

View File

@@ -1,307 +0,0 @@
// Copyright 2013 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"
)
// IssuesService handles communication with the issue related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/issues/
type IssuesService service
// Issue represents a GitHub issue on a repository.
//
// Note: As far as the GitHub API is concerned, every pull request is an issue,
// but not every issue is a pull request. Some endpoints, events, and webhooks
// may also return pull requests via this struct. If PullRequestLinks is nil,
// this is an issue, and if PullRequestLinks is not nil, this is a pull request.
type Issue struct {
ID *int `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"`
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"`
// 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"`
}
func (i Issue) String() string {
return Stringify(i)
}
// IssueRequest represents a request to create/edit an issue.
// It is separate from Issue above because otherwise Labels
// and Assignee fail to serialize to the correct JSON.
type IssueRequest struct {
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
Labels *[]string `json:"labels,omitempty"`
Assignee *string `json:"assignee,omitempty"`
State *string `json:"state,omitempty"`
Milestone *int `json:"milestone,omitempty"`
Assignees *[]string `json:"assignees,omitempty"`
}
// IssueListOptions specifies the optional parameters to the IssuesService.List
// and IssuesService.ListByOrg methods.
type IssueListOptions struct {
// Filter specifies which issues to list. Possible values are: assigned,
// created, mentioned, subscribed, all. Default is "assigned".
Filter string `url:"filter,omitempty"`
// State filters issues based on their state. Possible values are: open,
// closed, all. Default is "open".
State string `url:"state,omitempty"`
// Labels filters issues based on their label.
Labels []string `url:"labels,comma,omitempty"`
// Sort specifies how to sort issues. Possible values are: created, updated,
// and comments. Default value is "created".
Sort string `url:"sort,omitempty"`
// Direction in which to sort issues. Possible values are: asc, desc.
// Default is "desc".
Direction string `url:"direction,omitempty"`
// Since filters issues by time.
Since time.Time `url:"since,omitempty"`
ListOptions
}
// PullRequestLinks object is added to the Issue object when it's an issue included
// in the IssueCommentEvent webhook payload, if the webhooks is fired by a comment on a PR
type PullRequestLinks struct {
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
DiffURL *string `json:"diff_url,omitempty"`
PatchURL *string `json:"patch_url,omitempty"`
}
// List the issues for the authenticated user. If all is true, list issues
// across all the user's visible repositories including owned, member, and
// 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) {
var u string
if all {
u = "issues"
} else {
u = "user/issues"
}
return s.listIssues(ctx, u, opt)
}
// 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) {
u := fmt.Sprintf("orgs/%v/issues", org)
return s.listIssues(ctx, u, opt)
}
func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) {
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", mediaTypeReactionsPreview)
var issues []*Issue
resp, err := s.client.Do(ctx, req, &issues)
if err != nil {
return nil, resp, err
}
return issues, resp, nil
}
// IssueListByRepoOptions specifies the optional parameters to the
// IssuesService.ListByRepo method.
type IssueListByRepoOptions struct {
// Milestone limits issues for the specified milestone. Possible values are
// a milestone number, "none" for issues with no milestone, "*" for issues
// with any milestone.
Milestone string `url:"milestone,omitempty"`
// State filters issues based on their state. Possible values are: open,
// closed, all. Default is "open".
State string `url:"state,omitempty"`
// Assignee filters issues based on their assignee. Possible values are a
// user name, "none" for issues that are not assigned, "*" for issues with
// any assigned user.
Assignee string `url:"assignee,omitempty"`
// Creator filters issues based on their creator.
Creator string `url:"creator,omitempty"`
// Mentioned filters issues to those mentioned a specific user.
Mentioned string `url:"mentioned,omitempty"`
// Labels filters issues based on their label.
Labels []string `url:"labels,omitempty,comma"`
// Sort specifies how to sort issues. Possible values are: created, updated,
// and comments. Default value is "created".
Sort string `url:"sort,omitempty"`
// Direction in which to sort issues. Possible values are: asc, desc.
// Default is "desc".
Direction string `url:"direction,omitempty"`
// Since filters issues by time.
Since time.Time `url:"since,omitempty"`
ListOptions
}
// 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) {
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
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", mediaTypeReactionsPreview)
var issues []*Issue
resp, err := s.client.Do(ctx, req, &issues)
if err != nil {
return nil, resp, err
}
return issues, resp, nil
}
// Get a single issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-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)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
issue := new(Issue)
resp, err := s.client.Do(ctx, req, issue)
if err != nil {
return nil, resp, err
}
return issue, resp, nil
}
// Create a new issue on the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue
func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo)
req, err := s.client.NewRequest("POST", u, issue)
if err != nil {
return nil, nil, err
}
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}
// Edit an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/#edit-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)
if err != nil {
return nil, nil, err
}
i := new(Issue)
resp, err := s.client.Do(ctx, req, i)
if err != nil {
return nil, resp, err
}
return i, resp, nil
}
// 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) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Unlock an issue's conversation.
//
// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue
func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,85 +0,0 @@
// Copyright 2013 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"
)
// ListAssignees fetches all available assignees (owners and collaborators) to
// 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) {
u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo)
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 assignees []*User
resp, err := s.client.Do(ctx, req, &assignees)
if err != nil {
return nil, resp, err
}
return assignees, resp, nil
}
// IsAssignee checks if a user is an assignee for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee
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)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
assignee, err := parseBoolResponse(err)
return assignee, resp, err
}
// AddAssignees adds the provided GitHub users as assignees to the issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue
func (s *IssuesService) AddAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) {
users := &struct {
Assignees []string `json:"assignees,omitempty"`
}{Assignees: assignees}
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number)
req, err := s.client.NewRequest("POST", u, users)
if err != nil {
return nil, nil, err
}
issue := &Issue{}
resp, err := s.client.Do(ctx, req, issue)
return issue, resp, err
}
// RemoveAssignees removes the provided GitHub users as assignees from the issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue
func (s *IssuesService) RemoveAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) {
users := &struct {
Assignees []string `json:"assignees,omitempty"`
}{Assignees: assignees}
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, users)
if err != nil {
return nil, nil, err
}
issue := &Issue{}
resp, err := s.client.Do(ctx, req, issue)
return issue, resp, err
}

View File

@@ -1,148 +0,0 @@
// Copyright 2013 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"
)
// IssueComment represents a comment left on an issue.
type IssueComment struct {
ID *int `json:"id,omitempty"`
Body *string `json:"body,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
}
func (i IssueComment) String() string {
return Stringify(i)
}
// IssueListCommentsOptions specifies the optional parameters to the
// IssuesService.ListComments method.
type IssueListCommentsOptions struct {
// Sort specifies how to sort comments. Possible values are: created, updated.
Sort string `url:"sort,omitempty"`
// Direction in which to sort comments. Possible values are: asc, desc.
Direction string `url:"direction,omitempty"`
// Since filters comments by time.
Since time.Time `url:"since,omitempty"`
ListOptions
}
// ListComments lists all comments on the specified issue. Specifying an issue
// number of 0 will return all comments on all issues for the repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue
func (s *IssuesService) ListComments(ctx context.Context, owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) {
var u string
if number == 0 {
u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo)
} else {
u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number)
}
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", mediaTypeReactionsPreview)
var comments []*IssueComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// GetComment fetches the specified issue comment.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment
func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, id int) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
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", mediaTypeReactionsPreview)
comment := new(IssueComment)
resp, err := s.client.Do(ctx, req, comment)
if err != nil {
return nil, resp, err
}
return comment, resp, nil
}
// CreateComment creates a new comment on the specified issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#create-a-comment
func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number)
req, err := s.client.NewRequest("POST", u, comment)
if err != nil {
return nil, nil, err
}
c := new(IssueComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// EditComment updates an issue comment.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment
func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
}
c := new(IssueComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// DeleteComment deletes an issue comment.
//
// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment
func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, id int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,151 +0,0 @@
// Copyright 2014 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"
)
// IssueEvent represents an event that occurred around an Issue or Pull Request.
type IssueEvent struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
// The User that generated this event.
Actor *User `json:"actor,omitempty"`
// Event identifies the actual type of Event that occurred. Possible
// values are:
//
// closed
// The Actor closed the issue.
// If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit.
//
// merged
// The Actor merged into master a branch containing a commit mentioning the issue.
// CommitID holds the SHA1 of the merge commit.
//
// referenced
// The Actor committed to master a commit mentioning the issue in its commit message.
// CommitID holds the SHA1 of the commit.
//
// reopened, locked, unlocked
// The Actor did that to the issue.
//
// renamed
// The Actor changed the issue title from Rename.From to Rename.To.
//
// mentioned
// Someone unspecified @mentioned the Actor [sic] in an issue comment body.
//
// assigned, unassigned
// The Assigner assigned the issue to or removed the assignment from the Assignee.
//
// labeled, unlabeled
// The Actor added or removed the Label from the issue.
//
// milestoned, demilestoned
// The Actor added or removed the issue from the Milestone.
//
// subscribed, unsubscribed
// The Actor subscribed to or unsubscribed from notifications for an issue.
//
// head_ref_deleted, head_ref_restored
// The pull requests branch was deleted or restored.
//
Event *string `json:"event,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
Issue *Issue `json:"issue,omitempty"`
// Only present on certain events; see above.
Assignee *User `json:"assignee,omitempty"`
Assigner *User `json:"assigner,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
Label *Label `json:"label,omitempty"`
Rename *Rename `json:"rename,omitempty"`
}
// ListIssueEvents lists events for the specified issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue
func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number)
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 events []*IssueEvent
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// ListRepositoryEvents lists events for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository
func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo)
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 events []*IssueEvent
resp, err := s.client.Do(ctx, req, &events)
if err != nil {
return nil, resp, err
}
return events, resp, nil
}
// GetEvent returns the specified issue event.
//
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event
func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int) (*IssueEvent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
event := new(IssueEvent)
resp, err := s.client.Do(ctx, req, event)
if err != nil {
return nil, resp, err
}
return event, resp, nil
}
// Rename contains details for 'renamed' events.
type Rename struct {
From *string `json:"from,omitempty"`
To *string `json:"to,omitempty"`
}
func (r Rename) String() string {
return Stringify(r)
}

View File

@@ -1,226 +0,0 @@
// Copyright 2013 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"
)
// Label represents a GitHub label on an Issue
type Label struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Name *string `json:"name,omitempty"`
Color *string `json:"color,omitempty"`
}
func (l Label) String() string {
return Stringify(l)
}
// ListLabels lists all labels for a repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository
func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo)
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 labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {
return nil, resp, err
}
return labels, resp, nil
}
// GetLabel gets a single label.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label
func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, name string) (*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
label := new(Label)
resp, err := s.client.Do(ctx, req, label)
if err != nil {
return nil, resp, err
}
return label, resp, nil
}
// CreateLabel creates a new label on the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#create-a-label
func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo string, label *Label) (*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo)
req, err := s.client.NewRequest("POST", u, label)
if err != nil {
return nil, nil, err
}
l := new(Label)
resp, err := s.client.Do(ctx, req, l)
if err != nil {
return nil, resp, err
}
return l, resp, nil
}
// EditLabel edits a label.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#update-a-label
func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string, name string, label *Label) (*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name)
req, err := s.client.NewRequest("PATCH", u, label)
if err != nil {
return nil, nil, err
}
l := new(Label)
resp, err := s.client.Do(ctx, req, l)
if err != nil {
return nil, resp, err
}
return l, resp, nil
}
// DeleteLabel deletes a label.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#delete-a-label
func (s *IssuesService) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/labels/%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)
}
// ListLabelsByIssue lists all labels for an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue
func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number)
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 labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {
return nil, resp, err
}
return labels, resp, nil
}
// AddLabelsToIssue adds labels to an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue
func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number)
req, err := s.client.NewRequest("POST", u, labels)
if err != nil {
return nil, nil, err
}
var l []*Label
resp, err := s.client.Do(ctx, req, &l)
if err != nil {
return nil, resp, err
}
return l, resp, nil
}
// RemoveLabelForIssue removes a label for an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue
func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, repo string, number int, label string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ReplaceLabelsForIssue replaces all labels for an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue
func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number)
req, err := s.client.NewRequest("PUT", u, labels)
if err != nil {
return nil, nil, err
}
var l []*Label
resp, err := s.client.Do(ctx, req, &l)
if err != nil {
return nil, resp, err
}
return l, resp, nil
}
// RemoveLabelsForIssue removes all labels for an issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue
func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListLabelsForMilestone lists labels for every issue in a milestone.
//
// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone
func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number)
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 labels []*Label
resp, err := s.client.Do(ctx, req, &labels)
if err != nil {
return nil, resp, err
}
return labels, resp, nil
}

View File

@@ -1,147 +0,0 @@
// Copyright 2014 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"
)
// Milestone represents a GitHub repository milestone.
type Milestone struct {
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
LabelsURL *string `json:"labels_url,omitempty"`
ID *int `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Title *string `json:"title,omitempty"`
Description *string `json:"description,omitempty"`
Creator *User `json:"creator,omitempty"`
OpenIssues *int `json:"open_issues,omitempty"`
ClosedIssues *int `json:"closed_issues,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
DueOn *time.Time `json:"due_on,omitempty"`
}
func (m Milestone) String() string {
return Stringify(m)
}
// MilestoneListOptions specifies the optional parameters to the
// IssuesService.ListMilestones method.
type MilestoneListOptions struct {
// State filters milestones based on their state. Possible values are:
// open, closed, all. Default is "open".
State string `url:"state,omitempty"`
// Sort specifies how to sort milestones. Possible values are: due_on, completeness.
// Default value is "due_on".
Sort string `url:"sort,omitempty"`
// Direction in which to sort milestones. Possible values are: asc, desc.
// Default is "asc".
Direction string `url:"direction,omitempty"`
ListOptions
}
// ListMilestones lists all milestones for a repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository
func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo)
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 milestones []*Milestone
resp, err := s.client.Do(ctx, req, &milestones)
if err != nil {
return nil, resp, err
}
return milestones, resp, nil
}
// GetMilestone gets a single milestone.
//
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone
func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo string, number int) (*Milestone, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
milestone := new(Milestone)
resp, err := s.client.Do(ctx, req, milestone)
if err != nil {
return nil, resp, err
}
return milestone, resp, nil
}
// CreateMilestone creates a new milestone on the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone
func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo)
req, err := s.client.NewRequest("POST", u, milestone)
if err != nil {
return nil, nil, err
}
m := new(Milestone)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// EditMilestone edits a milestone.
//
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone
func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number)
req, err := s.client.NewRequest("PATCH", u, milestone)
if err != nil {
return nil, nil, err
}
m := new(Milestone)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// DeleteMilestone deletes a milestone.
//
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone
func (s *IssuesService) DeleteMilestone(ctx context.Context, owner string, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,149 +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"
)
// Timeline represents an event that occurred around an Issue or Pull Request.
//
// It is similar to an IssueEvent but may contain more information.
// GitHub API docs: https://developer.github.com/v3/issues/timeline/
type Timeline struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
CommitURL *string `json:"commit_url,omitempty"`
// The User object that generated the event.
Actor *User `json:"actor,omitempty"`
// Event identifies the actual type of Event that occurred. Possible values
// are:
//
// assigned
// The issue was assigned to the assignee.
//
// closed
// The issue was closed by the actor. When the commit_id is present, it
// identifies the commit that closed the issue using "closes / fixes #NN"
// syntax.
//
// commented
// A comment was added to the issue.
//
// committed
// A commit was added to the pull request's 'HEAD' branch. Only provided
// for pull requests.
//
// cross-referenced
// The issue was referenced from another issue. The 'source' attribute
// contains the 'id', 'actor', and 'url' of the reference's source.
//
// demilestoned
// The issue was removed from a milestone.
//
// head_ref_deleted
// The pull request's branch was deleted.
//
// head_ref_restored
// The pull request's branch was restored.
//
// labeled
// A label was added to the issue.
//
// locked
// The issue was locked by the actor.
//
// mentioned
// The actor was @mentioned in an issue body.
//
// merged
// The issue was merged by the actor. The 'commit_id' attribute is the
// SHA1 of the HEAD commit that was merged.
//
// milestoned
// The issue was added to a milestone.
//
// referenced
// The issue was referenced from a commit message. The 'commit_id'
// attribute is the commit SHA1 of where that happened.
//
// renamed
// The issue title was changed.
//
// reopened
// The issue was reopened by the actor.
//
// subscribed
// The actor subscribed to receive notifications for an issue.
//
// unassigned
// The assignee was unassigned from the issue.
//
// unlabeled
// A label was removed from the issue.
//
// unlocked
// The issue was unlocked by the actor.
//
// unsubscribed
// The actor unsubscribed to stop receiving notifications for an issue.
//
Event *string `json:"event,omitempty"`
// The string SHA of a commit that referenced this Issue or Pull Request.
CommitID *string `json:"commit_id,omitempty"`
// The timestamp indicating when the event occurred.
CreatedAt *time.Time `json:"created_at,omitempty"`
// The Label object including `name` and `color` attributes. Only provided for
// 'labeled' and 'unlabeled' events.
Label *Label `json:"label,omitempty"`
// The User object which was assigned to (or unassigned from) this Issue or
// Pull Request. Only provided for 'assigned' and 'unassigned' events.
Assignee *User `json:"assignee,omitempty"`
// The Milestone object including a 'title' attribute.
// Only provided for 'milestoned' and 'demilestoned' events.
Milestone *Milestone `json:"milestone,omitempty"`
// The 'id', 'actor', and 'url' for the source of a reference from another issue.
// Only provided for 'cross-referenced' events.
Source *Source `json:"source,omitempty"`
// An object containing rename details including 'from' and 'to' attributes.
// Only provided for 'renamed' events.
Rename *Rename `json:"rename,omitempty"`
}
// Source represents a reference's source.
type Source struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
Actor *User `json:"actor,omitempty"`
}
// ListIssueTimeline lists events for the specified issue.
//
// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue
func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number)
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", mediaTypeTimelinePreview)
var events []*Timeline
resp, err := s.client.Do(ctx, req, &events)
return events, resp, err
}

View File

@@ -1,103 +0,0 @@
// Copyright 2013 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"
)
// LicensesService handles communication with the license related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/licenses/
type LicensesService service
// RepositoryLicense represents the license for a repository.
type RepositoryLicense struct {
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
SHA *string `json:"sha,omitempty"`
Size *int `json:"size,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
DownloadURL *string `json:"download_url,omitempty"`
Type *string `json:"type,omitempty"`
Content *string `json:"content,omitempty"`
Encoding *string `json:"encoding,omitempty"`
License *License `json:"license,omitempty"`
}
func (l RepositoryLicense) String() string {
return Stringify(l)
}
// License represents an open source license.
type License struct {
Key *string `json:"key,omitempty"`
Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"`
SPDXID *string `json:"spdx_id,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Featured *bool `json:"featured,omitempty"`
Description *string `json:"description,omitempty"`
Implementation *string `json:"implementation,omitempty"`
Permissions *[]string `json:"permissions,omitempty"`
Conditions *[]string `json:"conditions,omitempty"`
Limitations *[]string `json:"limitations,omitempty"`
Body *string `json:"body,omitempty"`
}
func (l License) String() string {
return Stringify(l)
}
// List popular open source licenses.
//
// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses
func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) {
req, err := s.client.NewRequest("GET", "licenses", nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
var licenses []*License
resp, err := s.client.Do(ctx, req, &licenses)
if err != nil {
return nil, resp, err
}
return licenses, resp, nil
}
// Get extended metadata for one license.
//
// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license
func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License, *Response, error) {
u := fmt.Sprintf("licenses/%s", licenseName)
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", mediaTypeLicensesPreview)
license := new(License)
resp, err := s.client.Do(ctx, req, license)
if err != nil {
return nil, resp, err
}
return license, resp, nil
}

View File

@@ -1,210 +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.
// This file provides functions for validating payloads from GitHub Webhooks.
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
package github
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"hash"
"io/ioutil"
"net/http"
"strings"
)
const (
// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest.
sha1Prefix = "sha1"
// sha256Prefix and sha512Prefix are provided for future compatibility.
sha256Prefix = "sha256"
sha512Prefix = "sha512"
// signatureHeader is the GitHub header key used to pass the HMAC hexdigest.
signatureHeader = "X-Hub-Signature"
// eventTypeHeader is the GitHub header key used to pass the event type.
eventTypeHeader = "X-Github-Event"
// deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event.
deliveryIDHeader = "X-Github-Delivery"
)
var (
// eventTypeMapping maps webhooks types to their corresponding go-github struct types.
eventTypeMapping = map[string]string{
"commit_comment": "CommitCommentEvent",
"create": "CreateEvent",
"delete": "DeleteEvent",
"deployment": "DeploymentEvent",
"deployment_status": "DeploymentStatusEvent",
"fork": "ForkEvent",
"gollum": "GollumEvent",
"integration_installation": "IntegrationInstallationEvent",
"integration_installation_repositories": "IntegrationInstallationRepositoriesEvent",
"issue_comment": "IssueCommentEvent",
"issues": "IssuesEvent",
"label": "LabelEvent",
"member": "MemberEvent",
"membership": "MembershipEvent",
"milestone": "MilestoneEvent",
"organization": "OrganizationEvent",
"org_block": "OrgBlockEvent",
"page_build": "PageBuildEvent",
"ping": "PingEvent",
"project": "ProjectEvent",
"project_card": "ProjectCardEvent",
"project_column": "ProjectColumnEvent",
"public": "PublicEvent",
"pull_request_review": "PullRequestReviewEvent",
"pull_request_review_comment": "PullRequestReviewCommentEvent",
"pull_request": "PullRequestEvent",
"push": "PushEvent",
"repository": "RepositoryEvent",
"release": "ReleaseEvent",
"status": "StatusEvent",
"team_add": "TeamAddEvent",
"watch": "WatchEvent",
}
)
// genMAC generates the HMAC signature for a message provided the secret key
// and hashFunc.
func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte {
mac := hmac.New(hashFunc, key)
mac.Write(message)
return mac.Sum(nil)
}
// checkMAC reports whether messageMAC is a valid HMAC tag for message.
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool {
expectedMAC := genMAC(message, key, hashFunc)
return hmac.Equal(messageMAC, expectedMAC)
}
// messageMAC returns the hex-decoded HMAC tag from the signature and its
// corresponding hash function.
func messageMAC(signature string) ([]byte, func() hash.Hash, error) {
if signature == "" {
return nil, nil, errors.New("missing signature")
}
sigParts := strings.SplitN(signature, "=", 2)
if len(sigParts) != 2 {
return nil, nil, fmt.Errorf("error parsing signature %q", signature)
}
var hashFunc func() hash.Hash
switch sigParts[0] {
case sha1Prefix:
hashFunc = sha1.New
case sha256Prefix:
hashFunc = sha256.New
case sha512Prefix:
hashFunc = sha512.New
default:
return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0])
}
buf, err := hex.DecodeString(sigParts[1])
if err != nil {
return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err)
}
return buf, hashFunc, nil
}
// ValidatePayload validates an incoming GitHub Webhook event request
// and returns the (JSON) payload.
// secretKey is the GitHub Webhook secret message.
//
// Example usage:
//
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// payload, err := github.ValidatePayload(r, s.webhookSecretKey)
// if err != nil { ... }
// // Process payload...
// }
//
func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) {
payload, err = ioutil.ReadAll(r.Body)
if err != nil {
return nil, err
}
sig := r.Header.Get(signatureHeader)
if err := validateSignature(sig, payload, secretKey); err != nil {
return nil, err
}
return payload, nil
}
// validateSignature validates the signature for the given payload.
// signature is the GitHub hash signature delivered in the X-Hub-Signature header.
// payload is the JSON payload sent by GitHub Webhooks.
// secretKey is the GitHub Webhook secret message.
//
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github
func validateSignature(signature string, payload, secretKey []byte) error {
messageMAC, hashFunc, err := messageMAC(signature)
if err != nil {
return err
}
if !checkMAC(payload, messageMAC, secretKey, hashFunc) {
return errors.New("payload signature check failed")
}
return nil
}
// WebHookType returns the event type of webhook request r.
//
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers
func WebHookType(r *http.Request) string {
return r.Header.Get(eventTypeHeader)
}
// DeliveryID returns the unique delivery ID of webhook request r.
//
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers
func DeliveryID(r *http.Request) string {
return r.Header.Get(deliveryIDHeader)
}
// ParseWebHook parses the event payload. For recognized event types, a
// value of the corresponding struct type will be returned (as returned
// by Event.ParsePayload()). An error will be returned for unrecognized event
// types.
//
// Example usage:
//
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// payload, err := github.ValidatePayload(r, s.webhookSecretKey)
// if err != nil { ... }
// event, err := github.ParseWebHook(github.WebHookType(r), payload)
// if err != nil { ... }
// switch event := event.(type) {
// case *github.CommitCommentEvent:
// processCommitCommentEvent(event)
// case *github.CreateEvent:
// processCreateEvent(event)
// ...
// }
// }
//
func ParseWebHook(messageType string, payload []byte) (interface{}, error) {
eventType, ok := eventTypeMapping[messageType]
if !ok {
return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType)
}
event := Event{
Type: &eventType,
RawPayload: (*json.RawMessage)(&payload),
}
return event.ParsePayload()
}

View File

@@ -1,224 +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"
"errors"
"fmt"
"net/http"
"strings"
)
// MigrationService provides access to the migration related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/migration/
type MigrationService service
// Migration represents a GitHub migration (archival).
type Migration struct {
ID *int `json:"id,omitempty"`
GUID *string `json:"guid,omitempty"`
// State is the current state of a migration.
// Possible values are:
// "pending" which means the migration hasn't started yet,
// "exporting" which means the migration is in progress,
// "exported" which means the migration finished successfully, or
// "failed" which means the migration failed.
State *string `json:"state,omitempty"`
// LockRepositories indicates whether repositories are locked (to prevent
// manipulation) while migrating data.
LockRepositories *bool `json:"lock_repositories,omitempty"`
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"`
URL *string `json:"url,omitempty"`
CreatedAt *string `json:"created_at,omitempty"`
UpdatedAt *string `json:"updated_at,omitempty"`
Repositories []*Repository `json:"repositories,omitempty"`
}
func (m Migration) String() string {
return Stringify(m)
}
// MigrationOptions specifies the optional parameters to Migration methods.
type MigrationOptions struct {
// LockRepositories indicates whether repositories should be locked (to prevent
// manipulation) while migrating data.
LockRepositories bool
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments bool
}
// startMigration represents the body of a StartMigration request.
type startMigration struct {
// Repositories is a slice of repository names to migrate.
Repositories []string `json:"repositories,omitempty"`
// LockRepositories indicates whether repositories should be locked (to prevent
// manipulation) while migrating data.
LockRepositories *bool `json:"lock_repositories,omitempty"`
// ExcludeAttachments indicates whether attachments should be excluded from
// the migration (to reduce migration archive file size).
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"`
}
// StartMigration starts the generation of a migration archive.
// repos is a slice of repository names to migrate.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration
func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) {
u := fmt.Sprintf("orgs/%v/migrations", org)
body := &startMigration{Repositories: repos}
if opt != nil {
body.LockRepositories = Bool(opt.LockRepositories)
body.ExcludeAttachments = Bool(opt.ExcludeAttachments)
}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
m := &Migration{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// ListMigrations lists the most recent migrations.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations
func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*Migration, *Response, error) {
u := fmt.Sprintf("orgs/%v/migrations", org)
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", mediaTypeMigrationsPreview)
var m []*Migration
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// MigrationStatus gets the status of a specific migration archive.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration
func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int) (*Migration, *Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v", org, id)
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", mediaTypeMigrationsPreview)
m := &Migration{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// MigrationArchiveURL fetches a migration archive URL.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive
func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int) (url string, err error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return "", err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeMigrationsPreview)
s.client.clientMu.Lock()
defer s.client.clientMu.Unlock()
// Disable the redirect mechanism because AWS fails if the GitHub auth token is provided.
var loc string
saveRedirect := s.client.client.CheckRedirect
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
loc = req.URL.String()
return errors.New("disable redirect")
}
defer func() { s.client.client.CheckRedirect = saveRedirect }()
_, err = s.client.Do(ctx, req, nil) // expect error from disable redirect
if err == nil {
return "", errors.New("expected redirect, none provided")
}
if !strings.Contains(err.Error(), "disable redirect") {
return "", err
}
return loc, nil
}
// DeleteMigration deletes a previous migration archive.
// id is the migration ID.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive
func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int) (*Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, 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", mediaTypeMigrationsPreview)
return s.client.Do(ctx, req, nil)
}
// UnlockRepo unlocks a repository that was locked for migration.
// id is the migration ID.
// You should unlock each migrated repository and delete them when the migration
// is complete and you no longer need the source data.
//
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository
func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int, repo string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo)
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", mediaTypeMigrationsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,329 +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"
)
// Import represents a repository import request.
type Import struct {
// The URL of the originating repository.
VCSURL *string `json:"vcs_url,omitempty"`
// The originating VCS type. Can be one of 'subversion', 'git',
// 'mercurial', or 'tfvc'. Without this parameter, the import job will
// take additional time to detect the VCS type before beginning the
// import. This detection step will be reflected in the response.
VCS *string `json:"vcs,omitempty"`
// VCSUsername and VCSPassword are only used for StartImport calls that
// are importing a password-protected repository.
VCSUsername *string `json:"vcs_username,omitempty"`
VCSPassword *string `json:"vcs_password,omitempty"`
// For a tfvc import, the name of the project that is being imported.
TFVCProject *string `json:"tfvc_project,omitempty"`
// LFS related fields that may be preset in the Import Progress response
// Describes whether the import has been opted in or out of using Git
// LFS. The value can be 'opt_in', 'opt_out', or 'undecided' if no
// action has been taken.
UseLFS *string `json:"use_lfs,omitempty"`
// Describes whether files larger than 100MB were found during the
// importing step.
HasLargeFiles *bool `json:"has_large_files,omitempty"`
// The total size in gigabytes of files larger than 100MB found in the
// originating repository.
LargeFilesSize *int `json:"large_files_size,omitempty"`
// The total number of files larger than 100MB found in the originating
// repository. To see a list of these files, call LargeFiles.
LargeFilesCount *int `json:"large_files_count,omitempty"`
// Identifies the current status of an import. An import that does not
// have errors will progress through these steps:
//
// detecting - the "detection" step of the import is in progress
// because the request did not include a VCS parameter. The
// import is identifying the type of source control present at
// the URL.
// importing - the "raw" step of the import is in progress. This is
// where commit data is fetched from the original repository.
// The import progress response will include CommitCount (the
// total number of raw commits that will be imported) and
// Percent (0 - 100, the current progress through the import).
// mapping - the "rewrite" step of the import is in progress. This
// is where SVN branches are converted to Git branches, and
// where author updates are applied. The import progress
// response does not include progress information.
// pushing - the "push" step of the import is in progress. This is
// where the importer updates the repository on GitHub. The
// import progress response will include PushPercent, which is
// the percent value reported by git push when it is "Writing
// objects".
// complete - the import is complete, and the repository is ready
// on GitHub.
//
// If there are problems, you will see one of these in the status field:
//
// auth_failed - the import requires authentication in order to
// connect to the original repository. Make an UpdateImport
// request, and include VCSUsername and VCSPassword.
// error - the import encountered an error. The import progress
// response will include the FailedStep and an error message.
// Contact GitHub support for more information.
// detection_needs_auth - the importer requires authentication for
// the originating repository to continue detection. Make an
// UpdatImport request, and include VCSUsername and
// VCSPassword.
// detection_found_nothing - the importer didn't recognize any
// source control at the URL.
// detection_found_multiple - the importer found several projects
// or repositories at the provided URL. When this is the case,
// the Import Progress response will also include a
// ProjectChoices field with the possible project choices as
// values. Make an UpdateImport request, and include VCS and
// (if applicable) TFVCProject.
Status *string `json:"status,omitempty"`
CommitCount *int `json:"commit_count,omitempty"`
StatusText *string `json:"status_text,omitempty"`
AuthorsCount *int `json:"authors_count,omitempty"`
Percent *int `json:"percent,omitempty"`
PushPercent *int `json:"push_percent,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
AuthorsURL *string `json:"authors_url,omitempty"`
RepositoryURL *string `json:"repository_url,omitempty"`
Message *string `json:"message,omitempty"`
FailedStep *string `json:"failed_step,omitempty"`
// Human readable display name, provided when the Import appears as
// part of ProjectChoices.
HumanName *string `json:"human_name,omitempty"`
// When the importer finds several projects or repositories at the
// provided URLs, this will identify the available choices. Call
// UpdateImport with the selected Import value.
ProjectChoices []Import `json:"project_choices,omitempty"`
}
func (i Import) String() string {
return Stringify(i)
}
// SourceImportAuthor identifies an author imported from a source repository.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors
type SourceImportAuthor struct {
ID *int `json:"id,omitempty"`
RemoteID *string `json:"remote_id,omitempty"`
RemoteName *string `json:"remote_name,omitempty"`
Email *string `json:"email,omitempty"`
Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"`
ImportURL *string `json:"import_url,omitempty"`
}
func (a SourceImportAuthor) String() string {
return Stringify(a)
}
// LargeFile identifies a file larger than 100MB found during a repository import.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files
type LargeFile struct {
RefName *string `json:"ref_name,omitempty"`
Path *string `json:"path,omitempty"`
OID *string `json:"oid,omitempty"`
Size *int `json:"size,omitempty"`
}
func (f LargeFile) String() string {
return Stringify(f)
}
// StartImport initiates a repository import.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import
func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import", owner, repo)
req, err := s.client.NewRequest("PUT", u, in)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeImportPreview)
out := new(Import)
resp, err := s.client.Do(ctx, req, out)
if err != nil {
return nil, resp, err
}
return out, resp, nil
}
// ImportProgress queries for the status and progress of an ongoing repository import.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress
func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo string) (*Import, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import", owner, repo)
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", mediaTypeImportPreview)
out := new(Import)
resp, err := s.client.Do(ctx, req, out)
if err != nil {
return nil, resp, err
}
return out, resp, nil
}
// UpdateImport initiates a repository import.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import
func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import", owner, repo)
req, err := s.client.NewRequest("PATCH", u, in)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeImportPreview)
out := new(Import)
resp, err := s.client.Do(ctx, req, out)
if err != nil {
return nil, resp, err
}
return out, resp, nil
}
// CommitAuthors gets the authors mapped from the original repository.
//
// Each type of source control system represents authors in a different way.
// For example, a Git commit author has a display name and an email address,
// but a Subversion commit author just has a username. The GitHub Importer will
// make the author information valid, but the author might not be correct. For
// example, it will change the bare Subversion username "hubot" into something
// like "hubot <hubot@12341234-abab-fefe-8787-fedcba987654>".
//
// This method and MapCommitAuthor allow you to provide correct Git author
// information.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors
func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string) ([]*SourceImportAuthor, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo)
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", mediaTypeImportPreview)
var authors []*SourceImportAuthor
resp, err := s.client.Do(ctx, req, &authors)
if err != nil {
return nil, resp, err
}
return authors, resp, nil
}
// MapCommitAuthor updates an author's identity for the import. Your
// application can continue updating authors any time before you push new
// commits to the repository.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author
func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id)
req, err := s.client.NewRequest("PATCH", u, author)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeImportPreview)
out := new(SourceImportAuthor)
resp, err := s.client.Do(ctx, req, out)
if err != nil {
return nil, resp, err
}
return out, resp, nil
}
// SetLFSPreference sets whether imported repositories should use Git LFS for
// files larger than 100MB. Only the UseLFS field on the provided Import is
// used.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference
func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo)
req, err := s.client.NewRequest("PATCH", u, in)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeImportPreview)
out := new(Import)
resp, err := s.client.Do(ctx, req, out)
if err != nil {
return nil, resp, err
}
return out, resp, nil
}
// LargeFiles lists files larger than 100MB found during the import.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files
func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ([]*LargeFile, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo)
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", mediaTypeImportPreview)
var files []*LargeFile
resp, err := s.client.Do(ctx, req, &files)
if err != nil {
return nil, resp, err
}
return files, resp, nil
}
// CancelImport stops an import for a repository.
//
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import
func (s *MigrationService) CancelImport(ctx context.Context, owner, repo string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/import", owner, repo)
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", mediaTypeImportPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,198 +0,0 @@
// Copyright 2014 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 (
"bytes"
"context"
"fmt"
"net/url"
)
// MarkdownOptions specifies optional parameters to the Markdown method.
type MarkdownOptions struct {
// Mode identifies the rendering mode. Possible values are:
// markdown - render a document as plain Markdown, just like
// README files are rendered.
//
// gfm - to render a document as user-content, e.g. like user
// comments or issues are rendered. In GFM mode, hard line breaks are
// always taken into account, and issue and user mentions are linked
// accordingly.
//
// Default is "markdown".
Mode string
// Context identifies the repository context. Only taken into account
// when rendering as "gfm".
Context string
}
type markdownRequest struct {
Text *string `json:"text,omitempty"`
Mode *string `json:"mode,omitempty"`
Context *string `json:"context,omitempty"`
}
// Markdown renders an arbitrary Markdown document.
//
// GitHub API docs: https://developer.github.com/v3/markdown/
func (c *Client) Markdown(ctx context.Context, text string, opt *MarkdownOptions) (string, *Response, error) {
request := &markdownRequest{Text: String(text)}
if opt != nil {
if opt.Mode != "" {
request.Mode = String(opt.Mode)
}
if opt.Context != "" {
request.Context = String(opt.Context)
}
}
req, err := c.NewRequest("POST", "markdown", request)
if err != nil {
return "", nil, err
}
buf := new(bytes.Buffer)
resp, err := c.Do(ctx, req, buf)
if err != nil {
return "", resp, err
}
return buf.String(), resp, nil
}
// ListEmojis returns the emojis available to use on GitHub.
//
// GitHub API docs: https://developer.github.com/v3/emojis/
func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) {
req, err := c.NewRequest("GET", "emojis", nil)
if err != nil {
return nil, nil, err
}
var emoji map[string]string
resp, err := c.Do(ctx, req, &emoji)
if err != nil {
return nil, resp, err
}
return emoji, resp, nil
}
// APIMeta represents metadata about the GitHub API.
type APIMeta struct {
// An Array of IP addresses in CIDR format specifying the addresses
// that incoming service hooks will originate from on GitHub.com.
Hooks []string `json:"hooks,omitempty"`
// An Array of IP addresses in CIDR format specifying the Git servers
// for GitHub.com.
Git []string `json:"git,omitempty"`
// Whether authentication with username and password is supported.
// (GitHub Enterprise instances using CAS or OAuth for authentication
// will return false. Features like Basic Authentication with a
// username and password, sudo mode, and two-factor authentication are
// not supported on these servers.)
VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"`
// An array of IP addresses in CIDR format specifying the addresses
// which serve GitHub Pages websites.
Pages []string `json:"pages,omitempty"`
}
// APIMeta returns information about GitHub.com, the service. Or, if you access
// this endpoint on your organizations GitHub Enterprise installation, this
// endpoint provides information about that installation.
//
// GitHub API docs: https://developer.github.com/v3/meta/
func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) {
req, err := c.NewRequest("GET", "meta", nil)
if err != nil {
return nil, nil, err
}
meta := new(APIMeta)
resp, err := c.Do(ctx, req, meta)
if err != nil {
return nil, resp, err
}
return meta, resp, nil
}
// Octocat returns an ASCII art octocat with the specified message in a speech
// bubble. If message is empty, a random zen phrase is used.
func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) {
u := "octocat"
if message != "" {
u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message))
}
req, err := c.NewRequest("GET", u, nil)
if err != nil {
return "", nil, err
}
buf := new(bytes.Buffer)
resp, err := c.Do(ctx, req, buf)
if err != nil {
return "", resp, err
}
return buf.String(), resp, nil
}
// Zen returns a random line from The Zen of GitHub.
//
// see also: http://warpspire.com/posts/taste/
func (c *Client) Zen(ctx context.Context) (string, *Response, error) {
req, err := c.NewRequest("GET", "zen", nil)
if err != nil {
return "", nil, err
}
buf := new(bytes.Buffer)
resp, err := c.Do(ctx, req, buf)
if err != nil {
return "", resp, err
}
return buf.String(), resp, nil
}
// ServiceHook represents a hook that has configuration settings, a list of
// available events, and default events.
type ServiceHook struct {
Name *string `json:"name,omitempty"`
Events []string `json:"events,omitempty"`
SupportedEvents []string `json:"supported_events,omitempty"`
Schema [][]string `json:"schema,omitempty"`
}
func (s *ServiceHook) String() string {
return Stringify(s)
}
// ListServiceHooks lists all of the available service hooks.
//
// GitHub API docs: https://developer.github.com/webhooks/#services
func (c *Client) ListServiceHooks(ctx context.Context) ([]*ServiceHook, *Response, error) {
u := "hooks"
req, err := c.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var hooks []*ServiceHook
resp, err := c.Do(ctx, req, &hooks)
if err != nil {
return nil, resp, err
}
return hooks, resp, nil
}

View File

@@ -1,174 +0,0 @@
// Copyright 2013 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"
)
// OrganizationsService provides access to the organization related functions
// in the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/orgs/
type OrganizationsService service
// Organization represents a GitHub organization account.
type Organization struct {
Login *string `json:"login,omitempty"`
ID *int `json:"id,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
Name *string `json:"name,omitempty"`
Company *string `json:"company,omitempty"`
Blog *string `json:"blog,omitempty"`
Location *string `json:"location,omitempty"`
Email *string `json:"email,omitempty"`
Description *string `json:"description,omitempty"`
PublicRepos *int `json:"public_repos,omitempty"`
PublicGists *int `json:"public_gists,omitempty"`
Followers *int `json:"followers,omitempty"`
Following *int `json:"following,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
TotalPrivateRepos *int `json:"total_private_repos,omitempty"`
OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"`
PrivateGists *int `json:"private_gists,omitempty"`
DiskUsage *int `json:"disk_usage,omitempty"`
Collaborators *int `json:"collaborators,omitempty"`
BillingEmail *string `json:"billing_email,omitempty"`
Type *string `json:"type,omitempty"`
Plan *Plan `json:"plan,omitempty"`
// API URLs
URL *string `json:"url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
HooksURL *string `json:"hooks_url,omitempty"`
IssuesURL *string `json:"issues_url,omitempty"`
MembersURL *string `json:"members_url,omitempty"`
PublicMembersURL *string `json:"public_members_url,omitempty"`
ReposURL *string `json:"repos_url,omitempty"`
}
func (o Organization) String() string {
return Stringify(o)
}
// Plan represents the payment plan for an account. See plans at https://github.com/plans.
type Plan struct {
Name *string `json:"name,omitempty"`
Space *int `json:"space,omitempty"`
Collaborators *int `json:"collaborators,omitempty"`
PrivateRepos *int `json:"private_repos,omitempty"`
}
func (p Plan) String() string {
return Stringify(p)
}
// OrganizationsListOptions specifies the optional parameters to the
// OrganizationsService.ListAll method.
type OrganizationsListOptions struct {
// Since filters Organizations by ID.
Since int `url:"since,omitempty"`
ListOptions
}
// ListAll lists all organizations, in the order that they were created on GitHub.
//
// Note: Pagination is powered exclusively by the since parameter. To continue
// listing the next set of organizations, use the ID of the last-returned organization
// as the opts.Since parameter for the next call.
//
// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations
func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsListOptions) ([]*Organization, *Response, error) {
u, err := addOptions("organizations", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
orgs := []*Organization{}
resp, err := s.client.Do(ctx, req, &orgs)
if err != nil {
return nil, resp, err
}
return orgs, resp, nil
}
// List the organizations for a user. Passing the empty string will list
// organizations for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/orgs/#list-user-organizations
func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListOptions) ([]*Organization, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/orgs", user)
} else {
u = "user/orgs"
}
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 orgs []*Organization
resp, err := s.client.Do(ctx, req, &orgs)
if err != nil {
return nil, resp, err
}
return orgs, resp, nil
}
// Get fetches an organization by name.
//
// GitHub API docs: https://developer.github.com/v3/orgs/#get-an-organization
func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organization, *Response, error) {
u := fmt.Sprintf("orgs/%v", org)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
organization := new(Organization)
resp, err := s.client.Do(ctx, req, organization)
if err != nil {
return nil, resp, err
}
return organization, resp, nil
}
// Edit an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/#edit-an-organization
func (s *OrganizationsService) Edit(ctx context.Context, name string, org *Organization) (*Organization, *Response, error) {
u := fmt.Sprintf("orgs/%v", name)
req, err := s.client.NewRequest("PATCH", u, org)
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
}

View File

@@ -1,107 +0,0 @@
// Copyright 2015 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"
)
// ListHooks lists all Hooks for the specified organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks
func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *ListOptions) ([]*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks", 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
}
var hooks []*Hook
resp, err := s.client.Do(ctx, req, &hooks)
if err != nil {
return nil, resp, err
}
return hooks, resp, nil
}
// GetHook returns a single specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook
func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
hook := new(Hook)
resp, err := s.client.Do(ctx, req, hook)
return hook, resp, err
}
// CreateHook creates a Hook for the specified org.
// Name and Config are required fields.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook
func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks", org)
req, err := s.client.NewRequest("POST", u, hook)
if err != nil {
return nil, nil, err
}
h := new(Hook)
resp, err := s.client.Do(ctx, req, h)
if err != nil {
return nil, resp, err
}
return h, resp, nil
}
// EditHook updates a specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook
func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int, hook *Hook) (*Hook, *Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("PATCH", u, hook)
if err != nil {
return nil, nil, err
}
h := new(Hook)
resp, err := s.client.Do(ctx, req, h)
return h, resp, err
}
// PingHook triggers a 'ping' event to be sent to the Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook
func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int) (*Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id)
req, err := s.client.NewRequest("POST", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// DeleteHook deletes a specified Hook.
//
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook
func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int) (*Response, error) {
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,299 +0,0 @@
// Copyright 2013 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"
)
// Membership represents the status of a user's membership in an organization or team.
type Membership struct {
URL *string `json:"url,omitempty"`
// State is the user's status within the organization or team.
// Possible values are: "active", "pending"
State *string `json:"state,omitempty"`
// Role identifies the user's role within the organization or team.
// Possible values for organization membership:
// member - non-owner organization member
// admin - organization owner
//
// Possible values for team membership 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
Role *string `json:"role,omitempty"`
// For organization membership, the API URL of the organization.
OrganizationURL *string `json:"organization_url,omitempty"`
// For organization membership, the organization the membership is for.
Organization *Organization `json:"organization,omitempty"`
// For organization membership, the user the membership is for.
User *User `json:"user,omitempty"`
}
func (m Membership) String() string {
return Stringify(m)
}
// ListMembersOptions specifies optional parameters to the
// OrganizationsService.ListMembers method.
type ListMembersOptions struct {
// If true (or if the authenticated user is not an owner of the
// organization), list only publicly visible members.
PublicOnly bool `url:"-"`
// Filter members returned in the list. Possible values are:
// 2fa_disabled, all. Default is "all".
Filter string `url:"filter,omitempty"`
// Role filters members returned by their role in the organization.
// Possible values are:
// all - all members of the organization, regardless of role
// admin - organization owners
// member - non-organization members
//
// Default is "all".
Role string `url:"role,omitempty"`
ListOptions
}
// ListMembers lists the members for an organization. If the authenticated
// user is an owner of the organization, this will return both concealed and
// public members, otherwise it will only return public members.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#members-list
func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt *ListMembersOptions) ([]*User, *Response, error) {
var u string
if opt != nil && opt.PublicOnly {
u = fmt.Sprintf("orgs/%v/public_members", org)
} else {
u = fmt.Sprintf("orgs/%v/members", 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
}
var members []*User
resp, err := s.client.Do(ctx, req, &members)
if err != nil {
return nil, resp, err
}
return members, resp, nil
}
// IsMember checks if a user is a member of an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-membership
func (s *OrganizationsService) IsMember(ctx context.Context, org, user string) (bool, *Response, error) {
u := fmt.Sprintf("orgs/%v/members/%v", org, 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
}
// IsPublicMember checks if a user is a public member of an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-public-membership
func (s *OrganizationsService) IsPublicMember(ctx context.Context, org, user string) (bool, *Response, error) {
u := fmt.Sprintf("orgs/%v/public_members/%v", org, 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
}
// RemoveMember removes a user from all teams of an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-a-member
func (s *OrganizationsService) RemoveMember(ctx context.Context, org, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/members/%v", org, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// PublicizeMembership publicizes a user's membership in an organization. (A
// user cannot publicize the membership for another user.)
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#publicize-a-users-membership
func (s *OrganizationsService) PublicizeMembership(ctx context.Context, org, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ConcealMembership conceals a user's membership in an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#conceal-a-users-membership
func (s *OrganizationsService) ConcealMembership(ctx context.Context, org, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListOrgMembershipsOptions specifies optional parameters to the
// OrganizationsService.ListOrgMemberships method.
type ListOrgMembershipsOptions struct {
// Filter memberships to include only those with the specified state.
// Possible values are: "active", "pending".
State string `url:"state,omitempty"`
ListOptions
}
// ListOrgMemberships lists the organization memberships for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships
func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) {
u := "user/memberships/orgs"
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 memberships []*Membership
resp, err := s.client.Do(ctx, req, &memberships)
if err != nil {
return nil, resp, err
}
return memberships, resp, nil
}
// GetOrgMembership gets the membership for a user in a specified organization.
// Passing an empty string for user will get the membership for the
// authenticated user.
//
// GitHub API docs:
// https://developer.github.com/v3/orgs/members/#get-organization-membership
// https://developer.github.com/v3/orgs/members/#get-your-organization-membership
func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org string) (*Membership, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user)
} else {
u = fmt.Sprintf("user/memberships/orgs/%v", org)
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
membership := new(Membership)
resp, err := s.client.Do(ctx, req, membership)
if err != nil {
return nil, resp, err
}
return membership, resp, nil
}
// EditOrgMembership edits the membership for user in specified organization.
// Passing an empty string for user will edit the membership for the
// authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership
// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership
func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org string, membership *Membership) (*Membership, *Response, error) {
var u, method string
if user != "" {
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user)
method = "PUT"
} else {
u = fmt.Sprintf("user/memberships/orgs/%v", org)
method = "PATCH"
}
req, err := s.client.NewRequest(method, u, membership)
if err != nil {
return nil, nil, err
}
m := new(Membership)
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// RemoveOrgMembership removes user from the specified organization. If the
// user has been invited to the organization, this will cancel their invitation.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership
func (s *OrganizationsService) RemoveOrgMembership(ctx context.Context, user, org string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/memberships/%v", org, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// ListPendingOrgInvitations returns a list of pending invitations.
//
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations
func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org int, opt *ListOptions) ([]*Invitation, *Response, error) {
u := fmt.Sprintf("orgs/%v/invitations", 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
}
var pendingInvitations []*Invitation
resp, err := s.client.Do(ctx, req, &pendingInvitations)
if err != nil {
return nil, resp, err
}
return pendingInvitations, resp, nil
}

View File

@@ -1,50 +0,0 @@
// Copyright 2017 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"
)
// ListOutsideCollaboratorsOptions specifies optional parameters to the
// OrganizationsService.ListOutsideCollaborators method.
type ListOutsideCollaboratorsOptions struct {
// Filter outside collaborators returned in the list. Possible values are:
// 2fa_disabled, all. Default is "all".
Filter string `url:"filter,omitempty"`
ListOptions
}
// ListOutsideCollaborators lists outside collaborators of organization's repositories.
// This will only work if the authenticated
// user is an owner of the organization.
//
// 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/orgs/outside_collaborators/#list-outside-collaborators
func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opt *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("orgs/%v/outside_collaborators", 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
}
var members []*User
resp, err := s.client.Do(ctx, req, &members)
if err != nil {
return nil, resp, err
}
return members, resp, nil
}

View File

@@ -1,60 +0,0 @@
// Copyright 2017 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"
)
// ListProjects lists the projects for an organization.
//
// GitHub API docs: https://developer.github.com/v3/projects/#list-organization-projects
func (s *OrganizationsService) ListProjects(ctx context.Context, org string, opt *ProjectListOptions) ([]*Project, *Response, error) {
u := fmt.Sprintf("orgs/%v/projects", 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", mediaTypeProjectsPreview)
var projects []*Project
resp, err := s.client.Do(ctx, req, &projects)
if err != nil {
return nil, resp, err
}
return projects, resp, nil
}
// CreateProject creates a GitHub Project for the specified organization.
//
// GitHub API docs: https://developer.github.com/v3/projects/#create-an-organization-project
func (s *OrganizationsService) CreateProject(ctx context.Context, org string, opt *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("orgs/%v/projects", org)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(ctx, req, project)
if err != nil {
return nil, resp, err
}
return project, resp, nil
}

View File

@@ -1,428 +0,0 @@
// Copyright 2013 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"
)
// Team represents a team within a GitHub organization. Teams are used to
// manage access to an organization's repositories.
type Team struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
URL *string `json:"url,omitempty"`
Slug *string `json:"slug,omitempty"`
// 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"`
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"`
// 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 *int `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"`
}
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/orgs/teams/#list-teams
func (s *OrganizationsService) 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
}
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/orgs/teams/#get-team
func (s *OrganizationsService) GetTeam(ctx context.Context, team int) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
t := new(Team)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// CreateTeam creates a new team within an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#create-team
func (s *OrganizationsService) CreateTeam(ctx context.Context, org string, team *Team) (*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
}
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/orgs/teams/#edit-team
func (s *OrganizationsService) EditTeam(ctx context.Context, id int, team *Team) (*Team, *Response, error) {
u := fmt.Sprintf("teams/%v", id)
req, err := s.client.NewRequest("PATCH", u, team)
if err != nil {
return nil, nil, err
}
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/orgs/teams/#delete-team
func (s *OrganizationsService) DeleteTeam(ctx context.Context, team int) (*Response, error) {
u := fmt.Sprintf("teams/%v", team)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// OrganizationListTeamMembersOptions specifies the optional parameters to the
// OrganizationsService.ListTeamMembers method.
type OrganizationListTeamMembersOptions 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/orgs/teams/#list-team-members
func (s *OrganizationsService) ListTeamMembers(ctx context.Context, team int, opt *OrganizationListTeamMembersOptions) ([]*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
}
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/orgs/teams/#get-team-member
func (s *OrganizationsService) IsTeamMember(ctx context.Context, team int, 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
}
// ListTeamRepos lists the repositories that the specified team has access to.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos
func (s *OrganizationsService) ListTeamRepos(ctx context.Context, team int, 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
}
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/orgs/teams/#check-if-a-team-manages-a-repository
func (s *OrganizationsService) IsTeamRepo(ctx context.Context, team int, 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
}
req.Header.Set("Accept", mediaTypeOrgPermissionRepo)
repository := new(Repository)
resp, err := s.client.Do(ctx, req, repository)
if err != nil {
return nil, resp, err
}
return repository, resp, nil
}
// OrganizationAddTeamRepoOptions specifies the optional parameters to the
// OrganizationsService.AddTeamRepo method.
type OrganizationAddTeamRepoOptions 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/orgs/teams/#add-team-repo
func (s *OrganizationsService) AddTeamRepo(ctx context.Context, team int, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*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/orgs/teams/#remove-team-repo
func (s *OrganizationsService) RemoveTeamRepo(ctx context.Context, team int, 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/orgs/teams/#list-user-teams
func (s *OrganizationsService) 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
}
var teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// GetTeamMembership returns the membership status for a user in a team.
//
// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership
func (s *OrganizationsService) GetTeamMembership(ctx context.Context, team int, 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
}
t := new(Membership)
resp, err := s.client.Do(ctx, req, t)
if err != nil {
return nil, resp, err
}
return t, resp, nil
}
// OrganizationAddTeamMembershipOptions does stuff specifies the optional
// parameters to the OrganizationsService.AddTeamMembership method.
type OrganizationAddTeamMembershipOptions 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/orgs/teams/#add-team-membership
func (s *OrganizationsService) AddTeamMembership(ctx context.Context, team int, user string, opt *OrganizationAddTeamMembershipOptions) (*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/orgs/teams/#remove-team-membership
func (s *OrganizationsService) RemoveTeamMembership(ctx context.Context, team int, 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/orgs/teams/#list-pending-team-invitations
func (s *OrganizationsService) ListPendingTeamInvitations(ctx context.Context, team int, 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

@@ -1,91 +0,0 @@
// Copyright 2017 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"
)
// ListBlockedUsers lists all the users blocked by an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#list-blocked-users
func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opt *ListOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("orgs/%v/blocks", 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", mediaTypeBlockUsersPreview)
var blockedUsers []*User
resp, err := s.client.Do(ctx, req, &blockedUsers)
if err != nil {
return nil, resp, err
}
return blockedUsers, resp, nil
}
// IsBlocked reports whether specified user is blocked from an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#check-whether-a-user-is-blocked-from-an-organization
func (s *OrganizationsService) IsBlocked(ctx context.Context, org string, user string) (bool, *Response, error) {
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeBlockUsersPreview)
resp, err := s.client.Do(ctx, req, nil)
isBlocked, err := parseBoolResponse(err)
return isBlocked, resp, err
}
// BlockUser blocks specified user from an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#block-a-user
func (s *OrganizationsService) BlockUser(ctx context.Context, org string, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user)
req, err := s.client.NewRequest("PUT", u, nil)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeBlockUsersPreview)
return s.client.Do(ctx, req, nil)
}
// UnblockUser unblocks specified user from an organization.
//
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#unblock-a-user
func (s *OrganizationsService) UnblockUser(ctx context.Context, org string, user string) (*Response, error) {
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user)
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", mediaTypeBlockUsersPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,420 +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"
)
// ProjectsService provides access to the projects functions in the
// GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/projects/
type ProjectsService service
// Project represents a GitHub Project.
type Project struct {
ID *int `json:"id,omitempty"`
URL *string `json:"url,omitempty"`
OwnerURL *string `json:"owner_url,omitempty"`
Name *string `json:"name,omitempty"`
Body *string `json:"body,omitempty"`
Number *int `json:"number,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
// The User object that generated the project.
Creator *User `json:"creator,omitempty"`
}
func (p Project) String() string {
return Stringify(p)
}
// GetProject gets a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project
func (s *ProjectsService) GetProject(ctx context.Context, id int) (*Project, *Response, error) {
u := fmt.Sprintf("projects/%v", id)
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", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(ctx, req, project)
if err != nil {
return nil, resp, err
}
return project, resp, nil
}
// ProjectOptions specifies the parameters to the
// RepositoriesService.CreateProject and
// ProjectsService.UpdateProject methods.
type ProjectOptions struct {
// The name of the project. (Required for creation; optional for update.)
Name string `json:"name,omitempty"`
// The body of the project. (Optional.)
Body string `json:"body,omitempty"`
}
// UpdateProject updates a repository project.
//
// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project
func (s *ProjectsService) UpdateProject(ctx context.Context, id int, opt *ProjectOptions) (*Project, *Response, error) {
u := fmt.Sprintf("projects/%v", id)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
project := &Project{}
resp, err := s.client.Do(ctx, req, project)
if err != nil {
return nil, resp, err
}
return project, resp, nil
}
// DeleteProject deletes a GitHub Project from a repository.
//
// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project
func (s *ProjectsService) DeleteProject(ctx context.Context, id int) (*Response, error) {
u := fmt.Sprintf("projects/%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", mediaTypeProjectsPreview)
return s.client.Do(ctx, req, nil)
}
// ProjectColumn represents a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectColumn struct {
ID *int `json:"id,omitempty"`
Name *string `json:"name,omitempty"`
ProjectURL *string `json:"project_url,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectColumns lists the columns of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns
func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int, opt *ListOptions) ([]*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/%v/columns", projectID)
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", mediaTypeProjectsPreview)
columns := []*ProjectColumn{}
resp, err := s.client.Do(ctx, req, &columns)
if err != nil {
return nil, resp, err
}
return columns, resp, nil
}
// GetProjectColumn gets a column of a GitHub Project for a repo.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column
func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/columns/%v", id)
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", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(ctx, req, column)
if err != nil {
return nil, resp, err
}
return column, resp, nil
}
// ProjectColumnOptions specifies the parameters to the
// ProjectsService.CreateProjectColumn and
// ProjectsService.UpdateProjectColumn methods.
type ProjectColumnOptions struct {
// The name of the project column. (Required for creation and update.)
Name string `json:"name"`
}
// CreateProjectColumn creates a column for the specified (by number) project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column
func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/%v/columns", projectID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(ctx, req, column)
if err != nil {
return nil, resp, err
}
return column, resp, nil
}
// UpdateProjectColumn updates a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column
func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) {
u := fmt.Sprintf("projects/columns/%v", columnID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
column := &ProjectColumn{}
resp, err := s.client.Do(ctx, req, column)
if err != nil {
return nil, resp, err
}
return column, resp, nil
}
// DeleteProjectColumn deletes a column from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column
func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int) (*Response, error) {
u := fmt.Sprintf("projects/columns/%v", columnID)
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", mediaTypeProjectsPreview)
return s.client.Do(ctx, req, nil)
}
// ProjectColumnMoveOptions specifies the parameters to the
// ProjectsService.MoveProjectColumn method.
type ProjectColumnMoveOptions struct {
// Position can be one of "first", "last", or "after:<column-id>", where
// <column-id> is the ID of a column in the same project. (Required.)
Position string `json:"position"`
}
// MoveProjectColumn moves a column within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column
func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int, opt *ProjectColumnMoveOptions) (*Response, error) {
u := fmt.Sprintf("projects/columns/%v/moves", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(ctx, req, nil)
}
// ProjectCard represents a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/repos/projects/
type ProjectCard struct {
ColumnURL *string `json:"column_url,omitempty"`
ContentURL *string `json:"content_url,omitempty"`
ID *int `json:"id,omitempty"`
Note *string `json:"note,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
}
// ListProjectCards lists the cards in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards
func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int, opt *ListOptions) ([]*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/%v/cards", columnID)
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", mediaTypeProjectsPreview)
cards := []*ProjectCard{}
resp, err := s.client.Do(ctx, req, &cards)
if err != nil {
return nil, resp, err
}
return cards, resp, nil
}
// GetProjectCard gets a card in a column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card
func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", columnID)
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", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(ctx, req, card)
if err != nil {
return nil, resp, err
}
return card, resp, nil
}
// ProjectCardOptions specifies the parameters to the
// ProjectsService.CreateProjectCard and
// ProjectsService.UpdateProjectCard methods.
type ProjectCardOptions struct {
// The note of the card. Note and ContentID are mutually exclusive.
Note string `json:"note,omitempty"`
// The ID (not Number) of the Issue or Pull Request to associate with this card.
// Note and ContentID are mutually exclusive.
ContentID int `json:"content_id,omitempty"`
// The type of content to associate with this card. Possible values are: "Issue", "PullRequest".
ContentType string `json:"content_type,omitempty"`
}
// CreateProjectCard creates a card in the specified column of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card
func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/%v/cards", columnID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(ctx, req, card)
if err != nil {
return nil, resp, err
}
return card, resp, nil
}
// UpdateProjectCard updates a card of a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card
func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int, opt *ProjectCardOptions) (*ProjectCard, *Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", cardID)
req, err := s.client.NewRequest("PATCH", u, opt)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
card := &ProjectCard{}
resp, err := s.client.Do(ctx, req, card)
if err != nil {
return nil, resp, err
}
return card, resp, nil
}
// DeleteProjectCard deletes a card from a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card
func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int) (*Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v", cardID)
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", mediaTypeProjectsPreview)
return s.client.Do(ctx, req, nil)
}
// ProjectCardMoveOptions specifies the parameters to the
// ProjectsService.MoveProjectCard method.
type ProjectCardMoveOptions struct {
// Position can be one of "top", "bottom", or "after:<card-id>", where
// <card-id> is the ID of a card in the same project.
Position string `json:"position"`
// ColumnID is the ID of a column in the same project. Note that ColumnID
// is required when using Position "after:<card-id>" when that card is in
// another column; otherwise it is optional.
ColumnID int `json:"column_id,omitempty"`
}
// MoveProjectCard moves a card within a GitHub Project.
//
// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card
func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int, opt *ProjectCardMoveOptions) (*Response, error) {
u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID)
req, err := s.client.NewRequest("POST", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeProjectsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,356 +0,0 @@
// Copyright 2013 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 (
"bytes"
"context"
"fmt"
"time"
)
// PullRequestsService handles communication with the pull request related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/pulls/
type PullRequestsService service
// PullRequest represents a GitHub pull request on a repository.
type PullRequest struct {
ID *int `json:"id,omitempty"`
Number *int `json:"number,omitempty"`
State *string `json:"state,omitempty"`
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
ClosedAt *time.Time `json:"closed_at,omitempty"`
MergedAt *time.Time `json:"merged_at,omitempty"`
User *User `json:"user,omitempty"`
Merged *bool `json:"merged,omitempty"`
Mergeable *bool `json:"mergeable,omitempty"`
MergedBy *User `json:"merged_by,omitempty"`
MergeCommitSHA *string `json:"merge_commit_sha,omitempty"`
Comments *int `json:"comments,omitempty"`
Commits *int `json:"commits,omitempty"`
Additions *int `json:"additions,omitempty"`
Deletions *int `json:"deletions,omitempty"`
ChangedFiles *int `json:"changed_files,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
IssueURL *string `json:"issue_url,omitempty"`
StatusesURL *string `json:"statuses_url,omitempty"`
DiffURL *string `json:"diff_url,omitempty"`
PatchURL *string `json:"patch_url,omitempty"`
ReviewCommentsURL *string `json:"review_comments_url,omitempty"`
ReviewCommentURL *string `json:"review_comment_url,omitempty"`
Assignee *User `json:"assignee,omitempty"`
Assignees []*User `json:"assignees,omitempty"`
Milestone *Milestone `json:"milestone,omitempty"`
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"`
Head *PullRequestBranch `json:"head,omitempty"`
Base *PullRequestBranch `json:"base,omitempty"`
}
func (p PullRequest) String() string {
return Stringify(p)
}
// PullRequestBranch represents a base or head branch in a GitHub pull request.
type PullRequestBranch struct {
Label *string `json:"label,omitempty"`
Ref *string `json:"ref,omitempty"`
SHA *string `json:"sha,omitempty"`
Repo *Repository `json:"repo,omitempty"`
User *User `json:"user,omitempty"`
}
// PullRequestListOptions specifies the optional parameters to the
// PullRequestsService.List method.
type PullRequestListOptions struct {
// State filters pull requests based on their state. Possible values are:
// open, closed. Default is "open".
State string `url:"state,omitempty"`
// Head filters pull requests by head user and branch name in the format of:
// "user:ref-name".
Head string `url:"head,omitempty"`
// Base filters pull requests by base branch name.
Base string `url:"base,omitempty"`
// Sort specifies how to sort pull requests. Possible values are: created,
// updated, popularity, long-running. Default is "created".
Sort string `url:"sort,omitempty"`
// Direction in which to sort pull requests. Possible values are: asc, desc.
// If Sort is "created" or not specified, Default is "desc", otherwise Default
// is "asc"
Direction string `url:"direction,omitempty"`
ListOptions
}
// List the pull requests for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests
func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo)
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 pulls []*PullRequest
resp, err := s.client.Do(ctx, req, &pulls)
if err != nil {
return nil, resp, err
}
return pulls, resp, nil
}
// Get a single pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request
func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
pull := new(PullRequest)
resp, err := s.client.Do(ctx, req, pull)
if err != nil {
return nil, resp, err
}
return pull, resp, nil
}
// GetRaw gets raw (diff or patch) format of a pull request.
func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return "", nil, err
}
switch opt.Type {
case Diff:
req.Header.Set("Accept", mediaTypeV3Diff)
case Patch:
req.Header.Set("Accept", mediaTypeV3Patch)
default:
return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type)
}
ret := new(bytes.Buffer)
resp, err := s.client.Do(ctx, req, ret)
if err != nil {
return "", resp, err
}
return ret.String(), resp, nil
}
// NewPullRequest represents a new pull request to be created.
type NewPullRequest struct {
Title *string `json:"title,omitempty"`
Head *string `json:"head,omitempty"`
Base *string `json:"base,omitempty"`
Body *string `json:"body,omitempty"`
Issue *int `json:"issue,omitempty"`
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"`
}
// Create a new pull request on the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request
func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo)
req, err := s.client.NewRequest("POST", u, pull)
if err != nil {
return nil, nil, err
}
p := new(PullRequest)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}
return p, resp, nil
}
type pullRequestUpdate struct {
Title *string `json:"title,omitempty"`
Body *string `json:"body,omitempty"`
State *string `json:"state,omitempty"`
Base *string `json:"base,omitempty"`
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"`
}
// Edit a pull request.
// pull must not be nil.
//
// The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify.
// Base.Ref updates the base branch of the pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request
func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) {
if pull == nil {
return nil, nil, fmt.Errorf("pull must be provided")
}
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number)
update := &pullRequestUpdate{
Title: pull.Title,
Body: pull.Body,
State: pull.State,
MaintainerCanModify: pull.MaintainerCanModify,
}
if pull.Base != nil {
update.Base = pull.Base.Ref
}
req, err := s.client.NewRequest("PATCH", u, update)
if err != nil {
return nil, nil, err
}
p := new(PullRequest)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}
return p, resp, nil
}
// ListCommits lists the commits in a pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request
func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number)
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 commits []*RepositoryCommit
resp, err := s.client.Do(ctx, req, &commits)
if err != nil {
return nil, resp, err
}
return commits, resp, nil
}
// ListFiles lists the files in a pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files
func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number)
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 commitFiles []*CommitFile
resp, err := s.client.Do(ctx, req, &commitFiles)
if err != nil {
return nil, resp, err
}
return commitFiles, resp, nil
}
// IsMerged checks if a pull request has been merged.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged
func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
merged, err := parseBoolResponse(err)
return merged, resp, err
}
// PullRequestMergeResult represents the result of merging a pull request.
type PullRequestMergeResult struct {
SHA *string `json:"sha,omitempty"`
Merged *bool `json:"merged,omitempty"`
Message *string `json:"message,omitempty"`
}
// PullRequestOptions lets you define how a pull request will be merged.
type PullRequestOptions struct {
CommitTitle string // Extra detail to append to automatic commit message. (Optional.)
SHA string // SHA that pull request head must match to allow merge. (Optional.)
// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.)
MergeMethod string
}
type pullRequestMergeRequest struct {
CommitMessage string `json:"commit_message"`
CommitTitle string `json:"commit_title,omitempty"`
MergeMethod string `json:"merge_method,omitempty"`
SHA string `json:"sha,omitempty"`
}
// Merge a pull request (Merge Button™).
// commitMessage is the title for the automatic commit message.
//
// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade
func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number)
pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage}
if options != nil {
pullRequestBody.CommitTitle = options.CommitTitle
pullRequestBody.MergeMethod = options.MergeMethod
pullRequestBody.SHA = options.SHA
}
req, err := s.client.NewRequest("PUT", u, pullRequestBody)
if err != nil {
return nil, nil, err
}
// TODO: This header will be unnecessary when the API is no longer in preview.
req.Header.Set("Accept", mediaTypeSquashPreview)
mergeResult := new(PullRequestMergeResult)
resp, err := s.client.Do(ctx, req, mergeResult)
if err != nil {
return nil, resp, err
}
return mergeResult, resp, nil
}

View File

@@ -1,157 +0,0 @@
// Copyright 2013 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"
)
// PullRequestComment represents a comment left on a pull request.
type PullRequestComment struct {
ID *int `json:"id,omitempty"`
InReplyTo *int `json:"in_reply_to,omitempty"`
Body *string `json:"body,omitempty"`
Path *string `json:"path,omitempty"`
DiffHunk *string `json:"diff_hunk,omitempty"`
Position *int `json:"position,omitempty"`
OriginalPosition *int `json:"original_position,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
OriginalCommitID *string `json:"original_commit_id,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
PullRequestURL *string `json:"pull_request_url,omitempty"`
}
func (p PullRequestComment) String() string {
return Stringify(p)
}
// PullRequestListCommentsOptions specifies the optional parameters to the
// PullRequestsService.ListComments method.
type PullRequestListCommentsOptions struct {
// Sort specifies how to sort comments. Possible values are: created, updated.
Sort string `url:"sort,omitempty"`
// Direction in which to sort comments. Possible values are: asc, desc.
Direction string `url:"direction,omitempty"`
// Since filters comments by time.
Since time.Time `url:"since,omitempty"`
ListOptions
}
// ListComments lists all comments on the specified pull request. Specifying a
// pull request number of 0 will return all comments on all pull requests for
// the repository.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request
func (s *PullRequestsService) ListComments(ctx context.Context, owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) {
var u string
if number == 0 {
u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo)
} else {
u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number)
}
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", mediaTypeReactionsPreview)
var comments []*PullRequestComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// GetComment fetches the specified pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment
func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, number int) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
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", mediaTypeReactionsPreview)
comment := new(PullRequestComment)
resp, err := s.client.Do(ctx, req, comment)
if err != nil {
return nil, resp, err
}
return comment, resp, nil
}
// CreateComment creates a new comment on the specified pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment
func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number)
req, err := s.client.NewRequest("POST", u, comment)
if err != nil {
return nil, nil, err
}
c := new(PullRequestComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// EditComment updates a pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment
func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
}
c := new(PullRequestComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// DeleteComment deletes a pull request comment.
//
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment
func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, number int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,79 +0,0 @@
// Copyright 2017 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"
)
// RequestReviewers creates a review request for the provided GitHub users for the specified pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request
func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo string, number int, logins []string) (*PullRequest, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number)
reviewers := struct {
Reviewers []string `json:"reviewers,omitempty"`
}{
Reviewers: logins,
}
req, err := s.client.NewRequest("POST", u, &reviewers)
if err != nil {
return nil, nil, err
}
r := new(PullRequest)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// ListReviewers lists users whose reviews have been requested on the specified pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#list-review-requests
func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/requested_reviewers", owner, repo, number)
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 users []*User
resp, err := s.client.Do(ctx, req, &users)
if err != nil {
return nil, resp, err
}
return users, resp, nil
}
// RemoveReviewers removes the review request for the provided GitHub users for the specified pull request.
//
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request
func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, logins []string) (*Response, error) {
u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number)
reviewers := struct {
Reviewers []string `json:"reviewers,omitempty"`
}{
Reviewers: logins,
}
req, err := s.client.NewRequest("DELETE", u, &reviewers)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, reviewers)
}

View File

@@ -1,235 +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"
)
// PullRequestReview represents a review of a pull request.
type PullRequestReview struct {
ID *int `json:"id,omitempty"`
User *User `json:"user,omitempty"`
Body *string `json:"body,omitempty"`
SubmittedAt *time.Time `json:"submitted_at,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
PullRequestURL *string `json:"pull_request_url,omitempty"`
State *string `json:"state,omitempty"`
}
func (p PullRequestReview) String() string {
return Stringify(p)
}
// DraftReviewComment represents a comment part of the review.
type DraftReviewComment struct {
Path *string `json:"path,omitempty"`
Position *int `json:"position,omitempty"`
Body *string `json:"body,omitempty"`
}
func (c DraftReviewComment) String() string {
return Stringify(c)
}
// PullRequestReviewRequest represents a request to create a review.
type PullRequestReviewRequest struct {
Body *string `json:"body,omitempty"`
Event *string `json:"event,omitempty"`
Comments []*DraftReviewComment `json:"comments,omitempty"`
}
func (r PullRequestReviewRequest) String() string {
return Stringify(r)
}
// PullRequestReviewDismissalRequest represents a request to dismiss a review.
type PullRequestReviewDismissalRequest struct {
Message *string `json:"message,omitempty"`
}
func (r PullRequestReviewDismissalRequest) String() string {
return Stringify(r)
}
// ListReviews lists all reviews on the specified pull request.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request
func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number)
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 reviews []*PullRequestReview
resp, err := s.client.Do(ctx, req, &reviews)
if err != nil {
return nil, resp, err
}
return reviews, resp, nil
}
// GetReview fetches the specified pull request review.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-a-single-review
func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number, reviewID int) (*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
review := new(PullRequestReview)
resp, err := s.client.Do(ctx, req, review)
if err != nil {
return nil, resp, err
}
return review, resp, nil
}
// DeletePendingReview deletes the specified pull request pending review.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review
func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number, reviewID int) (*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, nil, err
}
review := new(PullRequestReview)
resp, err := s.client.Do(ctx, req, review)
if err != nil {
return nil, resp, err
}
return review, resp, nil
}
// ListReviewComments lists all the comments for the specified review.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review
func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number, reviewID int, opt *ListOptions) ([]*PullRequestComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID)
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 comments []*PullRequestComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// CreateReview creates a new review on the specified pull request.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review
func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number)
req, err := s.client.NewRequest("POST", u, review)
if err != nil {
return nil, nil, err
}
r := new(PullRequestReview)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// SubmitReview submits a specified review on the specified pull request.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review
func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number, reviewID int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID)
req, err := s.client.NewRequest("POST", u, review)
if err != nil {
return nil, nil, err
}
r := new(PullRequestReview)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// DismissReview dismisses a specified review on the specified pull request.
//
// TODO: Follow up with GitHub support about an issue with this method's
// returned error format and remove this comment once it's fixed.
// Read more about it here - https://github.com/google/go-github/issues/540
//
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review
func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number, reviewID int, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID)
req, err := s.client.NewRequest("PUT", u, review)
if err != nil {
return nil, nil, err
}
r := new(PullRequestReview)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}

View File

@@ -1,273 +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"
)
// ReactionsService provides access to the reactions-related functions in the
// GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/reactions/
type ReactionsService service
// Reaction represents a GitHub reaction.
type Reaction struct {
// ID is the Reaction ID.
ID *int `json:"id,omitempty"`
User *User `json:"user,omitempty"`
// Content is the type of reaction.
// Possible values are:
// "+1", "-1", "laugh", "confused", "heart", "hooray".
Content *string `json:"content,omitempty"`
}
// Reactions represents a summary of GitHub reactions.
type Reactions struct {
TotalCount *int `json:"total_count,omitempty"`
PlusOne *int `json:"+1,omitempty"`
MinusOne *int `json:"-1,omitempty"`
Laugh *int `json:"laugh,omitempty"`
Confused *int `json:"confused,omitempty"`
Heart *int `json:"heart,omitempty"`
Hooray *int `json:"hooray,omitempty"`
URL *string `json:"url,omitempty"`
}
func (r Reaction) String() string {
return Stringify(r)
}
// ListCommentReactions lists the reactions for a commit comment.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment
func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id)
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", mediaTypeReactionsPreview)
var m []*Reaction
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// CreateCommentReaction creates a reaction for a commit comment.
// Note that if you have already created a reaction of type content, the
// previously created reaction will be returned with Status: 200 OK.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment
func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int, content string) (*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id)
body := &Reaction{Content: String(content)}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
m := &Reaction{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// ListIssueReactions lists the reactions for an issue.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue
func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number)
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", mediaTypeReactionsPreview)
var m []*Reaction
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// CreateIssueReaction creates a reaction for an issue.
// Note that if you have already created a reaction of type content, the
// previously created reaction will be returned with Status: 200 OK.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue
func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number)
body := &Reaction{Content: String(content)}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
m := &Reaction{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// ListIssueCommentReactions lists the reactions for an issue comment.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment
func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id)
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", mediaTypeReactionsPreview)
var m []*Reaction
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// CreateIssueCommentReaction creates a reaction for an issue comment.
// Note that if you have already created a reaction of type content, the
// previously created reaction will be returned with Status: 200 OK.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int, content string) (*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id)
body := &Reaction{Content: String(content)}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
m := &Reaction{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// ListPullRequestCommentReactions lists the reactions for a pull request review comment.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment
func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int, opt *ListOptions) ([]*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id)
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", mediaTypeReactionsPreview)
var m []*Reaction
resp, err := s.client.Do(ctx, req, &m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// CreatePullRequestCommentReaction creates a reaction for a pull request review comment.
// Note that if you have already created a reaction of type content, the
// previously created reaction will be returned with Status: 200 OK.
//
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment
func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int, content string) (*Reaction, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id)
body := &Reaction{Content: String(content)}
req, err := s.client.NewRequest("POST", u, body)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeReactionsPreview)
m := &Reaction{}
resp, err := s.client.Do(ctx, req, m)
if err != nil {
return nil, resp, err
}
return m, resp, nil
}
// DeleteReaction deletes a reaction.
//
// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive
func (s *ReactionsService) DeleteReaction(ctx context.Context, id int) (*Response, error) {
u := fmt.Sprintf("reactions/%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", mediaTypeReactionsPreview)
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,740 +0,0 @@
// Copyright 2013 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"
)
// RepositoriesService handles communication with the repository related
// methods of the GitHub API.
//
// GitHub API docs: https://developer.github.com/v3/repos/
type RepositoriesService service
// Repository represents a GitHub repository.
type Repository struct {
ID *int `json:"id,omitempty"`
Owner *User `json:"owner,omitempty"`
Name *string `json:"name,omitempty"`
FullName *string `json:"full_name,omitempty"`
Description *string `json:"description,omitempty"`
Homepage *string `json:"homepage,omitempty"`
DefaultBranch *string `json:"default_branch,omitempty"`
MasterBranch *string `json:"master_branch,omitempty"`
CreatedAt *Timestamp `json:"created_at,omitempty"`
PushedAt *Timestamp `json:"pushed_at,omitempty"`
UpdatedAt *Timestamp `json:"updated_at,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
CloneURL *string `json:"clone_url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
MirrorURL *string `json:"mirror_url,omitempty"`
SSHURL *string `json:"ssh_url,omitempty"`
SVNURL *string `json:"svn_url,omitempty"`
Language *string `json:"language,omitempty"`
Fork *bool `json:"fork"`
ForksCount *int `json:"forks_count,omitempty"`
NetworkCount *int `json:"network_count,omitempty"`
OpenIssuesCount *int `json:"open_issues_count,omitempty"`
StargazersCount *int `json:"stargazers_count,omitempty"`
SubscribersCount *int `json:"subscribers_count,omitempty"`
WatchersCount *int `json:"watchers_count,omitempty"`
Size *int `json:"size,omitempty"`
AutoInit *bool `json:"auto_init,omitempty"`
Parent *Repository `json:"parent,omitempty"`
Source *Repository `json:"source,omitempty"`
Organization *Organization `json:"organization,omitempty"`
Permissions *map[string]bool `json:"permissions,omitempty"`
AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"`
AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"`
AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"`
// Only provided when using RepositoriesService.Get while in preview
License *License `json:"license,omitempty"`
// Additional mutable fields when creating and editing a repository
Private *bool `json:"private"`
HasIssues *bool `json:"has_issues"`
HasWiki *bool `json:"has_wiki"`
HasPages *bool `json:"has_pages"`
HasDownloads *bool `json:"has_downloads"`
LicenseTemplate *string `json:"license_template,omitempty"`
GitignoreTemplate *string `json:"gitignore_template,omitempty"`
// Creating an organization repository. Required for non-owners.
TeamID *int `json:"team_id"`
// API URLs
URL *string `json:"url,omitempty"`
ArchiveURL *string `json:"archive_url,omitempty"`
AssigneesURL *string `json:"assignees_url,omitempty"`
BlobsURL *string `json:"blobs_url,omitempty"`
BranchesURL *string `json:"branches_url,omitempty"`
CollaboratorsURL *string `json:"collaborators_url,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
CommitsURL *string `json:"commits_url,omitempty"`
CompareURL *string `json:"compare_url,omitempty"`
ContentsURL *string `json:"contents_url,omitempty"`
ContributorsURL *string `json:"contributors_url,omitempty"`
DeploymentsURL *string `json:"deployments_url,omitempty"`
DownloadsURL *string `json:"downloads_url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
ForksURL *string `json:"forks_url,omitempty"`
GitCommitsURL *string `json:"git_commits_url,omitempty"`
GitRefsURL *string `json:"git_refs_url,omitempty"`
GitTagsURL *string `json:"git_tags_url,omitempty"`
HooksURL *string `json:"hooks_url,omitempty"`
IssueCommentURL *string `json:"issue_comment_url,omitempty"`
IssueEventsURL *string `json:"issue_events_url,omitempty"`
IssuesURL *string `json:"issues_url,omitempty"`
KeysURL *string `json:"keys_url,omitempty"`
LabelsURL *string `json:"labels_url,omitempty"`
LanguagesURL *string `json:"languages_url,omitempty"`
MergesURL *string `json:"merges_url,omitempty"`
MilestonesURL *string `json:"milestones_url,omitempty"`
NotificationsURL *string `json:"notifications_url,omitempty"`
PullsURL *string `json:"pulls_url,omitempty"`
ReleasesURL *string `json:"releases_url,omitempty"`
StargazersURL *string `json:"stargazers_url,omitempty"`
StatusesURL *string `json:"statuses_url,omitempty"`
SubscribersURL *string `json:"subscribers_url,omitempty"`
SubscriptionURL *string `json:"subscription_url,omitempty"`
TagsURL *string `json:"tags_url,omitempty"`
TreesURL *string `json:"trees_url,omitempty"`
TeamsURL *string `json:"teams_url,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"`
}
func (r Repository) String() string {
return Stringify(r)
}
// RepositoryListOptions specifies the optional parameters to the
// RepositoriesService.List method.
type RepositoryListOptions struct {
// Visibility of repositories to list. Can be one of all, public, or private.
// Default: all
Visibility string `url:"visibility,omitempty"`
// List repos of given affiliation[s].
// Comma-separated list of values. Can include:
// * owner: Repositories that are owned by the authenticated user.
// * collaborator: Repositories that the user has been added to as a
// collaborator.
// * organization_member: Repositories that the user has access to through
// being a member of an organization. This includes every repository on
// every team that the user is on.
// Default: owner,collaborator,organization_member
Affiliation string `url:"affiliation,omitempty"`
// Type of repositories to list.
// Can be one of all, owner, public, private, member. Default: all
// Will cause a 422 error if used in the same request as visibility or
// affiliation.
Type string `url:"type,omitempty"`
// How to sort the repository list. Can be one of created, updated, pushed,
// full_name. Default: full_name
Sort string `url:"sort,omitempty"`
// Direction in which to sort repositories. Can be one of asc or desc.
// Default: when using full_name: asc; otherwise desc
Direction string `url:"direction,omitempty"`
ListOptions
}
// List the repositories for a user. Passing the empty string will list
// repositories for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-user-repositories
func (s *RepositoriesService) List(ctx context.Context, user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) {
var u string
if user != "" {
u = fmt.Sprintf("users/%v/repos", user)
} else {
u = "user/repos"
}
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 license support fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
var repos []*Repository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// RepositoryListByOrgOptions specifies the optional parameters to the
// RepositoriesService.ListByOrg method.
type RepositoryListByOrgOptions struct {
// Type of repositories to list. Possible values are: all, public, private,
// forks, sources, member. Default is "all".
Type string `url:"type,omitempty"`
ListOptions
}
// ListByOrg lists the repositories for an organization.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-organization-repositories
func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) {
u := fmt.Sprintf("orgs/%v/repos", 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 license support fully launches
req.Header.Set("Accept", mediaTypeLicensesPreview)
var repos []*Repository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// RepositoryListAllOptions specifies the optional parameters to the
// RepositoriesService.ListAll method.
type RepositoryListAllOptions struct {
// ID of the last repository seen
Since int `url:"since,omitempty"`
ListOptions
}
// ListAll lists all GitHub repositories in the order that they were created.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-all-public-repositories
func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAllOptions) ([]*Repository, *Response, error) {
u, err := addOptions("repositories", opt)
if err != nil {
return nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var repos []*Repository
resp, err := s.client.Do(ctx, req, &repos)
if err != nil {
return nil, resp, err
}
return repos, resp, nil
}
// Create a new repository. If an organization is specified, the new
// repository will be created under that org. If the empty string is
// specified, it will be created for the authenticated user.
//
// GitHub API docs: https://developer.github.com/v3/repos/#create
func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) {
var u string
if org != "" {
u = fmt.Sprintf("orgs/%v/repos", org)
} else {
u = "user/repos"
}
req, err := s.client.NewRequest("POST", u, repo)
if err != nil {
return nil, nil, err
}
r := new(Repository)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// Get fetches a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#get
func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) {
u := fmt.Sprintf("repos/%v/%v", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when the license support fully launches
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeSquashPreview}
req.Header.Set("Accept", strings.Join(acceptHeaders, ", "))
repository := new(Repository)
resp, err := s.client.Do(ctx, req, repository)
if err != nil {
return nil, resp, err
}
return repository, resp, nil
}
// GetByID fetches a repository.
//
// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id.
func (s *RepositoriesService) GetByID(ctx context.Context, id int) (*Repository, *Response, error) {
u := fmt.Sprintf("repositories/%d", id)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when the license support fully launches
// https://developer.github.com/v3/licenses/#get-a-repositorys-license
req.Header.Set("Accept", mediaTypeLicensesPreview)
repository := new(Repository)
resp, err := s.client.Do(ctx, req, repository)
if err != nil {
return nil, resp, err
}
return repository, resp, nil
}
// Edit updates a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#edit
func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repository *Repository) (*Repository, *Response, error) {
u := fmt.Sprintf("repos/%v/%v", owner, repo)
req, err := s.client.NewRequest("PATCH", u, repository)
if err != nil {
return nil, nil, err
}
// TODO: Remove this preview header after API is fully vetted.
req.Header.Set("Accept", mediaTypeSquashPreview)
r := new(Repository)
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}
// Delete a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository
func (s *RepositoriesService) Delete(ctx context.Context, owner, repo string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v", owner, repo)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}
// Contributor represents a repository contributor
type Contributor struct {
Login *string `json:"login,omitempty"`
ID *int `json:"id,omitempty"`
AvatarURL *string `json:"avatar_url,omitempty"`
GravatarID *string `json:"gravatar_id,omitempty"`
URL *string `json:"url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
FollowersURL *string `json:"followers_url,omitempty"`
FollowingURL *string `json:"following_url,omitempty"`
GistsURL *string `json:"gists_url,omitempty"`
StarredURL *string `json:"starred_url,omitempty"`
SubscriptionsURL *string `json:"subscriptions_url,omitempty"`
OrganizationsURL *string `json:"organizations_url,omitempty"`
ReposURL *string `json:"repos_url,omitempty"`
EventsURL *string `json:"events_url,omitempty"`
ReceivedEventsURL *string `json:"received_events_url,omitempty"`
Type *string `json:"type,omitempty"`
SiteAdmin *bool `json:"site_admin"`
Contributions *int `json:"contributions,omitempty"`
}
// ListContributorsOptions specifies the optional parameters to the
// RepositoriesService.ListContributors method.
type ListContributorsOptions struct {
// Include anonymous contributors in results or not
Anon string `url:"anon,omitempty"`
ListOptions
}
// ListContributors lists contributors for a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-contributors
func (s *RepositoriesService) ListContributors(ctx context.Context, owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository)
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 contributor []*Contributor
resp, err := s.client.Do(ctx, req, &contributor)
if err != nil {
return nil, nil, err
}
return contributor, resp, nil
}
// ListLanguages lists languages for the specified repository. The returned map
// specifies the languages and the number of bytes of code written in that
// language. For example:
//
// {
// "C": 78769,
// "Python": 7769
// }
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-languages
func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, repo string) (map[string]int, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/languages", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
languages := make(map[string]int)
resp, err := s.client.Do(ctx, req, &languages)
if err != nil {
return nil, resp, err
}
return languages, resp, nil
}
// ListTeams lists the teams for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-teams
func (s *RepositoriesService) ListTeams(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/teams", owner, repo)
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 teams []*Team
resp, err := s.client.Do(ctx, req, &teams)
if err != nil {
return nil, resp, err
}
return teams, resp, nil
}
// RepositoryTag represents a repository tag.
type RepositoryTag struct {
Name *string `json:"name,omitempty"`
Commit *Commit `json:"commit,omitempty"`
ZipballURL *string `json:"zipball_url,omitempty"`
TarballURL *string `json:"tarball_url,omitempty"`
}
// ListTags lists tags for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-tags
func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/tags", owner, repo)
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 tags []*RepositoryTag
resp, err := s.client.Do(ctx, req, &tags)
if err != nil {
return nil, resp, err
}
return tags, resp, nil
}
// Branch represents a repository branch
type Branch struct {
Name *string `json:"name,omitempty"`
Commit *RepositoryCommit `json:"commit,omitempty"`
Protected *bool `json:"protected,omitempty"`
}
// Protection represents a repository branch's protection.
type Protection struct {
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"`
RequiredPullRequestReviews *RequiredPullRequestReviews `json:"required_pull_request_reviews"`
EnforceAdmins *AdminEnforcement `json:"enforce_admins"`
Restrictions *BranchRestrictions `json:"restrictions"`
}
// ProtectionRequest represents a request to create/edit a branch's protection.
type ProtectionRequest struct {
RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"`
RequiredPullRequestReviews *RequiredPullRequestReviews `json:"required_pull_request_reviews"`
EnforceAdmins bool `json:"enforce_admins"`
Restrictions *BranchRestrictionsRequest `json:"restrictions"`
}
// RequiredStatusChecks represents the protection status of a individual branch.
type RequiredStatusChecks struct {
// Enforce required status checks for repository administrators. (Required.)
// Deprecated: Use EnforceAdmins instead.
IncludeAdmins bool `json:"include_admins"`
// Require branches to be up to date before merging. (Required.)
Strict bool `json:"strict"`
// The list of status checks to require in order to merge into this
// branch. (Required; use []string{} instead of nil for empty list.)
Contexts []string `json:"contexts"`
}
// RequiredPullRequestReviews represents the protection configuration for pull requests.
type RequiredPullRequestReviews struct {
// Enforce pull request reviews for repository administrators. (Required.)
// Deprecated: Use EnforceAdmins instead.
IncludeAdmins bool `json:"include_admins"`
}
// AdminEnforcement represents the configuration to enforce required status checks for repository administrators.
type AdminEnforcement struct {
URL *string `json:"url,omitempty"`
Enabled bool `json:"enabled"`
}
// BranchRestrictions represents the restriction that only certain users or
// teams may push to a branch.
type BranchRestrictions struct {
// The list of user logins with push access.
Users []*User `json:"users"`
// The list of team slugs with push access.
Teams []*Team `json:"teams"`
}
// BranchRestrictionsRequest represents the request to create/edit the
// restriction that only certain users or teams may push to a branch. It is
// separate from BranchRestrictions above because the request structure is
// different from the response structure.
type BranchRestrictionsRequest struct {
// The list of user logins with push access. (Required; use []string{} instead of nil for empty list.)
Users []string `json:"users"`
// The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.)
Teams []string `json:"teams"`
}
// ListBranches lists branches for the specified repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#list-branches
func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches", owner, repo)
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", mediaTypeProtectedBranchesPreview)
var branches []*Branch
resp, err := s.client.Do(ctx, req, &branches)
if err != nil {
return nil, resp, err
}
return branches, resp, nil
}
// GetBranch gets the specified branch for a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/#get-branch
func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string) (*Branch, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch)
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", mediaTypeProtectedBranchesPreview)
b := new(Branch)
resp, err := s.client.Do(ctx, req, b)
if err != nil {
return nil, resp, err
}
return b, resp, nil
}
// GetBranchProtection gets the protection of a given branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-branch-protection
func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*Protection, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
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", mediaTypeProtectedBranchesPreview)
p := new(Protection)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}
return p, resp, nil
}
// GetRequiredStatusChecks gets the required status checks for a given protected branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch
func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*RequiredStatusChecks, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch)
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", mediaTypeProtectedBranchesPreview)
p := new(RequiredStatusChecks)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}
return p, resp, nil
}
// ListRequiredStatusChecksContexts lists the required status checks contexts for a given protected branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch
func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Context, owner, repo, branch string) (contexts []string, resp *Response, err error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, branch)
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", mediaTypeProtectedBranchesPreview)
resp, err = s.client.Do(ctx, req, &contexts)
if err != nil {
return nil, resp, err
}
return contexts, resp, nil
}
// UpdateBranchProtection updates the protection of a given branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-branch-protection
func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *ProtectionRequest) (*Protection, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
req, err := s.client.NewRequest("PUT", u, preq)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches
req.Header.Set("Accept", mediaTypeProtectedBranchesPreview)
p := new(Protection)
resp, err := s.client.Do(ctx, req, p)
if err != nil {
return nil, resp, err
}
return p, resp, nil
}
// RemoveBranchProtection removes the protection of a given branch.
//
// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-branch-protection
func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch)
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", mediaTypeProtectedBranchesPreview)
return s.client.Do(ctx, req, nil)
}
// License gets the contents of a repository's license if one is detected.
//
// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license
func (s *RepositoriesService) License(ctx context.Context, owner, repo string) (*RepositoryLicense, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/license", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
r := &RepositoryLicense{}
resp, err := s.client.Do(ctx, req, r)
if err != nil {
return nil, resp, err
}
return r, resp, nil
}

View File

@@ -1,121 +0,0 @@
// Copyright 2013 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"
)
// ListCollaborators lists the GitHub users that have access to the repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list
func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo)
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 users []*User
resp, err := s.client.Do(ctx, req, &users)
if err != nil {
return nil, resp, err
}
return users, resp, nil
}
// IsCollaborator checks whether the specified GitHub user has collaborator
// access to the given repo.
// Note: This will return false if the user is not a collaborator OR the user
// is not a GitHub user.
//
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#get
func (s *RepositoriesService) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := s.client.Do(ctx, req, nil)
isCollab, err := parseBoolResponse(err)
return isCollab, resp, err
}
// RepositoryPermissionLevel represents the permission level an organization
// member has for a given repository.
type RepositoryPermissionLevel struct {
// Possible values: "admin", "write", "read", "none"
Permission *string `json:"permission,omitempty"`
User *User `json:"user,omitempty"`
}
// GetPermissionLevel retrieves the specific permission level a collaborator has for a given repository.
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level
func (s *RepositoriesService) GetPermissionLevel(ctx context.Context, owner, repo, user string) (*RepositoryPermissionLevel, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/collaborators/%v/permission", owner, repo, user)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
rpl := new(RepositoryPermissionLevel)
resp, err := s.client.Do(ctx, req, rpl)
if err != nil {
return nil, resp, err
}
return rpl, resp, nil
}
// RepositoryAddCollaboratorOptions specifies the optional parameters to the
// RepositoriesService.AddCollaborator method.
type RepositoryAddCollaboratorOptions struct {
// Permission specifies the permission to grant the user 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
//
// Default value is "push". This option is only valid for organization-owned repositories.
Permission string `json:"permission,omitempty"`
}
// AddCollaborator adds the specified GitHub user as collaborator to the given repo.
//
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator
func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview)
return s.client.Do(ctx, req, nil)
}
// RemoveCollaborator removes the specified GitHub user as collaborator from the given repo.
// Note: Does not return error if a valid user that is not a collaborator is removed.
//
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#remove-collaborator
func (s *RepositoriesService) RemoveCollaborator(ctx context.Context, owner, repo, user string) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,161 +0,0 @@
// Copyright 2013 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"
)
// RepositoryComment represents a comment for a commit, file, or line in a repository.
type RepositoryComment struct {
HTMLURL *string `json:"html_url,omitempty"`
URL *string `json:"url,omitempty"`
ID *int `json:"id,omitempty"`
CommitID *string `json:"commit_id,omitempty"`
User *User `json:"user,omitempty"`
Reactions *Reactions `json:"reactions,omitempty"`
CreatedAt *time.Time `json:"created_at,omitempty"`
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// User-mutable fields
Body *string `json:"body"`
// User-initialized fields
Path *string `json:"path,omitempty"`
Position *int `json:"position,omitempty"`
}
func (r RepositoryComment) String() string {
return Stringify(r)
}
// ListComments lists all the comments for the repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository
func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments", owner, repo)
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", mediaTypeReactionsPreview)
var comments []*RepositoryComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// ListCommitComments lists all the comments for a given commit SHA.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit
func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha)
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", mediaTypeReactionsPreview)
var comments []*RepositoryComment
resp, err := s.client.Do(ctx, req, &comments)
if err != nil {
return nil, resp, err
}
return comments, resp, nil
}
// CreateComment creates a comment for the given commit.
// Note: GitHub allows for comments to be created for non-existing files and positions.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#create-a-commit-comment
func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha)
req, err := s.client.NewRequest("POST", u, comment)
if err != nil {
return nil, nil, err
}
c := new(RepositoryComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// GetComment gets a single comment from a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment
func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string, id int) (*RepositoryComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id)
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", mediaTypeReactionsPreview)
c := new(RepositoryComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// UpdateComment updates the body of a single comment.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#update-a-commit-comment
func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int, comment *RepositoryComment) (*RepositoryComment, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id)
req, err := s.client.NewRequest("PATCH", u, comment)
if err != nil {
return nil, nil, err
}
c := new(RepositoryComment)
resp, err := s.client.Do(ctx, req, c)
if err != nil {
return nil, resp, err
}
return c, resp, nil
}
// DeleteComment deletes a single comment from a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/comments/#delete-a-commit-comment
func (s *RepositoriesService) DeleteComment(ctx context.Context, owner, repo string, id int) (*Response, error) {
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id)
req, err := s.client.NewRequest("DELETE", u, nil)
if err != nil {
return nil, err
}
return s.client.Do(ctx, req, nil)
}

View File

@@ -1,209 +0,0 @@
// Copyright 2013 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 (
"bytes"
"context"
"fmt"
"time"
)
// RepositoryCommit represents a commit in a repo.
// Note that it's wrapping a Commit, so author/committer information is in two places,
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details".
type RepositoryCommit struct {
SHA *string `json:"sha,omitempty"`
Commit *Commit `json:"commit,omitempty"`
Author *User `json:"author,omitempty"`
Committer *User `json:"committer,omitempty"`
Parents []Commit `json:"parents,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
URL *string `json:"url,omitempty"`
CommentsURL *string `json:"comments_url,omitempty"`
// Details about how many changes were made in this commit. Only filled in during GetCommit!
Stats *CommitStats `json:"stats,omitempty"`
// Details about which files, and how this commit touched. Only filled in during GetCommit!
Files []CommitFile `json:"files,omitempty"`
}
func (r RepositoryCommit) String() string {
return Stringify(r)
}
// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit.
type CommitStats struct {
Additions *int `json:"additions,omitempty"`
Deletions *int `json:"deletions,omitempty"`
Total *int `json:"total,omitempty"`
}
func (c CommitStats) String() string {
return Stringify(c)
}
// CommitFile represents a file modified in a commit.
type CommitFile struct {
SHA *string `json:"sha,omitempty"`
Filename *string `json:"filename,omitempty"`
Additions *int `json:"additions,omitempty"`
Deletions *int `json:"deletions,omitempty"`
Changes *int `json:"changes,omitempty"`
Status *string `json:"status,omitempty"`
Patch *string `json:"patch,omitempty"`
BlobURL *string `json:"blob_url,omitempty"`
RawURL *string `json:"raw_url,omitempty"`
ContentsURL *string `json:"contents_url,omitempty"`
}
func (c CommitFile) String() string {
return Stringify(c)
}
// CommitsComparison is the result of comparing two commits.
// See CompareCommits() for details.
type CommitsComparison struct {
BaseCommit *RepositoryCommit `json:"base_commit,omitempty"`
MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"`
// Head can be 'behind' or 'ahead'
Status *string `json:"status,omitempty"`
AheadBy *int `json:"ahead_by,omitempty"`
BehindBy *int `json:"behind_by,omitempty"`
TotalCommits *int `json:"total_commits,omitempty"`
Commits []RepositoryCommit `json:"commits,omitempty"`
Files []CommitFile `json:"files,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
PermalinkURL *string `json:"permalink_url,omitempty"`
DiffURL *string `json:"diff_url,omitempty"`
PatchURL *string `json:"patch_url,omitempty"`
URL *string `json:"url,omitempty"` // API URL.
}
func (c CommitsComparison) String() string {
return Stringify(c)
}
// CommitsListOptions specifies the optional parameters to the
// RepositoriesService.ListCommits method.
type CommitsListOptions struct {
// SHA or branch to start listing Commits from.
SHA string `url:"sha,omitempty"`
// Path that should be touched by the returned Commits.
Path string `url:"path,omitempty"`
// Author of by which to filter Commits.
Author string `url:"author,omitempty"`
// Since when should Commits be included in the response.
Since time.Time `url:"since,omitempty"`
// Until when should Commits be included in the response.
Until time.Time `url:"until,omitempty"`
ListOptions
}
// ListCommits lists the commits of a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#list
func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits", owner, repo)
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 commits []*RepositoryCommit
resp, err := s.client.Do(ctx, req, &commits)
if err != nil {
return nil, resp, err
}
return commits, resp, nil
}
// GetCommit fetches the specified commit, including all details about it.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit
// See also: https://developer.github.com//v3/git/commits/#get-a-single-commit provides the same functionality
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha)
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", mediaTypeGitSigningPreview)
commit := new(RepositoryCommit)
resp, err := s.client.Do(ctx, req, commit)
if err != nil {
return nil, resp, err
}
return commit, resp, nil
}
// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is
// supplied and no new commits have occurred, a 304 Unmodified response is returned.
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return "", nil, err
}
if lastSHA != "" {
req.Header.Set("If-None-Match", `"`+lastSHA+`"`)
}
req.Header.Set("Accept", mediaTypeV3SHA)
var buf bytes.Buffer
resp, err := s.client.Do(ctx, req, &buf)
if err != nil {
return "", resp, err
}
return buf.String(), resp, nil
}
// CompareCommits compares a range of commits with each other.
// todo: support media formats - https://github.com/google/go-github/issues/6
//
// GitHub API docs: https://developer.github.com/v3/repos/commits/index.html#compare-two-commits
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
comp := new(CommitsComparison)
resp, err := s.client.Do(ctx, req, comp)
if err != nil {
return nil, resp, err
}
return comp, resp, nil
}

View File

@@ -1,57 +0,0 @@
// Copyright 2017 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"
)
// Metric represents the different fields for one file in community health files.
type Metric struct {
Name *string `json:"name"`
Key *string `json:"key"`
URL *string `json:"url"`
HTMLURL *string `json:"html_url"`
}
// CommunityHealthFiles represents the different files in the community health metrics response.
type CommunityHealthFiles struct {
CodeOfConduct *Metric `json:"code_of_conduct"`
Contributing *Metric `json:"contributing"`
License *Metric `json:"license"`
Readme *Metric `json:"readme"`
}
// CommunityHealthMetrics represents a response containing the community metrics of a repository.
type CommunityHealthMetrics struct {
HealthPercentage *int `json:"health_percentage"`
Files *CommunityHealthFiles `json:"files"`
UpdatedAt *time.Time `json:"updated_at"`
}
// GetCommunityHealthMetrics retrieves all the community health metrics for a repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/community/#retrieve-community-health-metrics
func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo)
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
// TODO: remove custom Accept header when this API fully launches.
req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview)
metrics := &CommunityHealthMetrics{}
resp, err := s.client.Do(ctx, req, metrics)
if err != nil {
return nil, resp, err
}
return metrics, resp, nil
}

View File

@@ -1,266 +0,0 @@
// Copyright 2013 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.
// Repository contents API methods.
// GitHub API docs: https://developer.github.com/v3/repos/contents/
package github
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
)
// RepositoryContent represents a file or directory in a github repository.
type RepositoryContent struct {
Type *string `json:"type,omitempty"`
Encoding *string `json:"encoding,omitempty"`
Size *int `json:"size,omitempty"`
Name *string `json:"name,omitempty"`
Path *string `json:"path,omitempty"`
// Content contains the actual file content, which may be encoded.
// Callers should call GetContent which will decode the content if
// necessary.
Content *string `json:"content,omitempty"`
SHA *string `json:"sha,omitempty"`
URL *string `json:"url,omitempty"`
GitURL *string `json:"git_url,omitempty"`
HTMLURL *string `json:"html_url,omitempty"`
DownloadURL *string `json:"download_url,omitempty"`
}
// RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile.
type RepositoryContentResponse struct {
Content *RepositoryContent `json:"content,omitempty"`
Commit `json:"commit,omitempty"`
}
// RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile.
type RepositoryContentFileOptions struct {
Message *string `json:"message,omitempty"`
Content []byte `json:"content,omitempty"` // unencoded
SHA *string `json:"sha,omitempty"`
Branch *string `json:"branch,omitempty"`
Author *CommitAuthor `json:"author,omitempty"`
Committer *CommitAuthor `json:"committer,omitempty"`
}
// RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA,
// branch, or tag
type RepositoryContentGetOptions struct {
Ref string `url:"ref,omitempty"`
}
// String converts RepositoryContent to a string. It's primarily for testing.
func (r RepositoryContent) String() string {
return Stringify(r)
}
// GetContent returns the content of r, decoding it if necessary.
func (r *RepositoryContent) GetContent() (string, error) {
var encoding string
if r.Encoding != nil {
encoding = *r.Encoding
}
switch encoding {
case "base64":
c, err := base64.StdEncoding.DecodeString(*r.Content)
return string(c), err
case "":
if r.Content == nil {
return "", nil
}
return *r.Content, nil
default:
return "", fmt.Errorf("unsupported content encoding: %v", encoding)
}
}
// GetReadme gets the Readme file for the repository.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-the-readme
func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/readme", owner, repo)
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
}
readme := new(RepositoryContent)
resp, err := s.client.Do(ctx, req, readme)
if err != nil {
return nil, resp, err
}
return readme, resp, nil
}
// DownloadContents returns an io.ReadCloser that reads the contents of the
// specified file. This function will work with files of any size, as opposed
// to GetContents which is limited to 1 Mb files. It is the caller's
// responsibility to close the ReadCloser.
func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) {
dir := path.Dir(filepath)
filename := path.Base(filepath)
_, dirContents, _, err := s.GetContents(ctx, owner, repo, dir, opt)
if err != nil {
return nil, err
}
for _, contents := range dirContents {
if *contents.Name == filename {
if contents.DownloadURL == nil || *contents.DownloadURL == "" {
return nil, fmt.Errorf("No download link found for %s", filepath)
}
resp, err := s.client.client.Get(*contents.DownloadURL)
if err != nil {
return nil, err
}
return resp.Body, nil
}
}
return nil, fmt.Errorf("No file named %s found in %s", filename, dir)
}
// GetContents can return either the metadata and content of a single file
// (when path references a file) or the metadata of all the files and/or
// subdirectories of a directory (when path references a directory). To make it
// easy to distinguish between both result types and to mimic the API as much
// as possible, both result types will be returned but only one will contain a
// value and the other will be nil.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-contents
func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) {
escapedPath := (&url.URL{Path: path}).String()
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath)
u, err = addOptions(u, opt)
if err != nil {
return nil, nil, nil, err
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, nil, err
}
var rawJSON json.RawMessage
resp, err = s.client.Do(ctx, req, &rawJSON)
if err != nil {
return nil, nil, resp, err
}
fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent)
if fileUnmarshalError == nil {
return fileContent, nil, resp, nil
}
directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent)
if directoryUnmarshalError == nil {
return nil, directoryContent, resp, nil
}
return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError)
}
// CreateFile creates a new file in a repository at the given path and returns
// the commit and file metadata.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#create-a-file
func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, nil, err
}
createResponse := new(RepositoryContentResponse)
resp, err := s.client.Do(ctx, req, createResponse)
if err != nil {
return nil, resp, err
}
return createResponse, resp, nil
}
// UpdateFile updates a file in a repository at the given path and returns the
// commit and file metadata. Requires the blob SHA of the file being updated.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#update-a-file
func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path)
req, err := s.client.NewRequest("PUT", u, opt)
if err != nil {
return nil, nil, err
}
updateResponse := new(RepositoryContentResponse)
resp, err := s.client.Do(ctx, req, updateResponse)
if err != nil {
return nil, resp, err
}
return updateResponse, resp, nil
}
// DeleteFile deletes a file from a repository and returns the commit.
// Requires the blob SHA of the file to be deleted.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#delete-a-file
func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path)
req, err := s.client.NewRequest("DELETE", u, opt)
if err != nil {
return nil, nil, err
}
deleteResponse := new(RepositoryContentResponse)
resp, err := s.client.Do(ctx, req, deleteResponse)
if err != nil {
return nil, resp, err
}
return deleteResponse, resp, nil
}
// archiveFormat is used to define the archive type when calling GetArchiveLink.
type archiveFormat string
const (
// Tarball specifies an archive in gzipped tar format.
Tarball archiveFormat = "tarball"
// Zipball specifies an archive in zip format.
Zipball archiveFormat = "zipball"
)
// GetArchiveLink returns an URL to download a tarball or zipball archive for a
// repository. The archiveFormat can be specified by either the github.Tarball
// or github.Zipball constant.
//
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link
func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) {
u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat)
if opt != nil && opt.Ref != "" {
u += fmt.Sprintf("/%s", opt.Ref)
}
req, err := s.client.NewRequest("GET", u, nil)
if err != nil {
return nil, nil, err
}
var resp *http.Response
// Use http.DefaultTransport if no custom Transport is configured
ctx, 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, nil, err
}
resp.Body.Close()
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
}

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