Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support skipping stages by path pattern or commit message prefix #4922

Merged
merged 51 commits into from
Jun 18, 2024

Conversation

t-kikuc
Copy link
Member

@t-kikuc t-kikuc commented May 22, 2024

What this PR does / why we need it:

This PR enables skipping stages (Analysis,WaitApproval,Wait,ScriptRun) when the commit matches
configured path patterns or commit message prefixes.

Which issue(s) this PR fixes:

Fixes #4899

Does this PR introduce a user-facing change?: N/A (only append)

  • How are users affected by this change: N/A
  • Is this breaking change: N/A
  • How to migrate (if breaking change): N/A

How to use

Configuration: add with.skipOn section in stages to skip.

  • paths: When ALL changes of the commit match them, the stage will be skipped.
  • commitMessagePrefixes: When the prefix of the commit's message matches ANY of them, the stage will be skipped.
  pipeline:
    stages:
      - name: ECS_TRAFFIC_ROUTING
        with:
          canary: 30
      - name: WAIT_APPROVAL
        with:
          skipOn:
            commitMessagePrefixes:
              - [skip-approval] # When the commit message starts with '[skip-approval]', this stage will be skipped
      - name: WAIT
        with:
          duration: 60s
          skipOn:
            paths:
              - "**/*.yaml" # Any yaml file under any directory
      - name: ANALYSIS
        with:
          duration: 60s
          skipOn:
            paths:
              - "**/app.pipecd.yaml" # Any app.pipecd.yaml under any directory
              - "**/*.md" # Any markdown file under any directory
      - name: ECS_TRAFFIC_ROUTING
        with:
          primary: 100

When all skipOn conditions of each stage passed, the deployment will be:
image

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
…stage

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
@t-kikuc
Copy link
Member Author

t-kikuc commented May 30, 2024

"should we separate the skipstage as a new package/module or just place it in scheduler code"

I quit separating skipstage package because I also think we don't need to separate package here.

@Warashi
Copy link
Contributor

Warashi commented May 31, 2024

IMO, I agree with khanhtc1202. Because we have no need to hide functions from other controller codes.

Copy link
Member

@ffjlabo ffjlabo left a comment

Choose a reason for hiding this comment

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

Sorry for the late review 🙏 Added some comments.

