Skip to content

Conversation

@theakshaypant
Copy link
Contributor

@theakshaypant theakshaypant commented Nov 3, 2025

📝 Description of the Change

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.

👨🏻‍ Linked Jira

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

🔗 Linked GitHub Issue

N/A

🚀 Type of Change

  • 🐛 Bug fix (fix:)
  • ✨ New feature (feat:)
  • 💥 Breaking change (feat!:, fix!:)
  • 📚 Documentation update (docs:)
  • ⚙️ Chore (chore:)
  • 💅 Refactor (refactor:)
  • 🔧 Enhancement (enhance:)
  • 📦 Dependency update (deps:)

🧪 Testing Strategy

  • Unit tests
  • Integration tests
  • End-to-end tests
  • Manual testing
  • Not Applicable

🤖 AI Assistance

  • I have not used any AI assistance for this PR.
  • I have used AI assistance for this PR.

If you have used AI assistance, please provide the following details:

Which LLM was used?

  • GitHub Copilot
  • ChatGPT (OpenAI)
  • Claude (Anthropic)
  • Cursor
  • Gemini (Google)
  • Other: ____________

Extent of AI Assistance:

  • Documentation and research only
  • Unit tests or E2E tests only
  • Code generation (parts of the code)
  • Full code generation (most of the PR)
  • PR description and comments
  • Commit message(s)

Important

If the majority of the code in this PR was generated by an AI, please add a Co-authored-by trailer to your commit message.
For example:

Co-authored-by: Gemini gemini@google.com
Co-authored-by: ChatGPT noreply@chatgpt.com
Co-authored-by: Claude noreply@anthropic.com
Co-authored-by: Cursor noreply@cursor.com
Co-authored-by: Copilot Copilot@users.noreply.github.com

**💡You can use the script ./hack/add-llm-coauthor.sh to automatically add
these co-author trailers to your commits.

✅ Submitter Checklist

  • 📝 My commit messages are clear, informative, and follow the project's How to write a git commit message guide. The Gitlint linter ensures in CI it's properly validated
  • ✨ I have ensured my commit message prefix (e.g., fix:, feat:) matches the "Type of Change" I selected above.
  • ♽ I have run make test and make lint locally to check for and fix any
    issues. For an efficient workflow, I have considered installing
    pre-commit and running pre-commit install to
    automate these checks.
  • 📖 I have added or updated documentation for any user-facing changes.
  • 🧪 I have added sufficient unit tests for my code changes.
  • 🎁 I have added end-to-end tests where feasible. See README for more details.
  • 🔎 I have addressed any CI test flakiness or provided a clear reason to bypass it.
  • If adding a provider feature, I have filled in the following and updated the provider documentation:
    • GitHub App
    • GitHub Webhook
    • Gitea/Forgejo
    • GitLab
    • Bitbucket Cloud
    • Bitbucket Data Center

@pipelines-as-code
Copy link

pipelines-as-code bot commented Nov 3, 2025

🔍 PR Lint Feedback

Note: This automated check helps ensure your PR follows our contribution guidelines.

⚠️ Items that need attention:

🤖 AI attribution

The following commits lack an explicit AI attribution footer:

  • 5a43e25 feat: add skip CI command support

If no AI assistance was used for a commit, you can ignore this warning.
Otherwise add an Assisted-by: or Co-authored-by: footer referencing the AI used.


ℹ️ Next Steps

  • Review and address the items above
  • Push new commits to update this PR
  • This comment will be automatically updated when issues are resolved
🔧 Admin Tools (click to expand)

Automated Issue/Ticket Creation:

  • /issue-create - Generate a GitHub issue from this PR content using AI
  • /jira-create - Create a SRVKP Jira ticket from this PR content using AI

⚠️ Important: Always review and edit generated content before finalizing tickets/issues.
The AI-generated content should be used as a starting point and may need adjustments.

These commands are available to maintainers and will post the generated content as PR comments for review.

🤖 This feedback was generated automatically by the PR CI system

