Skip to content

Commit 465c535

Browse files
committed
feat: Implement setting to skip push events for PR commits
* Add `skip-push-event-for-pr-commits` configuration setting. * Skip processing push events when the commit SHA is present in an open pull request. * Prevent duplicate pipeline runs triggered by both push and pull request events for the same commit. * Document the new configuration setting. Signed-off-by: Chmouel Boudjnah <chmouel@redhat.com>
1 parent 8bb5bfa commit 465c535

File tree

6 files changed

+100
-18
lines changed

6 files changed

+100
-18
lines changed

config/302-pac-configmap.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,13 @@ data:
143143
# you may want to disable this if ok-to-test should be done on each iteration
144144
remember-ok-to-test: "false"
145145

146+
# When enabled, this option prevents duplicate pipeline runs when a commit appears in
147+
# both a push event and a pull request. If a push event comes from a commit that is
148+
# part of an open pull request, the push event will be skipped as it would create
149+
# a duplicate pipeline run.
150+
# Default: true
151+
skip-push-event-for-pr-commits: "true"
152+
146153
# Configure a custom console here, the driver support custom parameters from
147154
# Repo CR along a few other template variable, see documentation for more
148155
# details

docs/content/docs/install/settings.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,20 @@ There is a few things you can configure through the config map
139139
risk and should be aware of the potential security vulnerabilities.
140140
(only GitHub and Gitea is supported at the moment).
141141

142+
* `skip-push-event-for-pr-commits`
143+
144+
When enabled, this option prevents duplicate pipeline runs when a commit appears in
145+
both a push event and a pull request. If a push event comes from a commit that is
146+
part of an open pull request, the push event will be skipped as it would create
147+
a duplicate pipeline run.
148+
149+
This feature works by checking if a pushed commit SHA exists in any open pull request,
150+
and if so, skipping the push event processing.
151+
152+
Default: `true`
153+
154+
{{< support_matrix github_app="true" github_webhook="true" gitea="false" gitlab="false" bitbucket_cloud="false" bitbucket_datacenter="false" >}}
155+
142156
### Global Cancel In Progress Settings
143157

