Skip to content

A saved Showcase scene reopens in the Showcase (#174) #283

A saved Showcase scene reopens in the Showcase (#174)

A saved Showcase scene reopens in the Showcase (#174) #283

Workflow file for this run

name: Check and release
# What lands on main gets checked, and if it changed anything that ships, it gets
# a version and a release without anybody remembering to tag.
#
# WHY THIS EXISTS
#
# Releasing was `pnpm version patch && git push --follow-tags`, run by hand. It
# was run twice in the life of this repo, and one of those produced nothing
# because the release workflow was broken. Meanwhile the tap served a tarball
# from a tag nobody had rebuilt since, so an install predated `assets/` being
# published and showed blank cards on the New video page.
#
# Nothing checked a push either: the gate ran only inside the tag workflow, so
# main could go red and stay red until somebody cut a release.
#
# WHAT IT DOES
#
# 1. Runs the gate on every push to main (check:ci — see the step for what
# it leaves out and why).
# 2. If the gate passes AND the push changed something that ships, bumps the
# patch version, commits it, and pushes the tag.
# 3. release.yml sees the tag and does the publishing.
#
# The version bump is a real commit on main. That is deliberate: package.json is
# what `sync-tap` and the Studio's own version line read, so a tag without it
# would leave three places disagreeing about what is installed.
on:
push:
branches: [main]
permissions:
contents: write
packages: read
# A second push while one is mid-release would race on the tag. Queue rather than
# cancel: the first run may already have tagged, and cancelling it half way is how
# you get a tag with no release behind it.
concurrency:
group: release-main
cancel-in-progress: false
jobs:
check:
name: The gate
runs-on: ubuntu-latest
env:
# setup-node writes this placeholder into the npm config. Keep the token
# available for every pnpm step, not only install, so the runner does not
# report a missing registry credential while it runs the gate.
NODE_AUTH_TOKEN: ${{ secrets.ROLEMODEL_PACKAGE_REPO_READ_TOKEN || github.token }}
# Keep the gate against the same OpenScreen version the tag workflow
# verifies. This repository owns the source tokens; the app owns the
# generated CSS that consumes them.
OPENSCREEN_REF: v0.0.2
OPENSCREEN_ROOT: /tmp/openscreen
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v6
with: { version: 11.20.0 }
- uses: actions/setup-node@v7
with:
node-version: "24"
cache: "pnpm"
scope: "@rolemodel"
registry-url: "https://npm.pkg.github.com"
# The frozen pnpm lock keeps CI on the exact dependency graph contributors use.
# needs the dev ones the checks use.
- run: pnpm install --frozen-lockfile
- run: pnpm exec playwright install --with-deps chromium
- name: Check out OpenScreen for app-side token checks
run: git clone --depth 1 --branch "$OPENSCREEN_REF" https://github.com/RoleModel/openscreen.git "$OPENSCREEN_ROOT"
# This output belongs to the app checkout, not this toolkit. Generate it
# in the pinned temporary checkout before checking the complete gate.
- name: Generate the OpenScreen Optics token layer
run: node lib/optics-tokens.mjs --openscreen "$OPENSCREEN_ROOT"
# `check:ci`, not `check`.
#
# The full gate ends with sync-tap:check and sync-docs:check, and both
# compare this repo against a SIBLING checkout — the Homebrew tap and the
# fork's website. Neither exists on a runner, so both exit 1 and the gate
# would fail on every push for a reason that has nothing to do with the
# push. Those two are about cross-repo publication and are checked where
# publication happens: release.yml for the tap, the fork's own Docs
# workflow for the site.
- run: pnpm run check:ci
release:
name: Version and tag
needs: check
runs-on: ubuntu-latest
# Three ways not to release, all of them deliberate:
#
# - the bot's own version commit, or main would release itself forever
# - a commit that says not to
# - a push that changed nothing anybody installs
#
# The last is checked in the step below rather than with `paths:` on the
# trigger, because the gate should run on every push even when the release
# does not.
if: >-
github.actor != 'github-actions[bot]'
&& !contains(github.event.head_commit.message, '[skip release]')
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: true
# This job runs no checks, so it installs nothing — but the bump below is
# `pnpm version`, and a bare runner has no pnpm on PATH. Without this the
# job died at `pnpm: command not found` after the gate had already passed,
# which reads as a failed release of a perfectly good commit.
- uses: pnpm/action-setup@v6
with: { version: 11.20.0 }
- uses: actions/setup-node@v7
with:
node-version: "24"
- name: Did anything that ships change?
id: shipped
run: |
# The same directories package.json publishes, plus the manifest
# itself. Docs, CI and the verify suite are not installed by anybody,
# so a change to them is not a release — it would put a version number
# on a tarball identical to the one before it.
BEFORE="${{ github.event.before }}"
if [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
BEFORE="$(git rev-parse HEAD~1 2>/dev/null || git rev-parse HEAD)"
fi
CHANGED="$(git diff --name-only "$BEFORE" HEAD -- assets bin lib presets brand components skill package.json || true)"
# lib/verify.mjs is the suite, not the product. A push that only moves
# assertions is not a new version of anything.
CHANGED="$(printf '%s\n' "$CHANGED" | grep -v '^lib/verify\.mjs$' || true)"
if [ -z "$CHANGED" ]; then
echo "nothing that ships changed — no release"
echo "go=no" >> "$GITHUB_OUTPUT"
else
echo "$CHANGED"
echo "go=yes" >> "$GITHUB_OUTPUT"
fi
- name: Bump, commit and tag
if: steps.shipped.outputs.go == 'yes'
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# If package.json already names a version with no tag, that version is
# the release and nothing needs bumping — which is how a deliberate
# `pnpm version minor` on a branch reaches main and means what it says.
CURRENT="$(node -p "require('./package.json').version")"
if git rev-parse "v$CURRENT" >/dev/null 2>&1; then
pnpm version patch --no-git-tag-version >/dev/null
CURRENT="$(node -p "require('./package.json').version")"
git add package.json pnpm-lock.yaml
git commit -m "v$CURRENT [skip release]"
git push origin HEAD:main
fi
git tag -a "v$CURRENT" -m "v$CURRENT"
# The tag is what release.yml watches; pushing it is the whole handover.
git push origin "v$CURRENT"
echo "tagged v$CURRENT"