Skip to content

Commit

Permalink
Implement GetFiles Func of Bitbucket Server Provider for CEL
Browse files Browse the repository at this point in the history
implemented GetFiles func of Bitbucket server which lists changed
files on an event. it also solves the issue in Bitbucket server where
pathChanged() function in CEL expression was not working because
the func was not implemented. this does slove the issue.

https://issues.redhat.com/browse/SRVKP-5834

Signed-off-by: Zaki Shaikh <[email protected]>
  • Loading branch information
zakisk committed Feb 6, 2025
1 parent c6a7841 commit 910906d
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 2 deletions.
75 changes: 74 additions & 1 deletion pkg/provider/bitbucketserver/bitbucketserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,80 @@ func (v *Provider) GetConfig() *info.ProviderConfig {
}
}

func (v *Provider) GetFiles(_ context.Context, _ *info.Event) (changedfiles.ChangedFiles, error) {
func (v *Provider) GetFiles(ctx context.Context, runevent *info.Event) (changedfiles.ChangedFiles, error) {
limit := 100
OrgAndRepo := fmt.Sprintf("%s/%s", runevent.Organization, runevent.Repository)
if runevent.TriggerTarget == triggertype.PullRequest {
opts := &scm.ListOptions{Page: 1, Size: limit}
changedFiles := changedfiles.ChangedFiles{}
for {
changes, _, err := v.ScmClient.PullRequests.ListChanges(ctx, OrgAndRepo, runevent.PullRequestNumber, opts)
if err != nil {
return changedfiles.ChangedFiles{}, fmt.Errorf("failed to list changes for pull request: %w", err)
}

for _, c := range changes {
changedFiles.All = append(changedFiles.All, c.Path)
if c.Added {
changedFiles.Added = append(changedFiles.Added, c.Path)
}
if c.Modified {
changedFiles.Modified = append(changedFiles.Modified, c.Path)
}
if c.Renamed {
changedFiles.Renamed = append(changedFiles.Renamed, c.Path)
}
if c.Deleted {
changedFiles.Deleted = append(changedFiles.Deleted, c.Path)
}
}

// In the Jenkins-x/go-scm package, the `isLastPage` field is not available, and the value of
// `response.Page.Last` is set to `0`. Therefore, to determine if there are more items to fetch,
// we can check if the length of the currently fetched items is less than the specified limit.
// If the length is less than the limit, it indicates that there are no more items to retrieve.
if len(changes) < limit {
break
}

opts.Page++
}
return changedFiles, nil
}

if runevent.TriggerTarget == triggertype.Push {
opts := &scm.ListOptions{Page: 1, Size: limit}
changedFiles := changedfiles.ChangedFiles{}
for {
changes, _, err := v.ScmClient.Git.ListChanges(ctx, OrgAndRepo, runevent.SHA, opts)
if err != nil {
return changedfiles.ChangedFiles{}, fmt.Errorf("failed to list changes for commit %s: %w", runevent.SHA, err)
}

for _, c := range changes {
changedFiles.All = append(changedFiles.All, c.Path)
if c.Added {
changedFiles.Added = append(changedFiles.Added, c.Path)
}
if c.Modified {
changedFiles.Modified = append(changedFiles.Modified, c.Path)
}
if c.Renamed {
changedFiles.Renamed = append(changedFiles.Renamed, c.Path)
}
if c.Deleted {
changedFiles.Deleted = append(changedFiles.Deleted, c.Path)
}
}

if len(changes) < limit {
break
}

opts.Page++
}
return changedFiles, nil
}
return changedfiles.ChangedFiles{}, nil
}

Expand Down
164 changes: 163 additions & 1 deletion pkg/provider/bitbucketserver/bitbucketserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ import (
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"hash"
"net/http"
"path/filepath"
"strings"
"testing"

bbv1 "github.com/gfleury/go-bitbucket-v1"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
bbtest "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/bitbucketserver/test"

bbv1 "github.com/gfleury/go-bitbucket-v1"
"go.uber.org/zap"
zapobserver "go.uber.org/zap/zaptest/observer"
"gotest.tools/v3/assert"
Expand Down Expand Up @@ -541,3 +544,162 @@ func TestRemoveLastSegment(t *testing.T) {
})
}
}

