Skip to content

Commit a6b0d08

Browse files
committed
ci(astro): publish via Trusted Publisher OIDC
Workflow at .github/workflows/publish-astro.yml triggered via 'gh workflow run publish-astro.yml -F bump=<kind>'. Handles version bump, type-check, tests, build, tarball smoke (with stray-.ts and import-resolution checks), npm publish via OIDC (no stored tokens — npm exchanges the GitHub OIDC token for a short-lived publish credential at the moment of publish, gated by the Trusted Publisher config on npmjs.com), commit-back to main, tag v<x.y.z>-astro, GitHub release with auto-generated notes. Updates /publish-astro skill to drive the workflow instead of running npm publish locally. The user-facing flow shrinks to: pre-flight checks → 'gh workflow run' → 'gh run watch' → verify via direct registry curl. Local 'npm publish' is now a documented dead-end: only this workflow file running on main can pass npm's claim check, by design.
1 parent 600d97f commit a6b0d08

2 files changed

Lines changed: 261 additions & 78 deletions

File tree

.claude/commands/publish-astro.md

Lines changed: 59 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,97 @@
1-
# /publish-astro — Publish @run402/astro to npm
1+
# /publish-astro — Publish @run402/astro to npm via OIDC
22

3-
Publish the `@run402/astro` Astro integration package from `astro/` in this repo. Unlike `@run402/sdk`, `run402` CLI, and `run402-mcp` (which lockstep via `/publish`), `@run402/astro` has its own versioning cadence because it's a framework adapter, not part of the core SDK release train.
3+
Trigger the canonical publish pipeline at `.github/workflows/publish-astro.yml`. The workflow handles version bump, smoke test, npm publish (via OIDC Trusted Publisher — no stored tokens), commit-back, tag, and GitHub release. This skill is your local-machine wrapper around `gh workflow run`.
44

5-
> **`@run402/functions` is published from the private gateway monorepo** (`kychee-com/run402-private` via `/publish-functions`). This skill is only for `@run402/astro`.
5+
> **Why CI-driven not local-publish:** `@run402/astro` uses npm's Trusted Publisher OIDC federation. The npm package's settings page lists this repository, branch, and workflow filename as a trusted publisher. Any publish attempted from anywhere else (a developer laptop, a fork, a different workflow file) fails the npm-side claim check. The whole point of OIDC is to remove the "do you have a token" question — only this workflow file running on main can publish.
6+
>
7+
> **One bootstrap exception:** v0.1.0 was published manually with an OTP-elevated automation token because npm requires the package to exist before a Trusted Publisher can be configured. That bootstrap is done and never repeats; from 0.1.1 onwards, all publishes go through this skill → this workflow.
68
7-
Stop on any failure. Do NOT skip checks.
9+
> **`@run402/functions` is published from a different repo** (`kychee-com/run402-private` via `/publish-functions`). This skill is only for `@run402/astro`.
10+
>
11+
> **`run402-mcp`, `run402` CLI, and `@run402/sdk`** ship via `/publish` (lockstep). This skill is independent of that release train.
812
9-
## When to publish
13+
Stop on any failure. Do NOT skip checks.
1014

11-
- New `<Image>` component prop, integration option, or behavior change → publish
12-
- Bug fix to the resolver / scanner / uploader / picture-builder → publish patch
13-
- Compat fix for a new Astro major version → publish minor (after testing against the new major)
14-
- Documentation-only README fix → publish at your discretion (no functional change)
15+
## Pre-flight (local, before triggering the workflow)
1516

16-
The package has **no runtime relationship with the gateway** — there is no equivalent of `@run402/functions`'s "gateway redeploy alone propagates the fix." A consumer must bump their `@run402/astro` dependency to see any change.
17+
These run on your machine to catch obvious problems before the workflow burns Actions minutes:
1718

