Skip to content

Commit 04f6150

Browse files
zakiskclaude
andcommitted
feat(status): report skipped status for unmatched PipelineRuns
When a repository has multiple PipelineRuns in .tekton/ targeting different events, only the ones matching the incoming event run. The rest are silently ignored, leaving gaps in the Git provider's status checks UI. For example, a repository with two PipelineRuns: .tekton/build.yaml — on-event: pull_request, on-target-branch: main .tekton/deploy.yaml — on-event: push, on-target-branch: main When a pull request is opened, build.yaml matches and runs. Without this feature, deploy.yaml produces no status at all — it is impossible to tell from the PR whether it was skipped intentionally or never picked up. With status_check enabled: spec: settings: status_check: enabled: true mode: "per_unmatched_pipelinerun" Pipelines-as-Code now reports a "skipped" status for deploy.yaml on the pull request, making the full picture visible in the provider UI. The matcher now returns both matched and unmatched PipelineRuns. After the matched runs complete, the controller iterates the unmatched list and calls CreateStatus on each with the configured conclusion. The conclusion defaults to `skipped` but can be set to `success` or `neutral` via the `no_match_conclusion` field. Every provider maps that conclusion to its native state: - GitHub App: check run conclusion "skipped" - GitHub Webhook: commit status "success" (API has no skipped) - GitLab: pipeline status "skipped" - Bitbucket Cloud: build status "STOPPED" - Bitbucket Data Center: build status "UNKNOWN" - Gitea/Forgejo: commit status "success" (no skipped state) The setting is inheritable from the global Repository CR via the existing Settings.Merge path. A second mode (`aggregate`) is defined in the CRD but not yet implemented. The whole feature is behind an opt-in flag (enabled: false by default) and marked as tech preview in the documentation. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Zaki Shaikh <zashaikh@redhat.com>
1 parent bccc549 commit 04f6150

39 files changed

Lines changed: 1528 additions & 166 deletions

config/300-repositories.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,40 @@ spec:
432432
type: string
433433
type: array
434434
type: object
435+
status_check:
436+
description: StatusCheck configures the status checks for the repository.
437+
properties:
438+
aggregate_name:
439+
description: AggregateName defines the name of the aggregate status check. Only used when Mode is 'aggregate' which is not yet implemented.
440+
type: string
441+
enabled:
442+
description: Enabled defines if the status checks should be reported. Default is false.
443+
type: boolean
444+
mode:
445+
description: |-
446+
Mode defines how the status checks should be reported when is enabled.
447+
Options:
448+
- 'per_unmatched_pipelinerun': Report the status check of each PipelineRun separately.
449+
- 'aggregate': Aggregate the status checks of all PipelineRuns into a single status check. (planned, not yet implemented)
450+
enum:
451+
- ""
452+
- per_unmatched_pipelinerun
453+
type: string
454+
no_match_conclusion:
455+
description: |-
456+
NoMatchConclusion defines the conclusion to report when pipeline run is not matched. Default is 'skipped'.
457+
this will be used only if mode is 'per_unmatched_pipelinerun'.
458+
Options:
459+
- 'success': Report as success.
460+
- 'neutral': Report as neutral.
461+
- 'skipped': Report as skipped. Default.
462+
enum:
463+
- ""
464+
- success
465+
- neutral
466+
- skipped
467+
type: string
468+
type: object
435469
type: object
436470
url:
437471
description: |-

docs/content/docs/api/repository-spec.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,7 @@ spec:
307307
container_logs:
308308
enabled: true
309309
max_lines: 100
310+
status_check:
311+
enabled: true
312+
mode: "per_unmatched_pipelinerun"
310313
```

docs/content/docs/api/repository.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ spec:
143143
container_logs:
144144
enabled: true
145145
max_lines: 100
146+
status_check:
147+
enabled: true
148+
mode: "per_unmatched_pipelinerun"
146149
```
147150

148151
## Related resources

docs/content/docs/api/settings.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,72 @@ settings:
366366
{{< /param-group >}}
367367
{{< /param >}}
368368

