Files
oc/.github/workflows/publish.yml
T
only-cli b084e080bb ci: make a stable release refresh the skills.sh page
skills.sh renders skills/web-browsing-cli/SKILL.md straight from GitHub, and
the release checklist said there was nothing to do for it because the skills
CLI reads that file live off main. That is true of the install path and false
of the page: the page showed the 0.2.0 pin from 2026-08-20 while main had
already shipped 0.3.0 and 0.4.0 the same day, so every reader was handed a
two-release-old install command.

The site offers exactly one lever. Its documented API is read only, with no
refresh or re-index endpoint, and the skills CLI has no publish or sync
command; a repository is re-read after the telemetry service sees an install
from it, and repo pages are cached on top of that. So a stable publish now
runs one `skills add` against the repo, which is the invocation the
install-remove-loop experiment already proved out. It costs one install on the
counter per release, which is the price of the only mechanism there is. The
job is continue-on-error and runs after npm publish has already succeeded: a
page that catches up late is a smaller problem than a red release.

The refresh is worthless if the pin it publishes is stale, which is the actual
root cause here, so a latest publish now fails when SKILL.md disagrees with
package.json. Beta and dev skip the check, because a pin moves when a release
is stable rather than when it enters beta, which is the rule 0.3.0-beta.1
already followed. Verified both ways against the current tree: it passes on
0.4.0 with a matching pin and refuses a 0.4.0 release still pinning 0.2.0.

The channel the earlier step resolves is now a job output, so the refresh job
can gate on it instead of re-deriving it from the version string.
2026-08-23 23:40:08 -04:00

116 lines
4.9 KiB
YAML

# Publishes to npm via OIDC trusted publishing: no token, no OTP prompt.
# One-time setup on npmjs.com after the package exists: package settings,
# Trusted Publisher, GitHub Actions, repository only-cli/oc, workflow
# file publish.yml. From then on, publishing a GitHub release ships to npm.
#
# Release channels map to npm dist-tags:
# latest stable releases, what `npm install @only-cli/oc` gets
# beta release candidates, version like 0.2.0-beta.1
# alpha earlier previews, version like 0.2.0-alpha.1
# dev throwaway builds from main, version stamped per run
#
# A GitHub release picks its channel from the version suffix, so marking a
# release 0.2.0-beta.1 ships to beta automatically. Run the workflow by hand
# (Actions tab, publish, Run workflow) to cut a dev build without a release.
name: publish
on:
release:
types: [published]
workflow_dispatch:
inputs:
channel:
description: npm dist-tag to publish under
type: choice
options: [dev, alpha, beta, latest]
default: dev
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
outputs:
channel: ${{ steps.channel.outputs.channel }}
steps:
- uses: actions/checkout@v7
# No registry-url here: it writes an .npmrc auth-token line with a
# placeholder value, and npm then authenticates with that instead of
# falling through to OIDC trusted publishing.
- uses: actions/setup-node@v7
with:
node-version: 24
# Trusted publishing needs npm 11.5.1 or newer.
- run: npm install -g npm@latest
- run: npm ci
- run: npm test
- name: pick channel and version
id: channel
run: |
V=$(node -p "require('./package.json').version")
CHANNEL="${{ github.event_name == 'workflow_dispatch' && inputs.channel || '' }}"
if [ -z "$CHANNEL" ]; then
case "$V" in
*-alpha*) CHANNEL=alpha ;;
*-beta*) CHANNEL=beta ;;
*-dev*) CHANNEL=dev ;;
*) CHANNEL=latest ;;
esac
fi
# Fail loud instead of shipping a prerelease as stable.
if [ "$CHANNEL" = latest ] && [ "${V#*-}" != "$V" ]; then
echo "refusing to publish prerelease version $V to latest" >&2
exit 1
fi
# Dev builds get a unique version per run so repeat publishes never
# collide; the version in git stays untouched.
if [ "$CHANNEL" = dev ] && [ "${V%-dev*}" = "$V" ]; then
npm version --no-git-tag-version "${V%%-*}-dev.${{ github.run_number }}"
fi
echo "CHANNEL=$CHANNEL" >> "$GITHUB_ENV"
echo "channel=$CHANNEL" >> "$GITHUB_OUTPUT"
# Agents execute whatever the skill pins, and skills.sh renders that line
# verbatim, so a stable release shipping an older pin is a wrong install
# command in front of every reader. Beta and dev keep the last stable pin
# on purpose, so this only binds the latest channel.
- name: skill pin matches a stable release
run: |
if [ "$CHANNEL" != latest ]; then
echo "channel $CHANNEL: skill keeps the last stable pin on purpose"
exit 0
fi
V=$(node -p "require('./package.json').version")
PINS=$(grep -o '@only-cli/oc@[0-9][0-9A-Za-z.-]*' skills/web-browsing-cli/SKILL.md | sort -u)
if [ "$PINS" != "@only-cli/oc@$V" ]; then
echo "release is $V but skills/web-browsing-cli/SKILL.md pins:" >&2
echo "$PINS" >&2
echo "bump the pin before cutting a stable release" >&2
exit 1
fi
echo "skill pin is @only-cli/oc@$V"
- run: npm publish --access public --provenance --tag "$CHANNEL"
# skills.sh renders SKILL.md straight from GitHub, but it only re-reads a
# repository after its telemetry service sees an install from it, and repo
# pages are cached on top of that. Publishing to npm tells it nothing, which
# is how the page sat on the 0.2.0 pin while main had already shipped 0.4.0.
# One install per stable release is what makes the page catch up. There is no
# refresh API to call instead: the documented skills.sh API is read only.
refresh-skills-page:
needs: publish
if: needs.publish.outputs.channel == 'latest'
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v7
with:
node-version: 24
# Same invocation the install-loop experiment proved out, telemetry left
# on so the install is reported. Never fail a release over this: the
# package is already published by the time it runs, and the page catching
# up late is a smaller problem than a red release.
- name: install the skill so skills.sh re-reads the repo
continue-on-error: true
run: npx --yes skills add https://github.com/only-cli/oc --skill web-browsing-cli --yes