mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 19:31:05 +00:00
Use gitlab generic webhooks instead of drone-ci-service (#620)
Benefits: - the webhook delivery history of the drone-ci-service is broken (no way to check if a webhook was successfully delivered by Gitlab) - drone-ci-service has limited events support (for example no comment or branch deleted event) - independent from drone integration in general
This commit is contained in:
parent
ce462ce4ac
commit
ed0a9fd756
5 changed files with 133 additions and 33 deletions
|
@ -388,6 +388,16 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (g *Gitlab) getTokenAndWebURL(link string) (token string, webURL string, err error) {
|
||||
uri, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
token = uri.Query().Get("access_token")
|
||||
webURL = fmt.Sprintf("%s://%s/api/hook", uri.Scheme, uri.Host)
|
||||
return token, webURL, nil
|
||||
}
|
||||
|
||||
// Activate activates a repository by adding a Post-commit hook and
|
||||
// a Public Deploy key, if applicable.
|
||||
func (g *Gitlab) Activate(ctx context.Context, user *model.User, repo *model.Repo, link string) error {
|
||||
|
@ -395,23 +405,31 @@ func (g *Gitlab) Activate(ctx context.Context, user *model.User, repo *model.Rep
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uri, err := url.Parse(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token := uri.Query().Get("access_token")
|
||||
webURL := fmt.Sprintf("%s://%s", uri.Scheme, uri.Host)
|
||||
|
||||
_repo, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: "WoodpeckerCIService"
|
||||
_, err = client.Services.SetDroneCIService(_repo.ID, &gitlab.SetDroneCIServiceOptions{
|
||||
Token: &token,
|
||||
DroneURL: &webURL,
|
||||
|
||||
token, webURL, err := g.getTokenAndWebURL(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(token) == 0 {
|
||||
return fmt.Errorf("no token found")
|
||||
}
|
||||
|
||||
_, _, err = client.Projects.AddProjectHook(_repo.ID, &gitlab.AddProjectHookOptions{
|
||||
URL: gitlab.String(webURL),
|
||||
Token: gitlab.String(token),
|
||||
PushEvents: gitlab.Bool(true),
|
||||
TagPushEvents: gitlab.Bool(true),
|
||||
MergeRequestsEvents: gitlab.Bool(true),
|
||||
DeploymentEvents: gitlab.Bool(true),
|
||||
EnableSSLVerification: gitlab.Bool(!g.SkipVerify),
|
||||
}, gitlab.WithContext(ctx))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -427,8 +445,44 @@ func (g *Gitlab) Deactivate(ctx context.Context, user *model.User, repo *model.R
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: "WoodpeckerCIService"
|
||||
_, err = client.Services.DeleteDroneCIService(_repo.ID, gitlab.WithContext(ctx))
|
||||
|
||||
_, webURL, err := g.getTokenAndWebURL(link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hookID := -1
|
||||
listProjectHooksOptions := &gitlab.ListProjectHooksOptions{
|
||||
PerPage: 10,
|
||||
Page: 1,
|
||||
}
|
||||
for {
|
||||
hooks, resp, err := client.Projects.ListProjectHooks(_repo.ID, listProjectHooksOptions, gitlab.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, hook := range hooks {
|
||||
if hook.URL == webURL {
|
||||
hookID = hook.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Exit the loop when we've seen all pages
|
||||
if resp.CurrentPage >= resp.TotalPages {
|
||||
break
|
||||
}
|
||||
|
||||
// Update the page number to get the next page
|
||||
listProjectHooksOptions.Page = resp.NextPage
|
||||
}
|
||||
|
||||
if hookID == -1 {
|
||||
return fmt.Errorf("could not find hook to delete")
|
||||
}
|
||||
|
||||
_, err = client.Projects.DeleteProjectHook(_repo.ID, hookID, gitlab.WithContext(ctx))
|
||||
|
||||
return err
|
||||
}
|
||||
|
|
50
server/remote/gitlab/testdata/projects.go
vendored
50
server/remote/gitlab/testdata/projects.go
vendored
|
@ -224,3 +224,53 @@ var project6Paylod = []byte(`
|
|||
}
|
||||
}
|
||||
`)
|
||||
|
||||
var project4PayloadHook = []byte(`
|
||||
{
|
||||
"id": 10717088,
|
||||
"url": "http://example.com/api/hook",
|
||||
"created_at": "2021-12-18T23:29:33.852Z",
|
||||
"push_events": true,
|
||||
"tag_push_events": true,
|
||||
"merge_requests_events": true,
|
||||
"repository_update_events": false,
|
||||
"enable_ssl_verification": true,
|
||||
"project_id": 4,
|
||||
"issues_events": false,
|
||||
"confidential_issues_events": false,
|
||||
"note_events": false,
|
||||
"confidential_note_events": null,
|
||||
"pipeline_events": false,
|
||||
"wiki_page_events": false,
|
||||
"deployment_events": true,
|
||||
"job_events": false,
|
||||
"releases_events": false,
|
||||
"push_events_branch_filter": null
|
||||
}
|
||||
`)
|
||||
|
||||
var project4PayloadHooks = []byte(`
|
||||
[
|
||||
{
|
||||
"id": 10717088,
|
||||
"url": "http://example.com/api/hook",
|
||||
"created_at": "2021-12-18T23:29:33.852Z",
|
||||
"push_events": true,
|
||||
"tag_push_events": true,
|
||||
"merge_requests_events": true,
|
||||
"repository_update_events": false,
|
||||
"enable_ssl_verification": true,
|
||||
"project_id": 4,
|
||||
"issues_events": false,
|
||||
"confidential_issues_events": false,
|
||||
"note_events": false,
|
||||
"confidential_note_events": null,
|
||||
"pipeline_events": false,
|
||||
"wiki_page_events": false,
|
||||
"deployment_events": true,
|
||||
"job_events": false,
|
||||
"releases_events": false,
|
||||
"push_events_branch_filter": null
|
||||
}
|
||||
]
|
||||
`)
|
||||
|
|
26
server/remote/gitlab/testdata/testdata.go
vendored
26
server/remote/gitlab/testdata/testdata.go
vendored
|
@ -15,13 +15,9 @@
|
|||
package testdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// NewServer setup a mock server for testing purposes.
|
||||
|
@ -52,23 +48,17 @@ func NewServer(t *testing.T) *httptest.Server {
|
|||
case "/api/v4/projects/brightbox/puppet":
|
||||
w.Write(project6Paylod)
|
||||
return
|
||||
case "/api/v4/projects/4/services/drone-ci":
|
||||
case "/api/v4/projects/4/hooks":
|
||||
switch r.Method {
|
||||
case "PUT":
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
opts := make(map[string]interface{})
|
||||
assert.NoError(t, json.Unmarshal(body, &opts))
|
||||
token, ok := opts["token"].(string)
|
||||
assert.True(t, ok)
|
||||
if token == "" {
|
||||
w.WriteHeader(404)
|
||||
} else {
|
||||
w.WriteHeader(201)
|
||||
}
|
||||
case "DELETE":
|
||||
case "GET":
|
||||
w.Write(project4PayloadHooks)
|
||||
case "POST":
|
||||
w.Write(project4PayloadHook)
|
||||
w.WriteHeader(201)
|
||||
}
|
||||
|
||||
return
|
||||
case "/api/v4/projects/4/hooks/10717088":
|
||||
w.WriteHeader(201)
|
||||
return
|
||||
case "/oauth/token":
|
||||
w.Write(accessTokenPayload)
|
||||
|
|
|
@ -43,7 +43,9 @@ func (cf *configFetcher) Fetch(ctx context.Context) (files []*remote.FileMeta, e
|
|||
// try to fetch 3 times, timeout is one second longer each time
|
||||
for i := 0; i < 3; i++ {
|
||||
files, err = cf.fetch(ctx, time.Second*time.Duration(configFetchTimeout), strings.TrimSpace(cf.repo.Config))
|
||||
log.Trace().Msgf("%d try failed: %v", i, err)
|
||||
if err != nil {
|
||||
log.Trace().Err(err).Msgf("%d. try failed", i+1)
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -52,10 +52,9 @@ func parse(raw string, fn SecretFunc) (*Token, error) {
|
|||
}
|
||||
|
||||
func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
||||
token := r.Header.Get("Authorization")
|
||||
|
||||
// first we attempt to get the token from the
|
||||
// authorization header.
|
||||
token := r.Header.Get("Authorization")
|
||||
if len(token) != 0 {
|
||||
log.Trace().Msgf("token.ParseRequest: found token in header: %s", token)
|
||||
bearer := token
|
||||
|
@ -65,6 +64,11 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
|||
return parse(bearer, fn)
|
||||
}
|
||||
|
||||
token = r.Header.Get("X-Gitlab-Token")
|
||||
if len(token) != 0 {
|
||||
return parse(token, fn)
|
||||
}
|
||||
|
||||
// then we attempt to get the token from the
|
||||
// access_token url query parameter
|
||||
token = r.FormValue("access_token")
|
||||
|
|
Loading…
Reference in a new issue