369+
## Status check settings
370+
371+
{{< param name="status_check" type="StatusCheck" >}}
372+
Configures status check reporting for PipelineRuns that did not match the incoming event. See the [Status Check guide]({{< relref "/docs/guides/repository-crd/status-check" >}}) for full details and provider behavior.
373+
374+
{{< param-group label="Show StatusCheck Fields" >}}
375+
376+
{{< param name="status_check.enabled" type="boolean" id="param-status-check-enabled" >}}
377+
Enables or disables status check reporting for unmatched PipelineRuns. Default: `false`.
378+
379+
```yaml
380+
settings:
381+
status_check:
382+
enabled: true
383+
```
384+
385+
{{< /param >}}
386+
387+
{{< param name="status_check.mode" type="string" id="param-status-check-mode" >}}
388+
Controls how status checks are reported. Options:
389+
390+
- `per_unmatched_pipelinerun` - Report a separate status for each unmatched PipelineRun
391+
- `aggregate` - Report a single aggregated status (planned, not yet implemented)
392+
393+
```yaml
394+
settings:
395+
status_check:
396+
mode: "per_unmatched_pipelinerun"
397+
```
398+
399+
{{< /param >}}
400+
401+
{{< param name="status_check.no_match_conclusion" type="string" id="param-status-check-no-match-conclusion" >}}
402+
The conclusion to report for unmatched PipelineRuns. Only used when `mode` is `per_unmatched_pipelinerun`. Default: `skipped`. Options: `skipped`, `success`, `neutral`.
403+
404+
```yaml
405+
settings:
406+
status_check:
407+
no_match_conclusion: "skipped"
408+
```
409+
410+
{{< /param >}}
411+
412+
{{< param name="status_check.aggregate_name" type="string" id="param-status-check-aggregate-name" >}}
413+
The name of the aggregate status check. Only used when `mode` is `aggregate`.
414+
415+
```yaml
416+
settings:
417+
status_check:
418+
aggregate_name: "Pipelines as Code"
419+
```
420+
421+
{{< /param >}}
422+
423+
{{< /param-group >}}
424+
425+
```yaml
426+
settings:
427+
status_check:
428+
enabled: true
429+
mode: "per_unmatched_pipelinerun"
430+
no_match_conclusion: "skipped"
431+
```
432+
433+
{{< /param >}}
434+
369435
## Complete example
370436

371437
```yaml
@@ -432,6 +498,12 @@ spec:
432498
context_items:
433499
commit_content: true
434500
pr_content: true
501+
502+
# Status check reporting
503+
status_check:
504+
enabled: true
505+
mode: "per_unmatched_pipelinerun"
506+
no_match_conclusion: "skipped"
435507
```
436508

437509
## Settings inheritance

docs/content/docs/guides/_index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This section covers the core workflows you need to run CI/CD with Pipelines-as-C
99