func TestGetFiles(t *testing.T) {
pushEvent := &info.Event{
SHA: "IAMSHA123",
Organization: "pac",
Repository: "test",
TriggerTarget: triggertype.Push,
}
prEvent := &info.Event{
Organization: "pac",
Repository: "test",
TriggerTarget: triggertype.PullRequest,
PullRequestNumber: 1,
}

pushFiles := []*bbtest.DiffStat{
{
Path: bbtest.DiffPath{ToString: "added.md"},
Type: "ADD",
},
{
Path: bbtest.DiffPath{ToString: "modified.txt"},
Type: "MODIFY",
},
{
Path: bbtest.DiffPath{ToString: "renamed.yaml"},
Type: "MOVE",
},
{
Path: bbtest.DiffPath{ToString: "deleted.go"},
Type: "DELETE",
},
}

pullRequestFiles := []*bbtest.DiffStat{
{
Path: bbtest.DiffPath{ToString: "added.go"},
Type: "ADD",
},
{
Path: bbtest.DiffPath{ToString: "modified.yaml"},
Type: "MODIFY",
},
{
Path: bbtest.DiffPath{ToString: "renamed.txt"},
Type: "MOVE",
},
{
Path: bbtest.DiffPath{ToString: "deleted.md"},
Type: "DELETE",
},
}

tests := []struct {
name string
event *info.Event
changeFiles []*bbtest.DiffStat
wantAddedFilesCount int
wantDeletedFilesCount int
wantModifiedFilesCount int
wantRenamedFilesCount int
wantError bool
errMsg string
}{
{
name: "good/push event",
event: pushEvent,
changeFiles: pushFiles,
wantAddedFilesCount: 1,
wantDeletedFilesCount: 1,
wantModifiedFilesCount: 1,
wantRenamedFilesCount: 1,
},
{
name: "bad/push event",
event: pushEvent,
wantAddedFilesCount: 0,
wantDeletedFilesCount: 0,
wantModifiedFilesCount: 0,
wantRenamedFilesCount: 0,
wantError: true,
errMsg: "failed to list changes for commit IAMSHA123: not Authorized",
},
{
name: "good/pull_request event",
event: prEvent,
changeFiles: pullRequestFiles,
wantAddedFilesCount: 1,
wantDeletedFilesCount: 1,
wantModifiedFilesCount: 1,
wantRenamedFilesCount: 1,
},
{
name: "bad/pull_request event",
event: prEvent,
wantAddedFilesCount: 0,
wantDeletedFilesCount: 0,
wantModifiedFilesCount: 0,
wantRenamedFilesCount: 0,
wantError: true,
errMsg: "failed to list changes for pull request: not Authorized",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, _ := rtesting.SetupFakeContext(t)
_, client, mux, tearDown, tURL := bbtest.SetupBBServerClient(ctx)
defer tearDown()

stats := &bbtest.DiffStats{
Values: tt.changeFiles,
}

if tt.event.TriggerTarget == triggertype.Push {
mux.HandleFunc("/projects/pac/repos/test/commits/IAMSHA123/changes", func(w http.ResponseWriter, _ *http.Request) {
if tt.wantError {
w.WriteHeader(http.StatusUnauthorized)
} else {
b, _ := json.Marshal(stats)
fmt.Fprint(w, string(b))
}
})
}
if tt.event.TriggerTarget == triggertype.PullRequest {
mux.HandleFunc("/projects/pac/repos/test/pull-requests/1/changes", func(w http.ResponseWriter, _ *http.Request) {
if tt.wantError {
w.WriteHeader(http.StatusUnauthorized)
} else {
b, _ := json.Marshal(stats)
fmt.Fprint(w, string(b))
}
})
}
v := &Provider{ScmClient: client, baseURL: tURL}
changedFiles, err := v.GetFiles(ctx, tt.event)
if tt.wantError {
assert.Equal(t, err.Error(), tt.errMsg)
return
}
assert.NilError(t, err, nil)
assert.Equal(t, tt.wantAddedFilesCount, len(changedFiles.Added))
assert.Equal(t, tt.wantDeletedFilesCount, len(changedFiles.Deleted))
assert.Equal(t, tt.wantModifiedFilesCount, len(changedFiles.Modified))
assert.Equal(t, tt.wantRenamedFilesCount, len(changedFiles.Renamed))

if tt.event.TriggerTarget == triggertype.Push {
for i := range changedFiles.All {
assert.Equal(t, tt.changeFiles[i].Path.ToString, changedFiles.All[i])
}
}

if tt.event.TriggerTarget == triggertype.PullRequest {
for i := range changedFiles.All {
assert.Equal(t, tt.changeFiles[i].Path.ToString, changedFiles.All[i])
}
}
})
}
}
24 changes: 24 additions & 0 deletions pkg/provider/bitbucketserver/test/test_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package test

type Pagination struct {
Start int `json:"start"`
Size int `json:"size"`
Limit int `json:"limit"`
LastPage bool `json:"isLastPage"`
NextPage int `json:"nextPageStart"`
}

type DiffPath struct {
ToString string `json:"toString"`
}

