Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ChangedFiles for Github & Gitlab PRs and Gitea pushes #697

Merged
merged 22 commits into from
Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
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
2 changes: 1 addition & 1 deletion docs/docs/20-usage/22-conditional-execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ when:

:::info
This feature is currently only available for GitHub, Gitlab and Gitea.
Pull requests aren't supported at the moment ([#697](https://github.com/woodpecker-ci/woodpecker/pull/697)).
Pull requests aren't supported by gitea at the moment ([go-gitea/gitea#18228](https://github.com/go-gitea/gitea/pull/18228)).
Path conditions are ignored for tag events.
:::

Expand Down
3 changes: 2 additions & 1 deletion server/api/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func BlockTilQueueHasRunningItem(c *gin.Context) {
func PostHook(c *gin.Context) {
_store := store.FromContext(c)

tmpRepo, build, err := server.Config.Services.Remote.Hook(c.Request)
tmpRepo, build, err := server.Config.Services.Remote.Hook(c, c.Request)
if err != nil {
msg := "failure to parse hook"
log.Debug().Err(err).Msg(msg)
Expand Down Expand Up @@ -288,6 +288,7 @@ func branchFiltered(build *model.Build, remoteYamlConfigs []*remote.FileMeta) (b
return false, nil
}
}

return true, nil
}

Expand Down
2 changes: 1 addition & 1 deletion server/remote/bitbucket/bitbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func (c *config) Branches(ctx context.Context, u *model.User, r *model.Repo) ([]

// Hook parses the incoming Bitbucket hook and returns the Repository and
// Build details. If the hook is unsupported nil values are returned.
func (c *config) Hook(req *http.Request) (*model.Repo, *model.Build, error) {
func (c *config) Hook(ctx context.Context, req *http.Request) (*model.Repo, *model.Build, error) {
return parseHook(req)
}

Expand Down
2 changes: 1 addition & 1 deletion server/remote/bitbucket/bitbucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ func Test_bitbucket(t *testing.T) {
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPush)

r, _, err := c.Hook(req)
r, _, err := c.Hook(ctx, req)
g.Assert(err).IsNil()
g.Assert(r.FullName).Equal("user_name/repo_name")
})
Expand Down
2 changes: 1 addition & 1 deletion server/remote/bitbucketserver/bitbucketserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func (c *Config) Deactivate(ctx context.Context, u *model.User, r *model.Repo, l
return client.DeleteHook(r.Owner, r.Name, link)
}

func (c *Config) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
func (c *Config) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model.Build, error) {
return parseHook(r, c.URL)
}

