Skip to content

Commit e6e217b

Browse files
zakiskchmouel
authored andcommitted
fix: use dynamic prefix in retest error
Convert ErrNoFailedPipelineToRetest from a sentinel error variable to a custom error type that accepts the gitops command prefix. This ensures the error message displays the correct command syntax (e.g. /pac retest) based on the repository's configured prefix. Signed-off-by: Zaki Shaikh <zashaikh@redhat.com> Assisted-by: Claude Opus 4.6 (via Claude Code)
1 parent 870b15b commit e6e217b

File tree

10 files changed

+58
-29
lines changed

10 files changed

+58
-29
lines changed

docs/content/docs/guides/gitops-commands/advanced.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ Example:
8787
/pac test
8888
```
8989

90+
You can also configure GitOps command prefix in [Global Repository CR]({{< relref "/docs/operations/global-repository-settings" >}}) so that it will be applied to all Repository CRs those are not defining their own prefix.
91+
9092
## Cancelling a PipelineRun
9193

9294
**What it does:** The `/cancel` command stops running PipelineRuns by commenting on the pull request.

pkg/apis/pipelinesascode/v1alpha1/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ func (s *Settings) Merge(newSettings *Settings) {
228228
if newSettings.AIAnalysis != nil && s.AIAnalysis == nil {
229229
s.AIAnalysis = newSettings.AIAnalysis
230230
}
231+
232+
if newSettings.GitOpsCommandPrefix != "" && s.GitOpsCommandPrefix == "" {
233+
s.GitOpsCommandPrefix = newSettings.GitOpsCommandPrefix
234+
}
231235
}
232236

233237
type Policy struct {

pkg/apis/pipelinesascode/v1alpha1/types_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ func TestMergeSpecs(t *testing.T) {
5858
Policy: &Policy{
5959
OkToTest: []string{"ok1", "ok2"},
6060
},
61+
GitOpsCommandPrefix: "pac",
6162
}, // Initialize as needed
6263
GitProvider: gp, // Initialize as needed
6364
Incomings: incomings,
@@ -71,6 +72,7 @@ func TestMergeSpecs(t *testing.T) {
7172
Policy: &Policy{
7273
OkToTest: []string{"ok1", "ok2"},
7374
},
75+
GitOpsCommandPrefix: "pac",
7476
},
7577
Incomings: incomings,
7678
GitProvider: gp,
@@ -87,6 +89,7 @@ func TestMergeSpecs(t *testing.T) {
8789
Policy: &Policy{
8890
OkToTest: []string{"ok1", "ok2"},
8991
},
92+
GitOpsCommandPrefix: "pac",
9093
}, // Initialize as needed
9194
GitProvider: &GitProvider{}, // Initialize as needed
9295
},
@@ -110,6 +113,7 @@ func TestMergeSpecs(t *testing.T) {
110113
Policy: &Policy{
111114
OkToTest: []string{"ok1", "ok2"},
112115
},
116+
GitOpsCommandPrefix: "pac",
113117
},
114118
Incomings: incomings,
115119
GitProvider: gp,

pkg/matcher/annotation_matcher.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package matcher
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76
"regexp"
87
"strings"
@@ -37,9 +36,20 @@ const (
3736
maxCommentLogLength = 160
3837
)
3938

40-
// ErrNoFailedPipelineToRetest is returned when /retest or /ok-to-test is used
41-
// but all matching pipelines have already succeeded for this commit.
42-
var ErrNoFailedPipelineToRetest = errors.New("All PipelineRuns for this commit have already succeeded. Use `/retest <pipeline-name>` to re-run a specific pipeline or `/test` to re-run all pipelines.") // nolint:revive,staticcheck
39+
// NoFailedPipelineToRetestError is an error type returned when /retest or
40+
// /ok-to-test is used but all matching pipelines have already succeeded for
41+
// this commit. The underlying string value is the gitops comment prefix used
42+
// to construct the user-facing error message with the correct command syntax.
43+
type NoFailedPipelineToRetestError string
44+
45+
func (e NoFailedPipelineToRetestError) Error() string {
46+
return fmt.Sprintf("All PipelineRuns for this commit have already succeeded. Use `%sretest <pipeline-name>` to re-run a specific pipeline or `%stest` to re-run all pipelines.", string(e), string(e))
47+
}
48+
49+
func (e NoFailedPipelineToRetestError) Is(target error) bool {
50+
_, ok := target.(NoFailedPipelineToRetestError)
51+
return ok
52+
}
4353

4454
// prunBranch is value from annotations and baseBranch is event.Base value from event.
4555
func branchMatch(prunBranch, baseBranch string) bool {
@@ -426,7 +436,7 @@ func MatchPipelinerunByAnnotation(ctx context.Context, logger *zap.SugaredLogger
426436
logger.Debugf("MatchPipelinerunByAnnotation: filtering successful templates for event_type=%s", event.EventType)
427437
filtered := filterSuccessfulTemplates(ctx, logger, cs, event, repo, vcx, matchedPRs)
428438
if len(filtered) == 0 {
429-
return nil, ErrNoFailedPipelineToRetest
439+
return nil, NoFailedPipelineToRetestError(provider.GetGitOpsCommentPrefix(repo))
430440
}
431441
return filtered, nil
432442
}

pkg/matcher/annotation_matcher_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,7 +2282,12 @@ func TestMatchPipelinerunByAnnotation(t *testing.T) {
22822282
wantErrNoFailedPipelineToRetest: true,
22832283
repo: &v1alpha1.Repository{
22842284
ObjectMeta: metav1.ObjectMeta{Name: "test-repo", Namespace: "test-ns"},
2285-
Spec: v1alpha1.RepositorySpec{URL: "https://github.com/org/repo"},
2285+
Spec: v1alpha1.RepositorySpec{
2286+
URL: "https://github.com/org/repo",
2287+
Settings: &v1alpha1.Settings{
2288+
GitOpsCommandPrefix: "pac",
2289+
},
2290+
},
22862291
},
22872292
seedData: &testclient.Data{
22882293
PipelineRuns: []*tektonv1.PipelineRun{
@@ -2336,7 +2341,7 @@ func TestMatchPipelinerunByAnnotation(t *testing.T) {
23362341
matches, err := MatchPipelinerunByAnnotation(ctx, logger, tt.args.pruns, cs, &tt.args.runevent, &ghprovider.Provider{}, eventEmitter, repo, true)
23372342
if tt.wantErrNoFailedPipelineToRetest {
23382343
assert.Assert(t, err != nil, "expected ErrNoFailedPipelineToRetest")
2339-
assert.Assert(t, errors.Is(err, ErrNoFailedPipelineToRetest), "expected ErrNoFailedPipelineToRetest, got: %v", err)
2344+
assert.Assert(t, errors.Is(err, NoFailedPipelineToRetestError("/pac ")), "expected ErrNoFailedPipelineToRetest, got: %v", err)
23402345
assert.Equal(t, len(matches), 0, "expected no matches when all pipelines already succeeded")
23412346
return
23422347
}

pkg/pipelineascode/match.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/openshift-pipelines/pipelines-as-code/pkg/matcher"
1515
"github.com/openshift-pipelines/pipelines-as-code/pkg/opscomments"
1616
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
17+
"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
1718
providerstatus "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/status"
1819
"github.com/openshift-pipelines/pipelines-as-code/pkg/resolve"
1920
"github.com/openshift-pipelines/pipelines-as-code/pkg/secrets"
@@ -268,8 +269,9 @@ func (p *PacRun) getPipelineRunsFromRepo(ctx context.Context, repo *v1alpha1.Rep
268269
var matchedPRs []matcher.Match
269270
if p.event.TargetTestPipelineRun == "" {
270271
if matchedPRs, err = matcher.MatchPipelinerunByAnnotation(ctx, p.logger, pipelineRuns, p.run, p.event, p.vcx, p.eventEmitter, repo, true); err != nil {
272+
prefix := provider.GetGitOpsCommentPrefix(repo)
271273
// Check if all pipelines have already succeeded - post comment so user gets feedback
272-
if errors.Is(err, matcher.ErrNoFailedPipelineToRetest) {
274+
if errors.Is(err, matcher.NoFailedPipelineToRetestError(prefix)) {
273275
p.logger.Infof("RepositoryAllPipelinesSucceeded: %s", err.Error())
274276
p.eventEmitter.EmitMessage(nil, zap.InfoLevel, "RepositoryAllPipelinesSucceeded", err.Error())
275277
if commentErr := p.vcx.CreateComment(ctx, p.event, err.Error(), ""); commentErr != nil {

pkg/provider/github/acl.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ func (v *Provider) IsAllowed(ctx context.Context, event *info.Event) (bool, erro
8181
Logger: v.Logger,
8282
}
8383

84+
prefix := provider.GetGitOpsCommentPrefix(v.repo)
85+
8486
// Try to detect a policy rule allowing this
85-
tType, _ := v.detectTriggerTypeFromPayload("", event.Event)
87+
tType, _ := v.detectTriggerTypeFromPayload("", event.Event, prefix)
8688
policyAllowed, policyReason := aclPolicy.IsAllowed(ctx, tType)
8789

8890
switch policyAllowed {

pkg/provider/github/detect.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77

88
"github.com/google/go-github/v81/github"
9+
"github.com/openshift-pipelines/pipelines-as-code/pkg/opscomments"
910
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
1011
"github.com/openshift-pipelines/pipelines-as-code/pkg/provider"
1112
"go.uber.org/zap"
@@ -45,7 +46,9 @@ func (v *Provider) Detect(req *http.Request, payload string, logger *zap.Sugared
4546
}
4647

4748
_ = json.Unmarshal([]byte(payload), &eventInt)
48-
eType, errReason := v.detectTriggerTypeFromPayload(eventType, eventInt)
49+
// at this moment we don't any info about repository CR so passing "/" as gitOpsCommentPrefix
50+
// if its valid event then that will be done in ParsePayload function ahead.
51+
eType, errReason := v.detectTriggerTypeFromPayload(eventType, eventInt, "/")
4952
if eType != "" {
5053
return setLoggerAndProceed(true, "", nil)
5154
}
@@ -56,7 +59,7 @@ func (v *Provider) Detect(req *http.Request, payload string, logger *zap.Sugared
5659
// detectTriggerTypeFromPayload will detect the event type from the payload,
5760
// filtering out the events that are not supported.
5861
// first arg will get the event type and the second one will get an error string explaining why it's not supported.
59-
func (v *Provider) detectTriggerTypeFromPayload(ghEventType string, eventInt any) (triggertype.Trigger, string) {
62+
func (v *Provider) detectTriggerTypeFromPayload(ghEventType string, eventInt any, gitOpsCommentPrefix string) (triggertype.Trigger, string) {
6063
switch event := eventInt.(type) {
6164
case *github.PushEvent:
6265
if event.GetPusher() != nil {
@@ -76,13 +79,13 @@ func (v *Provider) detectTriggerTypeFromPayload(ghEventType string, eventInt any
7679
if event.GetAction() == "created" &&
7780
event.GetIssue().IsPullRequest() &&
7881
event.GetIssue().GetState() == "open" {
79-
if provider.IsTestRetestComment(event.GetComment().GetBody()) {
82+
if opscomments.IsTestRetestComment(event.GetComment().GetBody(), gitOpsCommentPrefix) {
8083
return triggertype.Retest, ""
8184
}
82-
if provider.IsOkToTestComment(event.GetComment().GetBody()) {
85+
if opscomments.IsOkToTestComment(event.GetComment().GetBody(), gitOpsCommentPrefix) {
8386
return triggertype.OkToTest, ""
8487
}
85-
if provider.IsCancelComment(event.GetComment().GetBody()) {
88+
if opscomments.IsCancelComment(event.GetComment().GetBody(), gitOpsCommentPrefix) {
8689
return triggertype.Cancel, ""
8790
}
8891
}
@@ -99,10 +102,10 @@ func (v *Provider) detectTriggerTypeFromPayload(ghEventType string, eventInt any
99102
return "", fmt.Sprintf("check_run: unsupported action \"%s\"", event.GetAction())
100103
case *github.CommitCommentEvent:
101104
if event.GetAction() == "created" {
102-
if provider.IsTestRetestComment(event.GetComment().GetBody()) {
105+
if opscomments.IsTestRetestComment(event.GetComment().GetBody(), gitOpsCommentPrefix) {
103106
return triggertype.Retest, ""
104107
}
105-
if provider.IsCancelComment(event.GetComment().GetBody()) {
108+
if opscomments.IsCancelComment(event.GetComment().GetBody(), gitOpsCommentPrefix) {
106109
return triggertype.Cancel, ""
107110
}
108111
// Here, the `/ok-to-test` command is ignored because it is intended for pull requests.

pkg/provider/provider.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,14 @@ func getNameFromComment(typeOfComment, comment string) string {
112112

113113
func GetPipelineRunAndBranchOrTagNameFromTestComment(comment, prefix string) (string, string, string, error) {
114114
commentType := opscomments.CommentEventType(comment, prefix)
115+
typeOfComment := ""
115116
if commentType == opscomments.TestSingleCommentEventType || commentType == opscomments.TestAllCommentEventType {
116-
typeOfComment := prefix + "test"
117-
return getPipelineRunAndBranchOrTagNameFromComment(typeOfComment, comment)
117+
typeOfComment = prefix + "test"
118+
} else {
119+
// if type of comment is not test single, it is retest single because we've checked the type before
120+
// calling this function.
121+
typeOfComment = prefix + "retest"
118122
}
119-
// if type of comment is not test single, it is retest single because we've checked the type before
120-
// calling this function.
121-
typeOfComment := prefix + "retest"
122123
return getPipelineRunAndBranchOrTagNameFromComment(typeOfComment, comment)
123124
}
124125

test/pkg/github/pr.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,9 @@ func (g *PRTest) RunPullRequest(ctx context.Context, t *testing.T) {
133133
assert.NilError(t, err)
134134
g.Logger = runcnx.Clients.Log
135135
g.Cnx = runcnx
136-
preSettings := g.Options.Settings
136+
settings := g.Options.Settings
137137
g.Options = opts
138-
if preSettings.Github != nil {
139-
g.Options.Settings = preSettings
140-
}
138+
g.Options.Settings = settings
141139
g.Provider = ghcnx
142140
g.TargetNamespace = targetNS
143141
g.CommitTitle = fmt.Sprintf("Testing %s with Github APPS integration on %s", g.Label, targetNS)
@@ -265,9 +263,7 @@ func (g *PRTest) RunPushRequest(ctx context.Context, t *testing.T) {
265263
g.Cnx = runcnx
266264
preSettings := g.Options.Settings
267265
g.Options = opts
268-
if preSettings.Github != nil {
269-
g.Options.Settings = preSettings
270-
}
266+
g.Options.Settings = preSettings
271267
g.Provider = ghcnx
272268
g.TargetNamespace = targetNS
273269
g.PRNumber = -1
@@ -306,7 +302,7 @@ func (g *PRTest) RunPushRequest(ctx context.Context, t *testing.T) {
306302
}
307303
}
308304

309-
if g.Options.Settings.Github != nil {
305+
if g.Options.Settings != nil && g.Options.Settings.Github != nil {
310306
opts.Settings = g.Options.Settings
311307
}
312308
err = CreateCRD(ctx, t, repoinfo, runcnx, opts, ghcnx, targetNS)

0 commit comments

Comments
 (0)