Compare commits

...
Author SHA1 Message Date
Kevin Codex 94d8148a06 fix: drop npm@latest from the OIDC publish path
Rely on the npm bundled with the pinned Node 24 toolchain (>= 11.5.1,
the trusted-publishing minimum) instead of pulling an unpinned
npm@latest from the registry inside the publish job, and fail fast
with a clear error if a future toolchain change regresses it.
2026-07-02 14:55:59 +08:00
Kevin Codex 0d0e5e9147 feat: publish to npm via OIDC trusted publishing instead of NPM_TOKEN
npm's trusted publishing exchanges the workflow's OIDC token for
publish auth, so no long-lived NPM_TOKEN secret exists to leak, and
provenance is generated automatically. Drops registry-url from
setup-node (its .npmrc line hard-fails on the unset NODE_AUTH_TOKEN)
and updates npm first (OIDC needs npm >= 11.5.1). One-time caveat:
npm only lets you configure a trusted publisher on an existing
package, so the first @gitlawb/zero publish is a manual bootstrap.
2026-07-02 14:46:00 +08:00
Kevin Codexandcoderabbitai[bot] 74367697f1 Update .github/workflows/release-please.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-07-02 14:42:04 +08:00
Kevin Codex 4f84e1e001 feat: publish zero to npm via release-please
Adds release-please to automate versioning and releases from
conventional commits on main:

- release-please-config.json / .release-please-manifest.json: node
  release type (package.json is the version source of truth), plain
  v-prefixed tags, bootstrap-sha at current HEAD, and release-as 0.1.0
  to pin the first release (remove after v0.1.0 ships).
- .github/workflows/release-please.yml: maintains the release PR; on
  merge it tags + creates the GitHub Release, then dispatches
  release-artifacts.yml at the tag (GITHUB_TOKEN-created tags cannot
  fire on-push-tags workflows).
- .github/workflows/release-artifacts.yml: tag-gated steps now match
  the ref instead of the push event so the dispatched-at-tag run
  publishes assets, and a new publish-npm job publishes @gitlawb/zero
  after verifying every postinstall asset URL is publicly downloadable
  (also blocks publishing while the repo is private).
2026-07-02 12:14:18 +08:00
4 changed files with 139 additions and 7 deletions

No files matched your search

