Skip to content

Commit

Permalink
Add GitLab PR generator for applicationset (#9264)
Browse files Browse the repository at this point in the history
Signed-off-by: Ivo Verberk <ivo.verberk@gmail.com>
  • Loading branch information
iverberk committed May 19, 2022
1 parent c2a9a7f commit dd4dbe2
Show file tree
Hide file tree
Showing 11 changed files with 630 additions and 0 deletions.
8 changes: 8 additions & 0 deletions applicationset/generators/pull_request.go
Expand Up @@ -87,6 +87,14 @@ func (g *PullRequestGenerator) selectServiceProvider(ctx context.Context, genera
}
return pullrequest.NewGithubService(ctx, token, providerConfig.API, providerConfig.Owner, providerConfig.Repo, providerConfig.Labels)
}
if generatorConfig.GitLab != nil {
providerConfig := generatorConfig.GitLab
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
if err != nil {
return nil, fmt.Errorf("error fetching Secret token: %v", err)
}
return pullrequest.NewGitLabService(ctx, token, providerConfig.API, providerConfig.Project, providerConfig.Labels)
}
if generatorConfig.Gitea != nil {
providerConfig := generatorConfig.Gitea
token, err := g.getSecretRef(ctx, providerConfig.TokenRef, applicationSetInfo.Namespace)
Expand Down
@@ -0,0 +1,89 @@
[
{
"id": 35385049,
"iid": 15442,
"project_id": 278964,
"title": "Draft: Use structured logging for DB load balancer",
"description": "",
"state": "opened",
"created_at": "2019-08-20T10:58:54.413Z",
"updated_at": "2019-08-20T12:01:49.849Z",
"merged_by": null,
"merged_at": null,
"closed_by": null,
"closed_at": null,
"target_branch": "master",
"source_branch": "use-structured-logging-for-db-load-balancer",
"user_notes_count": 1,
"upvotes": 0,
"downvotes": 0,
"assignee": {
"id": 4088036,
"name": "Hordur Freyr Yngvason",
"username": "hfyngvason",
"state": "active",
"avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png",
"web_url": "https://gitlab.com/hfyngvason"
},
"author": {
"id": 4088036,
"name": "Hordur Freyr Yngvason",
"username": "hfyngvason",
"state": "active",
"avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png",
"web_url": "https://gitlab.com/hfyngvason"
},
"assignees": [
{
"id": 4088036,
"name": "Hordur Freyr Yngvason",
"username": "hfyngvason",
"state": "active",
"avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/4088036/avatar.png",
"web_url": "https://gitlab.com/hfyngvason"
}
],
"reviewers": [
{
"id": 2535118,
"name": "Thong Kuah",
"username": "tkuah",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/f7b51bdd49a4914d29504d7ff4c3f7b9?s=80&d=identicon",
"web_url": "https://gitlab.com/tkuah"
}
],
"source_project_id": 278964,
"target_project_id": 278964,
"labels": [
"backend",
"backstage",
"database",
"database::review pending",
"group::autodevops and kubernetes"
],
"work_in_progress": true,
"milestone": null,
"merge_when_pipeline_succeeds": false,
"merge_status": "can_be_merged",
"sha": "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7",
"merge_commit_sha": null,
"discussion_locked": null,
"should_remove_source_branch": null,
"force_remove_source_branch": true,
"reference": "!15442",
"web_url": "https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/15442",
"time_stats": {
"time_estimate": 0,
"total_time_spent": 0,
"human_time_estimate": null,
"human_total_time_spent": null
},
"squash": true,
"task_completion_status": {
"count": 12,
"completed_count": 0
},
"approvals_before_merge": 1
}
]
77 changes: 77 additions & 0 deletions applicationset/services/pull_request/gitlab.go
@@ -0,0 +1,77 @@
package pull_request

import (
"context"
"fmt"
"os"

gitlab "github.com/xanzy/go-gitlab"
)

type GitLabService struct {
client *gitlab.Client
project string
labels []string
}

var _ PullRequestService = (*GitLabService)(nil)

func NewGitLabService(ctx context.Context, token, url, project string, labels []string) (PullRequestService, error) {
var clientOptionFns []gitlab.ClientOptionFunc

// Set a custom Gitlab base URL if one is provided
if url != "" {
clientOptionFns = append(clientOptionFns, gitlab.WithBaseURL(url))
}

if token == "" {
token = os.Getenv("GITLAB_TOKEN")
}

client, err := gitlab.NewClient(token, clientOptionFns...)
if err != nil {
return nil, fmt.Errorf("error creating Gitlab client: %v", err)
}

return &GitLabService{
client: client,
project: project,
labels: labels,
}, nil
}

func (g *GitLabService) List(ctx context.Context) ([]*PullRequest, error) {

// Filter the merge requests on labels, if they are specified.
var labels *gitlab.Labels
if len(g.labels) > 0 {
labels = (*gitlab.Labels)(&g.labels)
}

opts := &gitlab.ListProjectMergeRequestsOptions{
ListOptions: gitlab.ListOptions{
PerPage: 100,
},
Labels: labels,
}

pullRequests := []*PullRequest{}
for {
mrs, resp, err := g.client.MergeRequests.ListProjectMergeRequests(g.project, opts)
if err != nil {
return nil, fmt.Errorf("error listing merge requests for project '%s': %v", g.project, err)
}
for _, mr := range mrs {
pullRequests = append(pullRequests, &PullRequest{
Number: mr.IID,
Branch: mr.SourceBranch,
HeadSHA: mr.SHA,
})
}
if resp.NextPage == 0 {
break
}
opts.Page = resp.NextPage
}
return pullRequests, nil
}
103 changes: 103 additions & 0 deletions applicationset/services/pull_request/gitlab_test.go
@@ -0,0 +1,103 @@
package pull_request

import (
"context"
"io"
"net/http"
"net/http/httptest"
"os"
"testing"

"github.com/stretchr/testify/assert"
)

func writeMRListResponse(t *testing.T, w io.Writer) {
f, err := os.Open("fixtures/gitlab_mr_list_response.json")
if err != nil {
t.Fatalf("error opening fixture file: %v", err)
}

if _, err = io.Copy(w, f); err != nil {
t.Fatalf("error writing response: %v", err)
}
}

func TestGitLabServiceCustomBaseURL(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()

path := "/api/v4/projects/278964/merge_requests"

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, path+"?per_page=100", r.URL.RequestURI())
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", nil)
assert.NoError(t, err)

_, err = svc.List(context.Background())
assert.NoError(t, err)
}

