Update scripts + CI for dryrun + version checking (#6858)

* Update scripts + CI for dryrun + version checking

* Update publish.sh w trap

* Coderabbit
This commit is contained in:
mfahampshire
2026-06-05 14:00:04 +00:00
committed by GitHub
parent 08dc353e82
commit cff370a943
5 changed files with 8375 additions and 194 deletions
+15
View File
@@ -1,6 +1,19 @@
name: publish-sdk-npm
on:
workflow_dispatch:
inputs:
dry_run:
description: "Rehearse the publish (pnpm publish --dry-run, no tarballs uploaded). Untick to publish for real."
type: boolean
default: true
dist_tag:
description: "Tag mode. 'auto' picks per package: new packages and same-major releases -> latest; a breaking major (e.g. mix-fetch v2 over v1) -> next, promote later with `npm dist-tag add`. 'next'/'latest' force that tag on all four."
type: choice
options:
- auto
- next
- latest
default: auto
jobs:
publish:
@@ -42,4 +55,6 @@ jobs:
- name: Publish to NPM
env:
NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }}
DRY_RUN: ${{ inputs.dry_run && '1' || '0' }}
NPM_DIST_TAG: ${{ inputs.dist_tag }}
run: ./sdk/typescript/scripts/publish.sh
+8246 -180
View File
File diff suppressed because it is too large Load Diff
+8 -1
View File
@@ -14,8 +14,15 @@ rm -rf dist || true
pnpm build:wasm
# enable dev mode and then install dev packages
#
# `--no-frozen-lockfile` is required: dev:on injects the four smolmix-family
# packages (plus wasm/smolmix/pkg) as new workspace importers that the committed
# lockfile does not know about. CI sets CI=true, which makes a bare `pnpm install`
# default to frozen and fail with ERR_PNPM_OUTDATED_LOCKFILE. Use the `--no-`
# form, not `--frozen-lockfile false`: pnpm parses the bare `false` as a separate
# argument and ignores it.
pnpm dev:on
pnpm install
pnpm install --no-frozen-lockfile
# build the Typescript SDK packages
pnpm build:ci:sdk
+103 -10
View File
@@ -24,6 +24,24 @@ set -o pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
cd "$REPO_ROOT"
# `pnpm publish` rewrites each package's `workspace:*` deps (mix-fetch/dns/websocket
# all depend on mix-tunnel) to concrete versions at pack time by re-globbing
# pnpm-workspace.yaml for the sibling packages. They are only in the workspace
# while dev mode is on, and build-prod-sdk.sh turns dev mode off when it finishes,
# so enable it here and restore the clean state on exit. This is a YAML edit only;
# no reinstall is needed because pack-time resolution reads sibling package.json
# versions directly.
# Arm the cleanup before mutating the workspace: dev:off filters unconditionally,
# so it is safe even if dev:on fails mid-write, and this closes the window where a
# crash between the two would leave pnpm-workspace.yaml dirty.
#
# The trap must cd back to the repo root first: a failed `pnpm publish` exits
# (errexit) while still inside a pushd'd package dir, and `dev:off` is a script
# defined only in the root package.json. Running it from a package dir fails with
# "Command dev:off not found" and leaves dev mode on.
trap 'cd "$REPO_ROOT" && pnpm dev:off' EXIT
pnpm dev:on
packages=(
"sdk/typescript/packages/mix-tunnel"
"sdk/typescript/packages/mix-fetch"
@@ -39,12 +57,86 @@ popd () {
command popd > /dev/null
}
echo "Summary of versions to publish:"
# Resolve the npm dist-tag for one package.
#
# NPM_DIST_TAG=latest|next forces that tag on every package. The default,
# `auto`, derives the tag per package from what is already on npm so that no
# manual promote step has to be remembered:
#
# * version has a prerelease suffix (x.y.z-rc.N) -> next
# prereleases must never become the default install.
# * package is not yet on npm (npm view 404s) -> latest
# a first publish has to set `latest`, or a bare `npm i` finds no version.
# * publishing major == current `latest` major -> latest
# an ordinary patch/minor of the live line.
# * publishing major > current `latest` major -> next
# a breaking major: ship under `next`, leave existing users on `latest`,
# and promote later with `npm dist-tag add <pkg>@<version> latest`.
# Once `latest` points at the new major, the next patch resolves to
# `latest` again on its own, with nothing to clean up.
# * publishing major < current `latest` major -> next
# a backport; never silently move `latest` backwards. Pass an explicit
# NPM_DIST_TAG if a dedicated legacy tag is wanted.
resolve_tag() {
local name="$1" version="$2" mode="$3"
if [[ "$mode" != "auto" ]]; then
printf '%s' "$mode"
return
fi
if [[ "$version" == *-* ]]; then
printf 'next'
return
fi
# Find the package's current `latest` version on npm. There are three
# outcomes, each driving the tag decision below:
# - success -> compare majors (same major: latest; higher: next)
# - package not found -> first ever publish; it has to set `latest`
# - any other failure -> registry/network problem; abort rather than guess
#
# The third case is why this is careful. Defaulting to `latest` when the
# lookup merely failed would move `latest` onto this version even for a
# package that already exists, and so could push a breaking major onto every
# current consumer. Only a genuine 404 counts as "new"; anything else aborts.
#
# stderr is sent to a file rather than merged into stdout so the success value
# holds only the version (never an npm warning), while the failure text can
# still be inspected to recognise a 404.
local current err
err="$(mktemp)"
if current="$(npm view "$name" dist-tags.latest 2>"$err")"; then
rm -f "$err"
elif grep -qiE 'E404|404 Not Found' "$err"; then
current=""
rm -f "$err"
else
printf 'npm view failed for %s (not a 404), refusing to guess a dist-tag: %s\n' "$name" "$(cat "$err")" >&2
rm -f "$err"
return 1
fi
if [[ -z "$current" ]]; then
printf 'latest'
elif [[ "${version%%.*}" == "${current%%.*}" ]]; then
printf 'latest'
else
printf 'next'
fi
}
NPM_DIST_TAG="${NPM_DIST_TAG:-auto}"
# Resolve every tag up front so the summary shows exactly what each package will
# get. Under DRY_RUN this is the whole point: you see the tag decision before
# anything is uploaded.
declare -a TAGS
echo "Summary of packages to publish (mode: $NPM_DIST_TAG):"
echo
for item in "${packages[@]}"; do
pushd "$item"
jq -r '. | " " + .version + " " + .name' < package.json
popd
name="$(jq -r '.name' "$item/package.json")"
version="$(jq -r '.version' "$item/package.json")"
tag="$(resolve_tag "$name" "$version" "$NPM_DIST_TAG")"
TAGS+=("$tag")
printf ' %-34s %-8s --tag %s\n' "$name" "$version" "$tag"
done
echo
@@ -56,20 +148,21 @@ echo
# Flags:
# --access=public scoped packages default to private; smolmix-family is public
# --no-git-checks CI runners can have dirty working trees from the build step
# --tag $NPM_DIST_TAG defaults to `latest`; set to `next` for pre-release shipping
NPM_DIST_TAG="${NPM_DIST_TAG:-latest}"
# --tag <tag> per-package, resolved above
DRY_RUN_FLAG=""
if [[ "${DRY_RUN:-0}" == "1" ]]; then
DRY_RUN_FLAG="--dry-run"
echo "DRY_RUN=1 running pnpm publish in dry-run mode (no tarballs uploaded)"
echo "DRY_RUN=1: running pnpm publish in dry-run mode (no tarballs uploaded)"
fi
COUNTER=0
for item in "${packages[@]}"; do
for i in "${!packages[@]}"; do
item="${packages[$i]}"
tag="${TAGS[$i]}"
(( COUNTER+=1 ))
pushd "$item"
echo "Publishing $item (${COUNTER}/${#packages[@]}) --tag $NPM_DIST_TAG"
pnpm publish --access=public --no-git-checks --tag "$NPM_DIST_TAG" $DRY_RUN_FLAG
echo "Publishing $item (${COUNTER}/${#packages[@]}) --tag $tag"
pnpm publish --access=public --no-git-checks --tag "$tag" $DRY_RUN_FLAG
popd
echo
done
+1 -1
View File
@@ -9,7 +9,7 @@ make sdk-wasm-build
# Build Typescript packages
pnpm i
pnpm build:sdk
pnpm sdk:build
# Publish to NPM
./sdk/typescript/scripts/publish.sh