From 1e28c84241bb1b4c740c78250517e874817586fb Mon Sep 17 00:00:00 2001 From: Billy Lynch Date: Tue, 7 Jan 2020 13:36:17 -0500 Subject: [PATCH] PullRequest Resource: Derive API URL from PR URL. Currently, we always use the default API URL for all requests, which means that the PR resource cannot be used with GitHub Enterprise or self hosted GitLab instances. This changes the behavior to build the API Base URL from the PR resource if the URL is not the default: PR URL | API Base URL ---------------------------|------------- https://github.com | https://api.github.com https://github.example.com | https://github.example.com/v3/api https://gitlab.com | https://gitlab.com https://gitlab.example.com | https://gitlab.example.com GitHub Documentation: https://developer.github.com/v3/#schema GitHub Enterprise Documentation: https://developer.github.com/enterprise/2.17/v3/#schema GitLab Documentation: https://developer.github.com/enterprise/2.17/v3/#current-version Fixes #1816 --- docs/resources.md | 80 ++++++++++++++++++++++++------------- pkg/pullrequest/scm.go | 14 +++++++ pkg/pullrequest/scm_test.go | 66 ++++++++++++++++-------------- 3 files changed, 103 insertions(+), 57 deletions(-) diff --git a/docs/resources.md b/docs/resources.md index cffe9b77a4b..911a626f057 100644 --- a/docs/resources.md +++ b/docs/resources.md @@ -17,11 +17,11 @@ For example: - [Syntax](#syntax) - [Using Resources](#using-resources) - - [Variable substitution](#variable-substitution) - - [Controlling where resources are mounted](#controlling-where-resources-are-mounted) - - [Overriding where resources are copied from](#overriding-where-resources-are-copied-from) - - [Resource Status](#resource-status) - - [Optional Resources](#optional-resources) + - [Variable substitution](#variable-substitution) + - [Controlling where resources are mounted](#controlling-where-resources-are-mounted) + - [Overriding where resources are copied from](#overriding-where-resources-are-copied-from) + - [Resource Status](#resource-status) + - [Optional Resources](#optional-resources) - [Resource types](#resource-types) - [Git Resource](#git-resource) - [Pull Request Resource](#pull-request-resource) @@ -51,8 +51,9 @@ following fields: - Optional: - [`params`](#resource-types) - Parameters which are specific to each type of `PipelineResource` - - [`optional`](#optional-resources) - Boolean flag to mark a resource optional - (by default, `optional` is set to `false` making resources mandatory). + - [`optional`](#optional-resources) - Boolean flag to mark a resource + optional (by default, `optional` is set to `false` making resources + mandatory). [kubernetes-overview]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields @@ -227,8 +228,9 @@ resourcesResult: ### Optional Resources -By default, a resource is declared as mandatory unless `optional` is set `true` for that resource. -Resources declared as `optional` in a `Task` does not have be specified in `TaskRun`. +By default, a resource is declared as mandatory unless `optional` is set `true` +for that resource. Resources declared as `optional` in a `Task` does not have be +specified in `TaskRun`. ```yaml apiVersion: tekton.dev/v1alpha1 @@ -243,11 +245,12 @@ spec: optional: true ``` -You can refer to different examples demonstrating usage of optional resources in `Task` and `Condition`: +You can refer to different examples demonstrating usage of optional resources in +`Task` and `Condition`: -- [Task](../examples/taskruns/optional-resources.yaml) -- [Cluster Task](../examples/taskruns/optional-resources-with-clustertask.yaml) -- [Condition](../examples/pipelineruns/conditional-pipelinerun-with-optional-resources.yaml) +- [Task](../examples/taskruns/optional-resources.yaml) +- [Cluster Task](../examples/taskruns/optional-resources-with-clustertask.yaml) +- [Condition](../examples/pipelineruns/conditional-pipelinerun-with-optional-resources.yaml) ## Resource Types @@ -283,14 +286,15 @@ Params that can be added are the following: clone. You can use this to control what commit [or branch](#using-a-branch) is used. _If no revision is specified, the resource will default to `latest` from `master`._ -1. `submodules`: defines if the resource should initialize and - fetch the submodules, value is either `true` or `false`. _If not - specified, this will default to true_ +1. `submodules`: defines if the resource should initialize and fetch the + submodules, value is either `true` or `false`. _If not specified, this will + default to true_ 1. `depth`: performs a [shallow clone][git-depth] where only the most recent - commit(s) will be fetched. If set to `'0'`, all commits will be fetched. - _If not specified, the default depth is 1._ -1. `sslVerify`: defines if [http.sslVerify][git-http.sslVerify] should be set to `true` or `false` - in the global git config. _Defaults to `true` if omitted._ + commit(s) will be fetched. If set to `'0'`, all commits will be fetched. _If + not specified, the default depth is 1._ +1. `sslVerify`: defines if [http.sslVerify][git-http.sslVerify] should be set + to `true` or `false` in the global git config. _Defaults to `true` if + omitted._ [git-rev]: https://git-scm.com/docs/gitrevisions#_specifying_revisions [git-depth]: https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt @@ -396,10 +400,11 @@ Comments describe a pull request comment. They are represented as a set of json files. Other pull request information can be found in `pr.json`. This is a read-only -resource. Users should use other subresources (labels, comments, etc) -to interact with the PR. +resource. Users should use other subresources (labels, comments, etc) to +interact with the PR. -For an example of the output this resource provides, see [`example`](../cmd/pullrequest-init/example). +For an example of the output this resource provides, see +[`example`](../cmd/pullrequest-init/example). To create a pull request resource using the `PipelineResource` CRD: @@ -426,14 +431,13 @@ metadata: type: Opaque data: token: github_personal_access_token_secret # in base64 encoded form - ``` Params that can be added are the following: 1. `url`: represents the location of the pull request to fetch. -1. `provider`: represents the SCM provider to use. This will be "guessed" based on the url if not set. - Valid values are `github` or `gitlab` today. +1. `provider`: represents the SCM provider to use. This will be "guessed" based + on the url if not set. Valid values are `github` or `gitlab` today. #### Statuses @@ -442,11 +446,31 @@ https://godoc.org/github.com/jenkins-x/go-scm/scm#State #### Pull Request -The `pullRequest` resource will look for GitHub or Gitlab OAuth authentication tokens in -spec secrets with a field name called `authToken`. +The `pullRequest` resource will look for GitHub or Gitlab OAuth authentication +tokens in spec secrets with a field name called `authToken`. URLs should be of the form: https://github.com/tektoncd/pipeline/pull/1 +#### Self hosted / Enterprise instances + +The PullRequest resource works with self hosted or enterprise GitHub/GitLab +instances. Simply provide the pull request URL and set the `provider` parameter. + +```yaml +apiVersion: tekton.dev/v1alpha1 +kind: PipelineResource +metadata: + name: wizzbang-pr + namespace: default +spec: + type: pullRequest + params: + - name: url + value: https://github.example.com/wizzbangcorp/wizzbang/pulls/1 + - name: provider + value: github +``` + ### Image Resource An `image` resource represents an image that lives in a remote repository. It is diff --git a/pkg/pullrequest/scm.go b/pkg/pullrequest/scm.go index 34579a85cc6..0301beb8299 100644 --- a/pkg/pullrequest/scm.go +++ b/pkg/pullrequest/scm.go @@ -75,6 +75,13 @@ func githubHandlerFromURL(u *url.URL, token string, logger *zap.SugaredLogger) ( ) client := github.NewDefault() + if u.Host != "github.com" { + var err error + client, err = github.New(fmt.Sprintf("%s://%s/api/v3", u.Scheme, u.Host)) + if err != nil { + return nil, fmt.Errorf("error creating client: %w", err) + } + } ownerRepo := fmt.Sprintf("%s/%s", owner, repo) h := NewHandler(logger, client, ownerRepo, prNumber) if token != "" { @@ -110,6 +117,13 @@ func gitlabHandlerFromURL(u *url.URL, token string, logger *zap.SugaredLogger) ( zap.String("pr", prNum), ) client := gitlab.NewDefault() + if u.Host != "gitlab.com" { + var err error + client, err = gitlab.New(fmt.Sprintf("%s://%s", u.Scheme, u.Host)) + if err != nil { + return nil, fmt.Errorf("error creating client: %w", err) + } + } if token != "" { client.Client = &http.Client{ Transport: &gitlabClient{ diff --git a/pkg/pullrequest/scm_test.go b/pkg/pullrequest/scm_test.go index 5604f66ddb4..59d2e666e1a 100644 --- a/pkg/pullrequest/scm_test.go +++ b/pkg/pullrequest/scm_test.go @@ -26,39 +26,44 @@ import ( func TestNewSCMHandler(t *testing.T) { tests := []struct { - name string - raw string - wantRepo string - wantNum int - wantErr bool + name string + raw string + wantBaseURL string + wantRepo string + wantNum int + wantErr bool }{ { - name: "github", - raw: "https://github.com/foo/bar/pull/1", - wantRepo: "foo/bar", - wantNum: 1, - wantErr: false, + name: "github", + raw: "https://github.com/foo/bar/pull/1", + wantBaseURL: "https://api.github.com/", + wantRepo: "foo/bar", + wantNum: 1, + wantErr: false, }, { - name: "custom github", - raw: "https://github.tekton.dev/foo/baz/pull/2", - wantRepo: "foo/baz", - wantNum: 2, - wantErr: false, + name: "custom github", + raw: "https://github.tekton.dev/foo/baz/pull/2", + wantBaseURL: "https://github.tekton.dev/api/v3/", + wantRepo: "foo/baz", + wantNum: 2, + wantErr: false, }, { - name: "gitlab", - raw: "https://gitlab.com/foo/bar/merge_requests/3", - wantRepo: "foo/bar", - wantNum: 3, - wantErr: false, + name: "gitlab", + raw: "https://gitlab.com/foo/bar/merge_requests/3", + wantBaseURL: "https://gitlab.com/", + wantRepo: "foo/bar", + wantNum: 3, + wantErr: false, }, { - name: "gitlab multi-level", - raw: "https://gitlab.com/foo/bar/baz/merge_requests/3", - wantRepo: "foo/bar/baz", - wantNum: 3, - wantErr: false, + name: "gitlab multi-level", + raw: "https://gitlab.com/foo/bar/baz/merge_requests/3", + wantBaseURL: "https://gitlab.com/", + wantRepo: "foo/bar/baz", + wantNum: 3, + wantErr: false, }, { name: "unsupported", @@ -77,11 +82,14 @@ func TestNewSCMHandler(t *testing.T) { } return } - if !(got.prNum == tt.wantNum) { - t.Errorf("NewSCMHandler() = %v, want %v", got, tt.wantNum) + if got.prNum != tt.wantNum { + t.Errorf("NewSCMHandler() [pr num] = %v, want %v", got, tt.wantNum) } - if !(got.repo == tt.wantRepo) { - t.Errorf("NewSCMHandler() = %v, want %v", got, tt.wantRepo) + if got.repo != tt.wantRepo { + t.Errorf("NewSCMHandler() [repo] = %v, want %v", got, tt.wantRepo) + } + if baseURL := got.client.BaseURL.String(); baseURL != tt.wantBaseURL { + t.Errorf("NewSCMHandler() [base url] = %v, want %v", baseURL, tt.wantBaseURL) } }) }