Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c1ca728
enhance(scm/repo): mirror allowed events with sent events
ecrupper Aug 1, 2022
4937cb5
change auto push to utilize or logic
ecrupper Aug 3, 2022
b2d3870
update comments
ecrupper Aug 3, 2022
1152350
Merge branch 'master' into feat/hook-event-parity
ecrupper Aug 9, 2022
ef1653b
Merge branch 'master' into feat/hook-event-parity
ecrupper Aug 16, 2022
8f8d2f9
Merge branch 'master' into feat/hook-event-parity
ecrupper Aug 22, 2022
c80a2de
Merge branch 'master' into feat/hook-event-parity
ecrupper Sep 9, 2022
43de768
Merge branch 'master' into feat/hook-event-parity
ecrupper Sep 13, 2022
74e1f3b
move init hook db creation to after repo db creation
ecrupper Sep 14, 2022
131c712
Merge branch 'master' into feat/hook-event-parity
wass3r Sep 14, 2022
067f7f6
Merge branch 'master' into feat/hook-event-parity
ecrupper Sep 14, 2022
a4163e8
add required source id field and enable testing
ecrupper Sep 15, 2022
927fc5a
Merge branch 'master' into feat/hook-event-parity
ecrupper Sep 15, 2022
4afbf08
Merge branch 'master' into feat/hook-event-parity
wass3r Sep 15, 2022
032cc11
Merge branch 'master' into feat/hook-event-parity
wass3r Sep 16, 2022
cd7e317
Merge branch 'master' into feat/hook-event-parity
wass3r Sep 16, 2022
cacc48f
Merge branch 'main' into feat/hook-event-parity
jbrockopp Sep 20, 2022
d1d5b86
Merge branch 'main' into feat/hook-event-parity
jbrockopp Oct 14, 2022
5d74882
chore(release): v0.16.2 prep
ecrupper Nov 15, 2022
45a3f85
Merge branch 'main' of github.com:go-vela/server
ecrupper Nov 17, 2022
8de16dd
Merge branch 'main' of github.com:go-vela/server
ecrupper Dec 12, 2022
bd10141
Merge branch 'main' into feat/hook-event-parity
ecrupper Dec 13, 2022
49a79eb
Merge branch 'main' of github.com:go-vela/server
ecrupper Dec 15, 2022
721c051
Merge branch 'main' into feat/hook-event-parity
ecrupper Dec 15, 2022
264369f
Merge branch 'main' into feat/hook-event-parity
ecrupper Dec 16, 2022
ae5f917
allow plat admins to update webhooks on owner's behalf
ecrupper Dec 16, 2022
c72b765
Merge branch 'main' into feat/hook-event-parity
ecrupper Dec 19, 2022
a20ea2c
Merge branch 'main' into feat/hook-event-parity
ecrupper Feb 20, 2023
e5033da
Merge branch 'main' into feat/hook-event-parity
ecrupper Mar 7, 2023
05091b5
update last hook func
ecrupper Mar 7, 2023
4cfb2e3
Merge branch 'main' into feat/hook-event-parity
ecrupper Mar 13, 2023
3039e4f
Merge branch 'main' into feat/hook-event-parity
ecrupper Mar 15, 2023
746ad49
Merge branch 'main' into feat/hook-event-parity
plyr4 Mar 20, 2023
6594afc
Merge branch 'main' into feat/hook-event-parity
ecrupper Mar 21, 2023
7beb7a8
add logging for admin override
ecrupper Mar 21, 2023
e6f3fa8
move logging to capture owner too
ecrupper Mar 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion api/repo/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,11 @@ func CreateRepo(c *gin.Context) {
r.SetHash(dbRepo.GetHash())
}

