From 937c7eb72861dfdda6ca29059413d15940cfd5d3 Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Wed, 14 Oct 2026 13:19:37 -0700 Subject: [PATCH 1/2] Stage the browser sidecar's npm releases through trusted publishing The package is now on npm, and its trusted publisher allows staged publishes: CI stages each version over OIDC, with no NPM_TOKEN, and a maintainer approves it with 2FA before it is public. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CJAKwnkBaCGr5qjCcmc3kh --- .github/workflows/release.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4408b48e..8ac29655 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,9 +13,15 @@ name: Release # fails with an authorization error and the other gem is unaffected. # # The browser sidecar (browser-sidecar/) is published at the dashboard's -# version too: the npm package with the NPM_TOKEN secret, and the image to +# version too: the npm package through npm trusted publishing (OIDC, no +# stored token) as a staged publish, and the image to # ghcr.io/activeagents/browser-sidecar with the workflow's own token. Each is # skipped when that version is already published. +# +# A staged publish is not public until a maintainer approves it with 2FA, on +# npmjs.com or with `npm stage approve` (`npm stage list` shows what waits). +# The trusted publisher on npmjs.com for @activeagents/browser-sidecar must +# name this repository and this workflow file (release.yml). on: push: tags: [ "v*" ] @@ -112,7 +118,7 @@ jobs: runs-on: ubuntu-latest permissions: contents: read - id-token: write # npm provenance + id-token: write # npm trusted publishing (OIDC); provenance comes with it defaults: run: working-directory: browser-sidecar @@ -120,11 +126,13 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 registry-url: https://registry.npmjs.org - - name: Publish the package unless this version is on npm - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + # Trusted publishing needs npm 11.5.1 or later, and `npm stage` a + # recent 11.x; the npm bundled with Node may be older. + - name: Update npm + run: npm install -g npm@11 + - name: Stage the package unless this version is on npm run: | version="$(node -p 'require("./package.json").version')" engine="$(sed -nE 's/^ *VERSION = "([^"]+)".*/\1/p' ../actionagent/lib/action_agent/version.rb)" @@ -137,7 +145,8 @@ jobs: exit 0 fi npm ci - npm publish --provenance --access public + npm stage publish + echo "::notice::@activeagents/browser-sidecar ${version} is staged; approve it on npmjs.com or with npm stage approve." publish-browser-sidecar-image: if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' From 33624456736b20f72a10f7da1cf128497e3cf459 Mon Sep 17 00:00:00 2001 From: Justin Bowen Date: Wed, 14 Oct 2026 13:54:54 -0700 Subject: [PATCH 2/2] Fail a release whose sidecar changed under a published version The sidecar job skips npm when its version is already published, so a sidecar change released without a version bump would silently never reach npm. When the version is on npm, the job now packs the published tarball and browser-sidecar/, compares them file by file, and fails with "bump the version" when they differ (.github/scripts/browser-sidecar-unchanged.sh). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01CJAKwnkBaCGr5qjCcmc3kh --- .github/scripts/browser-sidecar-unchanged.sh | 27 ++++++++++++++++++++ .github/workflows/release.yml | 7 ++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100755 .github/scripts/browser-sidecar-unchanged.sh diff --git a/.github/scripts/browser-sidecar-unchanged.sh b/.github/scripts/browser-sidecar-unchanged.sh new file mode 100755 index 00000000..2b4f81a3 --- /dev/null +++ b/.github/scripts/browser-sidecar-unchanged.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# Fails when browser-sidecar/ packs differently from the version of +# @activeagents/browser-sidecar already on npm: a published version is never +# published again, so a sidecar change needs a new version. +# +# .github/scripts/browser-sidecar-unchanged.sh browser-sidecar 1.9.0 +set -euo pipefail + +dir="$(cd "${1:?the sidecar directory}" && pwd)" +version="${2:?the version on npm}" +package="@activeagents/browser-sidecar@${version}" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT +mkdir -p "$work/published" "$work/repo" + +# Both tarballs share a file name, so each is packed into its own directory. +published="$(cd "$work/published" && npm pack "$package" --silent)" +repo="$(cd "$dir" && npm pack --silent --pack-destination "$work/repo")" +tar -xzf "$work/published/$published" -C "$work/published" +tar -xzf "$work/repo/$repo" -C "$work/repo" + +if ! diff -r "$work/published/package" "$work/repo/package" > "$work/diff.txt"; then + echo "::error::browser-sidecar changed since ${package} was published; bump the version (browser-sidecar/package.json and ActionAgent::VERSION)." + head -n 80 "$work/diff.txt" + exit 1 +fi +echo "browser-sidecar matches ${package} on npm." diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ac29655..dbc912c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -16,7 +16,9 @@ name: Release # version too: the npm package through npm trusted publishing (OIDC, no # stored token) as a staged publish, and the image to # ghcr.io/activeagents/browser-sidecar with the workflow's own token. Each is -# skipped when that version is already published. +# skipped when that version is already published; the npm job then fails if +# browser-sidecar/ no longer packs the same as that published version, since +# a sidecar change needs a new version. # # A staged publish is not public until a maintainer approves it with 2FA, on # npmjs.com or with `npm stage approve` (`npm stage list` shows what waits). @@ -141,6 +143,9 @@ jobs: exit 1 fi if npm view "@activeagents/browser-sidecar@${version}" version > /dev/null 2>&1; then + # Skipping is right only when the sidecar is what was published: + # a change released under a published version would never reach npm. + ../.github/scripts/browser-sidecar-unchanged.sh . "$version" echo "@activeagents/browser-sidecar ${version} is already on npm; skipping." exit 0 fi