type DiffStat struct {
Path DiffPath `json:"path"`
SrcPath *DiffPath `json:"srcPath,omitempty"` // used in lib that's why leaving it here nil
Type string `json:"type"`
}

type DiffStats struct {
Pagination
Values []*DiffStat
}
29 changes: 29 additions & 0 deletions test/bitbucket_server_pull_request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,32 @@ func TestBitbucketServerPullRequest(t *testing.T) {
}
wait.Succeeded(ctx, t, runcnx, opts, successOpts)
}

func TestBitbucketServerCELPathChangeInPullRequest(t *testing.T) {
targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
ctx := context.Background()
bitbucketWSOwner := os.Getenv("TEST_BITBUCKET_SERVER_E2E_REPOSITORY")

ctx, runcnx, opts, client, err := tbbs.Setup(ctx)
assert.NilError(t, err)

repo := tbbs.CreateCRD(ctx, t, client, runcnx, bitbucketWSOwner, targetNS)
runcnx.Clients.Log.Infof("Repository %s has been created", repo.Name)
defer tbbs.TearDownNs(ctx, t, runcnx, targetNS)

files := map[string]string{
".tekton/pipelinerun.yaml": "testdata/pipelinerun-cel-path-changed.yaml",
}

pr := tbbs.CreatePR(ctx, t, client, runcnx, opts, repo, files, bitbucketWSOwner, targetNS)
runcnx.Clients.Log.Infof("Pull Request with title '%s' is created", pr.Title)
defer tbbs.TearDown(ctx, t, runcnx, client, pr.Number, bitbucketWSOwner, targetNS)

successOpts := wait.SuccessOpt{
TargetNS: targetNS,
OnEvent: triggertype.PullRequest.String(),
NumberofPRMatch: 1,
MinNumberStatus: 1,
}
wait.Succeeded(ctx, t, runcnx, opts, successOpts)
}
63 changes: 63 additions & 0 deletions test/bitbucket_server_push_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package test

import (
"context"
"fmt"
"os"
"testing"

"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
tbbs "github.com/openshift-pipelines/pipelines-as-code/test/pkg/bitbucketserver"
"github.com/openshift-pipelines/pipelines-as-code/test/pkg/payload"
"github.com/openshift-pipelines/pipelines-as-code/test/pkg/scm"
"github.com/openshift-pipelines/pipelines-as-code/test/pkg/wait"

"github.com/tektoncd/pipeline/pkg/names"
"gotest.tools/v3/assert"
)

func TestBitbucketServerCELPathChangeOnPush(t *testing.T) {
targetNS := names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-ns")
ctx := context.Background()
bitbucketWSOwner := os.Getenv("TEST_BITBUCKET_SERVER_E2E_REPOSITORY")

ctx, runcnx, opts, client, err := tbbs.Setup(ctx)
assert.NilError(t, err)

repo := tbbs.CreateCRD(ctx, t, client, runcnx, bitbucketWSOwner, targetNS)
runcnx.Clients.Log.Infof("Repository %s has been created", repo.Name)
defer tbbs.TearDownNs(ctx, t, runcnx, targetNS)

files := map[string]string{
".tekton/pipelinerun.yaml": "testdata/pipelinerun-cel-path-changed.yaml",
}

mainBranchRef := "refs/heads/main"
branch, resp, err := client.Git.CreateRef(ctx, bitbucketWSOwner, targetNS, mainBranchRef)
assert.NilError(t, err, "error creating branch: http status code: %d : %v", resp.Status, err)
runcnx.Clients.Log.Infof("Branch %s has been created", branch.Name)
defer tbbs.TearDown(ctx, t, runcnx, client, -1, bitbucketWSOwner, branch.Name)

files, err = payload.GetEntries(files, targetNS, branch.Name, triggertype.Push.String(), map[string]string{})
assert.NilError(t, err)
gitCloneURL, err := scm.MakeGitCloneURL(repo.Clone, opts.UserName, opts.Password)
assert.NilError(t, err)
scmOpts := &scm.Opts{
GitURL: gitCloneURL,
Log: runcnx.Clients.Log,
WebURL: repo.Clone,
TargetRefName: targetNS,
BaseRefName: repo.Branch,
CommitTitle: fmt.Sprintf("commit %s", targetNS),
}
scm.PushFilesToRefGit(t, scmOpts, files)
runcnx.Clients.Log.Infof("Branch %s has been created and pushed with files", targetNS)

successOpts := wait.SuccessOpt{
TargetNS: targetNS,
OnEvent: triggertype.Push.String(),
NumberofPRMatch: 1,
MinNumberStatus: 1,
}
wait.Succeeded(ctx, t, runcnx, opts, successOpts)
}
Loading

0 comments on commit 910906d

Please sign in to comment.