func TestGitLabServiceToken(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()

path := "/api/v4/projects/278964/merge_requests"

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "token-123", r.Header.Get("Private-Token"))
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "token-123", server.URL, "278964", nil)
assert.NoError(t, err)

_, err = svc.List(context.Background())
assert.NoError(t, err)
}

func TestList(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()

path := "/api/v4/projects/278964/merge_requests"

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, path+"?per_page=100", r.URL.RequestURI())
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{})
assert.NoError(t, err)

prs, err := svc.List(context.Background())
assert.NoError(t, err)
assert.Len(t, prs, 1)
assert.Equal(t, prs[0].Number, 15442)
assert.Equal(t, prs[0].Branch, "use-structured-logging-for-db-load-balancer")
assert.Equal(t, prs[0].HeadSHA, "2fc4e8b972ff3208ec63b6143e34ad67ff343ad7")
}

func TestListWithLabels(t *testing.T) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)
defer server.Close()

path := "/api/v4/projects/278964/merge_requests"

mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, path+"?labels=feature%2Cready&per_page=100", r.URL.RequestURI())
writeMRListResponse(t, w)
})

svc, err := NewGitLabService(context.Background(), "", server.URL, "278964", []string{"feature", "ready"})
assert.NoError(t, err)

_, err = svc.List(context.Background())
assert.NoError(t, err)
}
34 changes: 34 additions & 0 deletions docs/operator-manual/applicationset/Generators-Pull-Request.md
Expand Up @@ -53,6 +53,40 @@ spec:
* `tokenRef`: A `Secret` name and key containing the GitHub access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `labels`: Labels is used to filter the PRs that you want to target. (Optional)

## GitLab

Specify the project from which to fetch the GitLab merge requests.

```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapps
spec:
generators:
- pullRequest:
gitlab:
# The GitLab project.
project: myproject
# For self-hosted GitLab (optional)
api: https://git.example.com/
# Reference to a Secret containing an access token. (optional)
tokenRef:
secretName: gitlab-token
key: token
# Labels is used to filter the MRs that you want to target. (optional)
labels:
- preview
requeueAfterSeconds: 1800
template:
# ...
```

* `project`: Required name of the GitLab project.
* `api`: If using self-hosted GitLab, the URL to access it. (Optional)
* `tokenRef`: A `Secret` name and key containing the GitLab access token to use for requests. If not specified, will make anonymous requests which have a lower rate limit and can only see public repositories. (Optional)
* `labels`: Labels is used to filter the MRs that you want to target. (Optional)

## Gitea

Specify the repository from which to fetch the Gitea Pull requests.
Expand Down

0 comments on commit dd4dbe2

Please sign in to comment.