Skip to content

chore: publish [next] pre-release #180

chore: publish [next] pre-release

chore: publish [next] pre-release #180

Workflow file for this run

# https://help.github.com/en/categories/automating-your-workflow-with-github-actions
# See: https://github.com/JulianCataldo/gh-actions
# For matrix setup:
# https://github.com/withastro/astro/blob/main/.github/workflows/ci.yml
# https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow
name: CI / Release
on:
workflow_dispatch:
push:
paths-ignore:
- .github/**
- '!.github/workflows/release.yaml'
- '**/*.md'
branches:
- '([0-9])?(.{+([0-9]),x}).x'
- main
- next
- next-major
- alpha
- beta
- 'feat/*'
- 'fix/*'
# - to-integrate
# - to-integrate-next
permissions:
contents: read # for checkout
# TODO: Trusted Publishers
# id-token: write # Required for OIDC
jobs:
release:
name: CI / Release
timeout-minutes: 30
permissions:
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
id-token: write # to enable use of OIDC for npm provenance
runs-on: ubuntu-latest
# TODO:
# runs-on: ${{ matrix.os }}
# timeout-minutes: 25
# # needs: build
# strategy:
# matrix:
# OS: [ubuntu-latest]
# NODE_VERSION: [18, 20]
# include:
# - os: macos-14
# NODE_VERSION: 18
# - os: windows-latest
# NODE_VERSION: 18
# fail-fast: false
# env:
# NODE_VERSION: ${{ matrix.NODE_VERSION }}
steps:
# MARK: Setup GH Action
- name: 'Harden Runner'
uses: 'step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b' # v2.15.0
with:
egress-policy: 'audit'
- name: Git checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
# - run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
# persist-credentials: false
# env:
# GIT_COMMITTER_NAME: "GitHub Actions Shell"
# GIT_AUTHOR_NAME: "GitHub Actions Shell"
# EMAIL: "github-actions[bot]@users.noreply.github.com"
# MARK: Setup Node env.
- name: Setup PNPM
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
- name: Use Node.js 24
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
registry-url: 'https://registry.npmjs.org'
node-version: 24
cache: pnpm
- name: Install packages
shell: bash
run: pnpm install --frozen-lockfile
# TODO: More tests
# - name: Syncpack Lint
# shell: bash
# run: node --run syncpack:lint
# NOTE: Audit is for prod only because a lot of root packages (like lerna etc.)
# are used old packages with intricate dependency trees, and they are
# never shipped to the user. But that's not 100% optimal, as devDeps could
# provoke some sec issues, too? A middleground is better than nothing and
# regularly blocked releases for obscure root mono-repo tooling deps.
- name:
'Verify the integrity of provenance attestations and registry
signatures for installed [prod] dependencies'
run: 'node --run audit'
# MARK: Lint/Checks pre-build
- name: Lint last commit — Commitlint
shell: bash
run: node --run lint:commit
# - name: Lint CSS — Stylelint
# shell: bash
# run: node --run lint:css
- name: Check all formatting — Prettier
shell: bash
run: node --run format
# MARK: Build packages
- name: Setup Turbo cache
uses: dtinth/setup-github-actions-caching-for-turbo@cc723b4600e40a6b8815b65701d8614b91e2669e # v1.3.0
- name: Build libraries
shell: bash
run: node --run build:libs
- name: Generate Typedoc
shell: bash
run: node --run docs:generate
- name: Build docs website
shell: bash
env:
VITE_DOCS_IS_NEXT:
${{ github.ref == 'refs/heads/next' && 'true' || 'false' }}
run: node --run build:docs
# MARK: Lint/Checks post-build
- name: Cache ESLint results
uses: actions/cache@v4
with:
path: .eslintcache
key: eslint-${{ hashFiles('pnpm-lock.yaml', 'eslint.config.js') }}
- name: Lint JS/TS — ESLint
shell: bash
run: node --run lint:es
# MARK: Tests
- name: Tests — Units
shell: bash
run: node --run test:unit
# MARK: Playwright — single install for all test suites
# All packages use the same @playwright/test version (catalog:).
# Browsers land in ~/.cache/ms-playwright regardless of working directory.
- name: Cache Playwright browsers
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: playwright-${{ hashFiles('pnpm-lock.yaml') }}
- name: Install Playwright browsers
shell: bash
run: npx playwright install --with-deps chromium
working-directory: starter-projects
- name: Tests — Integration
shell: bash
run: node --run test:integration
# MARK: Starter Projects — Smoke Test (pre-publish, workspace-linked)
- name: 'Regenerate starter-project templates'
run: pnpm --prefix starter-projects run generate
- name: Tests — Starter projects smoke
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next'
shell: bash
run: pnpm --prefix starter-projects run test:smoke
- name: Tests — Docs smoke
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next'
shell: bash
run: pnpm --prefix docs/website run test:smoke
# MARK: Publish packages
# NOTE: Auth is configured by setup-node's registry-url above.
# The NPM_TOKEN secret MUST be a Granular Access Token (or classic Automation
# token) to bypass 2FA/OTP requirements in CI.
- name: Git user configuration
run: |
git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
# MARK: [MAIN]
# --force-git-tag: a prior interrupted CI run may have left orphan tags
# (e.g. @gracile/client@0.5.0) that collide with graduation.
- name: 'Lerna publish [main]'
if: github.ref == 'refs/heads/main'
# https://github.com/lerna/lerna/issues/2532
id: graduateRelease
continue-on-error: true
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
NPM_CONFIG_PROVENANCE: 'true'
run: |
pnpm lerna publish --message 'chore: publish [main] release [skip ci]' --create-release=github --conventional-graduate --force-git-tag --yes
# Fallback for https://github.com/lerna/lerna/issues/2532 (newly introduced
# packages that fail --conventional-graduate). Fail explicitly if neither
# the graduate nor the fallback actually published anything.
- name: 'Lerna publish [main] — fallback (non-graduate)'
if:
${{ always() && github.ref == 'refs/heads/main' &&
steps.graduateRelease.outcome == 'failure' }}
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
NPM_CONFIG_PROVENANCE: 'true'
run: |
echo "Graduate publish failed — attempting recovery"
git stash || true
# Recovery 1: from-package (handles Sigstore/Rekor TLOG_CREATE_ENTRY_ERROR 409)
# Queries npm directly and publishes any package versions not yet in the registry,
# regardless of git tags — safe to run even if some packages are already published.
echo "==> from-package: recovering partial publish"
FROM_PKG_OUT=$(pnpm lerna publish from-package --yes 2>&1)
FROM_PKG_EXIT=$?
echo "$FROM_PKG_OUT"
# Recovery 2: non-graduate lerna publish (handles lerna#2532: newly introduced
# packages that have no pre-release version to graduate from).
echo "==> non-graduate: publishing new or changed packages"
OUTPUT=$(pnpm lerna publish --message 'chore: publish [main] release [skip ci]' --create-release=github --yes 2>&1) || true
echo "$OUTPUT"
# Fail explicitly only when BOTH approaches published nothing useful.
if [ "$FROM_PKG_EXIT" -ne 0 ] && echo "$OUTPUT" | grep -q 'No changed packages to publish'; then
echo "::error::Neither from-package nor conventional fallback published anything"
exit 1
fi
# MARK: [NEXT]
- name: 'Lerna publish [next]'
if: github.ref == 'refs/heads/next'
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
NODE_AUTH_TOKEN: '${{ secrets.NPM_TOKEN }}'
NPM_CONFIG_PROVENANCE: 'true'
run: |
pnpm lerna publish --message 'chore: publish [next] pre-release' --conventional-prerelease --pre-dist-tag=next --preid=next --yes
# MARK: Commit regenerated starter-project templates
# Templates were generated pre-publish (rsync + README) and keep
# workspace:/catalog: refs — create-gracile resolves at scaffold time.
- name: 'Commit starter-project templates'
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/next'
run: |
git add starter-projects/templates
git commit -m 'chore: regenerate starter-project templates [skip ci]' || echo 'No template changes to commit'
git push
- name: Merge back main into next
if: github.ref == 'refs/heads/main'
continue-on-error: true
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: |
git checkout next
git merge main --no-ff -m 'chore: auto-merge main into next [skip ci]'
git push
- name: 'Deploy docs website [main → production]'
if: github.ref == 'refs/heads/main'
env:
NETLIFY_AUTH_TOKEN: '${{ secrets.NETLIFY_AUTH_TOKEN }}'
NETLIFY_SITE_ID: '${{ secrets.NETLIFY_SITE_ID }}'
shell: bash
run: pnpm --prefix ./docs/website deploy:main
- name: 'Deploy docs website [next → preview]'
if: github.ref == 'refs/heads/next'
env:
NETLIFY_AUTH_TOKEN: '${{ secrets.NETLIFY_AUTH_TOKEN }}'
NETLIFY_SITE_ID: '${{ secrets.NETLIFY_SITE_ID }}'
shell: bash
run: pnpm --prefix ./docs/website deploy:next