Skip to content

docs(skills/create-pr): infer Linear ticket from context, skip pre-cr… #864

docs(skills/create-pr): infer Linear ticket from context, skip pre-cr…

docs(skills/create-pr): infer Linear ticket from context, skip pre-cr… #864

Workflow file for this run

name: Publish to npm
on:
push:
branches: [main]
tags: ["!**"]
workflow_dispatch:
inputs:
version:
description: "Version to publish (e.g., 0.1.0, 0.1.0-beta.1). Leave empty to auto-calculate."
required: false
type: string
dist-tag:
description: "npm dist-tag (e.g., latest, dev, beta)"
required: true
default: "latest"
type: string
concurrency:
group: npm-publish
cancel-in-progress: false
jobs:
publish:
name: Publish packages to npm
runs-on: ubuntu-latest
# Safety net: only publish from main, even though push trigger is already constrained
if: github.ref == 'refs/heads/main'
permissions:
contents: write # Required to create the GitHub Release + tag for stable publishes
id-token: write # Required for npm OIDC Trusted Publishing
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup mise
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
with:
cache: true
- name: Setup pnpm
uses: pnpm/action-setup@91ab88e2619ed1f46221f0ba42d1492c02baf788 # v6.0.6
- name: Configure npm
run: pnpm config set registry https://registry.npmjs.org
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Determine version
id: version
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
INPUT_VERSION: ${{ github.event.inputs.version }}
INPUT_TAG: ${{ github.event.inputs.dist-tag }}
run: |
if [ -n "$INPUT_VERSION" ]; then
echo "Using provided version: $INPUT_VERSION"
# Validate version format (semantic versioning)
if ! echo "$INPUT_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$'; then
echo "Error: Invalid version format. Must match semantic versioning (e.g., 1.0.0 or 1.0.0-beta.1)"
exit 1
fi
# Validate dist-tag format (alphanumeric, hyphen, underscore only)
if ! echo "$INPUT_TAG" | grep -qE '^[a-zA-Z0-9_-]+$'; then
echo "Error: Invalid dist-tag format. Must contain only alphanumeric characters, hyphens, and underscores"
exit 1
fi
# Use heredoc to safely write to GITHUB_OUTPUT (prevents injection)
{
echo "version<<EOF"
echo "$INPUT_VERSION"
echo "EOF"
echo "tag<<EOF"
echo "$INPUT_TAG"
echo "EOF"
} >> "$GITHUB_OUTPUT"
else
echo "No version provided, calculating..."
node scripts/determine-version.ts
fi
- name: Set package versions
run: node scripts/set-version.ts "${{ steps.version.outputs.version }}"
- name: Build packages
run: pnpm build
# Publish-time gate (FR7.1 of init follow-up): packs every publishable
# workspace package and verifies the resolved package.json contains no
# `workspace:*` or `catalog:` dependency specifiers. `pnpm publish`
# rewrites these on its own, but `npm publish` (and some CI flows)
# don't — and a single leaked specifier breaks downstream installs.
# This gate fails the publish before anything reaches the registry.
- name: Check publish dependency specifiers
run: pnpm check:publish-deps
# NODE_AUTH_TOKEN is intentionally NOT set. npm detects the OIDC environment
# (id-token: write) and authenticates via Trusted Publishing automatically.
# Setting NODE_AUTH_TOKEN to any value -- even empty string -- would block OIDC.
#
# Enable npm provenance attestations for each published package.
# This requires a public source repository (npm rejects provenance from private repos).
- name: Publish packages
env:
NPM_CONFIG_PROVENANCE: "true"
run: pnpm -r publish --access public --tag "${{ steps.version.outputs.tag }}" --no-git-checks
# Emit a GitHub Release for stable publishes only (dist-tag `latest`).
# Dev / PR / beta builds publish to npm but do not produce a Release —
# those would drown out the changelog signal. The Release is created at
# $GITHUB_SHA so the tag points at the same commit the publish ran from.
# `--generate-notes` populates the body from merged-PR titles since the
# previous release tag (auto-discovered).
#
# Idempotent on workflow rerun: if a Release for `v$VERSION` already exists
# (e.g. a previous run published to npm but failed before this step), edit
# it in place rather than re-creating it. `gh release edit` doesn't support
# `--generate-notes`, so on rerun the existing notes are preserved — that's
# the right trade-off versus failing the rerun outright.
- name: Create GitHub Release for stable publishes
if: ${{ steps.version.outputs.tag == 'latest' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.version }}
run: |
if gh release view "v$VERSION" >/dev/null 2>&1; then
gh release edit "v$VERSION" \
--target "$GITHUB_SHA" \
--title "v$VERSION"
else
gh release create "v$VERSION" \
--target "$GITHUB_SHA" \
--title "v$VERSION" \
--generate-notes
fi