Skip to content

fix(pkg): ship JoltPhysics/Build/ — bloom_jolt depends on its CMakeLists #2

fix(pkg): ship JoltPhysics/Build/ — bloom_jolt depends on its CMakeLists

fix(pkg): ship JoltPhysics/Build/ — bloom_jolt depends on its CMakeLists #2

Workflow file for this run

name: Release
# Fires on a version tag push (e.g. `v0.3.2`). Gates on the Tests workflow
# passing for the exact same commit, then creates/updates the GitHub Release
# and publishes the package to npm as @bloomengine/engine.
#
# The /release Claude Code skill in .claude/skills/release/ drives this end
# to end: it bumps the version in package.json, commits, tags, pushes, and
# waits for this workflow to go green.
#
# Authentication: npm trusted publishing. The @bloomengine/engine package is
# configured on npmjs.com with this workflow (Bloom-Engine/engine →
# .github/workflows/release.yml → job publish-npm) as a trusted publisher.
# `id-token: write` on the publish job is enough — `npm publish` exchanges
# the GitHub OIDC token for a short-lived publish credential, no NPM_TOKEN
# secret needed. Provenance attestation is automatic under this flow.
on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: "Existing tag to (re-)publish a release for (e.g. v0.3.2)"
required: true
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false # never cancel a release mid-flight
permissions:
contents: write
jobs:
# ---------------------------------------------------------------------------
# Gate: wait for `Tests` to pass on this commit before we create the GitHub
# Release. Same pattern as Perry's release-packages.yml — query the API by
# workflow filename + head_sha and poll until it completes. Fails loud if
# Tests failed so we don't ship a known-broken tag.
# ---------------------------------------------------------------------------
await-tests:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Wait for Tests workflow to pass
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
SHA: ${{ github.sha }}
EVENT: ${{ github.event_name }}
run: |
set -euo pipefail
if [ "$EVENT" = "workflow_dispatch" ]; then
echo "workflow_dispatch — bypassing test gate (manual republish)."
exit 0
fi
echo "Gating release on commit $SHA"
deadline=$(( SECONDS + 45 * 60 ))
retry_on_error=1
while :; do
set +e
api_out=$(gh api \
"/repos/$REPO/actions/workflows/test.yml/runs?head_sha=$SHA&per_page=1" \
2>&1)
rc=$?
set -e
if [ $rc -ne 0 ]; then
echo " gh api failed (rc=$rc):"
echo "$api_out" | awk '{print " " $0}'
if [ $retry_on_error -gt 0 ]; then
retry_on_error=0
echo " retrying once after 10s"
sleep 10
continue
fi
echo " gh api still failing after retry — failing gate."
exit 1
fi
retry_on_error=1
status=$(echo "$api_out" | jq -r '.workflow_runs[0].status // empty')
conclusion=$(echo "$api_out" | jq -r '.workflow_runs[0].conclusion // empty')
url=$(echo "$api_out" | jq -r '.workflow_runs[0].html_url // empty')
if [ -z "$status" ]; then
echo " no Tests run found yet for $SHA — waiting"
elif [ "$status" = "completed" ]; then
if [ "$conclusion" = "success" ]; then
echo " OK Tests passed: $url"
break
else
echo " FAIL Tests $conclusion: $url"
exit 1
fi
else
echo " Tests: $status ($url) — waiting"
fi
if [ $SECONDS -ge $deadline ]; then
echo " timed out waiting for Tests"
exit 1
fi
sleep 30
done
echo "Gate passed — proceeding to publish."
# ---------------------------------------------------------------------------
# Create (or update) the GitHub Release for this tag. The /release skill
# usually creates the release *body* from the CLI after pushing the tag;
# this job is a safety net: if the tag exists but no release does yet, it
# creates one with auto-generated notes.
# ---------------------------------------------------------------------------
github-release:
needs: await-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve tag
id: tag
env:
DISPATCH_TAG: ${{ github.event.inputs.tag }}
run: |
if [ -n "$DISPATCH_TAG" ]; then
TAG="$DISPATCH_TAG"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Create GitHub Release (if missing)
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
if gh release view "$TAG" >/dev/null 2>&1; then
echo "Release $TAG already exists — leaving body alone."
exit 0
fi
echo "Creating release $TAG with auto-generated notes."
gh release create "$TAG" \
--title "$TAG" \
--generate-notes
- name: Verify package.json version matches tag
env:
VERSION: ${{ steps.tag.outputs.version }}
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
if [ "$PKG_VERSION" != "$VERSION" ]; then
echo "WARNING package.json version ($PKG_VERSION) does not match tag ($VERSION)."
echo " This is normally a bug — the /release skill should bump package.json"
echo " and commit before tagging. Continuing, but investigate."
else
echo "OK package.json version matches tag ($VERSION)"
fi
# ---------------------------------------------------------------------------
# Publish the package to npm as @bloomengine/engine. Runs after the GitHub
# Release so a failure here doesn't leave a dangling release-but-no-package
# state. Skips cleanly if the version is already on the registry, which
# keeps re-runs idempotent (workflow_dispatch on an existing tag won't
# double-publish or fail).
#
# We check out submodules recursively because scripts/prepack.sh refuses
# to ship a tarball without the JoltPhysics sources materialised — the
# package vendors them rather than relying on a postinstall git clone.
# ---------------------------------------------------------------------------
publish-npm:
needs: github-release
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # required for npm provenance attestations
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v5
with:
node-version: "24"
registry-url: "https://registry.npmjs.org"
- name: Resolve tag
id: tag
env:
DISPATCH_TAG: ${{ github.event.inputs.tag }}
run: |
if [ -n "$DISPATCH_TAG" ]; then
TAG="$DISPATCH_TAG"
else
TAG="${GITHUB_REF#refs/tags/}"
fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
- name: Verify package.json version matches tag
env:
VERSION: ${{ steps.tag.outputs.version }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
PKG_VERSION=$(node -p "require('./package.json').version")
if [ "$PKG_VERSION" != "$VERSION" ]; then
echo "::error::Tag $TAG ($VERSION) does not match package.json ($PKG_VERSION) — refusing to publish."
exit 1
fi
- name: Check if version already published
id: check
run: |
PKG_NAME=$(node -p "require('./package.json').name")
PKG_VERSION=$(node -p "require('./package.json').version")
if npm view "$PKG_NAME@$PKG_VERSION" version >/dev/null 2>&1; then
echo "$PKG_NAME@$PKG_VERSION is already on the registry — skipping publish."
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "$PKG_NAME@$PKG_VERSION not yet published — will publish."
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Publish to npm
if: steps.check.outputs.skip == 'false'
run: npm publish --provenance --access public