The previous commit renamed release.yaml to ci.yml but never staged
the content edit underneath it (git mv picked up the last-staged
version, not the unstaged working-tree changes) - it pushed with the
filename changed but the workflow itself untouched. This is the
content that commit was supposed to carry.
Same split as pulsenode's workflow: previously this only ran at all
when pushing a version tag, so lint/typecheck/format/test never ran on
regular commits or PRs - a broken push could sit unnoticed until
someone tried to cut a release. Now checks run on every push and PR;
publish to the npm registry stays gated to a tag push and requires
checks to pass first. Renamed release.yaml -> ci.yml to match.
`pnpm run build` and pnpm publish's automatic prepack hook
(rm -rf .next && next build && rm -rf .next/cache) both ran a full
next build - the explicit step's output got thrown away and rebuilt
from scratch seconds later inside publish anyway. Kept only prepack's
build, since it's the one that actually has to succeed for a
publishable package to exist; a deterministic build that just passed
isn't going to fail differently a few steps later.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
actions/setup-node's registry-url sets the *default* npm registry for
every install, not just publishing - since triggershell is an
unscoped package name, that meant `pnpm install` tried to fetch every
ordinary dependency (zod, typescript, ws, ...) from
dev.pivoine.art/api/packages/valknar/npm/ instead of the public npm
registry, and got hammered with 429s retrying each one.
Removes registry-url from setup-node entirely (installs go back to
the default public registry) and instead scopes the auth token to
just that one registry host+path via `pnpm config set
"//dev.pivoine.art/api/packages/valknar/npm/:_authToken" ...` right
before the publish step - publishConfig.registry in package.json
already tells `pnpm publish` specifically where to go (verified
locally via `pnpm publish --dry-run` earlier), this only supplies the
matching credential without touching install resolution.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
actions/setup-node's cache: pnpm option tries to hit this Gitea
instance's Actions cache service, which times out (ETIMEDOUT against
an internal address) rather than failing fast - burning ~5 minutes on
every run before falling back to an uncached install anyway. Not
worth it for a release workflow that runs once per tag.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
pnpm 11.21.0 (pinned in packageManager) now uses node:sqlite
internally, which requires Node >=22.13 - unrelated to this project's
own engines.node: >=20 floor for end users. With node-version: 20 the
runner's pnpm binary couldn't execute at all (ERR_UNKNOWN_BUILTIN_MODULE
on the first pnpm invocation inside actions/setup-node's cache-path
detection), before ever reaching the actual lint/build/publish steps.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Gitea rejects secret names starting with GIT (not just the GITEA_/
GITHUB_ prefixes), so GITEA_PACKAGE_TOKEN wasn't a valid name.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Triggered on tags matching v*.*.* - runs the quality gate (lint,
typecheck, format:check, the existing test suite, build) as
individual steps for clear failure attribution, then publishes to
this Gitea instance's own npm registry (dev.pivoine.art, unscoped
package name - Gitea's npm registry supports that directly, no
@owner/ rename needed).
The release version comes from the git tag (v1.0.0 -> 1.0.0 via
`npm pkg set`), patched into package.json only in the CI run, never
committed back. publishConfig.registry in package.json is a static
string (safe to commit); the auth token is supplied at publish time
via NODE_AUTH_TOKEN, written to a CI-generated user-level .npmrc by
actions/setup-node's registry-url option rather than a repo-committed
one - pnpm >=10.34.2/11.5.3 (this repo pins 11.21.0) blocks ${VAR}
expansion in repository-controlled npmrc/pnpm-workspace.yaml
specifically to stop a malicious repo from exfiltrating CI secrets
that way, so the token can't live in a committed .npmrc at all.
Verified locally end-to-end short of the actual registry upload:
lint/typecheck/format:check/test/build all pass, and
`pnpm publish --dry-run --no-git-checks` after a temporary version
bump confirms publishConfig.registry resolves to the right URL and
prepack (next build) fires automatically as part of publish.
One-time manual setup this can't do by itself (documented in the plan
file): a repo-scoped Gitea Personal Access Token with the `package`
Read&Write scope, stored as the GITEA_PACKAGE_TOKEN repo secret -
Gitea's own auto-injected GITEA_TOKEN explicitly cannot publish
packages (unimplemented per Gitea's own docs).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>