Expand Down
2 changes: 1 addition & 1 deletion server/remote/coding/coding.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func (c *Coding) Branches(ctx context.Context, u *model.User, r *model.Repo) ([]

// Hook parses the post-commit hook from the Request body and returns the
// required data in a standard format.
func (c *Coding) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
func (c *Coding) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model.Build, error) {
repo, build, err := parseHook(r)
if build != nil {
build.Avatar = c.resourceLink(build.Avatar)
Expand Down
2 changes: 1 addition & 1 deletion server/remote/coding/coding_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func Test_coding(t *testing.T) {
req.Header = http.Header{}
req.Header.Set(hookEvent, hookPush)

r, _, err := c.Hook(req)
r, _, err := c.Hook(ctx, req)
g.Assert(err).IsNil()
g.Assert(r.FullName).Equal("demo1/test1")
})
Expand Down
2 changes: 1 addition & 1 deletion server/remote/gitea/gitea.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ func (c *Gitea) Branches(ctx context.Context, u *model.User, r *model.Repo) ([]s

// Hook parses the incoming Gitea hook and returns the Repository and Build
// details. If the hook is unsupported nil values are returned.
func (c *Gitea) Hook(r *http.Request) (*model.Repo, *model.Build, error) {
func (c *Gitea) Hook(ctx context.Context, r *http.Request) (*model.Repo, *model.Build, error) {
return parseHook(r)
}

Expand Down
6 changes: 3 additions & 3 deletions server/remote/gitea/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"code.gitea.io/sdk/gitea"

"github.com/woodpecker-ci/woodpecker/server/model"
"github.com/woodpecker-ci/woodpecker/shared/utils"
)

// helper function that converts a Gitea repository to a Woodpecker repository.
Expand Down Expand Up @@ -110,15 +111,14 @@ func buildFromPush(hook *pushHook) *model.Build {
}

func getChangedFilesFromPushHook(hook *pushHook) []string {
files := make([]string, 0)

files := make([]string, 0, len(hook.Commits)*4)
6543 marked this conversation as resolved.
Show resolved Hide resolved
for _, c := range hook.Commits {
files = append(files, c.Added...)
files = append(files, c.Removed...)
files = append(files, c.Modified...)
}

return files
return utils.DedupStrings(files)
}

// helper function that extracts the Build data from a Gitea tag hook
Expand Down
169 changes: 26 additions & 143 deletions server/remote/github/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
package github

import (
"fmt"
"strings"

"github.com/google/go-github/v39/github"

"github.com/woodpecker-ci/woodpecker/server/model"
Expand All @@ -44,7 +41,7 @@ const (
const (
headRefs = "refs/pull/%d/head" // pull request unmerged
mergeRefs = "refs/pull/%d/merge" // pull request merged with base
refspec = "%s:%s"
refSpec = "%s:%s"
)

// convertStatus is a helper function used to convert a Woodpecker status to a
Expand Down Expand Up @@ -85,19 +82,19 @@ func convertDesc(status model.StatusValue) string {
// structure to the common Woodpecker repository structure.
func convertRepo(from *github.Repository, private bool) *model.Repo {
repo := &model.Repo{
Owner: *from.Owner.Login,
Name: *from.Name,
FullName: *from.FullName,
Link: *from.HTMLURL,
IsSCMPrivate: *from.Private,
Clone: *from.CloneURL,
Avatar: *from.Owner.AvatarURL,
Name: from.GetName(),
FullName: from.GetFullName(),
Link: from.GetHTMLURL(),
IsSCMPrivate: from.GetPrivate(),
Clone: from.GetCloneURL(),
Branch: from.GetDefaultBranch(),
Owner: from.GetOwner().GetLogin(),
Avatar: from.GetOwner().GetAvatarURL(),
Perm: convertPerm(from.GetPermissions()),
SCMKind: model.RepoGit,
Branch: defaultBranch,
Perm: convertPerm(from),
}
if from.DefaultBranch != nil {
repo.Branch = *from.DefaultBranch
if len(repo.Branch) == 0 {
repo.Branch = defaultBranch
}
if private {
repo.IsSCMPrivate = true
Expand All @@ -107,11 +104,11 @@ func convertRepo(from *github.Repository, private bool) *model.Repo {

// convertPerm is a helper function used to convert a GitHub repository
// permissions to the common Woodpecker permissions structure.
func convertPerm(from *github.Repository) *model.Perm {
func convertPerm(perm map[string]bool) *model.Perm {
return &model.Perm{
Admin: from.Permissions["admin"],
Push: from.Permissions["push"],
Pull: from.Permissions["pull"],
Admin: perm["admin"],
Push: perm["push"],
Pull: perm["pull"],
}
}

Expand Down Expand Up @@ -139,143 +136,29 @@ func convertTeamList(from []*github.Organization) []*model.Team {
// to the common Woodpecker repository structure.
func convertTeam(from *github.Organization) *model.Team {
return &model.Team{
Login: *from.Login,
Avatar: *from.AvatarURL,
Login: from.GetLogin(),
Avatar: from.GetAvatarURL(),
}
}

// convertRepoHook is a helper function used to extract the Repository details
// from a webhook and convert to the common Woodpecker repository structure.
func convertRepoHook(from *webhook) *model.Repo {
func convertRepoHook(eventRepo *github.PushEventRepository) *model.Repo {
repo := &model.Repo{
Owner: from.Repo.Owner.Login,
Name: from.Repo.Name,
FullName: from.Repo.FullName,
Link: from.Repo.HTMLURL,
IsSCMPrivate: from.Repo.Private,
Clone: from.Repo.CloneURL,
Branch: from.Repo.DefaultBranch,
Owner: eventRepo.GetOwner().GetLogin(),
Name: eventRepo.GetName(),
FullName: eventRepo.GetFullName(),
Link: eventRepo.GetHTMLURL(),
IsSCMPrivate: eventRepo.GetPrivate(),
Clone: eventRepo.GetCloneURL(),
Branch: eventRepo.GetDefaultBranch(),
SCMKind: model.RepoGit,
}
if repo.Branch == "" {
repo.Branch = defaultBranch
}
if repo.Owner == "" { // legacy webhooks
repo.Owner = from.Repo.Owner.Name
}
if repo.FullName == "" {
repo.FullName = repo.Owner + "/" + repo.Name
}
return repo
}

// convertPushHook is a helper function used to extract the Build details
// from a push webhook and convert to the common Woodpecker Build structure.
func convertPushHook(from *webhook) *model.Build {
files := getChangedFilesFromWebhook(from)
build := &model.Build{
Event: model.EventPush,
Commit: from.Head.ID,
Ref: from.Ref,
Link: from.Head.URL,
Branch: strings.Replace(from.Ref, "refs/heads/", "", -1),
Message: from.Head.Message,
Email: from.Head.Author.Email,
Avatar: from.Sender.Avatar,
Author: from.Sender.Login,
Remote: from.Repo.CloneURL,
Sender: from.Sender.Login,
ChangedFiles: files,
}

if len(build.Author) == 0 {
build.Author = from.Head.Author.Username
}

// if len(build.Email) == 0 {
// TODO: default to gravatar?
// }

if strings.HasPrefix(build.Ref, "refs/tags/") {
// just kidding, this is actually a tag event. Why did this come as a push
// event we'll never know!
build.Event = model.EventTag
// For tags, if the base_ref (tag's base branch) is set, we're using it
// as build's branch so that we can filter events base on it
if strings.HasPrefix(from.BaseRef, "refs/heads/") {
build.Branch = strings.Replace(from.BaseRef, "refs/heads/", "", -1)
}

// tags should not have changed files
build.ChangedFiles = nil
}

return build
}

// convertPushHook is a helper function used to extract the Build details
// from a deploy webhook and convert to the common Woodpecker Build structure.
func convertDeployHook(from *webhook) *model.Build {
build := &model.Build{
Event: model.EventDeploy,
Commit: from.Deployment.Sha,
Link: from.Deployment.URL,
Message: from.Deployment.Desc,
Avatar: from.Sender.Avatar,
Author: from.Sender.Login,
Ref: from.Deployment.Ref,
Branch: from.Deployment.Ref,
Deploy: from.Deployment.Env,
Sender: from.Sender.Login,
}
// if the ref is a sha or short sha we need to manuallyconstruct the ref.
if strings.HasPrefix(build.Commit, build.Ref) || build.Commit == build.Ref {
build.Branch = from.Repo.DefaultBranch
if build.Branch == "" {
build.Branch = defaultBranch
}
build.Ref = fmt.Sprintf("refs/heads/%s", build.Branch)
}
// if the ref is a branch we should make sure it has refs/heads prefix
if !strings.HasPrefix(build.Ref, "refs/") { // branch or tag
build.Ref = fmt.Sprintf("refs/heads/%s", build.Branch)
}
return build
}

// convertPullHook is a helper function used to extract the Build details
// from a pull request webhook and convert to the common Woodpecker Build structure.
func convertPullHook(from *webhook, merge bool) *model.Build {
build := &model.Build{
Event: model.EventPull,
Commit: from.PullRequest.Head.SHA,
Link: from.PullRequest.HTMLURL,
Ref: fmt.Sprintf(headRefs, from.PullRequest.Number),
Branch: from.PullRequest.Base.Ref,
Message: from.PullRequest.Title,
Author: from.PullRequest.User.Login,
Avatar: from.PullRequest.User.Avatar,
Title: from.PullRequest.Title,
Sender: from.Sender.Login,
Remote: from.PullRequest.Head.Repo.CloneURL,
Refspec: fmt.Sprintf(refspec,
from.PullRequest.Head.Ref,
from.PullRequest.Base.Ref,
),
}
if merge {
build.Ref = fmt.Sprintf(mergeRefs, from.PullRequest.Number)
}
return build
}

func getChangedFilesFromWebhook(from *webhook) []string {
var files []string
files = append(files, from.Head.Added...)
files = append(files, from.Head.Removed...)
files = append(files, from.Head.Modified...)
if len(files) == 0 {
files = make([]string, 0)
}
return files
}
Loading