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
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
06c5f5d
rename func to distinguish the trigger of skip stage
t-kikuc Apr 22, 2024
022e940
Draft: impl check of skipping stage
t-kikuc Apr 22, 2024
59b14ab
Rename to 'prefixes'
t-kikuc Apr 29, 2024
691db8a
separate to sub funcs
t-kikuc Apr 29, 2024
7c37add
Impl Repo.GetCommitFromRev
t-kikuc Apr 29, 2024
ced6054
Impl skipByPathPattern()
t-kikuc Apr 29, 2024
3809d14
Add skipOn options to Wait Stage Options
t-kikuc Apr 29, 2024
7d7f9b7
Add check of skipping stage in Analysis,Wait,WaitApproval Stages
t-kikuc Apr 29, 2024
c4a529d
Fix comments
t-kikuc Apr 29, 2024
e925ef7
draft: add an empty test file
t-kikuc Apr 30, 2024
ee89a23
fix comments
t-kikuc May 19, 2024
87efcd5
Fix func name
t-kikuc May 22, 2024
79fe4c5
Rename struct from confusing one
t-kikuc May 22, 2024
faf376d
Refine order of args
t-kikuc May 22, 2024
152e82f
add test of hasOnlyPathsToSkip
t-kikuc May 22, 2024
2532f42
Refine testcase names
t-kikuc May 22, 2024
8329c41
rename 'expected' to 'skip'
t-kikuc May 22, 2024
a7c87b4
Fix testcases
t-kikuc May 22, 2024
f3fa841
separate func
t-kikuc May 22, 2024
41e136b
add test for skipByCommitMessagePrefixes
t-kikuc May 22, 2024
794e2e5
Moved package to new 'skipstage'
t-kikuc May 22, 2024
06641de
Add SkipOptions to ScriptRunStageOptions
t-kikuc May 22, 2024
fbb350f
rename 'SkipOptions' to 'SkipOn' to match with yaml
t-kikuc May 22, 2024
817830a
Move skip logic to scheduler
t-kikuc May 22, 2024
94618f9
Add missing reporting
t-kikuc May 22, 2024
e9caff2
Merge branch 'master' of https://github.com/pipe-cd/pipecd into skip-…
t-kikuc May 22, 2024
5b3585e
fix nits
t-kikuc May 22, 2024
5c46fe5
Fix check logic
t-kikuc May 22, 2024
2e4c9df
Add missing error handling
t-kikuc May 22, 2024
a687cd7
Move skipstage to controller
t-kikuc May 22, 2024
431682c
Update docs
t-kikuc May 22, 2024
3f0c007
gen mock by 'make gen/code'
t-kikuc May 22, 2024
d59b686
Merge branch 'master' of https://github.com/pipe-cd/pipecd into skip-…
t-kikuc May 29, 2024
b465c66
nits: fix from 'fromCmd' to 'byCmd'
t-kikuc May 30, 2024
674ea92
nits: rename func
t-kikuc May 30, 2024
ea2858a
nits: refine configuration-reference
t-kikuc May 30, 2024
fb4d46b
fix error logging of zap.Error(err)
t-kikuc May 30, 2024
c01b667
fix the flow in error handling
t-kikuc May 30, 2024
6fe1325
Fix: check skipping before creating executor
t-kikuc May 30, 2024
4e67ccb
Remove unused func 'GetCommitHashForRev'
t-kikuc May 30, 2024
e2e35f4
fix: remove unnecessary formatting
t-kikuc May 30, 2024
cab3539
refactor: quit separating 'skipstage' package
t-kikuc May 30, 2024
7eda335
fix: make 'checkSkipStage()' private after refactoring
t-kikuc May 30, 2024
b9e8a2a
Fix: rename test func
t-kikuc May 30, 2024
8a96a8c
Refactor: merge func 'checkSkipStage' into 'shouldSkipStage'
t-kikuc Jun 10, 2024
27e4acd
Merge funcs
t-kikuc Jun 10, 2024
38fae58
Add TestSkipByCommitMessagePrefixes with mock
t-kikuc Jun 10, 2024
0b074fb
Refactore: merge funcs and use mock
t-kikuc Jun 10, 2024
b63c1b1
Merge branch 'master' into skip-stage
t-kikuc Jun 10, 2024
cb6fd10
Merge branch 'master' into skip-stage
t-kikuc Jun 10, 2024
e498822
Merge branch 'master' into skip-stage
t-kikuc Jun 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/content/en/docs-dev/user-guide/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,13 @@ Note: The available values are identical to those found in the aws-sdk-go-v2 Typ
| Field | Type | Description | Required |
|-|-|-|-|

