Skip to content

chore: 1.4.0 - named workspace ports (#851) #33

chore: 1.4.0 - named workspace ports (#851)

chore: 1.4.0 - named workspace ports (#851) #33

Workflow file for this run

# 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