Merge pull request #6137 from SivanCola/fix/auto-compound-bash-approval #74
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # CLI binary line. Tag namespace `v<semver>` triggers this — it builds the | |
| # release archives + checksums, publishes the GitHub release, and updates the | |
| # Homebrew cask. npm is a SEPARATE line (release-npm.yml, `npm-v*`) so an npm | |
| # prerelease can ship without forcing the stable cask, and vice versa; desktop is | |
| # `desktop-v*` (release-desktop.yml). A prerelease `v*-rc*` still builds archives | |
| # and a prerelease GitHub release, but goreleaser auto-skips the cask upload | |
| # (brew has no prerelease channel — see .goreleaser.yaml). | |
| on: | |
| push: | |
| tags: ['v*'] | |
| permissions: | |
| contents: write # create the release and upload archives | |
| jobs: | |
| cache-guard: | |
| name: cache hit guard | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - run: ./scripts/cache-guard.sh | |
| goreleaser: | |
| name: archives + checksums + homebrew tap | |
| needs: cache-guard | |
| runs-on: ubuntu-latest | |
| # CLI stable release (archives + GitHub release + Homebrew cask). It does not | |
| # claim GitHub's repository-wide Latest badge; that belongs to desktop so the | |
| # repository homepage points users at the installable app. Gated on the | |
| # `release` environment so only esengine can approve it going public. | |
| environment: release | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: goreleaser/goreleaser-action@v7 | |
| with: | |
| version: '~> v2' | |
| args: release --clean | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} | |
| - name: Attach desktop manifest compatibility asset | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| TAG: ${{ github.ref_name }} | |
| HAS_R2: ${{ secrets.R2_ACCESS_KEY_ID != '' && secrets.R2_SECRET_ACCESS_KEY != '' && secrets.R2_ACCOUNT_ID != '' && secrets.R2_BUCKET != '' }} | |
| R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} | |
| R2_BUCKET: ${{ secrets.R2_BUCKET }} | |
| run: | | |
| set -euo pipefail | |
| case "$TAG" in | |
| *-*) | |
| echo "prerelease $TAG — GitHub latest does not move here; skipping desktop manifest compatibility asset" | |
| exit 0 | |
| ;; | |
| esac | |
| if [ "$HAS_R2" != "true" ]; then | |
| echo "R2 secrets not configured; skipping desktop manifest compatibility asset" | |
| exit 0 | |
| fi | |
| # dl.reasonix.io serves 403 to GitHub Actions egress IPs (Cloudflare bot | |
| # protection), so read the manifest over the authenticated S3 API instead | |
| # of the public edge. | |
| aws configure set aws_access_key_id "${{ secrets.R2_ACCESS_KEY_ID }}" | |
| aws configure set aws_secret_access_key "${{ secrets.R2_SECRET_ACCESS_KEY }}" | |
| aws configure set region auto | |
| aws s3 cp "s3://${R2_BUCKET}/latest/latest.json" latest.raw.json \ | |
| --endpoint-url "https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com" | |
| jq '.download_page = "https://reasonix.io/#start"' latest.raw.json > latest.json | |
| jq -e ' | |
| ([.platforms[] | (.url, .sig)] | | |
| all(type == "string" and startswith("https://dl.reasonix.io/") and (contains("/releases/latest/") | not))) | |
| ' latest.json >/dev/null | |
| gh release upload "$TAG" latest.json --clobber | |
| # The compatibility asset exists for pre-v1.16 desktop updaters that | |
| # still poll GitHub's repository-wide latest URL. Desktop releases now | |
| # own that Latest badge, but this check still exercises the public fallback | |
| # path exactly the way those clients fetch it: anonymously, over the public | |
| # edge, with a Go client UA. Unlike dl.reasonix.io (whose bot protection | |
| # 403s Actions egress — see the R2 note above), GitHub serves its own | |
| # runners, so this can hard-fail. #5826/#5858 shipped a broken update check | |
| # for weeks precisely because nothing exercised the public path. Retries | |
| # cover the release CDN propagating the freshly uploaded asset. | |
| - name: Smoke public compatibility manifest | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| HAS_R2: ${{ secrets.R2_ACCESS_KEY_ID != '' && secrets.R2_SECRET_ACCESS_KEY != '' && secrets.R2_ACCOUNT_ID != '' && secrets.R2_BUCKET != '' }} | |
| run: | | |
| set -euo pipefail | |
| case "$TAG" in | |
| *-*) | |
| echo "prerelease $TAG — no compatibility asset uploaded; skipping" | |
| exit 0 | |
| ;; | |
| esac | |
| if [ "$HAS_R2" != "true" ]; then | |
| echo "R2 secrets not configured; no compatibility asset uploaded; skipping" | |
| exit 0 | |
| fi | |
| url="https://github.com/${{ github.repository }}/releases/latest/download/latest.json" | |
| for attempt in 1 2 3 4 5 6; do | |
| if curl -fsSL -A "Go-http-client/2.0" -o /tmp/compat-latest.json "$url"; then | |
| jq -e '(.version | type == "string") and (.platforms | type == "object")' /tmp/compat-latest.json >/dev/null | |
| echo "public compatibility manifest OK (desktop version $(jq -r .version /tmp/compat-latest.json))" | |
| exit 0 | |
| fi | |
| echo "attempt $attempt failed; retrying in 10s" | |
| sleep 10 | |
| done | |
| echo "::error::public compatibility manifest unreachable at $url" | |
| exit 1 | |
| # A stable CLI release must never leave the npm line behind: v1.17.5 | |
| # shipped as binaries/Homebrew while npm `latest` still pointed at 0.53.2 | |
| # (#5822) — every `npm update -g` user was silently downgraded to a | |
| # months-old version, and nothing noticed because the npm line | |
| # (release-npm.yml, `npm-vX.Y.Z` tags) is triggered independently and the | |
| # stable npm tag was simply never pushed. release-npm.yml's own verify | |
| # step only guards runs that happen; this guard catches the run that | |
| # DIDN'T. | |
| # | |
| # Two distinct states, two responses (the runs race: both workflows wait | |
| # on the release environment independently, and npm dist-tags propagate | |
| # asynchronously, so "tag pushed but latest not moved yet" is a NORMAL | |
| # mid-release state, not a failure): | |
| # - npm-v<version> tag missing -> hard fail. This is the #5822 gap: | |
| # nobody pushed the npm release at all. | |
| # - tag pushed, latest lagging -> poll briefly, then WARN and pass. | |
| # The npm run may still be awaiting its own environment approval; | |
| # release-npm.yml's verify step owns asserting the dist-tag lands. | |
| - name: Check npm latest dist-tag freshness | |
| env: | |
| TAG: ${{ github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| case "$TAG" in | |
| *-*) | |
| echo "prerelease $TAG — npm latest does not move on prereleases; skipping" | |
| exit 0 | |
| ;; | |
| esac | |
| version="${TAG#v}" | |
| if ! git ls-remote --exit-code origin "refs/tags/npm-v$version" >/dev/null; then | |
| echo "::error::the npm-v$version tag was never pushed — the npm channel is being left behind and 'npm update -g' users will be downgraded to the old 'latest'. Push it: git tag npm-v$version ${TAG} && git push origin npm-v$version (or 'npm dist-tag add reasonix@$version latest' for an already-published version)." | |
| exit 1 | |
| fi | |
| for attempt in 1 2 3 4 5 6; do | |
| got="$(npm view reasonix dist-tags.latest 2>/dev/null || true)" | |
| if [ -n "$got" ]; then | |
| newest="$(printf '%s\n%s\n' "$got" "$version" | sort -V | tail -1)" | |
| if [ "$newest" = "$got" ]; then | |
| echo "npm latest -> $got (>= $version) OK" | |
| exit 0 | |
| fi | |
| fi | |
| echo "npm latest -> ${got:-<unreadable>}, want >= $version (attempt $attempt)" | |
| sleep 10 | |
| done | |
| echo "::warning::npm-v$version is pushed but npm 'latest' is still ${got:-<unreadable>} — the npm publish run is likely awaiting release-environment approval or still propagating. Approve/monitor the release-npm run; its verify step asserts the dist-tag lands." | |
| exit 0 |