diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ae27767..261bc8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,18 +14,40 @@ on: permissions: contents: read # jobs that publish raise their own permissions +# macOS runners are severely constrained: macOS runs only on a push to main, plus the osx-arm64 AOT pack on a release +# (macOS AOT only compiles on macOS). A release runs no checks of its own beyond packing and the AOT smoke test: +# main-green requires main's run on the released commit to have passed. jobs: + # A release ships a commit main has already proven: wait for main's push run of ci.yml on the released commit and stop + # unless it passed. Every job that runs on a release needs this one, directly or through another. + main-green: + if: github.event_name == 'release' + name: main is green + runs-on: ubuntu-latest + timeout-minutes: 70 + permissions: + actions: read # read main's ci.yml runs + steps: + - uses: nullean/.github/actions/require-green-main@f9eb30d28e48b3f6b8ff1f4e7dae1d2b7b2d544b # v1.6.0 + # On pull requests, only linux-x64 runs, to prove AOT still links without burning the full matrix - # on packages nobody sees. All five run on push, where the packages are actually uploaded. + # on packages nobody sees. All five run on a push to main and on a release, where the packages are actually + # uploaded; osx-arm64 is the one macOS job a release runs. workflow_dispatch runs the four that need no macOS. aot-pack: + needs: main-green + # main-green is skipped on everything but a release, which would skip this job too without a status function. + if: ${{ !cancelled() && (github.event_name != 'release' || needs.main-green.result == 'success') }} runs-on: ${{ matrix.runner }} name: AOT pack (${{ matrix.rid }}) strategy: fail-fast: false matrix: - include: ${{ fromJSON(github.event_name == 'pull_request' - && '[{"rid":"linux-x64","runner":"ubuntu-latest"}]' - || '[{"rid":"linux-x64","runner":"ubuntu-latest"},{"rid":"linux-arm64","runner":"ubuntu-24.04-arm"},{"rid":"win-x64","runner":"windows-latest"},{"rid":"win-arm64","runner":"windows-11-arm"},{"rid":"osx-arm64","runner":"macos-latest"}]') }} + include: ${{ fromJSON( + (github.event_name == 'pull_request' + && '[{"rid":"linux-x64","runner":"ubuntu-latest"}]') + || ((github.event_name == 'push' || github.event_name == 'release') + && '[{"rid":"linux-x64","runner":"ubuntu-latest"},{"rid":"linux-arm64","runner":"ubuntu-24.04-arm"},{"rid":"win-x64","runner":"windows-latest"},{"rid":"win-arm64","runner":"windows-11-arm"},{"rid":"osx-arm64","runner":"macos-latest"}]') + || '[{"rid":"linux-x64","runner":"ubuntu-latest"},{"rid":"linux-arm64","runner":"ubuntu-24.04-arm"},{"rid":"win-x64","runner":"windows-latest"},{"rid":"win-arm64","runner":"windows-11-arm"}]') }} steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -75,7 +97,10 @@ jobs: build: runs-on: ubuntu-latest - needs: aot-pack + needs: [main-green, aot-pack] + if: >- + ${{ !cancelled() && needs.aot-pack.result == 'success' + && (github.event_name != 'release' || needs.main-green.result == 'success') }} permissions: contents: write # append the public API changes to the release notes packages: write # push to GitHub Packages and ghcr.io @@ -113,9 +138,10 @@ jobs: - run: ./build.sh generateapichanges -s name: "Inspect public API changes" - # the tools are restored by the steps above + # the tools are restored by the steps above. Not on a release: main's run on this commit checked it. - run: dotnet curb check . name: Check formatting (curb) + if: github.event_name != 'release' - name: publish to github package repository if: github.event_name == 'push' && startswith(github.ref, 'refs/heads') @@ -181,6 +207,6 @@ jobs: gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --notes-file "$RUNNER_TEMP/notes.md" zizmor: - uses: nullean/.github/.github/workflows/zizmor.yml@d850ce6eee9efa9f81b410d15fcd480409518d19 # v1.5.0 + uses: nullean/.github/.github/workflows/zizmor.yml@e21d5f59405aa7078e51825cd4c407191e28562f # v1.6.1 permissions: contents: read diff --git a/.github/workflows/create-major-tag.yml b/.github/workflows/create-major-tag.yml index 9226648..7ee5eb0 100644 --- a/.github/workflows/create-major-tag.yml +++ b/.github/workflows/create-major-tag.yml @@ -15,7 +15,19 @@ concurrency: cancel-in-progress: false jobs: + # A release moves the major tag only for a commit main has already proven: wait for main's push run of ci.yml on the + # released commit and stop unless it passed. + main-green: + name: main is green + runs-on: ubuntu-latest + timeout-minutes: 70 + permissions: + actions: read # read main's ci.yml runs + steps: + - uses: nullean/.github/actions/require-green-main@f9eb30d28e48b3f6b8ff1f4e7dae1d2b7b2d544b # v1.6.0 + create-major-tag: + needs: main-green permissions: contents: write runs-on: ubuntu-latest diff --git a/AGENTS.md b/AGENTS.md index 77fbe55..79b17de 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,8 +36,10 @@ build targets directly; CI calls it step by step, including `publishcontainers`, ## Releasing release-drafter keeps a draft release up to date from the labels of merged pull requests. **Publishing the draft** -releases: it creates the tag, and `ci.yml` runs on it to build every RID's native-AOT package, push -`ghcr.io/nullean/release-notes` (tagged `latest` and the version) and push the packages to nuget.org. `mise run release` +releases: it creates the tag, and `ci.yml` runs on it. Its `main-green` job first waits for main's push run of `ci.yml` on +the released commit and fails unless it passed (macOS runs only on pushes to main, plus the osx-arm64 AOT pack on a +release); then it builds every RID's native-AOT package, pushes +`ghcr.io/nullean/release-notes` (tagged `latest` and the version) and pushes the packages to nuget.org. `mise run release` shows the draft. The [releasing](.claude/skills/releasing/SKILL.md) and [breaking-changes](.claude/skills/breaking-changes/SKILL.md) skills have the details. Every pull request carries exactly one release label. This repository's own releases do not run the tool: release-drafter writes their notes.