18-
## Pre-publish checks
19+
1. **On `main`, in sync with `origin/main`.** `git rev-parse --abbrev-ref HEAD` must be `main`. `git fetch origin main && git rev-list --count HEAD..origin/main` must be `0`. If not, stop and tell the user. The workflow itself enforces `if: github.ref == 'refs/heads/main'`, but catching the mismatch locally avoids a wasted run.
20+
2. **Working tree clean** (`git status`). Uncommitted changes don't go to the workflow — they'd be invisible to the publish. If the user has work in flight that they want included in the publish, commit + push it first.
21+
3. **Unit tests pass:** `npm test --workspace=astro`. Expect ~50 tests, 0 failures. The workflow re-runs these but a local pre-check gives fast feedback.
22+
4. **Type-check clean:** `npx tsc --noEmit -p astro`. Empty output = clean.
23+
5. **Confirm npm Trusted Publisher is configured.** Open https://www.npmjs.com/package/@run402/astro/access in a browser and confirm the "Trusted Publishers" section lists this repo + workflow. If the section is empty, the publish step will fail with 401 — stop and ask the user to configure it before proceeding (org `kychee-com`, repo `run402`, workflow filename `publish-astro.yml`, no environment).
1924

20-
1. **On main, in sync with origin/main.** `git rev-parse --abbrev-ref HEAD` must be `main`. `git fetch origin main && git rev-list --count HEAD..origin/main` must be `0`. If not, stop and tell the user.
21-
2. **Working tree clean** (`git status`). Stop if not.
22-
3. **Unit tests pass:** `npm test --workspace=astro`. Expect 50+ tests, 0 failures.
23-
4. **Type-check clean:** `npx tsc --noEmit -p astro`. No output = clean.
24-
5. **Build the package:** `npm run build --workspace=astro`. Confirms `dist/` is current.
25+
If any local check fails, stop and tell the user.
2526

26-
If any step fails, stop and tell the user.
27+
## Choose the bump kind
2728

28-
## Version bump
29+
Ask the user: **patch, minor, or major.**
2930

30-
1. Ask the user: **patch, minor, or major.**
31-
- Patch (`0.1.0 → 0.1.1`): bug fix, no surface change.
32-
- Minor (`0.1.0 → 0.2.0`): new prop, new integration option, or new optional behavior. Backwards-compatible.
33-
- Major (`0.1.0 → 1.0.0` or `1.0.0 → 2.0.0`): breaking prop change, removed option, or behavior change that requires consumer code changes. v0.x → v1.x is the "stable surface" promotion.
34-
2. Read current version from `astro/package.json`.
35-
3. Compute target: apply bump kind. Apply directly to `astro/package.json` (use Edit, not `npm version``npm version` from inside a workspace can have surprising lockfile behavior).
36-
4. `npm install --package-lock-only` from repo root to sync `package-lock.json`.
31+
- **Patch** (`0.1.0 → 0.1.1`): bug fix, no surface change. Example: blurhash decoder crash on edge case, error message wording.
32+
- **Minor** (`0.1.0 → 0.2.0`): new prop, new integration option, or new optional behavior. Backwards-compatible. Example: new `placeholder="dominantColor"` mode, support for AVIF variants when they ship in v1.50.
33+
- **Major** (`0.x → 1.0` or `1.x → 2.0`): breaking prop change, removed option, or behavior change that requires consumer code changes. Example: removing `priority` in favor of `fetchpriority`, changing the variant URL format. Reserve `1.0` for the "stable surface" promotion when the prop API is locked.
3734

38-
## Tarball smoke test
35+
## Trigger the workflow
3936

40-
`npm test` runs against source, not the packed tarball. Pack it and verify the entry points resolve:
37+
The workflow does ALL the publish work — version bump, smoke test, npm publish, commit-back to main, tag, GitHub release. You just trigger it:
4138

