mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-06-02 21:49:25 +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
|
@ -388,6 +388,16 @@ func (g *Gitlab) Netrc(u *model.User, r *model.Repo) (*model.Netrc, error) {
|
||||||
}, nil
|
}, 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
|
// Activate activates a repository by adding a Post-commit hook and
|
||||||
// a Public Deploy key, if applicable.
|
// a Public Deploy key, if applicable.
|
||||||
func (g *Gitlab) Activate(ctx context.Context, user *model.User, repo *model.Repo, link string) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
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)
|
_repo, err := g.getProject(ctx, client, repo.Owner, repo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// TODO: "WoodpeckerCIService"
|
|
||||||
_, err = client.Services.SetDroneCIService(_repo.ID, &gitlab.SetDroneCIServiceOptions{
|
token, webURL, err := g.getTokenAndWebURL(link)
|
||||||
Token: &token,
|
if err != nil {
|
||||||
DroneURL: &webURL,
|
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),
|
EnableSSLVerification: gitlab.Bool(!g.SkipVerify),
|
||||||
}, gitlab.WithContext(ctx))
|
}, gitlab.WithContext(ctx))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,8 +445,44 @@ func (g *Gitlab) Deactivate(ctx context.Context, user *model.User, repo *model.R
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
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
|
package testdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewServer setup a mock server for testing purposes.
|
// 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":
|
case "/api/v4/projects/brightbox/puppet":
|
||||||
w.Write(project6Paylod)
|
w.Write(project6Paylod)
|
||||||
return
|
return
|
||||||
case "/api/v4/projects/4/services/drone-ci":
|
case "/api/v4/projects/4/hooks":
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case "PUT":
|
case "GET":
|
||||||
body, _ := io.ReadAll(r.Body)
|
w.Write(project4PayloadHooks)
|
||||||
opts := make(map[string]interface{})
|
case "POST":
|
||||||
assert.NoError(t, json.Unmarshal(body, &opts))
|
w.Write(project4PayloadHook)
|
||||||
token, ok := opts["token"].(string)
|
|
||||||
assert.True(t, ok)
|
|
||||||
if token == "" {
|
|
||||||
w.WriteHeader(404)
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(201)
|
|
||||||
}
|
|
||||||
case "DELETE":
|
|
||||||
w.WriteHeader(201)
|
w.WriteHeader(201)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
case "/api/v4/projects/4/hooks/10717088":
|
||||||
|
w.WriteHeader(201)
|
||||||
return
|
return
|
||||||
case "/oauth/token":
|
case "/oauth/token":
|
||||||
w.Write(accessTokenPayload)
|
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
|
// try to fetch 3 times, timeout is one second longer each time
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
files, err = cf.fetch(ctx, time.Second*time.Duration(configFetchTimeout), strings.TrimSpace(cf.repo.Config))
|
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) {
|
if errors.Is(err, context.DeadlineExceeded) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,10 +52,9 @@ func parse(raw string, fn SecretFunc) (*Token, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseRequest(r *http.Request, 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
|
// first we attempt to get the token from the
|
||||||
// authorization header.
|
// authorization header.
|
||||||
|
token := r.Header.Get("Authorization")
|
||||||
if len(token) != 0 {
|
if len(token) != 0 {
|
||||||
log.Trace().Msgf("token.ParseRequest: found token in header: %s", token)
|
log.Trace().Msgf("token.ParseRequest: found token in header: %s", token)
|
||||||
bearer := token
|
bearer := token
|
||||||
|
@ -65,6 +64,11 @@ func ParseRequest(r *http.Request, fn SecretFunc) (*Token, error) {
|
||||||
return parse(bearer, fn)
|
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
|
// then we attempt to get the token from the
|
||||||
// access_token url query parameter
|
// access_token url query parameter
|
||||||
token = r.FormValue("access_token")
|
token = r.FormValue("access_token")
|
||||||
|
|
Loading…
Reference in a new issue