Skip to content

chore: release core 0.7.46, server 0.8.58, cli 0.10.50 (#1198) #107

chore: release core 0.7.46, server 0.8.58, cli 0.10.50 (#1198)

chore: release core 0.7.46, server 0.8.58, cli 0.10.50 (#1198) #107

Workflow file for this run

name: Auto-release
# Every push to main that lands a new `changelog/<pkg>/<version>.md`
# file becomes:
# 1. A published npm.com package via scripts/publish-npm.js.
# 2. A published GitHub Packages copy via scripts/publish-github-packages.js
# (so the repo's Packages sidebar lists each version).
# 3. A GitHub Release via scripts/publish-release.js.
#
# All three scripts are idempotent: they skip when the version is
# already on the destination registry / a release with the tag
# already exists. So workflow retries and force-pushes never
# duplicate or error on previously-published versions.
#
# Order matters: npm.com runs first, then GitHub Packages, then GH
# Releases. If any step fails (auth, network, transient registry
# error), later steps are skipped and the workflow fails. A
# re-run picks up where it left off thanks to the idempotency
# checks.
#
# Triggered only on changelog/** changes so unrelated pushes do
# not run the job. workflow_dispatch is also wired so a one-time
# bootstrap can republish every existing changelog file to GitHub
# Packages without needing a fresh changelog file. Cost on public
# repos: $0 (Actions has unlimited free minutes for public repos
# on ubuntu-latest).
on:
push:
branches: [main]
paths:
- 'changelog/**'
workflow_dispatch:
inputs:
bootstrap_github_packages:
description: 'Republish every existing changelog file to GitHub Packages (one-time bootstrap; npm.com + GH Releases steps are skipped)'
type: boolean
default: false
lockstep_only:
description: 'Only (re)publish the unscoped wrappers (create-webjs, webjsdev) at the current @webjsdev/cli version. Use to recover a release whose wrapper publish did not land.'
type: boolean
default: false
permissions:
contents: write # gh release create needs write access to the repo
packages: write # npm publish to GitHub Packages
jobs:
release:
runs-on: ubuntu-latest
steps:
# Pinned to v6: both actions ship a Node 24 runtime, which is
# what GitHub Actions will default to from June 2nd, 2026. v4
# / v5 ran on Node 20 and triggered the deprecation banner.
- uses: actions/checkout@v6
with:
fetch-depth: 2
# setup-node writes an .npmrc with the standard
# //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN} line, so
# subsequent `npm publish` invocations pick up the token from
# the env var.
- uses: actions/setup-node@v6
with:
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: npm install --no-audit --no-fund
- name: Find new changelog files
id: diff
run: |
set -euo pipefail
# Two modes:
# 1. push event: diff HEAD~1..HEAD for newly added changelog files.
# 2. workflow_dispatch with bootstrap_github_packages=true: list
# every changelog/**.md file currently in the tree. The
# publish-github-packages.js step is idempotent (skips
# already-published versions) so a re-run is safe.
BOOTSTRAP='${{ inputs.bootstrap_github_packages }}'
if [ "$BOOTSTRAP" = 'true' ]; then
mapfile -t NEW < <(
find changelog -name '*.md' -not -name 'README.md' \
| while read -r f; do
ts=$(awk '/^date:/ { print $2; exit }' "$f")
printf '%s\t%s\n' "$ts" "$f"
done \
| sort -k1,1 -k2,2r \
| cut -f2-
)
else
# Sort by the `date:` timestamp inside each file's
# frontmatter, ASCending. GitHub Releases lists releases by
# created_at DESC, so publishing oldest-first means newest
# entries end up at the top of the Releases page.
#
# Within tied timestamps (multiple packages bumped in one
# PR), sort the filename DESC so the alphabetically-first
# package publishes LAST, gets the latest created_at, and
# ends up at the top of the GH list. The website's
# /changelog page iterates package dirs alphabetically
# too, so this produces matching order on both surfaces.
mapfile -t NEW < <(
git diff --name-only --diff-filter=A HEAD~1 HEAD -- 'changelog/**.md' \
| while read -r f; do
ts=$(awk '/^date:/ { print $2; exit }' "$f")
printf '%s\t%s\n' "$ts" "$f"
done \
| sort -k1,1 -k2,2r \
| cut -f2-
)
fi
if [ ${#NEW[@]} -eq 0 ]; then
echo "No new changelog files in this push; skipping."
echo "count=0" >> "$GITHUB_OUTPUT"
exit 0
fi
printf ' + %s\n' "${NEW[@]}"
printf '%s\n' "${NEW[@]}" > .new-changelog-files.txt
echo "count=${#NEW[@]}" >> "$GITHUB_OUTPUT"
echo "bootstrap=$BOOTSTRAP" >> "$GITHUB_OUTPUT"
- name: Publish to npm
if: steps.diff.outputs.count != '0' && steps.diff.outputs.bootstrap != 'true'
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
while IFS= read -r f; do
[ -z "$f" ] && continue
node scripts/publish-npm.js "$f"
done < .new-changelog-files.txt
- name: Publish to GitHub Packages
if: steps.diff.outputs.count != '0'
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
while IFS= read -r f; do
[ -z "$f" ] && continue
node scripts/publish-github-packages.js "$f"
done < .new-changelog-files.txt
- name: Create GitHub Releases
if: steps.diff.outputs.count != '0' && steps.diff.outputs.bootstrap != 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
while IFS= read -r f; do
[ -z "$f" ] && continue
node scripts/publish-release.js "$f"
done < .new-changelog-files.txt
# Lockstep-publish the unscoped wrapper packages (`create-webjs`,
# `webjsdev`) at the new @webjsdev/cli version whenever this push
# landed a new cli changelog. The wrappers exist purely as version
# mirrors of cli, so their npm versions MUST equal cli's version
# exactly; otherwise the npx cache serves an outdated cli through
# them (a wrapper cached at 0.8.4 keeps resolving cli@0.8.4 even
# after cli@0.8.5 publishes, because the wrapper's pkg-version is
# what npx keys its cache on).
#
# This step does NOT write back to the repo. Earlier designs tried
# to commit the wrapper bumps (direct push, then PR + merge), but
# the webjsdev org disables write permissions for GITHUB_TOKEN:
# direct pushes are refused by branch protection and `gh pr create`
# is refused with "GitHub Actions is not permitted to create or
# approve pull requests". Since the wrapper version is recomputed
# from CLI_VERSION here on every release, the repo copy of the
# wrapper package.json is irrelevant to publishing, so we simply
# set the version in the runner's working tree and `npm publish`.
# The repo's wrapper package.json versions intentionally drift (npm
# is the source of truth for them); nothing reads them.
#
# Publishing is idempotent: a wrapper already on the registry at
# CLI_VERSION is skipped, so re-runs and the `lockstep_only`
# manual dispatch are safe.
#
# `lockstep_only` (workflow_dispatch input) runs ONLY this step at
# the current CLI_VERSION: the recovery path for a release whose
# wrapper publish did not land.
- name: Lockstep-publish wrappers to match @webjsdev/cli
if: inputs.lockstep_only || (steps.diff.outputs.count != '0' && steps.diff.outputs.bootstrap != 'true')
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
set -euo pipefail
# In a normal release run, only proceed if a cli changelog
# landed in this push. The lockstep_only dispatch skips this
# gate (there is no .new-changelog-files.txt to consult).
if [ "${{ inputs.lockstep_only }}" != "true" ]; then
if ! grep -q '^changelog/cli/' .new-changelog-files.txt; then
echo "No new @webjsdev/cli changelog in this push; skipping wrapper lockstep."
exit 0
fi
fi
CLI_VERSION=$(node -p "require('./packages/cli/package.json').version")
echo "Lockstep-publishing create-webjs + webjsdev at @webjsdev/cli@${CLI_VERSION}"
for pkg in create-webjs webjsdev; do
# Set the version + cli dep range in the runner's working
# tree only (never committed). npm publish reads from here.
node -e "
const fs = require('node:fs');
const path = './packages/wrappers/${pkg}/package.json';
const j = JSON.parse(fs.readFileSync(path, 'utf8'));
j.version = '${CLI_VERSION}';
if (j.dependencies && j.dependencies['@webjsdev/cli']) {
j.dependencies['@webjsdev/cli'] = '^${CLI_VERSION}';
}
fs.writeFileSync(path, JSON.stringify(j, null, 2) + '\n');
"
REMOTE=$(npm view "${pkg}@${CLI_VERSION}" version 2>/dev/null || echo "")
if [ "$REMOTE" = "${CLI_VERSION}" ]; then
echo " skip ${pkg}@${CLI_VERSION}: already on registry"
else
echo " publishing ${pkg}@${CLI_VERSION}..."
npm publish --workspace="${pkg}" --access=public --ignore-scripts=false
fi
done