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:
Kun Chen
2026-04-24 00:02:20 -07:00
committed by GitHub
parent 73e169e672
commit 7b6e09fbf6
4 changed files with 64 additions and 115 deletions
+1 -1
View File
@@ -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
+1 -56
View File
@@ -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:
+36
View File
@@ -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
View File
@@ -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)
}
}
}