Comment on lines 709 to 721
stageConfig := in.StageConfig
var skipOptions config.SkipOptions
switch stageConfig.Name {
case model.StageAnalysis:
skipOptions = stageConfig.AnalysisStageOptions.SkipOn
case model.StageWait:
skipOptions = stageConfig.WaitStageOptions.SkipOn
case model.StageWaitApproval:
skipOptions = stageConfig.WaitApprovalStageOptions.SkipOn
case model.StageScriptRun:
skipOptions = stageConfig.ScriptRunStageOptions.SkipOn
default:
return false, nil
Copy link
Member

Choose a reason for hiding this comment

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

[imo] How about adding the func StageConfig.GetSkipConfig() in the config package?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think the current one is enough because

  • StageConfig.GetSkipConfig() will be used only in skipstage package
  • adding the func to each stage model seems redundant

WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

OK, I got it 👍

Comment on lines 67 to 74
func commitMessageHasAnyPrefix(commitMessage string, prefixes []string) bool {
for _, prefix := range prefixes {
if strings.HasPrefix(commitMessage, prefix) {
return true
}
}
return false
}
Copy link
Member

Choose a reason for hiding this comment

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

[imo] It might not be a func, just put it in the skipByCommitMessagePrefixes ?

Copy link
Member Author

Choose a reason for hiding this comment

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

You mean we need to mock repo.ChangedFiles in skipByCommitMessagePrefixes?

Copy link
Member

Choose a reason for hiding this comment

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

I feel commitMessageHasAnyPrefix doesn't have much logic, so IMO, it would be nice to gather the logic to the skipByCommitMessagePrefixes.

You mean we need to mock repo.ChangedFiles in skipByCommitMessagePrefixes?

When testing, we might need to mock as you said.

Copy link
Member Author

Choose a reason for hiding this comment

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

@ffjlabo thanks, I merged the func and used mock in tests.
TBH, I missed git.mock.go https://github.com/pipe-cd/pipecd/blob/master/pkg/git/gittest/git.mock.go

Comment on lines 90 to 103
func hasOnlyPathsToSkip(skipPatterns []string, changedFiles []string) (bool, error) {
matcher, err := filematcher.NewPatternMatcher(skipPatterns)
if err != nil {
return false, err
}

for _, changedFile := range changedFiles {
if !matcher.Matches(changedFile) {
return false, nil
}
}

return true, nil
}
Copy link
Member

Choose a reason for hiding this comment

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

[imo] It might not be a func, just put it in the skipByPathPattern?

Copy link
Member Author

Choose a reason for hiding this comment

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

@ffjlabo thanks, I merged the func and used mock in tests.

Comment on lines 27 to 32
// checkSkipStage checks whether the stage should be skipped or not.
func checkSkipStage(ctx context.Context, in executor.Input, opt config.SkipOptions) (skip bool, err error) {
if len(opt.Paths) == 0 && len(opt.CommitMessagePrefixes) == 0 {
// When no condition is specified.
return false, nil
}
Copy link
Member

Choose a reason for hiding this comment

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

[imo] It might not be a func, just put it in the shouldSkipStage ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, I did it here 8a96a8c

@ffjlabo
Copy link
Member

ffjlabo commented Jun 7, 2024

Sorry to be late. I agree with it.
#4922 (review)

should we separate the skipstage as a new package/module or just place it in scheduler code" is left. IMO, we should place it in the scheduler.go code. Happy to hear other thoughts here 👀

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
@t-kikuc t-kikuc requested a review from Warashi as a code owner June 10, 2024 00:39
t-kikuc and others added 4 commits June 10, 2024 18:03
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Copy link
Contributor

@Warashi Warashi left a comment

Choose a reason for hiding this comment

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

If I push several commits at once, this PR's code seems to process only the last commit.
I think all pushed commits should be handled the same way as the last commit. What do you think?

@t-kikuc
Copy link
Member Author

t-kikuc commented Jun 12, 2024

@Warashi

Thank you, that's important.

a. Path Pattern:

This PR already checks ALL changes from the running commit to the latest commit as below.

changedFiles, err := repo.ChangedFiles(ctx, runningRev, targetRev)

b. Commit Message:

As you said, only the latest commit is checked.

Cases when the latest commit matches the condition but not all commits match it would be:

  • Commit&merge frequently
  • Not using a squash merge

cf. commitMatcher only checks the triggered commit.

if pipelineRegex.MatchString(in.Trigger.Commit.Message) {

@peaceiris What do you think? Should ALL commits match the condition?

@peaceiris
Copy link
Contributor

Only the latest commit is good enough for my use case 👍

@t-kikuc
Copy link
Member Author

t-kikuc commented Jun 12, 2024

@peaceiris I got it, thank you so much!

@khanhtc1202
Copy link
Member

I think the same way, we should only treat the last commit message 👍

Copy link
Member

@khanhtc1202 khanhtc1202 left a comment

Choose a reason for hiding this comment

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

LGTM, can't wait for this in action 👍

Copy link
Contributor

@Warashi Warashi left a comment

Choose a reason for hiding this comment

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

Thank you. There is a consensus to see only the last commit message, so LGTM.

@t-kikuc t-kikuc merged commit e978760 into master Jun 18, 2024
16 of 18 checks passed
@t-kikuc t-kikuc deleted the skip-stage branch June 18, 2024 01:11
dgannon991 pushed a commit to dgannon991/pipecd-pipecd that referenced this pull request Jun 20, 2024
…e-cd#4922)

* rename func to distinguish the trigger of skip stage

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Draft: impl check of skipping stage

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Rename to 'prefixes'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* separate to sub funcs

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Impl Repo.GetCommitFromRev

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Impl skipByPathPattern()

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add skipOn options to Wait Stage Options

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add check of skipping stage in Analysis,Wait,WaitApproval Stages

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix comments

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* draft: add an empty test file

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix comments

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix func name

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Rename struct from confusing one

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Refine order of args

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* add test of hasOnlyPathsToSkip

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Refine testcase names

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* rename 'expected' to 'skip'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix testcases

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* separate func

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* add test for skipByCommitMessagePrefixes

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Moved package to new 'skipstage'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add SkipOptions to ScriptRunStageOptions

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* rename 'SkipOptions' to 'SkipOn' to match with yaml

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Move skip logic to scheduler

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add missing reporting

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix nits

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix check logic

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add missing error handling

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Move skipstage to controller

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Update docs

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* gen mock by 'make gen/code'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* nits: fix from 'fromCmd' to 'byCmd'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* nits: rename func

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* nits: refine configuration-reference

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix error logging of zap.Error(err)

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix the flow in error handling

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix: check skipping before creating executor

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Remove unused func 'GetCommitHashForRev'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix: remove unnecessary formatting

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* refactor: quit separating 'skipstage' package

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* fix: make 'checkSkipStage()' private after refactoring

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Fix: rename test func

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Refactor: merge func 'checkSkipStage' into 'shouldSkipStage'

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Merge funcs

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Add TestSkipByCommitMessagePrefixes with mock

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

* Refactore: merge funcs and use mock

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>

---------

Signed-off-by: t-kikuc <tkikuchi07f@gmail.com>
Signed-off-by: David Gannon <19214156+dgannon991@users.noreply.github.com>
@github-actions github-actions bot mentioned this pull request Jul 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/go kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[feat] Skip stages when the commit is not important (Analysis,WaitApproval,Wait,ScriptRun)
5 participants