1010
{{< cards >}}
1111
{{< card link="creating-pipelines" title="Authoring PipelineRuns" subtitle="Create pipelines, CEL variables, GitHub token" >}}
12-
{{< card link="repository-crd" title="Repository CR" subtitle="Configure repos, concurrency, comment settings" >}}
12+
{{< card link="repository-crd" title="Repository CR" subtitle="Configure repos, concurrency, comment settings, status checks" >}}
1313
{{< card link="event-matching" title="Event matching" subtitle="on-event, on-target-branch, path, CEL, labels" >}}
1414
{{< card link="gitops-commands" title="GitOps commands" subtitle="/retest, /test, /cancel and more" >}}
1515
{{< card link="statuses" title="PipelineRun status" subtitle="Status reporting and failure detection" >}}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
---
2+
title: Status Check
3+
weight: 5
4+
---
5+
6+
{{< tech_preview "Status Check settings in Repository CR" >}}
7+
8+
This page explains how to report status checks for PipelineRuns that did not match the incoming event. Use this when you want visibility into which PipelineRuns in your `.tekton/` directory were skipped because their annotations (target branch, event type, CEL expression, or path filter) did not match.
9+
10+
By default, Pipelines-as-Code only reports status for PipelineRuns that matched
11+
and ran. PipelineRuns that did not match are silently ignored. Enabling
12+
`status_check` makes Pipelines-as-Code report a status for each unmatched
13+
PipelineRun so you can see the full picture in your Git provider's UI.
14+
15+
## Configuration
16+
17+
Add the `status_check` block under `spec.settings` in your Repository CR:
18+
19+
```yaml
20+
apiVersion: "pipelinesascode.tekton.dev/v1alpha1"
21+
kind: Repository
22+
metadata:
23+
name: my-repo
24+
spec:
25+
url: "https://github.com/owner/repo"
26+
settings:
27+
status_check:
28+
enabled: true
29+
mode: "per_unmatched_pipelinerun"
30+
```
31+
32+
### Fields
33+
34+
| Field | Type | Default | Description |
35+
| --- | --- | --- | --- |
36+
| `enabled` | bool | `false` | Enable status check reporting for unmatched PipelineRuns. |
37+
| `mode` | string | | How to report status checks. See [Modes](#modes). |
38+
| `no_match_conclusion` | string | `skipped` | The conclusion to report for unmatched PipelineRuns. Only used when `mode` is `per_unmatched_pipelinerun`. Accepted values: `skipped`, `success`, `neutral`. |
39+
| `aggregate_name` | string | | The name of the aggregate status check. Only used when `mode` is `aggregate`. |
40+
41+
### Modes
42+
43+
Two modes are available: `per_unmatched_pipelinerun` and `aggregate`.
44+
45+
#### `per_unmatched_pipelinerun`
46+
47+
Reports a separate status for each PipelineRun that did not match the event.
48+
This is useful when you have multiple PipelineRuns targeting different events
49+
(for example, one for `pull_request` and one for `push`) and you want to see
50+
which ones were skipped on each event.
51+
52+
```yaml
53+
spec:
54+
settings:
55+
status_check:
56+
enabled: true
57+
mode: "per_unmatched_pipelinerun"
58+
```
59+
60+
#### `aggregate` (planned)
61+
62+
Will report a single aggregated status check for all PipelineRuns. It is not
63+
yet implemented.
64+
65+
```yaml
66+
spec:
67+
settings:
68+
status_check:
69+
enabled: true
70+
mode: "aggregate"
71+
aggregate_name: "Pipelines as Code"
72+
```
73+
74+
### Customizing the conclusion
75+
76+
By default, unmatched PipelineRuns are reported with a `skipped` conclusion.
77+
You can change this to `success` or `neutral` using the `no_match_conclusion`
78+
field:
79+
80+
```yaml
81+
spec:
82+
settings:
83+
status_check:
84+
enabled: true
85+
mode: "per_unmatched_pipelinerun"
86+
no_match_conclusion: "success"
87+
```
88+
89+
## Provider behavior
90+
91+
The `skipped` conclusion maps to different states depending on your Git provider:
92+
93+
| Provider | Reported state | Notes |
94+
| --- | --- | --- |
95+
| GitHub App | `skipped` | Shown as a skipped check run. |
96+
| GitHub Webhook | `success` | GitHub commit status API does not support `skipped`. Reported as `success` with a "Skipped" description. |
97+
| GitLab | `skipped` | Shown as a skipped pipeline in the Pipelines tab. |
98+
| Bitbucket Cloud | `STOPPED` | Shown as a stopped build status. |
99+
| Bitbucket Data Center | `UNKNOWN` | Reported with an unknown state. |
100+
| Gitea / Forgejo | `success` | Gitea does not support `skipped`. Reported as `success` with a "Skipped" description. |
101+
102+
## Example
103+
104+
Consider a repository with two PipelineRuns:
105+
106+
- `.tekton/build.yaml` -- targets `pull_request` events on the `main` branch
107+
- `.tekton/deploy.yaml` -- targets `push` events on the `main` branch
108+
109+
When a pull request is opened, `build.yaml` matches and runs. Without
110+
`status_check`, `deploy.yaml` is silently ignored. With it enabled:
111+
112+
```yaml
113+
spec:
114+
settings:
115+
status_check:
116+
enabled: true
117+
mode: "per_unmatched_pipelinerun"
118+
```
119+
120+
Pipelines-as-Code reports a `skipped` status for `deploy.yaml`, making it
121+
visible in the pull request's status checks that the PipelineRun exists but did
122+
not apply to this event.

docs/content/docs/guides/statuses.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ You can use the `tkn pac describe` command from the [CLI]({{< relref "/docs/cli/
113113
all statuses of PipelineRuns associated with your repository and
114114
their metadata.
115115

116+
## Status checks for unmatched PipelineRuns
117+
118+
By default, Pipelines-as-Code only reports status for PipelineRuns that matched and ran. If you want to see which PipelineRuns were skipped because their annotations did not match the incoming event, enable the `status_check` setting in your Repository CR. See the [Status Check guide]({{< relref "/docs/guides/repository-crd/status-check" >}}) for configuration and provider behavior.
119+
116120
## Notifications
117121

118122
Pipelines-as-Code does not manage notifications directly. Instead, you can add notifications to your PipelineRuns using the [finally feature of

docs/content/docs/operations/global-repository-settings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ You can define the following settings in the global Repository CR:
3232
- The `type` must be defined in the namespace repository settings and must match the `type` of the global repository (see below for an example).
3333
- [Custom Parameters]({{< relref "/docs/advanced/custom-parameters" >}}).
3434
- [Incoming Webhooks Rules]({{< relref "/docs/advanced/incoming-webhooks" >}}).
35+
- [Status Check]({{< relref "/docs/guides/repository-crd/status-check" >}}).
3536

3637
{{< callout type="info" >}}
3738
Global settings are only applied when running via a Git provider event; they are not applied when for example using the `tkn pac` cli.

pkg/apis/pipelinesascode/v1alpha1/types.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import (
44
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
55
)
66

7+
const (
8+
StatusCheckModeAggregate = "aggregate"
9+
StatusCheckModePerUnmatchedPipelineRun = "per_unmatched_pipelinerun"
10+
)
11+
712
// +genclient
813
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
914

@@ -135,6 +140,10 @@ type Settings struct {
135140
// AIAnalysis contains AI/LLM analysis configuration for automated CI/CD pipeline analysis.
136141
// +optional
137142
AIAnalysis *AIAnalysisConfig `json:"ai,omitempty"`
143+
144+
// StatusCheck configures the status checks for the repository.
145+
// +optional
146+
StatusCheck *StatusCheck `json:"status_check,omitempty"`
138147
}
139148

140149
type GitlabSettings struct {
@@ -182,6 +191,35 @@ type ForgejoSettings struct {
182191
CommentStrategy string `json:"comment_strategy,omitempty"`
183192
}
184193

194+
// StatusCheck configures the status checks for the repository.
195+
type StatusCheck struct {
196+
// Enabled defines if the status checks should be reported. Default is false.
197+
// +optional
198+
Enabled bool `json:"enabled,omitempty"`
199+
200+
// Mode defines how the status checks should be reported when is enabled.
201+
// Options:
202+
// - 'per_unmatched_pipelinerun': Report the status check of each PipelineRun separately.
203+
// - 'aggregate': Aggregate the status checks of all PipelineRuns into a single status check. (planned, not yet implemented)
204+
// +optional
205+
// +kubebuilder:validation:Enum="";per_unmatched_pipelinerun
206+
Mode string `json:"mode,omitempty"`
207+
208+
// TODO(zaki): Remove this comment when aggregate mode is implemented.
209+
// AggregateName defines the name of the aggregate status check. Only used when Mode is 'aggregate' which is not yet implemented.
210+
AggregateName string `json:"aggregate_name,omitempty"`
211+
212+
// NoMatchConclusion defines the conclusion to report when pipeline run is not matched. Default is 'skipped'.
213+
// this will be used only if mode is 'per_unmatched_pipelinerun'.
214+
// Options:
215+
// - 'success': Report as success.
216+
// - 'neutral': Report as neutral.
217+
// - 'skipped': Report as skipped. Default.
218+
// +optional
219+
// +kubebuilder:validation:Enum="";success;neutral;skipped
220+
NoMatchConclusion string `json:"no_match_conclusion,omitempty"`
221+
}
222+
185223
func (s *Settings) Merge(newSettings *Settings) {
186224
if newSettings.PipelineRunProvenance != "" && s.PipelineRunProvenance == "" {
187225
s.PipelineRunProvenance = newSettings.PipelineRunProvenance
@@ -211,6 +249,10 @@ func (s *Settings) Merge(newSettings *Settings) {
211249
if newSettings.GitOpsCommandPrefix != "" && s.GitOpsCommandPrefix == "" {
212250
s.GitOpsCommandPrefix = newSettings.GitOpsCommandPrefix
213251
}
252+
253+
if newSettings.StatusCheck != nil && s.StatusCheck == nil {
254+
s.StatusCheck = newSettings.StatusCheck
255+
}
214256
}
215257

216258
func (s *GitlabSettings) Merge(newSettings *GitlabSettings) {

0 commit comments

Comments
 (0)