4239
```
43-
SMOKE=/tmp/smoke-astro-<new_version> && rm -rf $SMOKE && mkdir $SMOKE
44-
(cd astro && npm pack --pack-destination $SMOKE)
45-
mkdir $SMOKE/astro && tar xzf $SMOKE/run402-astro-<new_version>.tgz -C $SMOKE/astro
46-
(cd $SMOKE/astro/package && npm install --omit=dev --before=9999-12-31)
47-
node -e "import('$SMOKE/astro/package/dist/index.js').then(m => console.log('OK', typeof m.run402)).catch(e => { console.error('FAIL', e.message); process.exit(1) })"
40+
gh workflow run publish-astro.yml -F bump=<patch|minor|major> -R kychee-com/run402
4841
```
4942

50-
Expect `OK function`. The script exits non-zero on any failure.
51-
52-
**Also verify the .astro component file ships:**
43+
For a dry-run that builds + smoke-tests without publishing (useful when validating a workflow change):
5344

5445
```
55-
ls $SMOKE/astro/package/src/Image.astro
46+
gh workflow run publish-astro.yml -F bump=patch -F dry_run=true -R kychee-com/run402
5647
```
5748

58-
Must print the path. If the file is missing, the `files` allowlist in `package.json` is broken — do not publish.
49+
The dry run still bumps the local version on the runner and packs the tarball, but skips the publish + commit + tag + release steps. Useful for "does the bumped version pack right?" testing.
50+
51+
## Watch the workflow
5952

60-
**Also verify the tarball does NOT include source `.ts` files:**
53+
Find the run ID and watch:
6154

6255
```
63-
find $SMOKE/astro/package -name "*.ts" -not -name "*.d.ts" | head
56+
RUN_ID=$(gh run list -w publish-astro.yml -R kychee-com/run402 --limit 1 --json databaseId --jq '.[0].databaseId')
57+
gh run watch "$RUN_ID" -R kychee-com/run402 --exit-status
6458
```
6559

66-
Should print nothing (only `.d.ts` types ship). Source `.ts` files in the tarball means `files` allowlist is wrong.
60+
`--exit-status` makes the watch command exit non-zero if the workflow fails — useful for chaining into a verification step.
6761

68-
**About `--before=9999-12-31`:** if the user's global npm has a `before` date pinned (supply-chain mitigation), the scratch install needs this flag to bypass it for `/tmp` installs. Do **not** suggest removing the global config.
62+
## Verify post-publish
6963

70-
## Commit and publish
64+
After the workflow completes successfully:
7165

72-
1. Stage and commit the version bump:
66+
1. **Verify the new version is live:**
7367
```
74-
git add astro/package.json package-lock.json
75-
git commit -m "chore(astro): bump @run402/astro to v<new_version>"
68+
curl -sS "https://registry.npmjs.org/@run402/astro/<new_version>" | jq -r .version
7669
```
77-
2. Publish:
78-
```
79-
cd astro && npm publish --access public
80-
```
81-
The `publishConfig.access: public` field is set in `package.json`, so the explicit flag is redundant but harmless. The tarball contains `dist/`, `src/Image.astro`, and `README.md` only (per the `files` allowlist; `node_modules`, tests, and `*.test.*` are excluded).
70+
The user's local npm may have a `--before` date pin set as a supply-chain mitigation that filters out newly-published packages from `npm view`. Use direct `curl` to confirm registry state. If the user wants to see it via `npm view`, they need `--before=9999-12-31` (do NOT suggest changing their global config).
8271