@pipelines-as-code pipelines-as-code bot added feature New feature or request gitops providers labels Nov 3, 2025
@theakshaypant theakshaypant force-pushed the SRVKP-8933-skip-ci branch 2 times, most recently from 86a3521 to 84ea887 Compare November 3, 2025 12:04
Comment on lines 93 to 100
// Check skip logic after annotation matching, so that the EventType has been properly updated.
// In order for the PR to be skipped, the commit message should include the skip command.
// We don't want to skip the PR when the event is a GitOps command. For example, if the
// user has added `[skip ci]` to the commit, they don't need to push again to trigger the
// PipelineRun, they can simply add a comment to trigger the CI via `/test pr-name` or on-comment.
if p.event.HasSkipCommand && !opscomments.IsAnyOpsEventType(p.event.EventType) {
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@theakshaypant you don't need to let the flow come here, you can skip the event right there in ParsePayload func of every provider with an appropriate message

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for example, we do for skip-push-events-for-pr-commits

if v.pacInfo.SkipPushEventForPRCommits && len(prs) > 0 {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is helpful.
I was trying to keep this check in providers initially but was facing an issue where the on-comment event type (hence the first line of the comment) was a no-ops event before annotation matching.
I will try the flow you suggested.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zakisk ! I was trying out your suggestion and IIUC we are doing an early exit by having the check here, right? Would it be correct to say that we can only early exit for push event since the payload contains the commit message? We will still have to wait for GetCommitInfo in case of pull_request event type and of course we handle the comments at a later stage as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed we're going to ignore comment events under this feature, we only do it in push and pull_request and if you don't have commit message in pull_request payload then you have github client in parse_payload you can get the commit message there. and also you need to assign appropriate values to the fields in the event struct which are being assigned on call of GetCommitInfo so that you can avoid double API call to GetCommit endpoint.

@theakshaypant theakshaypant force-pushed the SRVKP-8933-skip-ci branch 2 times, most recently from 3947c53 to 752398f Compare November 5, 2025 09:40
wantErr: true,
wantErrMsg: "failed to run create status, user is not allowed to run the CI",
},
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"commit not found" test removed - GetCommitInfo is now called earlier in sinker.go for PR events, not in verifyRepoAndUser. The error "could not find commit info" would be caught in processEvent before reaching verifyRepoAndUser.

Comment on lines +94 to +102
// Defensive skip-CI check: this is a safety net in case events bypass the early check in sinker.
// Primary skip detection happens in sinker.processEvent() for performance, but this ensures
// nothing slips through (e.g., tests that call Run() directly, or edge cases).
// Skip only for non-GitOps events (GitOps commands can override skip-CI).
if p.event.HasSkipCommand && !opscomments.IsAnyOpsEventType(p.event.EventType) {
p.logger.Infof("CI skipped: commit contains skip command in message (secondary check)")
return nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned in the comment, have kept this as a safety net, ideally this would not be required.

Comment on lines 68 to 69
// Set up authenticated client with proper token scoping
// NOTE: This is typically already done in sinker.processEvent() for all event types,
// but we call it here as a safety net for edge cases (e.g., tests calling Run() directly,
// or if the early setup in sinker failed/was skipped). The call is idempotent.
err = SetupAuthenticatedClient(ctx, p.vcx, p.k8int, p.run, p.event, repo, p.globalRepo, p.pacInfo, p.logger)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Open to hearing if we need this. I saw an error during early setup of git client so added this backup.
I was not able to reproduce that issue. Again, ideally this would not be required.

Comment on lines -186 to -208
// regenerate token scoped to the repo IDs
if v.pacInfo.SecretGHAppRepoScoped && installationIDFrompayload != -1 && len(v.RepositoryIDs) > 0 {
repoLists := []string{}
if v.pacInfo.SecretGhAppTokenScopedExtraRepos != "" {
// this is going to show up a lot in the logs but i guess that
// would make people fix the value instead of being lost into
// the top of the logs at controller start.
for _, configValue := range strings.Split(v.pacInfo.SecretGhAppTokenScopedExtraRepos, ",") {
configValueS := strings.TrimSpace(configValue)
if configValueS == "" {
continue
}
repoLists = append(repoLists, configValueS)
}
v.Logger.Infof("Github token scope extended to %v keeping SecretGHAppRepoScoped to true", repoLists)
}
token, err := v.CreateToken(ctx, repoLists, processedEvent)
if err != nil {
return nil, err
}
processedEvent.Provider.Token = token
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have moved the token scoping to client_setup to avoid calling it twice.
Did not consider a fail safe here, I guess. 😅

seedData: vaildSecret,
nilClient: false,
},
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests for repository ID setting and token scoping with extra repos have been moved to pkg/adapter/sinker_test.go:TestSetupClient_GitHubAppVsOther since this logic is handled in sinker.go via the centralized SetupAuthenticatedClient function.

@theakshaypant
Copy link
Contributor Author

/gemini review

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a well-implemented feature for skipping CI builds via commit messages. The changes are robust, featuring an efficient early check in the sinker and a reliable fallback mechanism. The refactoring to centralize client authentication is a significant improvement to the codebase's maintainability. The feature is consistently applied across all supported providers, and the accompanying documentation and extensive test coverage are excellent. I have a couple of suggestions to further enhance maintainability.

@theakshaypant
Copy link
Contributor Author

/retest

@theakshaypant theakshaypant force-pushed the SRVKP-8933-skip-ci branch 2 times, most recently from 5df28c0 to 963047e Compare November 5, 2025 10:50
@theakshaypant theakshaypant marked this pull request as ready for review November 5, 2025 11:32
@theakshaypant theakshaypant requested a review from zakisk November 6, 2025 04:41
@theakshaypant
Copy link
Contributor Author

@chmouel @zakisk Should we also have a metric for skipped pipelines?

Comment on lines +74 to +109

// For ALL events: Setup authenticated client early (including token scoping)
// This centralizes client setup and token scoping in one place for all event types
repo, err := s.findMatchingRepository(ctx)
if err != nil {
// Continue with normal flow - repository matching will be handled in matchRepoPR
s.logger.Debugf("Could not find matching repository for early client setup: %v", err)
} else {
// We found the repository, now setup client with token scoping
// If setup fails here, it's a configuration error and we should fail fast
if err := s.setupClient(ctx, repo); err != nil {
return fmt.Errorf("client setup failed: %w", err)
}
s.logger.Debugf("Client setup completed early in sinker for event type: %s", s.event.EventType)
}

// For PUSH events: commit message is already in event.SHATitle from the webhook payload
// We can check immediately without any API calls or repository lookups
if s.event.EventType == "push" && provider.SkipCI(s.event.SHATitle) {
s.logger.Infof("CI skipped for push event: commit %s contains skip command in message", s.event.SHA)
return nil
}

// For PULL REQUEST events: commit message needs to be fetched via API
// Get commit info for skip-CI detection (only if we successfully set up client above)
if s.event.EventType == "pull_request" && repo != nil {
// Get commit info (including commit message) via API
if err := s.vcx.GetCommitInfo(ctx, s.event); err != nil {
return fmt.Errorf("could not get commit info: %w", err)
}
// Check for skip-ci commands in pull request events
if s.event.HasSkipCommand {
s.logger.Infof("CI skipped for pull request event: commit %s contains skip command in message", s.event.SHA)
return nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I've said before, you can move this to ParsePayload, as name suggests ParsePayload func is to verify the payload

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this logic was common for all providers, I kept it here to avoid duplication.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, but there is one more call to GetCommitInfo then that's not needed to avoid more API calls

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of calls which are duplicated in this code at the moment. I have highlighted some of them in my comments, looking for inputs for how useful (or expensive) they can be.

As for having GetCommitInfo only once, I would personally prefer having it here (moving it outside the pull_request conditional of course) rather than duplicating this logic for each provider. (And removing from match.go if we want to avoid multiple API calls.)

Open to hearing contrary perspective to this.

Copy link
Contributor

@zakisk zakisk Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And removing from match.go if we want to avoid multiple API calls.

yeah, removing would work. and also call get_commit endpoint only if one of the field SHA, SHATitle, and ShaURL is empty so that we can avoid API call for GitLab etc.

if event.SHA == "" || event.SHATitle == "" || event.SHAURL == ""

@zakisk
Copy link
Contributor

zakisk commented Nov 12, 2025

@theakshaypant IIRC, on the design doc we have agreed upon having a neutral check for skipped event letting user know that event is being skipped intentionally.
cc: @chmouel

@theakshaypant
Copy link
Contributor Author

@theakshaypant IIRC, on the design doc we have agreed upon having a neutral check for skipped event letting user know that event is being skipped intentionally. cc: @chmouel

From my understanding, we only had started a discussion about adding a neutral check, but no final agreement was reached. The current implementation follows GitHub Actions’ behavior for skipped events, which keeps things consistent IMO.

For instance, if a CI has GitHub workflow and PipelineRun configured and a user adds [skip ci] to the commit message:

  • In current implementation, none of the pipelines would be listed.
  • For skipped pipeline behaviour, the status would should a neutral check for PAC CI and not list the GH workflows.

Happy to continue the discussion here.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request gitops providers

Development

Successfully merging this pull request may close these issues.

2 participants