chore: 1.4.0 - named workspace ports (#851) #33
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
| # Publishes the five packages to npm via OIDC trusted publishing when a | |
| # version tag is pushed. There is NO token: npm trusts a short-lived OIDC | |
| # credential minted for exactly this repo + workflow + environment, and the | |
| # `release` environment requires a manual approval, which replaces the OTP. | |
| # | |
| # The tarballs come from `pnpm pack`: the packages declare their edges to each | |
| # other with pnpm's `workspace:` protocol, which only pnpm substitutes with the | |
| # real version. npm would upload the range verbatim, so npm only publishes the | |
| # already-packed tarball. | |
| # | |
| # Third-party actions are pinned by commit SHA and the job is deliberately | |
| # minimal -- nothing here may run code that could tamper with the tarballs. | |
| name: Release | |
| on: | |
| push: | |
| tags: ['v*'] | |
| permissions: | |
| contents: read | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| environment: release | |
| permissions: | |
| contents: read | |
| id-token: write | |
| env: | |
| PACKAGE_DIRS: core cache metro expo-build-cache stim-cli | |
| steps: | |
| - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 | |
| - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 | |
| with: | |
| node-version: '24' | |
| registry-url: 'https://registry.npmjs.org' | |
| package-manager-cache: false | |
| - uses: pnpm/setup@84cb39b217b10273981911c288cd62326dc7c6d2 # v2.0.2 | |
| with: | |
| # Release installs must not restore a package-manager cache. | |
| cache: false | |
| install: false | |
| - name: Show release runtime | |
| run: | | |
| node --version | |
| npm --version | |
| - name: Install (lifecycle scripts stay blocked) | |
| run: pnpm install --frozen-lockfile | |
| - name: Build | |
| run: pnpm run build | |
| - name: Tag matches the package versions | |
| run: | | |
| tag="${GITHUB_REF_NAME#v}" | |
| for p in $PACKAGE_DIRS; do | |
| v=$(node -p "require('./packages/$p/package.json').version") | |
| if [ "$v" != "$tag" ]; then | |
| echo "packages/$p is $v but the tag is $tag"; exit 1 | |
| fi | |
| done | |
| - name: Pack the tarballs with pnpm | |
| run: | | |
| for p in $PACKAGE_DIRS; do | |
| tarball=$(cd "packages/$p" && pnpm pack --pack-destination "$RUNNER_TEMP/tarballs" | tail -1) | |
| mv "$tarball" "$RUNNER_TEMP/tarballs/$p.tgz" | |
| tar -xzOf "$RUNNER_TEMP/tarballs/$p.tgz" package/package.json > "$RUNNER_TEMP/tarballs/$p.package.json" | |
| done | |
| - name: Packed manifests name the tag, not a workspace range | |
| run: | | |
| tag="${GITHUB_REF_NAME#v}" | |
| for p in $PACKAGE_DIRS; do | |
| manifest="$RUNNER_TEMP/tarballs/$p.package.json" | |
| if grep -nE '"[^"]*": *"workspace:' "$manifest"; then | |
| echo "packages/$p packed an unsubstituted workspace: range"; exit 1 | |
| fi | |
| node -e ' | |
| const manifest = require(process.argv[1]); | |
| const tag = process.argv[2]; | |
| const accepted = new Set([tag, `^${tag}`, `~${tag}`]); | |
| const groups = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]; | |
| let bad = manifest.version === tag ? 0 : 1; | |
| if (bad) console.log(`${manifest.name} is ${manifest.version} but the tag is ${tag}`); | |
| for (const group of groups) { | |
| for (const [name, range] of Object.entries(manifest[group] ?? {})) { | |
| if (name !== "stim" && !name.startsWith("@stim-cli/")) continue; | |
| const ok = accepted.has(range); | |
| console.log(`${manifest.name} ${group}.${name} ${range} ${ok ? "ok" : "MISMATCH"}`); | |
| if (!ok) bad += 1; | |
| } | |
| } | |
| process.exit(bad === 0 ? 0 : 1); | |
| ' "$manifest" "$tag" | |
| done | |
| - name: Compute publish dist-tag | |
| # Dist-tag selection: see RELEASE.md section 1. | |
| run: | | |
| tag_version="${GITHUB_REF_NAME#v}" | |
| dist_tag=latest | |
| if [[ "$tag_version" == *-* ]]; then | |
| err_log="$(mktemp)" | |
| if current=$(npm view @stim-cli/core version 2>"$err_log"); then | |
| if [[ -n "$current" && "$current" != *-* ]]; then | |
| dist_tag=next | |
| fi | |
| elif grep -q 'code E404' "$err_log"; then | |
| : | |
| else | |
| echo "::error::npm view @stim-cli/core version failed and did not report E404; refusing to guess a dist-tag: $(cat "$err_log")" | |
| rm -f "$err_log" | |
| exit 1 | |
| fi | |
| rm -f "$err_log" | |
| fi | |
| echo "tag_version=$tag_version dist_tag=$dist_tag" | |
| echo "DIST_TAG=$dist_tag" >> "$GITHUB_ENV" | |
| - name: Publish @stim-cli/core | |
| run: | | |
| version=$(node -p "require('$RUNNER_TEMP/tarballs/core.package.json').version") | |
| if npm view "@stim-cli/core@$version" version >/dev/null 2>&1; then | |
| echo "@stim-cli/core@$version is already published" | |
| else | |
| npm publish "$RUNNER_TEMP/tarballs/core.tgz" --provenance --access public --tag "$DIST_TAG" | |
| fi | |
| - name: Publish @stim-cli/cache | |
| run: | | |
| version=$(node -p "require('$RUNNER_TEMP/tarballs/cache.package.json').version") | |
| if npm view "@stim-cli/cache@$version" version >/dev/null 2>&1; then | |
| echo "@stim-cli/cache@$version is already published" | |
| else | |
| npm publish "$RUNNER_TEMP/tarballs/cache.tgz" --provenance --access public --tag "$DIST_TAG" | |
| fi | |
| - name: Publish @stim-cli/metro | |
| run: | | |
| version=$(node -p "require('$RUNNER_TEMP/tarballs/metro.package.json').version") | |
| if npm view "@stim-cli/metro@$version" version >/dev/null 2>&1; then | |
| echo "@stim-cli/metro@$version is already published" | |
| else | |
| npm publish "$RUNNER_TEMP/tarballs/metro.tgz" --provenance --access public --tag "$DIST_TAG" | |
| fi | |
| - name: Publish @stim-cli/expo-build-cache | |
| run: | | |
| version=$(node -p "require('$RUNNER_TEMP/tarballs/expo-build-cache.package.json').version") | |
| if npm view "@stim-cli/expo-build-cache@$version" version >/dev/null 2>&1; then | |
| echo "@stim-cli/expo-build-cache@$version is already published" | |
| else | |
| npm publish "$RUNNER_TEMP/tarballs/expo-build-cache.tgz" --provenance --access public --tag "$DIST_TAG" | |
| fi | |
| - name: Publish stim | |
| run: | | |
| version=$(node -p "require('$RUNNER_TEMP/tarballs/stim-cli.package.json').version") | |
| if npm view "stim@$version" version >/dev/null 2>&1; then | |
| echo "stim@$version is already published" | |
| else | |
| npm publish "$RUNNER_TEMP/tarballs/stim-cli.tgz" --provenance --access public --tag "$DIST_TAG" | |
| fi | |
| - name: Smoke-test the registry | |
| run: | | |
| version="${GITHUB_REF_NAME#v}" | |
| max_attempts=12 | |
| retry_delay=10 | |
| for d in $PACKAGE_DIRS; do | |
| p=$(node -p "require('$RUNNER_TEMP/tarballs/$d.package.json').name") | |
| for attempt in $(seq 1 "$max_attempts"); do | |
| if v=$(npm view "$p@$version" version 2>/dev/null) && [ "$v" = "$version" ]; then | |
| echo "$p -> $v" | |
| break | |
| fi | |
| if [ "$attempt" -eq "$max_attempts" ]; then | |
| echo "$p@$version was not visible after $max_attempts attempts" | |
| exit 1 | |
| fi | |
| echo "$p@$version is not visible yet (attempt $attempt/$max_attempts); retrying in ${retry_delay}s" | |
| sleep "$retry_delay" | |
| done | |
| for attempt in $(seq 1 "$max_attempts"); do | |
| if tagged=$(npm view "$p" "dist-tags.$DIST_TAG" 2>/dev/null) && [ "$tagged" = "$version" ]; then | |
| echo "$p dist-tag $DIST_TAG -> $tagged" | |
| break | |
| fi | |
| if [ "$attempt" -eq "$max_attempts" ]; then | |
| echo "$p dist-tag $DIST_TAG points at ${tagged:-<unset>}, expected $version" | |
| exit 1 | |
| fi | |
| echo "$p dist-tag $DIST_TAG is ${tagged:-<unset>}, not $version yet (attempt $attempt/$max_attempts); retrying in ${retry_delay}s" | |
| sleep "$retry_delay" | |
| done | |
| done |