Skip to content

Commit 5e4489d

Browse files
chmouelpiyush-garg
authored andcommitted
Add support to run PR without an event matching
Users now have the ability to explicitly start a PipelineRun using the /test comment, irrespective of whether the pipelinerun matches the annotations. This feature grants users explicit control over the execution of PipelineRuns for specific types of Pull Requests, allowing manual initiation instead of relying solely on events. This enhancement is particularly useful for scenarios where users need fine-grained control over the testing process, such as for Pull Requests managed by users rather than events. Add e2e for gitlab/github(ghe)/gitea Jira: https://issues.redhat.com/browse/SRVKP-3561 Signed-off-by: Chmouel Boudjnah <chmouel@redhat.com>
1 parent 576b137 commit 5e4489d

File tree

12 files changed

+340
-41
lines changed

12 files changed

+340
-41
lines changed

docs/content/docs/guide/running.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,16 @@ entire suite of checks once again.
114114

115115
![github apps rerun check](/images/github-apps-rerun-checks.png)
116116

117-
### GitOps command on pull or merge request
117+
## GitOps commands
118118

119-
If you are targeting a push, pull or merge request you can use `GitOps` comment
120-
inside your pull request, to restart all or specific Pipelines.
119+
The GitOps commands are a way to trigger Pipelines-as-Code actions via comments
120+
on a `Pull Request` or comment on a `Push`ed commit.
121121

122-
For example, you want to restart all your pipeline you can add a comment starting
123-
with `/retest` and all PipelineRun attached to that pull or merge request will be
124-
restarted :
122+
### GitOps commands on Pull Requests
123+
124+
When you are on a Pull Request you may want to restart all your pipelineruns
125+
you can add a comment starting with `/retest` and all PipelineRuns attached to
126+
that pull request will be restarted :
125127

126128
Example :
127129