144158
* `enable-cancel-in-progress-on-pull-requests`
@@ -429,28 +443,28 @@ A few settings are available to configure this feature:
429443
}
430444
```
431445

432-
The `loglevel.*` fields define the log level for the controllers:
446+
The `loglevel.*` fields define the log level for the controllers:
433447

434448
* loglevel.pipelinesascode - the log level for the pipelines-as-code-controller component
435449
* loglevel.pipelines-as-code-webhook - the log level for the pipelines-as-code-webhook component
436450
* loglevel.pac-watcher - the log level for the pipelines-as-code-watcher component
437451

438-
You can change the log level from `info` to `debug` or any other supported values. For example, select the `debug` log level for the pipelines-as-code-watcher component:
452+
You can change the log level from `info` to `debug` or any other supported values. For example, select the `debug` log level for the pipelines-as-code-watcher component:
439453

440-
```bash
441-
kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[{"op": "replace", "path": "/data/loglevel.pac-watcher", "value":"debug"}]'
442-
```
454+
```bash
455+
kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[{"op": "replace", "path": "/data/loglevel.pac-watcher", "value":"debug"}]'
456+
```
443457

444-
After this command, the controller gets a new log level value.
445-
If you want to use the same log level for all Pipelines-as-Code components, delete `level.*` values from configmap:
458+
After this command, the controller gets a new log level value.
459+
If you want to use the same log level for all Pipelines-as-Code components, delete `level.*` values from configmap:
446460

447-
```bash
448-
kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[ {"op": "remove", "path": "/data/loglevel.pac-watcher"}, {"op": "remove", "path": "/data/loglevel.pipelines-as-code-webhook"}, {"op": "remove", "path": "/data/loglevel.pipelinesascode"}]'
449-
```
461+
```bash
462+
kubectl patch configmap pac-config-logging -n pipelines-as-code --type json -p '[ {"op": "remove", "path": "/data/loglevel.pac-watcher"}, {"op": "remove", "path": "/data/loglevel.pipelines-as-code-webhook"}, {"op": "remove", "path": "/data/loglevel.pipelinesascode"}]'
463+
```
450464

451-
In this case, all Pipelines-as-Code components get a common log level from `zap-logger-config` - `level` field from the json.
465+
In this case, all Pipelines-as-Code components get a common log level from `zap-logger-config` - `level` field from the json.
452466

453-
`zap-logger-config` supports the following log levels:
467+
`zap-logger-config` supports the following log levels:
454468

455469
* debug - fine-grained debugging
456470
* info - normal logging
@@ -460,4 +474,4 @@ A few settings are available to configure this feature:
460474
* panic - trigger a panic (crash)
461475
* fatal - immediately exit with exit status 1 (failure)
462476

463-
See more: <https://knative.dev/docs/serving/observability/logging/config-logging>
477+
See more: <https://knative.dev/docs/serving/observability/logging/config-logging>

pkg/params/settings/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type Settings struct {
6666
EnableCancelInProgressOnPullRequests bool `json:"enable-cancel-in-progress-on-pull-requests"`
6767
EnableCancelInProgressOnPush bool `json:"enable-cancel-in-progress-on-push"`
6868

69+
SkipPushEventForPRCommits bool `json:"skip-push-event-for-pr-commits" default:"true"` // nolint:tagalign
70+
6971
CustomConsoleName string `json:"custom-console-name"`
7072
CustomConsoleURL string `json:"custom-console-url"`
7173
CustomConsolePRdetail string `json:"custom-console-url-pr-details"`

pkg/params/settings/config_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func TestSyncConfig(t *testing.T) {
4040
ErrorDetectionSimpleRegexp: "^(?P<filename>[^:]*):(?P<line>[0-9]+):(?P<column>[0-9]+)?([ ]*)?(?P<error>.*)",
4141
EnableCancelInProgressOnPullRequests: false,
4242
EnableCancelInProgressOnPush: false,
43+
SkipPushEventForPRCommits: true,
4344
CustomConsoleName: "",
4445
CustomConsoleURL: "",
4546
CustomConsolePRdetail: "",
@@ -73,6 +74,7 @@ func TestSyncConfig(t *testing.T) {
7374
"custom-console-url-pr-tasklog": "https://custom-console-pr-tasklog",
7475
"custom-console-url-namespace": "https://custom-console-namespace",
7576
"remember-ok-to-test": "false",
77+
"skip-push-event-for-pr-commits": "true",
7678
},
7779
expectedStruct: Settings{
7880
ApplicationName: "pac-pac",
@@ -98,6 +100,7 @@ func TestSyncConfig(t *testing.T) {
98100
CustomConsolePRTaskLog: "https://custom-console-pr-tasklog",
99101
CustomConsoleNamespaceURL: "https://custom-console-namespace",
100102
RememberOKToTest: false,
103+
SkipPushEventForPRCommits: true,
101104
},
102105
},
103106
{

pkg/params/settings/convert_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func TestConvert(t *testing.T) {
3535
"error-log-snippet": "true",
3636
"enable-cancel-in-progress-on-pull-requests": "false",
3737
"enable-cancel-in-progress-on-push": "false",
38+
"skip-push-event-for-pr-commits": "true",
3839
"hub-catalog-name": "tekton",
3940
"hub-url": "https://api.hub.tekton.dev/v1",
4041
"max-keep-run-upper-limit": "0",
@@ -75,6 +76,7 @@ func TestConvert(t *testing.T) {
7576
"error-log-snippet": "true",
7677
"enable-cancel-in-progress-on-pull-requests": "false",
7778
"enable-cancel-in-progress-on-push": "false",
79+
"skip-push-event-for-pr-commits": "true",
7880
"hub-catalog-name": "tekton",
7981
"hub-url": "https://api.hub.tekton.dev/v1",
8082
"max-keep-run-upper-limit": "0",
@@ -116,6 +118,7 @@ func TestConvert(t *testing.T) {
116118
"error-log-snippet": "true",
117119
"enable-cancel-in-progress-on-pull-requests": "false",
118120
"enable-cancel-in-progress-on-push": "false",
121+
"skip-push-event-for-pr-commits": "true",
119122
"hub-catalog-name": "test tekton",
120123
"hub-url": "https://api.hub.tekton.dev/v2",
121124
"max-keep-run-upper-limit": "0",
@@ -169,6 +172,7 @@ func TestConvert(t *testing.T) {
169172
"error-log-snippet": "true",
170173
"enable-cancel-in-progress-on-pull-requests": "false",
171174
"enable-cancel-in-progress-on-push": "false",
175+
"skip-push-event-for-pr-commits": "true",
172176
"hub-catalog-name": "test tekton",
173177
"hub-url": "https://api.hub.tekton.dev/v2",
174178
"max-keep-run-upper-limit": "0",

pkg/provider/github/parse_payload.go

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,40 @@ func (v *Provider) ParsePayload(ctx context.Context, run *params.Run, request *h
208208
return processedEvent, nil
209209
}
210210

211+
// isCommitPartOfPullRequest checks if the commit from a push event is part of an open pull request
212+
// If it is, it returns true and the PR number.
213+
func (v *Provider) isCommitPartOfPullRequest(ctx context.Context, sha, org, repo string) (bool, int, error) {
214+
if v.ghClient == nil {
215+
return false, 0, nil
216+
}
217+
218+
// List all open pull requests in the repository
219+
prs, _, err := v.ghClient.PullRequests.List(ctx, org, repo, &github.PullRequestListOptions{
220+
State: "open",
221+
})
222+
if err != nil {
223+
return false, 0, err
224+
}
225+
226+
// Check each PR to see if it contains the commit
227+
for _, pr := range prs {
228+
// Get the commits in this PR
229+
commits, _, err := v.ghClient.PullRequests.ListCommits(ctx, org, repo, pr.GetNumber(), &github.ListOptions{})
230+
if err != nil {
231+
continue
232+
}
233+
234+
// Check if our SHA is in the PR's commits
235+
for _, commit := range commits {
236+
if commit.GetSHA() == sha {
237+
return true, pr.GetNumber(), nil
238+
}
239+
}
240+
}
241+
242+
return false, 0, nil
243+
}
244+
211245
func (v *Provider) processEvent(ctx context.Context, event *info.Event, eventInt any) (*info.Event, error) {
212246
var processedEvent *info.Event
213247
var err error
@@ -268,16 +302,34 @@ func (v *Provider) processEvent(ctx context.Context, event *info.Event, eventInt
268302
}
269303
}
270304

305+
// Check if this push commit is part of an open pull request
306+
sha := gitEvent.GetHeadCommit().GetID()
307+
if sha == "" {
308+
sha = gitEvent.GetBefore()
309+
}
310+
org := gitEvent.GetRepo().GetOwner().GetLogin()
311+
repoName := gitEvent.GetRepo().GetName()
312+
313+
// Only check if the flag is enabled
314+
if v.pacInfo.SkipPushEventForPRCommits {
315+
isPartOfPR, prNumber, err := v.isCommitPartOfPullRequest(ctx, sha, org, repoName)
316+
if err != nil {
317+
v.Logger.Warnf("Error checking if push commit is part of PR: %v", err)
318+
}
319+
320+
// If the commit is part of a PR, skip processing the push event
321+
if isPartOfPR {
322+
v.Logger.Infof("Skipping push event for commit %s as it belongs to pull request #%d", sha, prNumber)
323+
return nil, fmt.Errorf("commit %s is part of pull request #%d, skipping push event", sha, prNumber)
324+
}
325+
}
326+
271327
processedEvent.Organization = gitEvent.GetRepo().GetOwner().GetLogin()
272328
processedEvent.Repository = gitEvent.GetRepo().GetName()
273329
processedEvent.DefaultBranch = gitEvent.GetRepo().GetDefaultBranch()
274330
processedEvent.URL = gitEvent.GetRepo().GetHTMLURL()
275331
v.RepositoryIDs = []int64{gitEvent.GetRepo().GetID()}
276-
processedEvent.SHA = gitEvent.GetHeadCommit().GetID()
277-
// on push event we may not get a head commit but only
278-
if processedEvent.SHA == "" {
279-
processedEvent.SHA = gitEvent.GetBefore()
280-
}
332+
processedEvent.SHA = sha
281333
processedEvent.SHAURL = gitEvent.GetHeadCommit().GetURL()
282334
processedEvent.SHATitle = gitEvent.GetHeadCommit().GetMessage()
283335
processedEvent.Sender = gitEvent.GetSender().GetLogin()

0 commit comments

Comments
 (0)