From d716360b0854f69d0cfbd1eb3c4e5e93bf126f7d Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 16 Aug 2026 14:05:23 +0530 Subject: [PATCH] refactor(ci): move the release out of ci.yml release.yml is the only thing that publishes now, and it is what creates the tags, so ci no longer triggers on them. Claude-Session: https://claude.ai/code/session_01ShuAy8q8ZfPi8KHxwc8JpQ --- .github/workflows/ci.yml | 192 +-------------------------------------- 1 file changed, 1 insertion(+), 191 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb3ab1e..c2f8545 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,6 @@ on: pull_request: push: branches: [master] - tags: ["v*"] workflow_dispatch: permissions: @@ -434,194 +433,6 @@ jobs: path: runs/ retention-days: 14 - # On master this publishes @sanderling/spec, and only when - # pkg/spec/package.json carries a version npm does not have yet. On a tag it - # publishes the version the tag names. - release-npm: - name: Release (npm) - needs: checks - if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - permissions: - contents: read - steps: - # A refname is attacker-controlled text and git permits backtick, `$`, - # `(`, `;`, `&` and `|` in it, so it goes through env: a `${{ }}` is - # substituted before bash ever sees the line. Every step below reads these - # outputs rather than the refname, and nothing reaches a shell before it - # has matched the pattern. The pattern is anchored and admits no newline, - # which is what stops the value below forging a second $GITHUB_OUTPUT key. - # Release (cli) validates the same way, from its own copy: the two jobs - # hold different permissions and neither should wait on the other. - - name: Validate the tag - id: tag - if: startsWith(github.ref, 'refs/tags/v') - run: | - pattern='^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$' - if [[ ! "$TAG" =~ $pattern ]]; then - echo "release: refusing to publish from '$TAG'" >&2 - echo "release: a release tag is vMAJOR.MINOR.PATCH with an optional -prerelease, e.g. v0.1.0 or v0.0.1-rc1" >&2 - exit 1 - fi - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" - env: - TAG: ${{ github.ref_name }} - - - uses: actions/checkout@v7 - with: - # Empty on master, where the commit that triggered the run is the one - # to publish and master may have moved on since. - ref: ${{ steps.tag.outputs.tag || github.sha }} - # `npm ci` below runs dependency lifecycle scripts, and no step in - # this job needs the git credential afterwards. - persist-credentials: false - - - name: Set up Node 22 - uses: actions/setup-node@v7 - with: - node-version: "22" - registry-url: "https://registry.npmjs.org" - cache: npm - cache-dependency-path: pkg/spec/package-lock.json - - - name: Install dependencies - working-directory: pkg/spec - run: npm ci - - - name: Stamp version - if: startsWith(github.ref, 'refs/tags/v') - working-directory: pkg/spec - run: npm version "$VERSION" --no-git-tag-version --allow-same-version - env: - VERSION: ${{ steps.tag.outputs.version }} - - # npm refuses a version it already has, so most merges to master have - # nothing to publish and must not be red for it. The registry is asked - # rather than the diff of package.json: that answer is still right after a - # revert, after a merge that publishes nothing, and after a publish that - # failed halfway. Only stdout decides, because `npm view` on a version - # that does not exist is empty on some npm releases and an error on - # others, and an unreachable registry must end in a publish that fails - # loudly rather than a skip that looks like success. - - name: Ask npm whether this version is already published - id: version - working-directory: pkg/spec - run: | - version="$(node -p 'require("./package.json").version')" - # Held to the pattern the tag is held to above, and for the same - # reason: a value with a newline in it would forge a second key. - if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$ ]]; then - echo "release: pkg/spec/package.json carries '$version', which is not a version this publishes" >&2 - exit 1 - fi - published="$(npm view "@sanderling/spec@$version" version 2>/dev/null || true)" - if [ -n "$published" ]; then - echo "npm already has @sanderling/spec@$version, nothing to publish" - echo "publish=false" >> "$GITHUB_OUTPUT" - else - echo "publishing @sanderling/spec@$version" - echo "publish=true" >> "$GITHUB_OUTPUT" - fi - echo "version=$version" >> "$GITHUB_OUTPUT" - - - name: Publish @sanderling/spec to npm - if: steps.version.outputs.publish == 'true' - working-directory: pkg/spec - # npm tag pre-releases (e.g. 0.1.0-rc1) as "next" so npm install @sanderling/spec - # keeps resolving the latest stable. - run: | - if [[ "$VERSION" == *-* ]]; then - npm publish --access public --tag next - else - npm publish --access public - fi - # The publish credential is scoped to the one step that publishes rather - # than to the job, so no other step runs with it in reach. - env: - VERSION: ${{ steps.version.outputs.version }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - # Tags only: there is no CLI to cut on a merge. This is the job that holds - # contents: write, and it holds no publish credential of its own. - release-cli: - name: Release (cli) - needs: checks - if: startsWith(github.ref, 'refs/tags/v') - runs-on: ubuntu-latest - permissions: - contents: write - steps: - # The same validation Release (npm) runs, on the same pattern, for the - # same reason. Both copies must stay identical. - - name: Validate the tag - id: tag - run: | - pattern='^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+(\.[0-9A-Za-z]+)*)?$' - if [[ ! "$TAG" =~ $pattern ]]; then - echo "release: refusing to publish from '$TAG'" >&2 - echo "release: a release tag is vMAJOR.MINOR.PATCH with an optional -prerelease, e.g. v0.1.0 or v0.0.1-rc1" >&2 - exit 1 - fi - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" - env: - TAG: ${{ github.ref_name }} - - - uses: actions/checkout@v7 - with: - ref: ${{ steps.tag.outputs.tag }} - # GoReleaser reads the tag history for its changelog. - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v7 - with: - go-version-file: go.mod - cache: true - - - name: Set up JDK 17 - uses: actions/setup-java@v5 - with: - distribution: temurin - java-version: "17" - - - name: Set up Android SDK - uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 - - - name: Cache Gradle - uses: actions/cache@v6 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} - restore-keys: | - gradle-${{ runner.os }}- - - - name: Build sidecar JAR - run: make sidecar - - - name: Publish the sanderling CLI to GitHub Releases - uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7.2.3 - with: - version: "~> v2" - args: release --clean - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - release: - name: Release - if: always() - needs: - - release-npm - - release-cli - runs-on: ubuntu-latest - steps: - - name: Check the group passed - if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') - run: exit 1 - # The docs used to build only when docs/ or the Makefile changed. A path # filter here would have to sit on the whole workflow, so the site is rebuilt # on every merge instead: it is pandoc over a few pages, and a deploy of bytes @@ -629,7 +440,7 @@ jobs: docs: name: Docs needs: checks - if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') + if: github.ref == 'refs/heads/master' runs-on: ubuntu-latest permissions: contents: read @@ -673,7 +484,6 @@ jobs: - checks - folio - replay-ui - - release - docs runs-on: ubuntu-latest steps: