Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,17 @@ USAGE:
OPTIONS:
--commit-info-file value Path to a file which the commit info will be written. The file will be overwritten if it already exists.
--exclude value, -x value [ --exclude value, -x value ] Exclude the status of a specific CI check from failing the wait. By default, a failed status check will exit the pr wait command. [$GITHUB_CI_EXCLUDE]
--stop-on-failed-ci Stop and exit with error when CI fails. Defaults to true. [$GITHUB_STOP_ON_FAILED_CI]
--help, -h show help
```

This command will wait for the given PR (URL or owner/repo/number) to be merged
or closed. If merged, it will exit with code `0` (success) and if closed without
being merged it will exit with code `1` (failure).

By default, the command will also exit with code `1` if the CI checks on the PR
fail. This behavior can be disabled by setting `--stop-on-failed-ci=false`.

#### `ci`

```
Expand Down Expand Up @@ -155,6 +159,11 @@ which are not added immediately.
Recheck interval (i.e. poll this often) in golang duration format. Optional.
Default is `30s`.

#### `stop-on-failed-ci`

Stop and exit with error when CI fails. Only used when `wait-for` is set to
`"pr"`. Optional. Default is `true`.

#### `owner`

GitHub repo owner. Optional. Default is the current repository's owner,
Expand Down
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ inputs:
checks-to-wait-for:
description: 'The comma-separated names of the checks to wait for. Only used when wait-for is "ci"'
required: false
stop-on-failed-ci:
description: 'Stop and exit with error when CI fails. Only used when wait-for is "pr". Defaults to true for backwards compatibility'
required: false
default: "true"

runs:
using: composite
Expand All @@ -68,6 +72,7 @@ runs:
GITHUB_TOKEN: ${{ inputs.token }}
GITHUB_CI_CHECKS: ${{ inputs.checks-to-wait-for }}
GITHUB_CI_EXCLUDE: ${{ inputs.exclude-checks }}
GITHUB_STOP_ON_FAILED_CI: ${{ inputs.stop-on-failed-ci }}
GITHUB_APP_ID: ${{ inputs.app-id }}
GITHUB_APP_INSTALLATION_ID: ${{ inputs.app-installation-id }}
GITHUB_APP_PRIVATE_KEY: ${{ inputs.app-private-key }}
Expand Down
14 changes: 14 additions & 0 deletions cmd/wait-for-github/pr.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type prConfig struct {

commitInfoFile string
excludes []string
stopOnFailedCI bool
writer fileWriter
}

Expand Down Expand Up @@ -122,6 +123,7 @@ func parsePRArguments(ctx context.Context, cmd *cli.Command, logger *slog.Logger
pr: n,
commitInfoFile: cmd.String("commit-info-file"),
excludes: cmd.StringSlice("exclude"),
stopOnFailedCI: cmd.Bool("stop-on-failed-ci"),
writer: osFileWriter{},
}, nil
}
Expand Down Expand Up @@ -178,6 +180,10 @@ func (pr prCheck) Check(ctx context.Context) error {
return cli.Exit("PR is closed", 1)
}

if !pr.stopOnFailedCI {
return nil
}

// not merged, not closed, let's see what the CI status is. If that's bad,
// we can exit early.
sha, err := pr.githubClient.GetPRHeadSHA(ctx, pr.owner, pr.repo, pr.pr)
Expand Down Expand Up @@ -233,6 +239,14 @@ func prCommand(cfg *config) *cli.Command {
cli.EnvVar("GITHUB_CI_EXCLUDE"),
),
},
&cli.BoolFlag{
Name: "stop-on-failed-ci",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would be more clear if the flag were named something like continue-on-failed-ci or ignore-failed-ci which would default to false to preserve the existing behavior.

Usage: "Stop and exit with error when CI fails. Defaults to true.",
Value: true,
Sources: cli.NewValueSourceChain(
cli.EnvVar("GITHUB_STOP_ON_FAILED_CI"),
),
},
},
Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) {
var err error
Expand Down
52 changes: 40 additions & 12 deletions cmd/wait-for-github/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func TestPRCheck(t *testing.T) {
name string
fakeClient fakeGithubClientPRCheck
expectedExitCode *int
allowFailedCI bool
}{
{
name: "PR is merged",
Expand Down Expand Up @@ -90,12 +91,30 @@ func TestPRCheck(t *testing.T) {
},
},
{
name: "Not merged, but CI failed",
name: "Not merged, but CI failed (stop-on-failed-ci enabled)",
fakeClient: fakeGithubClientPRCheck{
CIStatus: github.CIStatusFailed,
},
expectedExitCode: &one,
},
{
name: "Merged, CI failed",
fakeClient: fakeGithubClientPRCheck{
CIStatus: github.CIStatusFailed,
MergedCommit: "abc123",
MergedAt: 1234567890,
},
expectedExitCode: &zero,
},
{
name: "Closed, CI failed (stop-on-failed-ci disabled)",
fakeClient: fakeGithubClientPRCheck{
Closed: true,
CIStatus: github.CIStatusFailed,
},
allowFailedCI: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

Instead of using a test-only field here, I think it makes more sense to directly set the prConfig field for the test scenario:

Suggested change
allowFailedCI: true,
continueOnFailedCI: false,

expectedExitCode: &one,
},
{
name: "Not merged, getting PR head SHA failed",
fakeClient: fakeGithubClientPRCheck{
Expand All @@ -122,9 +141,10 @@ func TestPRCheck(t *testing.T) {
cancel()

prConfig := prConfig{
owner: "owner",
repo: "repo",
pr: 1,
owner: "owner",
repo: "repo",
pr: 1,
stopOnFailedCI: !tt.allowFailedCI,
}

err := checkPRMerged(ctx, fakePRStatusChecker, cfg, &prConfig)
Expand Down Expand Up @@ -237,20 +257,22 @@ func TestParsePRArguments(t *testing.T) {
name: "Valid pull request URL",
args: []string{"https://github.com/owner/repo/pull/1"},
want: prConfig{
owner: "owner",
repo: "repo",
pr: 1,
writer: osFileWriter{},
owner: "owner",
repo: "repo",
pr: 1,
stopOnFailedCI: false,
writer: osFileWriter{},
},
},
{
name: "Valid arguments owner, repo, pr",
args: []string{"owner", "repo", "1"},
want: prConfig{
owner: "owner",
repo: "repo",
pr: 1,
writer: osFileWriter{},
owner: "owner",
repo: "repo",
pr: 1,
stopOnFailedCI: false,
writer: osFileWriter{},
},
},
{
Expand Down Expand Up @@ -278,6 +300,12 @@ func TestParsePRArguments(t *testing.T) {
rootCmd := &cli.Command{}
prCmd := &cli.Command{
Name: "pr",
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "stop-on-failed-ci",
Value: false,
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
got, err := parsePRArguments(ctx, cmd, testLogger)
if tt.wantErr != nil {
Expand Down
Loading