Skip to content

chore: drop the demo recording tapes #32

chore: drop the demo recording tapes

chore: drop the demo recording tapes #32

Workflow file for this run

# Publish `@orcarouter/code-review` to npm when its version is not on the registry yet.
#
# The trigger is every push to main, but the decision is "is this exact version
# already published?" — not "did package.json change in this commit?". Diffing
# against the previous commit looks equivalent and is not: a squash merge, a
# force push, or a revert all make "the previous commit" the wrong thing to
# compare against, and the failure is silent in both directions (a missed
# publish, or a publish attempt that dies on E409). Asking the registry is one
# HTTP call and has no such states.
#
# So: bump `version` in package.json when you want a release. Every other
# commit no-ops with a green check.
#
# Publishing is irreversible in practice — npm's unpublish window is 72 hours
# and a withdrawn version number can never be reused. Everything that can be
# checked before the point of no return is checked below, and the job fails
# closed if any of it is off.
#
# NOTE: this workflow deliberately creates no git tags. The npm package version
# and the action's own `v1.x` release tags are SEPARATE version lines (the
# action is on v1.4.x while the CLI starts at 1.0.0). Tagging `v1.0.0` here
# would collide with the action's tag series and could move consumers'
# `uses: ...@v1` onto the wrong commit. Action tags stay manual — see RELEASE.md.
name: Publish
concurrency:
group: publish-npm
cancel-in-progress: false # never abort a run that may be mid-publish
on:
push:
branches: [main]
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # provenance attestation; not used for auth
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # the v1 tag check below needs the tag objects
- uses: actions/setup-node@v4
with:
node-version: "20"
registry-url: "https://registry.npmjs.org"
# ---- Is there anything to do? ------------------------------------
#
# `npm view <pkg>@<version>` prints the version when it exists, exits
# non-zero (E404) when the package or version does not. Both are normal
# answers here, so the failure is swallowed and only the output is read.
- id: check
name: Check whether this version is already published
run: |
set -uo pipefail
NAME=$(node -p "require('./package.json').name")
VERSION=$(node -p "require('./package.json').version")
echo "name=$NAME" >> "$GITHUB_OUTPUT"
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
PUBLISHED=$(npm view "$NAME@$VERSION" version 2>/dev/null || true)
if [ -n "$PUBLISHED" ]; then
echo "needed=false" >> "$GITHUB_OUTPUT"
echo "::notice::$NAME@$VERSION is already on npm — nothing to publish."
else
echo "needed=true" >> "$GITHUB_OUTPUT"
echo "::notice::$NAME@$VERSION is not on npm — will publish."
fi
# ---- Everything below runs only for a real release ---------------
- name: Verify the three versions are in sync
if: steps.check.outputs.needed == 'true'
run: |
set -euo pipefail
PKG=$(node -p "require('./package.json').version")
PLUGIN=$(node -p "require('./.claude-plugin/plugin.json').version")
MARKET=$(node -p "require('./.claude-plugin/marketplace.json').metadata.version")
echo "package.json=$PKG plugin.json=$PLUGIN marketplace.json=$MARKET"
if [ "$PKG" != "$PLUGIN" ] || [ "$PKG" != "$MARKET" ]; then
echo "::error::Version mismatch. package.json, .claude-plugin/plugin.json and" \
".claude-plugin/marketplace.json must move together (see RELEASE.md)."
exit 1
fi
# The installers generate a workflow pinned to `@v1`. If that tag predates
# scripts/settings.mjs and scripts/report.mjs, every new user gets working
# reviews with a silently inert Settings tab and empty Analytics — no
# error, just two dead console tabs. Refuse to ship an installer into that.
- name: Verify the v1 tag can serve what the installer generates
if: steps.check.outputs.needed == 'true'
run: |
set -euo pipefail
if ! git rev-parse -q --verify refs/tags/v1 >/dev/null; then
echo "::error::No v1 tag. The generated workflow pins @v1 — see RELEASE.md."
exit 1
fi
for f in scripts/settings.mjs scripts/report.mjs; do
if ! git cat-file -e "v1:$f" 2>/dev/null; then
echo "::error::v1 is missing $f. Move the v1 tag before publishing (RELEASE.md)."
exit 1
fi
done
echo "v1 = $(git rev-parse --short v1), ships settings.mjs and report.mjs"
- name: Test
if: steps.check.outputs.needed == 'true'
run: node --test scripts/*.test.mjs
# A tarball that omits bin/ or skills/ still publishes cleanly and then
# fails on the user's first `npx`. Assert the payload before shipping it.
- name: Verify the tarball carries the CLI and the skill
if: steps.check.outputs.needed == 'true'
run: |
set -euo pipefail
npm pack --dry-run --json > /tmp/pack.json
node -e '
const files = require("/tmp/pack.json")[0].files.map(f => f.path);
const required = [
"bin/orcacode-review.mjs",
"bin/platforms.mjs",
"bin/i18n.mjs",
"bin/harness.mjs",
"bin/review.mjs",
"bin/selection.mjs",
"bin/localconfig.mjs",
"scripts/postfilter.mjs",
"scripts/severity.mjs",
"rules/severity-instruction.md",
"rules/output-shape.md",
"vendor/open-code-review/LICENSE",
"vendor/open-code-review/system_rules.json",
"vendor/open-code-review/rule_docs/default.md",
"skills/orca-review-action/SKILL.md",
"skills/orca-review/SKILL.md",
"skills/orca-review/references/contract.md",
];
const missing = required.filter(f => !files.includes(f));
if (missing.length) {
console.error("::error::tarball is missing: " + missing.join(", "));
process.exit(1);
}
console.log("tarball ok — " + files.length + " files");
'
# Install the real tarball and run it through npm's real `.bin` shim.
#
# This exists because 1.0.0 shipped a CLI that did nothing. npm installs a
# `bin` as a symlink, so argv[1] is the link while import.meta.url is its
# target; the entry-point guard compared them without realpath and was
# false for every npx and every global install. The process exited 0
# having printed nothing, and every check above passed. `node bin/...`
# never takes that path, so only an actual install can catch its kind.
- name: Smoke-test the packed tarball
if: steps.check.outputs.needed == 'true'
run: |
set -euo pipefail
TARBALL="$PWD/$(npm pack --json | node -pe 'JSON.parse(require("fs").readFileSync(0,"utf8"))[0].filename')"
SMOKE=$(mktemp -d)
cd "$SMOKE"
npm init -y >/dev/null 2>&1
npm install --no-audit --no-fund --silent "$TARBALL"
SHIM=./node_modules/.bin/orcacode-review
test -L "$SHIM" || echo "::warning::$SHIM is not a symlink — this platform cannot exercise that path"
PRINTED=$("$SHIM" --version)
echo "shim printed: '$PRINTED'"
if [ "$PRINTED" != "${{ steps.check.outputs.version }}" ]; then
echo "::error::the installed bin printed '$PRINTED', expected '${{ steps.check.outputs.version }}'." \
"An empty value means the entry-point guard rejected npm's symlink."
exit 1
fi
# A real subcommand, so the check covers more than argument parsing.
"$SHIM" skill list --lang en | grep -q "claude" || {
echo "::error::\`skill list\` did not list the platform catalog"; exit 1; }
echo "tarball smoke test passed"
- name: Publish
if: steps.check.outputs.needed == 'true'
run: npm publish --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# `npm publish` exiting 0 does not mean anyone can install it.
#
# A scoped package defaults to RESTRICTED. When 1.0.2 first went out the
# log read `+ @orcarouter/code-review@1.0.2` and the job was green, while
# the registry 404'd and the package page 403'd for everyone outside the
# org — despite both `--access public` and `publishConfig.access`.
#
# So: assert it, then prove it. `npm access set` is idempotent and fixes
# the state; the anonymous fetch is what actually decides, because it asks
# the question a user asks — can a stranger install this?
- name: Force public access
if: steps.check.outputs.needed == 'true'
continue-on-error: true # the verification below is the real gate
run: npm access set status=public "${{ steps.check.outputs.name }}"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Verify the published version is installable by a stranger
if: steps.check.outputs.needed == 'true'
run: |
set -uo pipefail
NAME='${{ steps.check.outputs.name }}'
VERSION='${{ steps.check.outputs.version }}'
ENCODED=$(node -pe 'encodeURIComponent(process.argv[1])' "$NAME")
# Fetch the PACKUMENT, not the per-version document.
#
# `npm install` reads the packument and picks a version out of it, so
# that is the document whose freshness actually decides whether a
# stranger can install. The /<name>/<version> route is a colder path:
# it 404'd for more than three minutes after two correct publishes,
# failing builds that had shipped fine. Checking the wrong document
# was the bug, not the timeout.
#
# No auth header on purpose — an authenticated read succeeds against a
# restricted package and would prove nothing.
for attempt in $(seq 1 20); do
BODY=$(curl -sS -H 'Cache-Control: no-cache' "https://registry.npmjs.org/$ENCODED" || echo '')
FOUND=$(printf '%s' "$BODY" | node -pe '
try {
const d = JSON.parse(require("fs").readFileSync(0, "utf8"));
d.versions?.[process.argv[1]] ? "yes" : "no";
} catch { "no" }
' "$VERSION" 2>/dev/null || echo no)
if [ "$FOUND" = "yes" ]; then
echo "$NAME@$VERSION is in the public packument — installable"
exit 0
fi
echo "attempt $attempt/20: not in the public packument yet"
sleep 15
done
echo "::error::$NAME@$VERSION published but is absent from the public packument after 5 minutes."
echo "::error::Either it is restricted — fix with: npm access set status=public $NAME"
echo "::error::or the CDN is unusually slow. Check https://registry.npmjs.org/$ENCODED by hand."
exit 1
- name: Summary
if: steps.check.outputs.needed == 'true'
run: |
{
echo "### Published \`${{ steps.check.outputs.name }}@${{ steps.check.outputs.version }}\`"
echo
echo '```'
echo "npx ${{ steps.check.outputs.name }}@${{ steps.check.outputs.version }} skill list"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"