diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 57a2988fba..df77fb882f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,6 +77,11 @@ jobs: id: changesets uses: changesets/action@v1 with: + # publish (pnpm run release) ends in scripts/release-publish.sh, + # which pushes all new version tags in ONE atomic git push. This + # pre-empts changesets/action's own concurrent per-tag pushes, which + # otherwise race GitHub's ref backend (remote: fatal error in + # commit_refs) and reject ~half the tags on a large fixed-group bump (#2191). publish: pnpm run release version: pnpm run version commit: 'chore: version packages' diff --git a/package.json b/package.json index 72d0c7e9d1..73dd8cbe8e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "clean": "turbo run clean && rm -rf dist", "setup": "pnpm install && pnpm --filter @objectstack/spec build", "version": "changeset version", - "release": "pnpm run build && bash scripts/build-console.sh && changeset publish", + "release": "pnpm run build && bash scripts/build-console.sh && bash scripts/release-publish.sh", "docs:dev": "pnpm --filter @objectstack/docs dev", "docs:build": "pnpm --filter @objectstack/docs build", "docs:start": "pnpm --filter @objectstack/docs start", diff --git a/scripts/release-publish.sh b/scripts/release-publish.sh new file mode 100755 index 0000000000..8d881a5051 --- /dev/null +++ b/scripts/release-publish.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# release-publish.sh — npm publish + atomic git-tag push for the Release workflow. +# +# Why this exists (the bug it fixes): +# `changeset publish` publishes every package to npm and creates a local +# annotated git tag (`@`) for each one. changesets/action +# then pushes those tags — but it fires one `git push origin ` per tag +# *concurrently* (Promise.all). With this monorepo's large Changesets "fixed" +# group (~70+ packages all bumping in lockstep), that burst of simultaneous +# ref-creation pushes races on GitHub's ref backend, which responds with +# `remote: fatal error in commit_refs` and rejects a chunk of the tags. npm +# publishing has already fully succeeded by then, yet the job fails and the +# rejected version tags never make it to the remote (#2191). +# +# The fix: +# Push ALL new tags ourselves in a SINGLE atomic `git push origin --tags` +# immediately after `changeset publish`. One push = one ref transaction, so +# there is no concurrency to race. By the time changesets/action runs its own +# per-tag pushes, every tag already exists on the remote at the same SHA, so +# each of those pushes is a harmless no-op ("Everything up-to-date"). +# +# git push auth comes from the persisted actions/checkout credentials. The +# tags themselves are already created by `changeset publish` (which configures +# the CI git identity); this script only pushes them. Run as the `publish:` +# command of changesets/action (see +# .github/workflows/release.yml) so the atomic push happens before the action +# pushes tags itself. +set -euo pipefail + +# Publish to npm and create the local version tags. +changeset publish + +# Push every new tag in one atomic transaction (see header). --tags pushes ALL +# local tags regardless of reachability; --follow-tags would push nothing here +# since this is a bare push with no branch ref attached. +git push origin --tags