## SkipOptions

| Field | Type | Description | Required |
|-|-|-|-|
| commitMessagePrefixes | []string | List of commit message's prefixes. The stage will be skipped when the prefix of the commit's message matches any of them. Empty means the stage will not be skipped by this condition. | No |
| paths | []string | List of paths to directories or files. When all commit changes match them, the stage will be skipped. Empty means the stage will not be skipped by this condition. Regular expression can be used. | No |

## StageOptions

### KubernetesPrimaryRolloutStageOptions
Expand Down Expand Up @@ -685,12 +692,14 @@ Note: By default, the sum of traffic is rounded to 100. If both `primary` and `c
|-|-|-|-|
| duration | duration | Maximum time to perform the analysis. | Yes |
| metrics | [][AnalysisMetrics](#analysismetrics) | Configuration for analysis by metrics. | No |
| skipOn | [SkipOptions](#skipoptions) | When to skip this stage. | No |

### WaitStageOptions

| Field | Type | Description | Required |
|-|-|-|-|
| duration | duration | Time to wait. | Yes |
| skipOn | [SkipOptions](#skipoptions) | When to skip this stage. | No |

### WaitApprovalStageOptions

Expand All @@ -699,6 +708,7 @@ Note: By default, the sum of traffic is rounded to 100. If both `primary` and `c
| timeout | duration | The maximum length of time to wait before giving up. Default is 6h. | No |
| approvers | []string | List of username who has permission to approve. | Yes |
| minApproverNum | int | Number of minimum needed approvals to make this stage complete. Default is 1. | No |
| skipOn | [SkipOptions](#skipoptions) | When to skip this stage. | No |

### CustomSyncStageOptions (deprecated)
| Field | Type | Description | Required |
Expand All @@ -713,6 +723,7 @@ Note: By default, the sum of traffic is rounded to 100. If both `primary` and `c
| run | string | Script run on this stage. | Yes |
| env | map[string]string | Environment variables used with scripts. | No |
| timeout | duration | The maximum time the stage can be taken to run. Default is `6h`| No |
| skipOn | [SkipOptions](#skipoptions) | When to skip this stage. | No |

## PostSync

Expand Down
37 changes: 37 additions & 0 deletions pkg/app/piped/controller/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,24 @@
Notifier: s.notifier,
}

// Skip the stage if needed based on the skip config.
skip, err := s.shouldSkipStage(sig.Context(), input)
if err != nil {
lp.Errorf("failed to check whether skipping the stage: %w", err.Error())
if err := s.reportStageStatus(ctx, ps.Id, model.StageStatus_STAGE_FAILURE, ps.Requires); err != nil {
s.logger.Error("failed to report stage status", zap.Error(err))
}
return model.StageStatus_STAGE_FAILURE

Check warning on line 538 in pkg/app/piped/controller/scheduler.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/scheduler.go#L531-L538

Added lines #L531 - L538 were not covered by tests
}
if skip {
if err := s.reportStageStatus(ctx, ps.Id, model.StageStatus_STAGE_SKIPPED, ps.Requires); err != nil {
s.logger.Error("failed to report stage status", zap.Error(err))
return model.StageStatus_STAGE_FAILURE
}
lp.Info("The stage was successfully skipped due to the skip configuration of the stage.")
return model.StageStatus_STAGE_SKIPPED

Check warning on line 546 in pkg/app/piped/controller/scheduler.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/scheduler.go#L540-L546

Added lines #L540 - L546 were not covered by tests
}

// Find the executor for this stage.
ex, ok := executorFactory(input)
if !ok {
Expand Down Expand Up @@ -687,6 +705,25 @@
return err
}

func (s *scheduler) shouldSkipStage(ctx context.Context, in executor.Input) (skip bool, err error) {
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

Check warning on line 721 in pkg/app/piped/controller/scheduler.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/scheduler.go#L708-L721

Added lines #L708 - L721 were not covered by tests
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 👍

}

return checkSkipStage(ctx, in, skipOptions)

Check warning on line 724 in pkg/app/piped/controller/scheduler.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/scheduler.go#L724

Added line #L724 was not covered by tests
}

func (s *scheduler) getMentionedAccounts(event model.NotificationEventType) ([]string, error) {
n, ok := s.metadataStore.Shared().Get(model.MetadataKeyDeploymentNotification)
if !ok {
Expand Down
103 changes: 103 additions & 0 deletions pkg/app/piped/controller/skipstage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2024 The PipeCD Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
"context"
"strings"

"github.com/pipe-cd/pipecd/pkg/app/piped/executor"
"github.com/pipe-cd/pipecd/pkg/config"
"github.com/pipe-cd/pipecd/pkg/filematcher"
"github.com/pipe-cd/pipecd/pkg/git"
)

// 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
}

Check warning on line 32 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L28-L32

Added lines #L28 - L32 were not covered by tests
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


repoCfg := in.Application.GitPath.Repo
repo, err := in.GitClient.Clone(ctx, repoCfg.Id, repoCfg.Remote, repoCfg.Branch, "")
if err != nil {
return false, err
}

Check warning on line 38 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L34-L38

Added lines #L34 - L38 were not covered by tests

// Check by path pattern
skip, err = skipByPathPattern(ctx, opt, repo, in.RunningDSP.Revision(), in.TargetDSP.Revision())
if err != nil {
return false, err
}
if skip {
return true, nil
}

Check warning on line 47 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L41-L47

Added lines #L41 - L47 were not covered by tests

// Check by prefix of commit message
skip, err = skipByCommitMessagePrefixes(ctx, opt, repo, in.TargetDSP.Revision())
return skip, err

Check warning on line 51 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L50-L51

Added lines #L50 - L51 were not covered by tests
}

func skipByCommitMessagePrefixes(ctx context.Context, opt config.SkipOptions, repo git.Repo, targetRev string) (skip bool, err error) {
if len(opt.CommitMessagePrefixes) == 0 {
return false, nil
}

Check warning on line 57 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L54-L57

Added lines #L54 - L57 were not covered by tests

commit, err := repo.GetCommitForRev(ctx, targetRev)
if err != nil {
return false, err
}

Check warning on line 62 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L59-L62

Added lines #L59 - L62 were not covered by tests

return commitMessageHasAnyPrefix(commit.Message, opt.CommitMessagePrefixes), nil

Check warning on line 64 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L64

Added line #L64 was not covered by tests
}

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


func skipByPathPattern(ctx context.Context, opt config.SkipOptions, repo git.Repo, runningRev, targetRev string) (skip bool, err error) {
if len(opt.Paths) == 0 {
return false, nil
}

Check warning on line 79 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L76-L79

Added lines #L76 - L79 were not covered by tests

changedFiles, err := repo.ChangedFiles(ctx, runningRev, targetRev)
if err != nil {
return false, err
}
return hasOnlyPathsToSkip(opt.Paths, changedFiles)

Check warning on line 85 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L81-L85

Added lines #L81 - L85 were not covered by tests
}

// hasOnlyPathsToSkip returns true if and only if all changed files are included in `skipPatterns`.
// If any changed file does not match all `skipPatterns`, it returns false.
func hasOnlyPathsToSkip(skipPatterns []string, changedFiles []string) (bool, error) {
matcher, err := filematcher.NewPatternMatcher(skipPatterns)
if err != nil {
return false, err
}

Check warning on line 94 in pkg/app/piped/controller/skipstage.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/controller/skipstage.go#L93-L94

Added lines #L93 - L94 were not covered by tests

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.

133 changes: 133 additions & 0 deletions pkg/app/piped/controller/skipstage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// Copyright 2024 The PipeCD Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestCommitMessageHasAnyPrefix(t *testing.T) {
t.Parallel()
testcases := []struct {
name string
commitMessage string
prefixes []string
skip bool
}{
{
name: "no prefixes",
commitMessage: "test message",
prefixes: []string{},
skip: false,
},
{
name: "no commit message",
commitMessage: "",
prefixes: []string{"to-skip"},
skip: false,
},
{
name: "prefix matches",
commitMessage: "to-skip: test message",
prefixes: []string{"to-skip"},
skip: true,
},
}

for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
skip := commitMessageHasAnyPrefix(tc.commitMessage, tc.prefixes)
assert.Equal(t, tc.skip, skip)
})
}
}

func TestHasOnlyPathsToSkip(t *testing.T) {
t.Parallel()
testcases := []struct {
name string
skipPatterns []string
changedFiles []string
skip bool
}{
{
name: "no skip patterns",
skipPatterns: nil,
changedFiles: []string{"file1"},
skip: false,
},
{
name: "no changed files",
skipPatterns: []string{"file1"},
changedFiles: nil,
skip: true,
},
{
name: "no skip patterns and no changed files",
skipPatterns: nil,
changedFiles: nil,
skip: true,
},
{
name: "skip pattern matches all changed files",
skipPatterns: []string{"file1", "file2"},
changedFiles: []string{"file1", "file2"},
skip: true,
},
{
name: "skip pattern does not match changed files",
skipPatterns: []string{"file1", "file2"},
changedFiles: []string{"file1", "file3"},
skip: false,
},
{
name: "skip files of a directory",
skipPatterns: []string{"dir1/*"},
changedFiles: []string{"dir1/file1", "dir1/file2"},
skip: true,
},
{
name: "skip files recursively",
skipPatterns: []string{"dir1/**"},
changedFiles: []string{"dir1/file1", "dir1/sub/file2"},
skip: true,
},
{
name: "skip files with the extension recursively",
skipPatterns: []string{"dir1/**/*.yaml"},
changedFiles: []string{"dir1/file1.yaml", "dir1/sub1/file2.yaml", "dir1/sub1/sub2/file3.yaml"},
skip: true,
},
{
name: "skip files not recursively",
skipPatterns: []string{"*.yaml"},
changedFiles: []string{"dir1/file1.yaml"},
skip: false,
},
}

for _, tc := range testcases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
// We do not use t.Parallel() here due to https://pkg.go.dev/github.com/pipe-cd/pipecd/pkg/filematcher#PatternMatcher.Matches.
actual, err := hasOnlyPathsToSkip(tc.skipPatterns, tc.changedFiles)
assert.NoError(t, err)
assert.Equal(t, tc.skip, actual)
})
}
}
4 changes: 2 additions & 2 deletions pkg/app/piped/executor/analysis/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
for {
select {
case <-ticker.C:
if !e.checkSkipped(ctx) {
if !e.checkSkippedByCmd(ctx) {

Check warning on line 115 in pkg/app/piped/executor/analysis/analysis.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/executor/analysis/analysis.go#L115

Added line #L115 was not covered by tests
continue
}
status = model.StageStatus_STAGE_SKIPPED
Expand Down Expand Up @@ -339,7 +339,7 @@
return args
}

func (e *Executor) checkSkipped(ctx context.Context) bool {
func (e *Executor) checkSkippedByCmd(ctx context.Context) bool {

Check warning on line 342 in pkg/app/piped/executor/analysis/analysis.go

View check run for this annotation

Codecov / codecov/patch

pkg/app/piped/executor/analysis/analysis.go#L342

Added line #L342 was not covered by tests
var skipCmd *model.ReportableCommand
commands := e.CommandLister.ListCommands()

Expand Down
18 changes: 14 additions & 4 deletions pkg/config/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,18 +404,26 @@ func (s *PipelineStage) UnmarshalJSON(data []byte) error {
return err
}

// SkipOptions contains all configurable values for skipping a stage.
type SkipOptions struct {
CommitMessagePrefixes []string `json:"commitMessagePrefixes,omitempty"`
Paths []string `json:"paths,omitempty"`
}

// WaitStageOptions contains all configurable values for a WAIT stage.
type WaitStageOptions struct {
Duration Duration `json:"duration"`
Duration Duration `json:"duration"`
SkipOn SkipOptions `json:"skipOn,omitempty"`
}

// WaitStageOptions contains all configurable values for a WAIT_APPROVAL stage.
type WaitApprovalStageOptions struct {
// The maximum length of time to wait before giving up.
// Defaults to 6h.
Timeout Duration `json:"timeout" default:"6h"`
Approvers []string `json:"approvers"`
MinApproverNum int `json:"minApproverNum" default:"1"`
Timeout Duration `json:"timeout" default:"6h"`
Approvers []string `json:"approvers"`
MinApproverNum int `json:"minApproverNum" default:"1"`
SkipOn SkipOptions `json:"skipOn,omitempty"`
}

func (w *WaitApprovalStageOptions) Validate() error {
Expand Down Expand Up @@ -448,6 +456,7 @@ type AnalysisStageOptions struct {
Metrics []TemplatableAnalysisMetrics `json:"metrics,omitempty"`
Logs []TemplatableAnalysisLog `json:"logs,omitempty"`
HTTPS []TemplatableAnalysisHTTP `json:"https,omitempty"`
SkipOn SkipOptions `json:"skipOn,omitempty"`
}

func (a *AnalysisStageOptions) Validate() error {
Expand Down Expand Up @@ -498,6 +507,7 @@ type ScriptRunStageOptions struct {
Run string `json:"run"`
Timeout Duration `json:"timeout" default:"6h"`
OnRollback string `json:"onRollback"`
SkipOn SkipOptions `json:"skipOn,omitempty"`
}

// Validate checks the required fields of ScriptRunStageOptions.
Expand Down
Loading
Loading