ci: simplify release workflow and restore macOS coverage (#147)
* ci: drop check and test jobs from release workflow * no-mistakes(review): Restore macOS CI coverage * no-mistakes: apply agent fixes
This commit is contained in:
@@ -31,7 +31,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
@@ -14,59 +14,7 @@ concurrency:
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: "!startsWith(github.event.head_commit.message, 'chore(main): release')"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Format check
|
||||
run: |
|
||||
output=$(gofmt -l .)
|
||||
if [ -n "$output" ]; then
|
||||
echo "Files not formatted:"
|
||||
echo "$output"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
|
||||
test:
|
||||
if: "!startsWith(github.event.head_commit.message, 'chore(main): release')"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Test on Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: go test -race ./...
|
||||
|
||||
- name: Test on Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: go test ./...
|
||||
|
||||
- name: Build
|
||||
run: go build ./cmd/no-mistakes
|
||||
|
||||
release-please:
|
||||
needs: [check, test]
|
||||
if: |
|
||||
!cancelled() &&
|
||||
(needs.check.result == 'success' || needs.check.result == 'skipped') &&
|
||||
(needs.test.result == 'success' || needs.test.result == 'skipped')
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
@@ -82,10 +30,7 @@ jobs:
|
||||
build-and-upload:
|
||||
runs-on: ubuntu-latest
|
||||
needs: release-please
|
||||
if: |
|
||||
!cancelled() &&
|
||||
needs.release-please.result == 'success' &&
|
||||
needs.release-please.outputs.release_created == 'true'
|
||||
if: needs.release-please.outputs.release_created == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCIWorkflowRunsTestsOnAllSupportedDesktopPlatforms(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/ci.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
for _, osName := range []string{"ubuntu-latest", "macos-latest", "windows-latest"} {
|
||||
if !strings.Contains(content, osName) {
|
||||
t.Fatalf("CI workflow must test %q", osName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCIWorkflowUsesRaceTestsOnUnixRunners(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/ci.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
if !strings.Contains(content, "if: runner.os != 'Windows'") {
|
||||
t.Fatalf("CI workflow must keep the Unix test branch so macOS runs the Unix suite")
|
||||
}
|
||||
if !strings.Contains(content, "run: go test -race ./...") {
|
||||
t.Fatalf("CI workflow must run the race-enabled suite on Unix runners")
|
||||
}
|
||||
}
|
||||
+26
-58
@@ -22,81 +22,49 @@ func TestReleaseWorkflowUsesScopedConcurrencyGroup(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseWorkflowSkipsValidationJobsForReleaseCommits(t *testing.T) {
|
||||
func TestReleaseWorkflowDoesNotDefineValidationJobs(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/release.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
guard := "if: \"!startsWith(github.event.head_commit.message, 'chore(main): release')\""
|
||||
if strings.Count(content, guard) != 2 {
|
||||
t.Fatalf("release workflow must skip both validation jobs for release commits")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseWorkflowAllowsReleasePleaseAfterSkippedValidation(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/release.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
checks := []string{
|
||||
"if: |",
|
||||
"!cancelled() &&",
|
||||
"(needs.check.result == 'success' || needs.check.result == 'skipped') &&",
|
||||
"(needs.test.result == 'success' || needs.test.result == 'skipped')",
|
||||
}
|
||||
for _, check := range checks {
|
||||
if !strings.Contains(content, check) {
|
||||
t.Fatalf("release workflow must allow release-please to proceed when validation jobs are skipped: missing %q", check)
|
||||
for _, job := range []string{"check", "test"} {
|
||||
if strings.Contains(content, "\n "+job+":\n") {
|
||||
t.Fatalf("release workflow must not define %q; CI owns validation now", job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regression for the v1.8.1 incident: when check/test are skipped for release
|
||||
// PR merge commits, GitHub Actions implicitly wraps downstream job `if:`
|
||||
// expressions with success(), which returns false because upstream jobs did
|
||||
// not succeed. build-and-upload and checksums must include !cancelled() (or
|
||||
// success()/always()) so the implicit wrap is skipped, and must gate on
|
||||
// release-please succeeding plus release_created=='true'.
|
||||
func TestReleaseWorkflowRunsBuildAndChecksumsAfterSkippedValidation(t *testing.T) {
|
||||
func TestReleaseWorkflowRunsReleasePleaseWithoutValidationGuards(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/release.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
content := string(data)
|
||||
|
||||
jobs := []struct {
|
||||
name string
|
||||
required []string
|
||||
}{
|
||||
{
|
||||
name: "build-and-upload",
|
||||
required: []string{
|
||||
"!cancelled()",
|
||||
"needs.release-please.result == 'success'",
|
||||
"needs.release-please.outputs.release_created == 'true'",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "checksums",
|
||||
required: []string{
|
||||
"!cancelled()",
|
||||
"needs.release-please.result == 'success'",
|
||||
"needs.build-and-upload.result == 'success'",
|
||||
"needs.release-please.outputs.release_created == 'true'",
|
||||
},
|
||||
},
|
||||
block := extractJobBlock(t, string(data), "release-please")
|
||||
if strings.Contains(block, "needs:") {
|
||||
t.Fatalf("release-please must not depend on in-workflow validation jobs")
|
||||
}
|
||||
guard := "!startsWith(github.event.head_commit.message, 'chore(main): release')"
|
||||
if strings.Contains(block, guard) {
|
||||
t.Fatalf("release-please must not carry the old release-commit skip guard")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReleaseWorkflowBuildStartsOnlyWhenReleaseIsCreated(t *testing.T) {
|
||||
data, err := os.ReadFile(".github/workflows/release.yml")
|
||||
if err != nil {
|
||||
t.Fatalf("read workflow: %v", err)
|
||||
}
|
||||
|
||||
for _, j := range jobs {
|
||||
block := extractJobBlock(t, content, j.name)
|
||||
for _, req := range j.required {
|
||||
if !strings.Contains(block, req) {
|
||||
t.Fatalf("%s job must contain %q so it runs after skipped validation jobs", j.name, req)
|
||||
}
|
||||
block := extractJobBlock(t, string(data), "build-and-upload")
|
||||
if !strings.Contains(block, "if: needs.release-please.outputs.release_created == 'true'") {
|
||||
t.Fatalf("build-and-upload must run only when release-please created a release")
|
||||
}
|
||||
for _, unexpected := range []string{"!cancelled()", "needs.release-please.result == 'success'"} {
|
||||
if strings.Contains(block, unexpected) {
|
||||
t.Fatalf("build-and-upload must not keep the old skipped-validation guard %q", unexpected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user