@@ -141,14 +143,11 @@ roses are red, violets are blue. pipeline are bound to flake by design.
141143
/test <pipelinerun-name>
142144
```
143145

144-
You can expose custom GitOps commands on `Pull Request` comment via the
145-
[on-comment]({{< relref "/docs/guide/authoringprs.md#matching-a-pipelinerun-on-a-regexp-in-a-comment" >}}) annotation.
146-
147-
### GitOps command on push request
146+
### GitOps commands on pushed commits
148147

149-
To trigger GitOps commands in response to a push request, you can include `GitOps`
150-
comments within your commit messages. These comments can be used to restart
151-
either all pipelines or specific ones. Here's how it works:
148+
If you want to trigger a GitOps command on a pushed commit, you can include the
149+
`GitOps` comments within your commit messages. These comments can be used to
150+
restart either all pipelines or specific ones. Here's how it works:
152151

153152
For restarting all pipeline runs:
154153

@@ -185,7 +184,7 @@ located, with the context of the **test** branch.
185184
3. `/retest <pipelinerun-name> branch:test`
186185
4. `/test <pipelinerun-name> branch:test`
187186

188-
To add `GitOps` comments to a push request, follow these steps:
187+
To issue a `GitOps` comment on a pushed commit you can follow these steps:
189188

190189
1. Go to your repository.
191190
2. Click on the **Commits** section.
@@ -196,12 +195,25 @@ To add `GitOps` comments to a push request, follow these steps:
196195

197196
Please note that this feature is supported for the GitHub provider only.
198197

199-
## Cancelling the PipelineRun
198+
### GitOps commands on non-matching PipelineRun
199+
200+
The PipelineRun will be restarted regardless of the annotations if the comment
201+
`/test <pipelinerun-name>` or `/retest <pipelinerun-name>` is used . This let
202+
you have control of PipelineRuns that gets only triggered by a comment on the
203+
pull request.
204+
205+
### Custom GitOps commands
206+
207+
Using the [on-comment]({{< relref "/docs/guide/authoringprs.md#matching-a-pipelinerun-on-a-regexp-in-a-comment" >}}) annotation on your `PipelineRun` you can define custom GitOps commands that will be triggered by the comments on the pull request.
208+
209+
See the [on-comment]({{< relref "/docs/guide/authoringprs.md#matching-a-pipelinerun-on-a-regexp-in-a-comment" >}}) guide for more information.
210+
211+
## Cancelling a PipelineRun
200212

201213
You can cancel a running PipelineRun by commenting on the PullRequest.
202214

203215
For example if you want to cancel all your PipelinerRuns you can add a comment starting
204-
with `/cancel` and all PipelineRun attached to that pull or merge request will be cancelled.
216+
with `/cancel` and all PipelineRun attached to that pull request will be cancelled.
205217

206218
Example :
207219

pkg/pipelineascode/match.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,13 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
182182
}
183183

184184
// Match the PipelineRun with annotation
185-
matchedPRs, err := matcher.MatchPipelinerunByAnnotation(ctx, p.logger, pipelineRuns, p.run, p.event, p.vcx)
186-
if err != nil {
187-
// Don't fail when you don't have a match between pipeline and annotations
188-
p.eventEmitter.EmitMessage(nil, zap.WarnLevel, "RepositoryNoMatch", err.Error())
189-
return nil, nil
185+
var matchedPRs []matcher.Match
186+
if p.event.TargetTestPipelineRun == "" {
187+
if matchedPRs, err = matcher.MatchPipelinerunByAnnotation(ctx, p.logger, pipelineRuns, p.run, p.event, p.vcx); err != nil {
188+
// Don't fail when you don't have a match between pipeline and annotations
189+
p.eventEmitter.EmitMessage(nil, zap.WarnLevel, "RepositoryNoMatch", err.Error())
190+
return nil, nil
191+
}
190192
}
191193

192194
// if the event is a comment event, but we don't have any match from the keys.OnComment then do the ACL checks again
@@ -215,13 +217,15 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
215217

216218
// finally resolve with fetching the remote tasks (if enabled)
217219
if p.run.Info.Pac.RemoteTasks {
218-
// only resolve on the matched pipelineruns
219-
types.PipelineRuns = nil
220-
for _, match := range matchedPRs {
221-
for pr := range pipelineRuns {
222-
if match.PipelineRun.GetName() == "" && match.PipelineRun.GetGenerateName() == pipelineRuns[pr].GenerateName ||
223-
match.PipelineRun.GetName() != "" && match.PipelineRun.GetName() == pipelineRuns[pr].Name {
224-
types.PipelineRuns = append(types.PipelineRuns, pipelineRuns[pr])
220+
// only resolve on the matched pipelineruns if we don't do explicit /test of unmatched pipelineruns
221+
if p.event.TargetTestPipelineRun == "" {
222+
types.PipelineRuns = nil
223+
for _, match := range matchedPRs {
224+
for pr := range pipelineRuns {
225+
if match.PipelineRun.GetName() == "" && match.PipelineRun.GetGenerateName() == pipelineRuns[pr].GenerateName ||
226+
match.PipelineRun.GetName() != "" && match.PipelineRun.GetName() == pipelineRuns[pr].Name {
227+
types.PipelineRuns = append(types.PipelineRuns, pipelineRuns[pr])
228+
}
225229
}
226230
}
227231
}
@@ -239,6 +243,14 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
239243
if err != nil {
240244
return nil, err
241245
}
246+
// if we are doing explicit /test command then we only want to run the one that has matched the /test
247+
if p.event.TargetTestPipelineRun != "" {
248+
p.eventEmitter.EmitMessage(repo, zap.InfoLevel, "RepositoryMatchedPipelineRun", fmt.Sprintf("explicit testing via /test of PipelineRun %s", p.event.TargetTestPipelineRun))
249+
return []matcher.Match{{
250+
PipelineRun: pipelineRuns[0],
251+
Repo: repo,
252+
}}, nil
253+
}
242254
matchedPRs, err = matcher.MatchPipelinerunByAnnotation(ctx, p.logger, pipelineRuns, p.run, p.event, p.vcx)
243255
if err != nil {
244256
// Don't fail when you don't have a match between pipeline and annotations

pkg/pipelineascode/match_test.go

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,37 @@ func TestFilterRunningPipelineRunOnTargetTest(t *testing.T) {
8585
}
8686

8787
func TestGetPipelineRunsFromRepo(t *testing.T) {
88+
pullRequestEvent := &info.Event{
89+
SHA: "principale",
90+
Organization: "organizationes",
91+
Repository: "lagaffe",
92+
URL: "https://service/documentation",
93+
HeadBranch: "main",
94+
BaseBranch: "main",
95+
Sender: "fantasio",
96+
EventType: "pull_request",
97+
TriggerTarget: "pull_request",
98+
}
99+
testExplicitNoMatchPREvent := &info.Event{
100+
SHA: "principale",
101+
Organization: "organizationes",
102+
Repository: "lagaffe",
103+
URL: "https://service/documentation",
104+
HeadBranch: "main",
105+
BaseBranch: "main",
106+
Sender: "fantasio",
107+
TriggerTarget: "pull_request",
108+
State: info.State{
109+
TargetTestPipelineRun: "no-match",
110+
},
111+
}
112+
88113
tests := []struct {
89114
name string
90115
repositories *v1alpha1.Repository
91116
tektondir string
92117
expectedNumberOfPruns int
118+
event *info.Event
93119
}{
94120
{
95121
name: "more than one pipelinerun in .tekton dir",
@@ -102,6 +128,7 @@ func TestGetPipelineRunsFromRepo(t *testing.T) {
102128
},
103129
tektondir: "testdata/pull_request_multiplepipelineruns",
104130
expectedNumberOfPruns: 2,
131+
event: pullRequestEvent,
105132
},
106133
{
107134
name: "single pipelinerun in .tekton dir",
@@ -114,6 +141,37 @@ func TestGetPipelineRunsFromRepo(t *testing.T) {
114141
},
115142
tektondir: "testdata/pull_request",
116143
expectedNumberOfPruns: 1,
144+
event: pullRequestEvent,
145+
},
146+
{
147+
name: "no-match pipelineruns in .tekton dir, only matched should be returned",
148+
repositories: &v1alpha1.Repository{
149+
ObjectMeta: metav1.ObjectMeta{
150+
Name: "testrepo",
151+
Namespace: "test",
152+
},
153+
Spec: v1alpha1.RepositorySpec{},
154+
},
155+
// we have 3 PR in there 2 that has a match on pull request and 1 that is a no-matching
156+
// matching those two that is matching here
157+
tektondir: "testdata/no-match",
158+
expectedNumberOfPruns: 2,
159+
event: pullRequestEvent,
160+
},
161+
{
162+
name: "no-match pipelineruns in .tekton dir, only match the no-match",
163+
repositories: &v1alpha1.Repository{
164+
ObjectMeta: metav1.ObjectMeta{
165+
Name: "testrepo",
166+
Namespace: "test",
167+
},
168+
Spec: v1alpha1.RepositorySpec{},
169+
},
170+
// we have 3 PR in there 2 that has a match on pull request and 1 that is a no-matching
171+
// matching that only one here
172+
tektondir: "testdata/no-match",
173+
expectedNumberOfPruns: 1,
174+
event: testExplicitNoMatchPREvent,
117175
},
118176
}
119177
for _, tt := range tests {
@@ -124,19 +182,8 @@ func TestGetPipelineRunsFromRepo(t *testing.T) {
124182
fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
125183
defer teardown()
126184

127-
runevent := &info.Event{
128-
SHA: "principale",
129-
Organization: "organizationes",
130-
Repository: "lagaffe",
131-
URL: "https://service/documentation",
132-
HeadBranch: "main",
133-
BaseBranch: "main",
134-
Sender: "fantasio",
135-
EventType: "pull_request",
136-
TriggerTarget: "pull_request",
137-
}
138185
if tt.tektondir != "" {
139-
ghtesthelper.SetupGitTree(t, mux, tt.tektondir, runevent, false)
186+
ghtesthelper.SetupGitTree(t, mux, tt.tektondir, tt.event, false)
140187
}
141188

142189
stdata, _ := testclient.SeedTestData(t, ctx, testclient.Data{})
@@ -165,7 +212,7 @@ func TestGetPipelineRunsFromRepo(t *testing.T) {
165212
Token: github.String("None"),
166213
Logger: logger,
167214
}
168-
p := NewPacs(runevent, vcx, cs, k8int, logger)
215+
p := NewPacs(tt.event, vcx, cs, k8int, logger)
169216
matchedPRs, err := p.getPipelineRunsFromRepo(ctx, tt.repositories)
170217
assert.NilError(t, err)
171218
matchedPRNames := []string{}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: PipelineRun
3+
metadata:
4+
name: pull_request-1
5+
annotations:
6+
pipelinesascode.tekton.dev/on-target-branch: "[main]"
7+
pipelinesascode.tekton.dev/on-event: "[pull_request]"
8+
spec:
9+
pipelineRef:
10+
name: pipeline1
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: PipelineRun
3+
metadata:
4+
name: pull_request-2
5+
annotations:
6+
pipelinesascode.tekton.dev/on-target-branch: "[main]"
7+
pipelinesascode.tekton.dev/on-event: "[pull_request]"
8+
spec:
9+
pipelineRef:
10+
name: pipeline1
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: PipelineRun
3+
metadata:
4+
name: no-match
5+
spec:
6+
pipelineRef:
7+
name: pipeline1
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
apiVersion: tekton.dev/v1beta1
3+
kind: Pipeline
4+
metadata:
5+
name: pipeline1
6+
tasks:
7+
- name: task-from-tektondir
8+
taskRef:
9+
name: task-from-tektondir
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
apiVersion: tekton.dev/v1beta1
3+
kind: Task
4+
metadata:
5+
name: task-from-tektondir
6+
spec:
7+
steps:
8+
- name: task-1
9+
image: gcr.io/distroless/python3:nonroot
10+
script: |
11+
#!/usr/bin/python3
12+
print("Hello task-from-tektondir")

test/gitea_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,82 @@ func TestGiteaOnCommentAnnotation(t *testing.T) {
814814
assert.NilError(t, err)
815815
}
816816

817+
// TestGiteaTestPipelineRunExplicitelyWithTestComment will test a pipelinerun
818+
// even if it hasn't matched when we are doing a /test comment.
819+
func TestGiteaTestPipelineRunExplicitelyWithTestComment(t *testing.T) {
820+
var err error
821+
ctx := context.Background()
822+
topts := &tgitea.TestOpts{
823+
TargetRefName: names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"),
824+
}
825+
topts.TargetNS = topts.TargetRefName
826+
topts.ParamsRun, topts.Opts, topts.GiteaCNX, err = tgitea.Setup(ctx)
827+
assert.NilError(t, err, fmt.Errorf("cannot do gitea setup: %w", err))
828+
ctx, err = cctx.GetControllerCtxInfo(ctx, topts.ParamsRun)
829+
assert.NilError(t, err)
830+
assert.NilError(t, pacrepo.CreateNS(ctx, topts.TargetNS, topts.ParamsRun))
831+
assert.NilError(t, secret.Create(ctx, topts.ParamsRun, map[string]string{"secret": "SHHHHHHH"}, topts.TargetNS, "pac-secret"))
832+
topts.TargetEvent = triggertype.PullRequest.String()
833+
topts.YAMLFiles = map[string]string{
834+
".tekton/pr.yaml": "testdata/pipelinerun-nomatch.yaml",
835+
}
836+
_, f := tgitea.TestPR(t, topts)
837+
defer f()
838+
tgitea.PostCommentOnPullRequest(t, topts, "/test no-match")
839+
tgitea.WaitForStatus(t, topts, "heads/"+topts.TargetRefName, "", false)
840+
waitOpts := twait.Opts{
841+
RepoName: topts.TargetNS,
842+
Namespace: topts.TargetNS,
843+
MinNumberStatus: 1,
844+
PollTimeout: twait.DefaultTimeout,
845+
}
846+
847+
repo, err := twait.UntilRepositoryUpdated(context.Background(), topts.ParamsRun.Clients, waitOpts)
848+
assert.NilError(t, err)
849+
assert.Equal(t, len(repo.Status), 1, "should have only 1 status")
850+
assert.Equal(t, *repo.Status[0].EventType, opscomments.TestSingleCommentEventType.String(), "should have a test comment event in status")
851+
}
852+
853+
func TestGiteaRetestAll(t *testing.T) {
854+
var err error
855+
ctx := context.Background()
856+
topts := &tgitea.TestOpts{
857+
TargetRefName: names.SimpleNameGenerator.RestrictLengthWithRandomSuffix("pac-e2e-test"),
858+
}
859+
topts.TargetNS = topts.TargetRefName
860+
topts.ParamsRun, topts.Opts, topts.GiteaCNX, err = tgitea.Setup(ctx)
861+
assert.NilError(t, err, fmt.Errorf("cannot do gitea setup: %w", err))
862+
ctx, err = cctx.GetControllerCtxInfo(ctx, topts.ParamsRun)
863+
assert.NilError(t, err)
864+
assert.NilError(t, pacrepo.CreateNS(ctx, topts.TargetNS, topts.ParamsRun))
865+
assert.NilError(t, secret.Create(ctx, topts.ParamsRun, map[string]string{"secret": "SHHHHHHH"}, topts.TargetNS, "pac-secret"))
866+
topts.TargetEvent = triggertype.PullRequest.String()
867+
topts.YAMLFiles = map[string]string{
868+
".tekton/pr.yaml": "testdata/pipelinerun.yaml",
869+
".tekton/nomatch.yaml": "testdata/pipelinerun-nomatch.yaml",
870+
}
871+
_, f := tgitea.TestPR(t, topts)
872+
defer f()
873+
tgitea.PostCommentOnPullRequest(t, topts, "/retest")
874+
waitOpts := twait.Opts{
875+
RepoName: topts.TargetNS,
876+
Namespace: topts.TargetNS,
877+
MinNumberStatus: 2,
878+
PollTimeout: twait.DefaultTimeout,
879+
}
880+
881+
repo, err := twait.UntilRepositoryUpdated(context.Background(), topts.ParamsRun.Clients, waitOpts)
882+
assert.NilError(t, err)
883+
var rt bool
884+
for _, status := range repo.Status {
885+
if *status.EventType == opscomments.RetestAllCommentEventType.String() {
886+
rt = true
887+
}
888+
}
889+
assert.Assert(t, rt, "should have a retest all comment event in status")
890+
assert.Equal(t, len(repo.Status), 2, "should have only 2 status")
891+
}
892+
817893
// Local Variables:
818894
// compile-command: "go test -tags=e2e -v -run TestGiteaPush ."
819895
// End:

0 commit comments

Comments
 (0)