83-
## Post-publish
84-
85-
1. `git push` to push the version bump commit.
86-
2. Create a git tag:
87-
```
88-
git tag v<new_version>-astro && git push --tags
72+
2. **Confirm provenance attestation:**
8973
```
90-
Use the `-astro` suffix because the other public-repo packages (`run402-mcp`, `run402`, `@run402/sdk`) have their own tag scheme; this clarifies which package the tag belongs to.
91-
3. Create a GitHub release (public repo):
74+
curl -sS "https://registry.npmjs.org/@run402/astro/<new_version>" | jq '.dist.attestations'
9275
```
93-
gh release create v<new_version>-astro -R kychee-com/run402 --notes "..."
94-
```
95-
Write a human-readable summary naming user-facing changes. Don't rely on auto-generated notes.
96-
4. **Verify live on npm:**
97-
```
98-
npm view @run402/astro@<new_version> version
99-
```
100-
Should print the new version. May take up to a minute to propagate.
101-
5. **Update `documentation.md`** if the prop surface or integration options changed. The doc serves as the canonical reference for the public repo. Commit + push.
102-
6. **Update `llms-full.txt` in the private repo** (`kychee-com/run402-private` at `site/llms-full.txt`) if the Astro integration section needs to point at a new version or document a new feature. The site at https://run402.com/llms-full.txt is regenerated from there; trigger a redeploy after the update:
103-
```
104-
gh workflow run deploy-site.yml -R kychee-com/run402-private
105-
```
106-
7. Print a summary:
76+
Should return a non-null object. If null, OIDC didn't kick in and the publish silently fell back to anonymous — investigate.
77+
78+
3. **Print summary** for the user:
10779
- Published version
108-
- npm URL: `https://www.npmjs.com/package/@run402/astro`
109-
- GitHub release URL
80+
- npm URL: `https://www.npmjs.com/package/@run402/astro/v/<new_version>`
81+
- Workflow run URL
82+
- GitHub release URL: `https://github.com/kychee-com/run402/releases/tag/v<new_version>-astro`
11083

11184
## What this skill does NOT do
11285

113-
- Does NOT bump `mcp`, `cli`, or `sdk`. Those lockstep via `/publish`.
114-
- Does NOT bump `@run402/functions`. That lives in the private gateway monorepo and ships via `/publish-functions` there.
115-
- Does NOT trigger any gateway redeploy. There is no gateway-side dependency on this package.
116-
- Does NOT migrate consumer projects. After publishing, consumers (Kychon, etc.) update their `@run402/astro` dependency on their own cadence.
86+
- Does NOT publish locally via `npm publish`. The Trusted Publisher OIDC trust ONLY accepts publishes from THIS workflow file on `main`. Attempting a local publish would fail the npm-side claim check (or succeed only if the user has an unrelated bypass-2FA token, which defeats the whole point).
87+
- Does NOT bump `mcp`, `cli`, `sdk` — those lockstep via `/publish`.
88+
- Does NOT bump `@run402/functions` — that lives in the private gateway monorepo.
89+
- Does NOT pull a release branch — the workflow operates on `main` directly. Use a feature branch for the code change, merge to main, then run this skill.
90+
- Does NOT prompt for an OTP or token. If you ever see the workflow asking for one, the Trusted Publisher config drifted — fix the npm-side config rather than reverting to token auth.
91+
92+
## Troubleshooting
93+
94+
- **Workflow fails at `Publish to npm` with 401 or 403:** Trusted Publisher config doesn't match. Check the npm package access page — org / repo / workflow filename must exactly match this workflow's metadata.
95+
- **Workflow fails at `Commit version bump` with permission denied:** Repo Settings → Actions → General → Workflow permissions → must be "Read and write." The default GITHUB_TOKEN's permissions are read-only unless this is flipped.
96+
- **Workflow fails at `Tarball smoke test` after a successful publish-in-progress:** the tarball was generated incorrectly. Recover by manually patching `astro/package.json` to bump the version back (workflow had already bumped locally on the runner but not committed) and re-run. The npm-side already-published version blocks re-publish, which is the safety net.
97+
- **`npm view` returns 404 right after a successful publish:** almost always the user's `--before` date pin filtering, not a real propagation issue. Confirm with the direct `curl` against `registry.npmjs.org`.

0 commit comments

Comments
 (0)