Skip to content

Reference

Cross-cutting reference material for the design-system integration workflows.

The @arda-cards scope is hosted in GitHub Packages (https://npm.pkg.github.com), not the public npm registry. Access requires authentication.

Publishing uses the workflow’s auto-provided GITHUB_TOKEN with packages: write permission. No manual token configuration is required — both publish.yml and publish-preview.yml declare the permission in their permissions: block.

Consumers need a Personal Access Token (PAT) with read:packages scope to install any @arda-cards/* package — stable or preview.

npm reads the auth token from .npmrc, which interpolates an environment variable. The variable name must match between .npmrc and the environment that runs npm install. Two equivalent setups follow; pick one and use it consistently.

Local development:

  1. Create a PAT at https://github.com/settings/tokens with read:packages scope.
  2. Add to your shell profile:
    Terminal window
    export NODE_AUTH_TOKEN=ghp_
  3. Commit a .npmrc at the repo root that uses the same variable name:
    @arda-cards:registry=https://npm.pkg.github.com
    //npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
  4. Run npm install as usual.

CI (GitHub Actions): do not commit a CI-specific .npmrc. Use actions/setup-node with registry-url to generate one at runtime, and pass the PAT as NODE_AUTH_TOKEN. The same committed .npmrc from local dev works because both use ${NODE_AUTH_TOKEN}:

- uses: actions/setup-node@v6
with:
node-version: 22
registry-url: https://npm.pkg.github.com
scope: '@arda-cards'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.GHP_READ_PACKAGES }}

The setup-node step writes its own .npmrc to the runner’s home directory (which takes precedence over the repo-local one for the registry URL); npm ci then sees NODE_AUTH_TOKEN and authenticates.

If you prefer to use ${GITHUB_TOKEN} instead, that’s fine — but change both the shell export and the _authToken= line in .npmrc to match. Mixing the two names is what causes 401 Unauthorized on install.

PATs expire. When yours does, install steps will fail with 401 Unauthorized from npm.pkg.github.com. Rotate by generating a new PAT and updating the env var or repo secret.

All published versions are valid semver 2.0. The CHANGELOG.md format and CLQ (Arda-cards/clq-action) enforce that the changelog version matches a valid semver progression.

A stable version has the form X.Y.Z with no pre-release suffix:

ExampleBump driverCLQ status
4.11.5Fixed / Securityreleased
4.12.0Added / Deprecatedreleased
5.0.0Changed / Removedreleased

Stable versions are published only by publish.yml on push to main, and always carry the npm dist-tag latest.

A pre-release version has the form X.Y.Z-<author>-<id>:

ExampleMeaning
4.12.0-alice-FD42Alice’s work toward 4.12.0, ticket FD-42
4.12.0-bob-spike-grid-virtBob’s spike toward 4.12.0, no ticket
5.0.0-team-major-rewriteTeam-wide work toward a major version

The <author>-<id> part is treated opaquely by the publish workflow — anything after the first - becomes the preview’s npm dist-tag.

Each push of a pre-release entry publishes a uniquely versioned artifact by appending github.run_number:

4.12.0-alice-FD42 ← changelog entry (semver pre-release)
4.12.0-alice-FD42-7 ← published version (run #7 of publish-preview.yml)
dist-tag: alice-FD42

The dist-tag alice-FD42 always points at the most recent successful publish in the series.

FieldRecommendation
<author>Your GitHub username, lowercase.
<id>A ticket number (FD42), branch slug (grid-virt), or both. Keep short. Avoid generic names (fix, wip).
Combined dist-tagShould be unique enough that a different dev’s preview can’t accidentally clobber yours. <gh-username>-<ticket> is safe.
  • latest — only publish.yml updates this. Never use as a preview dist-tag.
  • next, canary, beta — currently unused; reserved for future graduated-preview workflows. Avoid.

The cleanup-preview.yml workflow runs daily at 03:00 UTC (and on-demand via workflow_dispatch). It deletes obsolete preview versions from GitHub Packages.

Two passes, each producing deletion candidates:

  1. Obsolete-stable pass: any preview <base>-<series>-<uid> is marked when stable <base> exists in the registry.
    • Example: stable 4.12.0 shipped → all 4.12.0-*-* previews are marked.
  2. Retention pass: previews whose stable base has not shipped are grouped by <base>-<series>. Within each group, only the last 3 by <uid> are kept; the rest are marked.
    • Example: an active alice-FD42 series with 8 preview versions keeps versions 6, 7, 8 and marks 1-5 for deletion.
  1. Never delete stable: if any candidate has no pre-release suffix (i.e. matches X.Y.Z only), the entire job fails before any delete API call.
  2. Pattern conformance: if any candidate doesn’t end in a numeric -<uid>, the job fails.
  3. 24-hour grace period: any version published less than 24 hours ago is exempt regardless of marking.
  4. Manual dispatch defaults to dry-run: workflow_dispatch defaults dry_run=true, logging candidates without deleting. Scheduled runs perform real deletions.
Consumer stateOutcome of cleanup
Migrated to stable promptly after releaseNo impact
Still using a preview after stable shippedNext install fails (404) within ~24h. Forces upgrade to stable.
In-flight CI run during cleanupProtected by the 24h-since-publish guard
Active dev branch using a preview series with last-3 retentionRecent versions remain — re-running npm install @<series> picks the newest
  • Before the first cron run in a fresh environment, dispatch the workflow manually with dry_run=true to validate logic against the actual registry state.
  • After modifying cleanup-preview.yml, dry-run again before the next scheduled execution.
  • If a destructive bug is discovered, disable the workflow (Settings → Actions → Workflows → Disable) until the fix is verified via dry-run.