Skip to content

Commit

Permalink
Add release event trigger (#3226)
Browse files Browse the repository at this point in the history
Supersedes #764 

Bitbucket does not support release webhooks.

---------

Co-authored-by: Patrick Schratz <patrick.schratz@gmail.com>
  • Loading branch information
qwerty287 and pat-s authored Jan 30, 2024
1 parent da4bd8b commit 9df572e
Show file tree
Hide file tree
Showing 35 changed files with 628 additions and 18 deletions.
1 change: 1 addition & 0 deletions cli/secret/secret_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,6 @@ func secretCreate(c *cli.Context) error {
var defaultSecretEvents = []string{
woodpecker.EventPush,
woodpecker.EventTag,
woodpecker.EventRelease,
woodpecker.EventDeploy,
}
5 changes: 5 additions & 0 deletions cmd/server/docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -3937,6 +3937,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"is_prerelease": {
"type": "boolean"
},
"message": {
"type": "string"
},
Expand Down Expand Up @@ -4383,6 +4386,7 @@ const docTemplate = `{
"pull_request",
"pull_request_closed",
"tag",
"release",
"deployment",
"cron",
"manual"
Expand All @@ -4392,6 +4396,7 @@ const docTemplate = `{
"EventPull",
"EventPullClosed",
"EventTag",
"EventRelease",
"EventDeploy",
"EventCron",
"EventManual"
Expand Down
1 change: 1 addition & 0 deletions docs/docs/20-usage/15-terminiology/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- `pull_request`: A pull request event is triggered when a pull request is opened or a new commit is pushed to it.
- `pull_request_closed`: A pull request closed event is triggered when a pull request is closed or merged.
- `tag`: A tag event is triggered when a tag is pushed.
- `release`: A release event is triggered when a release is created.
- `manual`: A manual event is triggered when a user manually triggers a pipeline.
- `cron`: A cron event is triggered when a cron job is executed.

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/20-usage/20-workflow-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ when:

#### `event`

Available events: `push`, `pull_request`, `pull_request_closed`, `tag`, `deployment`, `cron`, `manual`
Available events: `push`, `pull_request`, `pull_request_closed`, `tag`, `release`, `deployment`, `cron`, `manual`

Execute a step if the build event is a `tag`:

Expand Down
1 change: 1 addition & 0 deletions docs/docs/20-usage/50-environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ This is the reference list of all environment variables available to your pipeli
| `CI_COMMIT_AUTHOR` | commit author username |
| `CI_COMMIT_AUTHOR_EMAIL` | commit author email address |
| `CI_COMMIT_AUTHOR_AVATAR` | commit author avatar |
| `CI_COMMIT_PRERELEASE` | release is a pre-release (empty if event is not `release`) |
| | **Current pipeline** |
| `CI_PIPELINE_NUMBER` | pipeline number |
| `CI_PIPELINE_PARENT` | number of parent pipeline |
Expand Down
1 change: 1 addition & 0 deletions docs/docs/30-administration/11-forges/10-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
| Event: Push | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Event: Tag | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Event: Pull-Request | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Event: Release | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
| Event: Deploy | :white_check_mark: | :x: | :x: | :x: |
| [Multiple workflows](../../20-usage/25-workflows.md) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| [when.path filter](../../20-usage/20-workflow-syntax.md#path) | :white_check_mark: | :white_check_mark: | :white_check_mark: | :x: |
1 change: 1 addition & 0 deletions pipeline/frontend/metadata/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
EventPull = "pull_request"
EventPullClosed = "pull_request_closed"
EventTag = "tag"
EventRelease = "release"
EventDeploy = "deployment"
EventCron = "cron"
EventManual = "manual"
Expand Down
5 changes: 4 additions & 1 deletion pipeline/frontend/metadata/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,12 @@ func (m *Metadata) Environ() map[string]string {
// TODO Deprecated, remove in 3.x
"CI_COMMIT_URL": m.Curr.ForgeURL,
}
if m.Curr.Event == EventTag || strings.HasPrefix(m.Curr.Commit.Ref, "refs/tags/") {
if m.Curr.Event == EventTag || m.Curr.Event == EventRelease || strings.HasPrefix(m.Curr.Commit.Ref, "refs/tags/") {
params["CI_COMMIT_TAG"] = strings.TrimPrefix(m.Curr.Commit.Ref, "refs/tags/")
}
if m.Curr.Event == EventRelease {
params["CI_COMMIT_PRERELEASE"] = strconv.FormatBool(m.Curr.Commit.IsPrerelease)
}
if m.Curr.Event == EventPull {
params["CI_COMMIT_PULL_REQUEST"] = pullRegexp.FindString(m.Curr.Commit.Ref)
params["CI_COMMIT_PULL_REQUEST_LABELS"] = strings.Join(m.Curr.Commit.PullRequestLabels, ",")
Expand Down
1 change: 1 addition & 0 deletions pipeline/frontend/metadata/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type (
Author Author `json:"author,omitempty"`
ChangedFiles []string `json:"changed_files,omitempty"`
PullRequestLabels []string `json:"labels,omitempty"`
IsPrerelease bool `json:"is_prerelease,omitempty"`
}

// Author defines runtime metadata for a commit author.
Expand Down
2 changes: 1 addition & 1 deletion server/api/pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ func PostPipeline(c *gin.Context) {
if event, ok := c.GetQuery("event"); ok {
pl.Event = model.WebhookEvent(event)

if err := model.ValidateWebhookEvent(pl.Event); err != nil {
if err := pl.Event.Validate(); err != nil {
_ = c.AbortWithError(http.StatusBadRequest, err)
return
}
Expand Down
76 changes: 76 additions & 0 deletions server/forge/gitea/fixtures/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -1071,3 +1071,79 @@ const HookPullRequestClosed = `
"review": null
}
`

const HookRelease = `
{
"action": "published",
"release": {
"id": 48,
"tag_name": "0.0.5",
"target_commitish": "main",
"name": "Version 0.0.5",
"body": "",
"url": "https://git.xxx/api/v1/repos/anbraten/demo/releases/48",
"html_url": "https://git.xxx/anbraten/demo/releases/tag/0.0.5",
"tarball_url": "https://git.xxx/anbraten/demo/archive/0.0.5.tar.gz",
"zipball_url": "https://git.xxx/anbraten/demo/archive/0.0.5.zip",
"draft": false,
"prerelease": false,
"created_at": "2022-02-09T20:23:05Z",
"published_at": "2022-02-09T20:23:05Z",
"author": {"id":1,"login":"anbraten","full_name":"Anton Bracke","email":"anbraten@noreply.xxx","avatar_url":"https://git.xxx/user/avatar/anbraten/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-03-21T10:04:48Z","restricted":false,"active":false,"prohibit_login":false,"location":"world","website":"https://xxx","description":"","visibility":"public","followers_count":1,"following_count":1,"starred_repos_count":1,"username":"anbraten"},
"assets": []
},
"repository": {
"id": 77,
"owner": {"id":1,"login":"anbraten","full_name":"Anton Bracke","email":"anbraten@noreply.xxx","avatar_url":"https://git.xxx/user/avatar/anbraten/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-03-21T10:04:48Z","restricted":false,"active":false,"prohibit_login":false,"location":"world","website":"https://xxx","description":"","visibility":"public","followers_count":1,"following_count":1,"starred_repos_count":1,"username":"anbraten"},
"name": "demo",
"full_name": "anbraten/demo",
"description": "",
"empty": false,
"private": true,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 59,
"html_url": "https://git.xxx/anbraten/demo",
"ssh_url": "ssh://git@git.xxx:22/anbraten/demo.git",
"clone_url": "https://git.xxx/anbraten/demo.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 1,
"watchers_count": 1,
"open_issues_count": 2,
"open_pr_counter": 2,
"release_counter": 4,
"default_branch": "main",
"archived": false,
"created_at": "2021-08-30T20:54:13Z",
"updated_at": "2022-01-09T01:29:23Z",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": true,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": false,
"has_pull_requests": true,
"has_projects": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"default_merge_style": "squash",
"avatar_url": "",
"internal": false,
"mirror_interval": ""
},
"sender": {"id":1,"login":"anbraten","full_name":"Anbraten","email":"anbraten@noreply.xxx","avatar_url":"https://git.xxx/user/avatar/anbraten/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2018-03-21T10:04:48Z","restricted":false,"active":false,"prohibit_login":false,"location":"World","website":"https://xxx","description":"","visibility":"public","followers_count":1,"following_count":1,"starred_repos_count":1,"username":"anbraten"}
}
`
39 changes: 39 additions & 0 deletions server/forge/gitea/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,15 @@ func (c *Gitea) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model.
return nil, nil, err
}

if pipeline != nil && pipeline.Event == model.EventRelease && pipeline.Commit == "" {
tagName := strings.Split(pipeline.Ref, "/")[2]
sha, err := c.getTagCommitSHA(ctx, repo, tagName)
if err != nil {
return nil, nil, err
}
pipeline.Commit = sha
}

if pipeline != nil && (pipeline.Event == model.EventPull || pipeline.Event == model.EventPullClosed) && len(pipeline.ChangedFiles) == 0 {
index, err := strconv.ParseInt(strings.Split(pipeline.Ref, "/")[2], 10, 64)
if err != nil {
Expand Down Expand Up @@ -655,6 +664,36 @@ func (c *Gitea) getChangedFilesForPR(ctx context.Context, repo *model.Repo, inde
})
}

func (c *Gitea) getTagCommitSHA(ctx context.Context, repo *model.Repo, tagName string) (string, error) {
_store, ok := store.TryFromContext(ctx)
if !ok {
log.Error().Msg("could not get store from context")
return "", nil
}

repo, err := _store.GetRepoNameFallback(repo.ForgeRemoteID, repo.FullName)
if err != nil {
return "", err
}

user, err := _store.GetUser(repo.UserID)
if err != nil {
return "", err
}

client, err := c.newClientToken(ctx, user.Token)
if err != nil {
return "", err
}

tag, _, err := client.GetTag(repo.Owner, repo.Name, tagName)
if err != nil {
return "", err
}

return tag.Commit.SHA, nil
}

func (c *Gitea) perPage(ctx context.Context) int {
if c.pageSize == 0 {
client, err := c.newClientToken(ctx, "")
Expand Down
25 changes: 25 additions & 0 deletions server/forge/gitea/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,25 @@ func pipelineFromPullRequest(hook *pullRequestHook) *model.Pipeline {
return pipeline
}

func pipelineFromRelease(hook *releaseHook) *model.Pipeline {
avatar := expandAvatar(
hook.Repo.HTMLURL,
fixMalformedAvatar(hook.Sender.AvatarURL),
)

return &model.Pipeline{
Event: model.EventRelease,
Ref: fmt.Sprintf("refs/tags/%s", hook.Release.TagName),
ForgeURL: hook.Release.HTMLURL,
Branch: hook.Release.Target,
Message: fmt.Sprintf("created release %s", hook.Release.Title),
Avatar: avatar,
Author: hook.Sender.UserName,
Sender: hook.Sender.UserName,
IsPrerelease: hook.Release.IsPrerelease,
}
}

// helper function that parses a push hook from a read closer.
func parsePush(r io.Reader) (*pushHook, error) {
push := new(pushHook)
Expand All @@ -188,6 +207,12 @@ func parsePullRequest(r io.Reader) (*pullRequestHook, error) {
return pr, err
}

func parseRelease(r io.Reader) (*releaseHook, error) {
pr := new(releaseHook)
err := json.NewDecoder(r).Decode(pr)
return pr, err
}

// fixMalformedAvatar is a helper function that fixes an avatar url if malformed
// (currently a known bug with gitea)
func fixMalformedAvatar(url string) string {
Expand Down
22 changes: 21 additions & 1 deletion server/forge/gitea/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
hookPush = "push"
hookCreated = "create"
hookPullRequest = "pull_request"
hookRelease = "release"

actionOpen = "opened"
actionSync = "synchronized"
Expand All @@ -40,7 +41,7 @@ const (
refTag = "tag"
)

// parseHook parses a Gitea hook from an http.Request request and returns
// parseHook parses a Gitea hook from an http.Request and returns
// Repo and Pipeline detail. If a hook type is unsupported nil values are returned.
func parseHook(r *http.Request) (*model.Repo, *model.Pipeline, error) {
hookType := r.Header.Get(hookEvent)
Expand All @@ -51,6 +52,8 @@ func parseHook(r *http.Request) (*model.Repo, *model.Pipeline, error) {
return parseCreatedHook(r.Body)
case hookPullRequest:
return parsePullRequestHook(r.Body)
case hookRelease:
return parseReleaseHook(r.Body)
}
log.Debug().Msgf("unsupported hook type: '%s'", hookType)
return nil, nil, &types.ErrIgnoreEvent{Event: hookType}
Expand Down Expand Up @@ -118,3 +121,20 @@ func parsePullRequestHook(payload io.Reader) (*model.Repo, *model.Pipeline, erro
pipeline = pipelineFromPullRequest(pr)
return repo, pipeline, err
}

// parseReleaseHook parses a release hook and returns the Repo and Pipeline details.
func parseReleaseHook(payload io.Reader) (*model.Repo, *model.Pipeline, error) {
var (
repo *model.Repo
pipeline *model.Pipeline
)

release, err := parseRelease(payload)
if err != nil {
return nil, nil, err
}

repo = toRepo(release.Repo)
pipeline = pipelineFromRelease(release)
return repo, pipeline, err
}
11 changes: 11 additions & 0 deletions server/forge/gitea/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,17 @@ func Test_parser(t *testing.T) {
g.Assert(err).IsNil()
g.Assert(b.Event).Equal(model.EventPullClosed)
})
g.It("should handle release hook", func() {
buf := bytes.NewBufferString(fixtures.HookRelease)
req, _ := http.NewRequest("POST", "/hook", buf)
req.Header = http.Header{}
req.Header.Set(hookEvent, hookRelease)
r, b, err := parseHook(req)
g.Assert(err).IsNil()
g.Assert(r).IsNotNil()
g.Assert(b).IsNotNil()
g.Assert(b.Event).Equal(model.EventRelease)
})
})
})
}
7 changes: 7 additions & 0 deletions server/forge/gitea/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,10 @@ type pullRequestHook struct {
Repo *gitea.Repository `json:"repository"`
Sender *gitea.User `json:"sender"`
}

type releaseHook struct {
Action string `json:"action"`
Repo *gitea.Repository `json:"repository"`
Sender *gitea.User `json:"sender"`
Release *gitea.Release
}
Loading

0 comments on commit 9df572e

Please sign in to comment.