+91 -7
View File
@@ -54,8 +54,10 @@ jobs:
# is published under the pushed git tag. If they disagree, install.sh/install.ps1
# and the npm postinstall all build a download URL that 404s. Fail fast so a
# forgotten version bump can never publish mis-versioned, un-installable assets.
# Matches on the ref (not event_name) because release-please dispatches this
# workflow at the tag ref — its GITHUB_TOKEN-created tag cannot fire `push`.
- name: Check tag matches package.json version
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
shell: bash
run: |
pkg_version="$(node -p "require('./package.json').version")"
@@ -78,13 +80,15 @@ jobs:
path: dist/release/*
if-no-files-found: error
# On a `v*` tag push, publish the packaged archive + .sha256 as GitHub Release
# assets so the documented `scripts/install.sh` / `install.ps1` path (which reads
# /releases/latest) works. Uses the preinstalled `gh` (no new action dependency);
# each matrix OS creates the release if absent (best-effort) then uploads its own
# assets with --clobber. NOTE: maintainers must push the tag — this never tags.
# On a `v*` tag ref (pushed manually, or dispatched at the tag by the
# release-please workflow), publish the packaged archive + .sha256 as GitHub
# Release assets so the documented `scripts/install.sh` / `install.ps1` path
# (which reads /releases/latest) works. Uses the preinstalled `gh` (no new
# action dependency); each matrix OS creates the release if absent
# (best-effort — release-please normally creates it first) then uploads its
# own assets with --clobber. Tags come from merging the release-please PR.
- name: Publish to GitHub Release
if: ${{ github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') }}
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
shell: bash
env:
GH_TOKEN: ${{ github.token }}
@@ -92,3 +96,83 @@ jobs:
tag="${GITHUB_REF_NAME}"
gh release create "$tag" --repo "$GITHUB_REPOSITORY" --title "$tag" --generate-notes 2>/dev/null || true
gh release upload "$tag" dist/release/* --repo "$GITHUB_REPOSITORY" --clobber
# Publishes the npm wrapper (@gitlawb/zero) once every platform's release
# assets are live — the package's postinstall downloads its binary from the
# GitHub Release, so publishing before the assets exist would ship a package
# that fails to install.
#
# Auth is npm OIDC trusted publishing: no NPM_TOKEN secret. The package's
# npmjs.com settings must list this repo + workflow file as a trusted
# publisher, and provenance is then generated automatically. Note npm only
# allows configuring a trusted publisher on an EXISTING package, so the very
# first @gitlawb/zero publish is a one-time manual `npm publish` bootstrap.
publish-npm:
name: Publish npm package
needs: package
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # OIDC token exchange for npm trusted publishing + provenance
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# No registry-url here on purpose: setup-node's registry-url writes an
# .npmrc line referencing $NODE_AUTH_TOKEN, and npm hard-fails on the
# unset env var. Trusted publishing needs no token at all.
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 24
# OIDC trusted publishing needs npm >= 11.5.1. Use the npm bundled with
# the pinned Node toolchain (Node 24 ships >= 11.5.1) rather than pulling
# npm@latest from the registry inside the publish path — an unpinned
# install there would be avoidable supply-chain exposure. Fail fast if a
# future runner/toolchain change ever regresses the bundled npm.
- name: Check npm supports trusted publishing
run: |
npm --version | node -e '
const version = require("fs").readFileSync(0, "utf8").trim();
const [major, minor, patch] = version.split(".").map(Number);
const ok =
major > 11 ||
(major === 11 && (minor > 5 || (minor === 5 && patch >= 1)));
if (!ok) {
console.error(
`::error::bundled npm ${version} is too old for OIDC trusted ` +
`publishing (needs >= 11.5.1) — bump node-version in this job.`,
);
process.exit(1);
}
console.log(`npm ${version} supports trusted publishing.`);
'
# Probe the exact URLs postinstall will build (internal/release/release.go
# asset-name scheme) as an anonymous client. This also blocks publishing
# while the repo is still private — private release assets 404 without
# auth, exactly as they would for an installing user.
- name: Verify release assets are downloadable
shell: bash
run: |
version="$(node -p "require('./package.json').version")"
base="https://github.com/${GITHUB_REPOSITORY}/releases/download/v${version}"
for asset in \
"zero-v${version}-linux-x64.tar.gz" \
"zero-v${version}-linux-arm64.tar.gz" \
"zero-v${version}-macos-x64.tar.gz" \
"zero-v${version}-macos-arm64.tar.gz" \
"zero-v${version}-windows-x64.zip"; do
for url in "${base}/${asset}" "${base}/${asset}.sha256"; do
if ! curl -fsSLI -o /dev/null "$url"; then
echo "::error::${url} is not publicly downloadable — npm postinstall would fail. Is the repo public and are all assets uploaded?" >&2
exit 1
fi
done
done
echo "all release assets for v${version} are publicly downloadable."
- name: Publish to npm
run: npm publish --access public
+35
View File
@@ -0,0 +1,35 @@
name: Release Please
on:
push:
branches:
- main
permissions:
contents: write # push the release-PR branch, create the tag + GitHub Release
pull-requests: write # open/update the release PR
issues: write # create/apply release-tracking labels on the release PR
actions: write # dispatch release-artifacts.yml once a release is cut
jobs:
release-please:
name: Release PR / tag
runs-on: ubuntu-latest
steps:
- name: Run release-please
id: release
uses: googleapis/release-please-action@a02a34c4d625f9be7cb89156071d8567266a2445 # v4.2.0
# Tags created with the default GITHUB_TOKEN never fire `on: push: tags`
# workflows (GitHub suppresses workflows triggered by workflow-created
# events), so release-artifacts.yml would not run for a release-please
# tag. workflow_dispatch invoked through the API is exempt from that
# suppression — dispatch the artifacts build at the new tag explicitly.
- name: Build and publish release artifacts
if: ${{ steps.release.outputs.release_created }}
env:
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run release-artifacts.yml \
--repo "$GITHUB_REPOSITORY" \
--ref "${{ steps.release.outputs.tag_name }}"
+1
View File
@@ -0,0 +1 @@
{}
+12
View File
@@ -0,0 +1,12 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "node",
"include-component-in-tag": false,
"bootstrap-sha": "02f0c0929bec65f5fc41bdcab3eb518d2b0b329a",
"packages": {
".": {
"package-name": "@gitlawb/zero",
"release-as": "0.1.0"
}
}
}