Repository navigation
A saved Showcase scene reopens in the Showcase (#174) #283
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
| 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" |