hook := new(library.Hook)
// check if we should create the webhook
if c.Value("webhookvalidation").(bool) {
// send API call to create the webhook
_, err = scm.FromContext(c).Enable(u, r.GetOrg(), r.GetName(), r.GetHash())
hook, _, err = scm.FromContext(c).Enable(u, r)
if err != nil {
retErr := fmt.Errorf("unable to create webhook for %s: %w", r.GetFullName(), err)

Expand Down Expand Up @@ -296,6 +297,21 @@ func CreateRepo(c *gin.Context) {
r, _ = database.FromContext(c).GetRepoForOrg(r.GetOrg(), r.GetName())
}

// create init hook in the DB after repo has been added in order to capture its ID
if c.Value("webhookvalidation").(bool) {
// update initialization hook
hook.SetRepoID(r.GetID())
// create first hook for repo in the database
err = database.FromContext(c).CreateHook(hook)
if err != nil {
retErr := fmt.Errorf("unable to create initialization webhook for %s: %w", r.GetFullName(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
}

c.JSON(http.StatusCreated, r)
}

Expand Down
13 changes: 12 additions & 1 deletion api/repo/repair.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func RepairRepo(c *gin.Context) {
}

// send API call to create the webhook
_, err = scm.FromContext(c).Enable(u, r.GetOrg(), r.GetName(), r.GetHash())
hook, _, err := scm.FromContext(c).Enable(u, r)
if err != nil {
retErr := fmt.Errorf("unable to create webhook for %s: %w", r.GetFullName(), err)

Expand All @@ -95,6 +95,17 @@ func RepairRepo(c *gin.Context) {

return
}

hook.SetRepoID(r.GetID())

err = database.FromContext(c).CreateHook(hook)
if err != nil {
retErr := fmt.Errorf("unable to create initialization webhook for %s: %w", r.GetFullName(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
}

// if the repo was previously inactive, mark it as active
Expand Down
42 changes: 42 additions & 0 deletions api/repo/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/go-vela/server/router/middleware/org"
"github.com/go-vela/server/router/middleware/repo"
"github.com/go-vela/server/router/middleware/user"
"github.com/go-vela/server/scm"
"github.com/go-vela/server/util"
"github.com/go-vela/types/constants"
"github.com/go-vela/types/library"
Expand Down Expand Up @@ -238,6 +239,47 @@ func UpdateRepo(c *gin.Context) {
}
}

// grab last hook from repo to fetch the webhook ID
lastHook, err := database.FromContext(c).LastHookForRepo(r)
if err != nil {
retErr := fmt.Errorf("unable to retrieve last hook for repo %s: %w", r.GetFullName(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}

// if repo has no hook deliveries, skip webhook update
if lastHook.GetWebhookID() != 0 {
// if user is platform admin, fetch the repo owner token to make changes to webhook
if u.GetAdmin() {
// log admin override update repo hook
logrus.WithFields(logrus.Fields{
"org": o,
"repo": r.GetName(),
"user": u.GetName(),
}).Infof("platform admin updating repo webhook events for repo %s", r.GetFullName())

u, err = database.FromContext(c).GetUser(r.GetUserID())
if err != nil {
retErr := fmt.Errorf("unable to get repo owner of %s for platform admin webhook update: %w", r.GetFullName(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
}
// update webhook with new events
err = scm.FromContext(c).Update(u, r, lastHook.GetWebhookID())
if err != nil {
retErr := fmt.Errorf("unable to update repo webhook for %s: %w", r.GetFullName(), err)

util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
}

// send API call to update the repo
err = database.FromContext(c).UpdateRepo(r)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions scm/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
eventDeployment = "deployment"
eventIssueComment = "issue_comment"
eventRepository = "repository"
eventInitialize = "initialize"
)

var ctx = context.Background()
Expand Down
106 changes: 89 additions & 17 deletions scm/github/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,47 +151,119 @@ func (c *client) Disable(u *library.User, org, name string) error {
}

// Enable activates a repo by creating the webhook.
func (c *client) Enable(u *library.User, org, name, secret string) (string, error) {
func (c *client) Enable(u *library.User, r *library.Repo) (*library.Hook, string, error) {
c.Logger.WithFields(logrus.Fields{
"org": org,
"repo": name,
"org": r.GetOrg(),
"repo": r.GetName(),
"user": u.GetName(),
}).Tracef("creating repository webhook for %s/%s", org, name)
}).Tracef("creating repository webhook for %s/%s", r.GetOrg(), r.GetName())

// create GitHub OAuth client with user's token
client := c.newClientToken(*u.Token)

// always listen to repository events in case of repo name change
events := []string{eventRepository}

if r.GetAllowComment() {
events = append(events, eventIssueComment)
}

if r.GetAllowDeploy() {
events = append(events, eventDeployment)
}

if r.GetAllowPull() {
events = append(events, eventPullRequest)
}

if r.GetAllowPush() || r.GetAllowTag() {
events = append(events, eventPush)
}

// create the hook object to make the API call
hook := &github.Hook{
Events: []string{
eventPush,
eventPullRequest,
eventDeployment,
eventIssueComment,
eventRepository,
},
Events: events,
Config: map[string]interface{}{
"url": fmt.Sprintf("%s/webhook", c.config.ServerWebhookAddress),
"content_type": "form",
"secret": secret,
"secret": r.GetHash(),
},
Active: github.Bool(true),
}

// send API call to create the webhook
_, resp, err := client.Repositories.CreateHook(ctx, org, name, hook)
hookInfo, resp, err := client.Repositories.CreateHook(ctx, r.GetOrg(), r.GetName(), hook)

// create the first hook for the repo and record its ID from GitHub
webhook := new(library.Hook)
webhook.SetWebhookID(hookInfo.GetID())
webhook.SetSourceID(r.GetName() + "-" + eventInitialize)
webhook.SetCreated(hookInfo.GetCreatedAt().Unix())
webhook.SetEvent(eventInitialize)
webhook.SetNumber(1)

switch resp.StatusCode {
case http.StatusUnprocessableEntity:
return "", fmt.Errorf("repo already enabled")
return nil, "", fmt.Errorf("repo already enabled")
case http.StatusNotFound:
return "", fmt.Errorf("repo not found")
return nil, "", fmt.Errorf("repo not found")
}

// create the URL for the repo
url := fmt.Sprintf("%s/%s/%s", c.config.Address, org, name)
url := fmt.Sprintf("%s/%s/%s", c.config.Address, r.GetOrg(), r.GetName())

return webhook, url, err
}

// Update edits a repo webhook.
func (c *client) Update(u *library.User, r *library.Repo, hookID int64) error {
c.Logger.WithFields(logrus.Fields{
"org": r.GetOrg(),
"repo": r.GetName(),
"user": u.GetName(),
}).Tracef("updating repository webhook for %s/%s", r.GetOrg(), r.GetName())

// create GitHub OAuth client with user's token
client := c.newClientToken(*u.Token)

// always listen to repository events in case of repo name change
events := []string{eventRepository}

if r.GetAllowComment() {
events = append(events, eventIssueComment)
}

if r.GetAllowDeploy() {
events = append(events, eventDeployment)
}

if r.GetAllowPull() {
events = append(events, eventPullRequest)
}

if r.GetAllowPush() || r.GetAllowTag() {
events = append(events, eventPush)
}

// create the hook object to make the API call
hook := &github.Hook{
Events: events,
Config: map[string]interface{}{
"url": fmt.Sprintf("%s/webhook", c.config.ServerWebhookAddress),
"content_type": "form",
"secret": r.GetHash(),
},
Active: github.Bool(true),
}

// send API call to update the webhook
_, _, err := client.Repositories.EditHook(ctx, r.GetOrg(), r.GetName(), hookID, hook)

if err != nil {
return err
}

return url, err
return nil
}

// Status sends the commit status for the given SHA from the GitHub repo.
Expand Down
69 changes: 68 additions & 1 deletion scm/github/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -602,10 +602,26 @@ func TestGithub_Enable(t *testing.T) {
u.SetName("foo")
u.SetToken("bar")

wantHook := new(library.Hook)
wantHook.SetWebhookID(1)
wantHook.SetSourceID("bar-initialize")
wantHook.SetCreated(1315329987)
wantHook.SetNumber(1)
wantHook.SetEvent("initialize")

r := new(library.Repo)
r.SetID(1)
r.SetName("bar")
r.SetOrg("foo")
r.SetHash("secret")
r.SetAllowPush(true)
r.SetAllowPull(true)
r.SetAllowDeploy(true)

client, _ := NewTest(s.URL)

// run test
_, err := client.Enable(u, "foo", "bar", "secret")
got, _, err := client.Enable(u, r)

if resp.Code != http.StatusOK {
t.Errorf("Enable returned %v, want %v", resp.Code, http.StatusOK)
Expand All @@ -614,6 +630,57 @@ func TestGithub_Enable(t *testing.T) {
if err != nil {
t.Errorf("Enable returned err: %v", err)
}

if !reflect.DeepEqual(wantHook, got) {
t.Errorf("Enable returned hook %v, want %v", got, wantHook)
}
}

func TestGithub_Update(t *testing.T) {
// setup context
gin.SetMode(gin.TestMode)

resp := httptest.NewRecorder()
_, engine := gin.CreateTestContext(resp)

// setup mock server
engine.PATCH("/api/v3/repos/:org/:repo/hooks/:hook_id", func(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Status(http.StatusOK)
c.File("testdata/hook.json")
})

s := httptest.NewServer(engine)
defer s.Close()

// setup types
u := new(library.User)
u.SetName("foo")
u.SetToken("bar")

r := new(library.Repo)
r.SetID(1)
r.SetName("bar")
r.SetOrg("foo")
r.SetHash("secret")
r.SetAllowPush(true)
r.SetAllowPull(true)
r.SetAllowDeploy(true)

hookID := int64(1)

client, _ := NewTest(s.URL)

// run test
err := client.Update(u, r, hookID)

if resp.Code != http.StatusOK {
t.Errorf("Update returned %v, want %v", resp.Code, http.StatusOK)
}

if err != nil {
t.Errorf("Update returned err: %v", err)
}
}

func TestGithub_Status_Deployment(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion scm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ type Service interface {
Disable(*library.User, string, string) error
// Enable defines a function that activates
// a repo by creating the webhook.
Enable(*library.User, string, string, string) (string, error)
Enable(*library.User, *library.Repo) (*library.Hook, string, error)
// Update defines a function that updates
// a webhook for a specified repo.
Update(*library.User, *library.Repo, int64) error
// Status defines a function that sends the
// commit status for the given SHA from a repo.
Status(*library.User, *library.Build, string, string) error
Expand Down