Skip to content

Commit 3ea4e39

Browse files
committed
feat: add skip CI command support
Implement support for skip CI commands in commit messages to allow users to skip PipelineRun execution. Supports [skip ci], [ci skip], [skip tkn], and [tkn skip] commands. When a skip command is detected in the commit message, PipelineRun execution is skipped. However, GitOps commands (/test, /retest, etc.) will still trigger PipelineRuns regardless of the skip command, allowing users to manually trigger CI when needed. Jira: https://issues.redhat.com/browse/SRVKP-8933 Signed-off-by: Akshay Pant <akshay.akshaypant@gmail.com>
1 parent a8212d3 commit 3ea4e39

File tree

22 files changed

+1978
-193
lines changed

22 files changed

+1978
-193
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Before getting started with Pipelines-as-Code, ensure you have:
6565
- **Multi-provider support**: Works with GitHub (via GitHub App & Webhook), GitLab, Gitea, Bitbucket Data Center & Cloud via webhooks.
6666
- **Annotation-driven workflows**: Target specific events, branches, or CEL expressions and gate untrusted PRs with `/ok-to-test` and `OWNERS`; see [Running the PipelineRun](https://pipelinesascode.com/docs/guide/running/).
6767
- **ChatOps style control**: `/test`, `/retest`, `/cancel`, and branch or tag selectors let you rerun or stop PipelineRuns from PR comments or commit messages; see [GitOps Commands](https://pipelinesascode.com/docs/guide/gitops_commands/).
68+
- **Skip CI support**: Use `[skip ci]`, `[ci skip]`, `[skip tkn]`, or `[tkn skip]` in commit messages to skip automatic PipelineRun execution for documentation updates or minor changes; GitOps commands can still override and trigger runs manually; see [Skip CI Commands](https://pipelinesascode.com/docs/guide/gitops_commands/#skip-ci-commands).
6869
- **Feedback**: GitHub Checks capture per-task timing, log snippets, and optional error annotations while redacting secrets; see [PipelineRun status](https://pipelinesascode.com/docs/guide/statuses/).
6970
- **Inline resolution**: The resolver bundles `.tekton/` resources, inlines remote tasks from Artifact Hub or Tekton Hub, and validates YAML before cluster submission; see [Resolver](https://pipelinesascode.com/docs/guide/resolver/).
7071
- **CLI**: `tkn pac` bootstraps installs, manages Repository CRDs, inspects logs, and resolves runs locally; see the [CLI guide](https://pipelinesascode.com/docs/guide/cli/).

docs/content/docs/guide/matchingevents.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,3 +482,94 @@ main and the branch called `release,nightly` you can do this:
482482
```yaml
483483
pipelinesascode.tekton.dev/on-target-branch: [main, release&#44;nightly]
484484
```
485+
486+
## Skip CI Commands
487+
488+
Pipelines-as-Code supports skip commands in commit messages that allow you to skip
489+
PipelineRun execution for specific commits. This is useful when making documentation
490+
changes, minor fixes, or work-in-progress commits where running the full CI pipeline
491+
is unnecessary.
492+
493+
### Supported Skip Commands
494+
495+
You can include any of the following commands anywhere in your commit message to skip
496+
PipelineRun execution:
497+
498+
* `[skip ci]` - Skip continuous integration
499+
* `[ci skip]` - Alternative format for skipping CI
500+
* `[skip tkn]` - Skip Tekton PipelineRuns
501+
* `[tkn skip]` - Alternative format for skipping Tekton
502+
503+
**Note:** Skip commands are **case-sensitive** and must be in lowercase with brackets.
504+
505+
### Example Usage
506+
507+
```text
508+
docs: update README with installation instructions [skip ci]
509+
```
510+
511+
or
512+
513+
```text
514+
WIP: refactor authentication module
515+
516+
This is still in progress and not ready for testing yet.
517+
518+
[ci skip]
519+
```
520+
521+
### How Skip Commands Work
522+
523+
When a commit message contains a skip command:
524+
525+
1. **Pull Requests**: No PipelineRuns will be created when the PR is opened or updated and HEAD commit contains skip command.
526+
2. **Push Events**: No PipelineRuns will be created when pushing to a branch with that commit message
527+
528+
### GitOps Commands Override Skip CI
529+
530+
**Important:** Skip CI commands can be overridden by using GitOps commands. Even if
531+
a commit contains a skip command like `[skip ci]`, you can still manually trigger
532+
PipelineRuns using:
533+
534+
* `/test` - Trigger all matching PipelineRuns
535+
* `/test <pipelinerun-name>` - Trigger a specific PipelineRun
536+
* `/retest` - Retrigger failed PipelineRuns
537+
* `/retest <pipelinerun-name>` - Retrigger a specific PipelineRun
538+
* `/ok-to-test` - Allow running CI for external contributors
539+
* `/custom-comment` - Trigger PipelineRun having on-comment annotation
540+
541+
This allows you to skip automatic CI execution while still maintaining the ability
542+
to manually trigger builds when needed.
543+
544+
### Example: Skipping CI Then Manually Triggering
545+
546+
```bash
547+
# Initial commit with skip command
548+
git commit -m "docs: update contributing guide [skip ci]"
549+
git push origin my-feature-branch
550+
# No PipelineRuns are created automatically
551+
552+
# Later, you can manually trigger CI by commenting on the PR:
553+
# /test
554+
# This will create PipelineRuns despite the [skip ci] command
555+
```
556+
557+
### Examples of When to Use Skip Commands
558+
559+
Skip commands are useful for:
560+
561+
* Documentation-only changes
562+
* README updates
563+
* Comment or formatting changes
564+
* Work-in-progress commits
565+
* Minor typo fixes
566+
* Configuration file updates that don't affect code
567+
568+
### Examples of When NOT to Use Skip Commands
569+
570+
Avoid using skip commands for:
571+
572+
* Code changes that affect functionality
573+
* Changes to CI/CD pipeline definitions
574+
* Dependency updates
575+
* Any changes that should be tested before merging

pkg/adapter/sinker.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package adapter
33
import (
44
"bytes"
55
"context"
6+
"fmt"
67
"net/http"
78

89
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
910
"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
11+
"github.com/openshift-pipelines/pipelines-as-code/pkg/matcher"
1012
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
1113
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
1214
"github.com/openshift-pipelines/pipelines-as-code/pkg/pipelineascode"
@@ -69,8 +71,78 @@ func (s *sinker) processEvent(ctx context.Context, request *http.Request) error
6971
if err := s.processEventPayload(ctx, request); err != nil {
7072
return err
7173
}
74+
75+
// For ALL events: Setup authenticated client early (including token scoping)
76+
// This centralizes client setup and token scoping in one place for all event types
77+
repo, err := s.findMatchingRepository(ctx)
78+
if err != nil {
79+
// Continue with normal flow - repository matching will be handled in matchRepoPR
80+
s.logger.Debugf("Could not find matching repository for early client setup: %v", err)
81+
} else {
82+
// We found the repository, now setup client with token scoping
83+
// If setup fails here, it's a configuration error and we should fail fast
84+
if err := s.setupClient(ctx, repo); err != nil {
85+
return fmt.Errorf("client setup failed: %w", err)
86+
}
87+
s.logger.Debugf("Client setup completed early in sinker for event type: %s", s.event.EventType)
88+
}
89+
90+
// For PUSH events: commit message is already in event.SHATitle from the webhook payload
91+
// We can check immediately without any API calls or repository lookups
92+
if s.event.EventType == "push" && provider.SkipCI(s.event.SHATitle) {
93+
s.logger.Infof("CI skipped for push event: commit %s contains skip command in message", s.event.SHA)
94+
return nil
95+
}
96+
97+
// For PULL REQUEST events: commit message needs to be fetched via API
98+
// Get commit info for skip-CI detection (only if we successfully set up client above)
99+
if s.event.EventType == "pull_request" && repo != nil {
100+
// Get commit info (including commit message) via API
101+
if err := s.vcx.GetCommitInfo(ctx, s.event); err != nil {
102+
return fmt.Errorf("could not get commit info: %w", err)
103+
}
104+
// Check for skip-ci commands in pull request events
105+
if s.event.HasSkipCommand {
106+
s.logger.Infof("CI skipped for pull request event: commit %s contains skip command in message", s.event.SHA)
107+
return nil
108+
}
109+
}
72110
}
73111

74112
p := pipelineascode.NewPacs(s.event, s.vcx, s.run, s.pacInfo, s.kint, s.logger, s.globalRepo)
75113
return p.Run(ctx)
76114
}
115+
116+
// findMatchingRepository finds the Repository CR that matches the event.
117+
// This is a lightweight lookup to get credentials for early skip-ci checks.
118+
// Uses the canonical matcher implementation to avoid code duplication.
119+
func (s *sinker) findMatchingRepository(ctx context.Context) (*v1alpha1.Repository, error) {
120+
// Use canonical matcher to find repository (empty string searches all namespaces)
121+
repo, err := matcher.MatchEventURLRepo(ctx, s.run, s.event, "")
122+
if err != nil {
123+
return nil, fmt.Errorf("failed to match repository: %w", err)
124+
}
125+
if repo == nil {
126+
return nil, fmt.Errorf("no repository found matching URL: %s", s.event.URL)
127+
}
128+
129+
return repo, nil
130+
}
131+
132+
// setupClient sets up the authenticated client with token scoping for ALL event types.
133+
// This is the primary location where client setup and GitHub App token scoping happens.
134+
// Centralizing this here ensures consistent behavior across all events and enables early
135+
// optimizations like skip-CI detection before expensive processing.
136+
func (s *sinker) setupClient(ctx context.Context, repo *v1alpha1.Repository) error {
137+
return pipelineascode.SetupAuthenticatedClient(
138+
ctx,
139+
s.vcx,
140+
s.kint,
141+
s.run,
142+
s.event,
143+
repo,
144+
s.globalRepo,
145+
s.pacInfo,
146+
s.logger,
147+
)
148+
}

0 commit comments

Comments
 (0)