From ad3f4aa16dfd9e6af587408507d99ea9b2523c3c Mon Sep 17 00:00:00 2001 From: "M. Adel Alhashemi" Date: Tue, 15 Sep 2026 15:29:18 +0300 Subject: [PATCH 1/2] ci: use npm trusted publishing without stored tokens --- .github/workflows/ci.yml | 14 ++++++-------- README.md | 2 +- docs/releasing.md | 15 +++++++-------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b2ef36..1ef7c46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,10 +43,13 @@ jobs: name: Publish to npm needs: check if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' - runs-on: blacksmith-4vcpu-ubuntu-2404 + # npm trusted publishing requires a GitHub-hosted runner. + # All dependency installation, builds and tests stay on Blacksmith above. + runs-on: ubuntu-latest timeout-minutes: 10 permissions: contents: write + id-token: write steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: @@ -65,19 +68,14 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 if: steps.package.outputs.published == 'false' with: - node-version: '22' + node-version: '24' registry-url: https://registry.npmjs.org - name: Publish the tested archive if: steps.package.outputs.published == 'false' env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} ARCHIVE: ${{ steps.package.outputs.archive }} run: | - if [ -z "$NODE_AUTH_TOKEN" ]; then - echo 'Set the NPM_TOKEN repository secret with a granular npm publish token.' >&2 - exit 1 - fi - npm publish "release/$ARCHIVE" --access public --ignore-scripts + npm publish "release/$ARCHIVE" --access public --ignore-scripts --provenance - name: Create GitHub release if: steps.package.outputs.published == 'false' env: diff --git a/README.md b/README.md index e8efaab..da5a91f 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ The interface follows the Agent964 design system and the Balanced playground dir Bug reports and focused PRs are welcome. See [CONTRIBUTING.md](https://github.com/malhashemi/workflow-observer/blob/main/CONTRIBUTING.md) for local setup and checks. Run `bun run demo` for an isolated workspace with synthetic workflows. -CI uses **Blacksmith runners**. To release, run `bun run release:prepare patch` on your PR branch and commit the version changes. Merging to `main` runs checks, publishes the tested npm archive and creates a GitHub release. Merges without a new version skip publishing. The [release guide](https://github.com/malhashemi/workflow-observer/blob/main/docs/releasing.md) covers setup and retries. +Builds and tests use **Blacksmith runners**. A small GitHub-hosted job publishes through **npm trusted publishing**, with provenance and no stored npm token. To release, run `bun run release:prepare patch` on your PR branch and commit the version changes. Merging to `main` runs checks, publishes the tested npm archive and creates a GitHub release. Merges without a new version skip publishing. The [release guide](https://github.com/malhashemi/workflow-observer/blob/main/docs/releasing.md) covers setup and retries. ## License diff --git a/docs/releasing.md b/docs/releasing.md index f695962..412f57a 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,6 +1,6 @@ # Releasing -CI runs on Blacksmith. A push to `main`, including a merged PR, builds the app, runs TypeScript/OXC checks and tests, and exercises the packed CLI with isolated data. The release job publishes that tested archive when its version is absent from npm, then creates a matching GitHub tag and release. A version already on npm is skipped. PR checks never receive the publishing token. +Builds and tests run on Blacksmith. A push to `main`, including a merged PR, builds the app, runs TypeScript/OXC checks and tests, and exercises the packed CLI with isolated data. A small GitHub-hosted release job publishes that tested archive through npm trusted publishing, then creates a matching GitHub tag and release. A version already on npm is skipped. No npm token is stored in GitHub. ## Release a change @@ -18,12 +18,11 @@ Version preparation only changes package metadata and the lockfile. It does not ## One-time setup -1. Enable the Blacksmith GitHub App for this repository. Both jobs use `blacksmith-4vcpu-ubuntu-2404`. -2. Authenticate with npm and confirm ownership of `workflow-observer`. If this is the first publication, use the manual bootstrap below. -3. Create a granular npm token with read/write access to this package and **Bypass two-factor authentication** enabled for unattended publication. Store it as the repository Actions secret **NPM_TOKEN**. Set an expiration reminder and replace the secret when rotating the token. -4. Require **Check and package** before merging into `main` in the repository's branch rules. +1. Enable the Blacksmith GitHub App for this repository. **Check and package** uses `blacksmith-4vcpu-ubuntu-2404`. +2. In [the npm package settings](https://www.npmjs.com/package/workflow-observer/access), add a **GitHub Actions** trusted publisher with owner **malhashemi**, repository **workflow-observer**, and workflow filename **ci.yml**. Leave the environment name empty and allow direct publishing. The package must already exist; use the manual bootstrap below for a new package. +3. Require **Check and package** before merging into `main` in the repository's branch rules. -[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) currently supports GitHub-hosted runners, not self-hosted runners. This workflow uses a token so publishing stays on Blacksmith. It does not request npm provenance or claim OIDC authentication. Node is used only by the CI npm publisher; Observer itself runs on Bun. +[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) currently supports GitHub-hosted runners, not self-hosted runners. Only the publishing job uses `ubuntu-latest`, with `id-token: write` and Node 24. npm exchanges the workflow's short-lived OIDC identity for permission to publish and attaches provenance. Builds and tests remain on Blacksmith; Observer itself runs on Bun. For the first publication, from a checked-out release commit: @@ -37,11 +36,11 @@ bun run test:package npm publish ./workflow-observer-0.9.4.tgz --access public --ignore-scripts ``` -Use the archive for the version in `package.json`. After bootstrap, the CI token can be restricted to the newly created package. +Use the archive for the version in `package.json`. After bootstrap, configure its trusted publisher once. Later releases require no npm login or token rotation. ## Retry a release -Fix a missing/expired `NPM_TOKEN` or Blacksmith access issue, then rerun the failed workflow from Actions. Transient registry errors fail explicitly instead of being treated as an unpublished version. +Fix a trusted-publisher mismatch or Blacksmith access issue, then rerun the failed workflow from Actions. Check the exact owner, repository and workflow filename against npm's saved publisher. Transient registry errors fail explicitly instead of being treated as an unpublished version. If npm succeeded but GitHub release creation failed, the published version is immutable. Create the missing release against the original successful run's commit, and attach its `npm-package` artifact: From cd97e275b3f4869eda2133a120f7ffc591e9b165 Mon Sep 17 00:00:00 2001 From: "M. Adel Alhashemi" Date: Tue, 15 Sep 2026 15:35:16 +0300 Subject: [PATCH 2/2] ci: enable GitHub-hosted releases for personal repository --- .github/actionlint.yaml | 2 -- .github/workflows/ci.yml | 4 ++-- README.md | 4 ++-- docs/releasing.md | 8 ++++---- package.json | 11 +++++++++-- 5 files changed, 17 insertions(+), 12 deletions(-) delete mode 100644 .github/actionlint.yaml diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml deleted file mode 100644 index f6ca2b3..0000000 --- a/.github/actionlint.yaml +++ /dev/null @@ -1,2 +0,0 @@ -self-hosted-runner: - labels: [blacksmith-4vcpu-ubuntu-2404] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ef7c46..90ed748 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ concurrency: jobs: check: name: Check and package - runs-on: blacksmith-4vcpu-ubuntu-2404 + runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 @@ -44,7 +44,7 @@ jobs: needs: check if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request' # npm trusted publishing requires a GitHub-hosted runner. - # All dependency installation, builds and tests stay on Blacksmith above. + # Publish only the archive produced and tested by the check job. runs-on: ubuntu-latest timeout-minutes: 10 permissions: diff --git a/README.md b/README.md index da5a91f..2cd9be9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@

npm version - CI and release on Blacksmith + CI and release MIT license Bun 1.3.14 or newer

@@ -302,7 +302,7 @@ The interface follows the Agent964 design system and the Balanced playground dir Bug reports and focused PRs are welcome. See [CONTRIBUTING.md](https://github.com/malhashemi/workflow-observer/blob/main/CONTRIBUTING.md) for local setup and checks. Run `bun run demo` for an isolated workspace with synthetic workflows. -Builds and tests use **Blacksmith runners**. A small GitHub-hosted job publishes through **npm trusted publishing**, with provenance and no stored npm token. To release, run `bun run release:prepare patch` on your PR branch and commit the version changes. Merging to `main` runs checks, publishes the tested npm archive and creates a GitHub release. Merges without a new version skip publishing. The [release guide](https://github.com/malhashemi/workflow-observer/blob/main/docs/releasing.md) covers setup and retries. +CI runs on **GitHub-hosted runners** and publishes through **npm trusted publishing**, with provenance and no stored npm token. To release, run `bun run release:prepare patch` on your PR branch and commit the version changes. Merging to `main` runs checks, publishes the tested npm archive and creates a GitHub release. Merges without a new version skip publishing. The [release guide](https://github.com/malhashemi/workflow-observer/blob/main/docs/releasing.md) covers setup and retries. ## License diff --git a/docs/releasing.md b/docs/releasing.md index 412f57a..f0b68c0 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -1,6 +1,6 @@ # Releasing -Builds and tests run on Blacksmith. A push to `main`, including a merged PR, builds the app, runs TypeScript/OXC checks and tests, and exercises the packed CLI with isolated data. A small GitHub-hosted release job publishes that tested archive through npm trusted publishing, then creates a matching GitHub tag and release. A version already on npm is skipped. No npm token is stored in GitHub. +CI runs on GitHub-hosted runners. A push to `main`, including a merged PR, builds the app, runs TypeScript/OXC checks and tests, and exercises the packed CLI with isolated data. A separate release job publishes that tested archive through npm trusted publishing, then creates a matching GitHub tag and release. A version already on npm is skipped. No npm token is stored in GitHub. ## Release a change @@ -18,11 +18,11 @@ Version preparation only changes package metadata and the lockfile. It does not ## One-time setup -1. Enable the Blacksmith GitHub App for this repository. **Check and package** uses `blacksmith-4vcpu-ubuntu-2404`. +1. Enable GitHub Actions for this repository. Both jobs use `ubuntu-latest`. 2. In [the npm package settings](https://www.npmjs.com/package/workflow-observer/access), add a **GitHub Actions** trusted publisher with owner **malhashemi**, repository **workflow-observer**, and workflow filename **ci.yml**. Leave the environment name empty and allow direct publishing. The package must already exist; use the manual bootstrap below for a new package. 3. Require **Check and package** before merging into `main` in the repository's branch rules. -[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) currently supports GitHub-hosted runners, not self-hosted runners. Only the publishing job uses `ubuntu-latest`, with `id-token: write` and Node 24. npm exchanges the workflow's short-lived OIDC identity for permission to publish and attaches provenance. Builds and tests remain on Blacksmith; Observer itself runs on Bun. +[npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) currently supports GitHub-hosted runners, not self-hosted runners. The publishing job uses `id-token: write` and Node 24. npm exchanges the workflow's short-lived OIDC identity for permission to publish and attaches provenance. Observer itself runs on Bun. For the first publication, from a checked-out release commit: @@ -40,7 +40,7 @@ Use the archive for the version in `package.json`. After bootstrap, configure it ## Retry a release -Fix a trusted-publisher mismatch or Blacksmith access issue, then rerun the failed workflow from Actions. Check the exact owner, repository and workflow filename against npm's saved publisher. Transient registry errors fail explicitly instead of being treated as an unpublished version. +Fix the reported failure, then rerun the workflow from Actions. For authentication failures, check the exact owner, repository and workflow filename against npm's saved publisher. Transient registry errors fail explicitly instead of being treated as an unpublished version. If npm succeeded but GitHub release creation failed, the published version is immutable. Create the missing release against the original successful run's commit, and attach its `npm-package` artifact: diff --git a/package.json b/package.json index fca8089..2d5812b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "workflow-observer", - "version": "0.9.4", + "version": "0.9.5", "private": false, "type": "module", "scripts": { @@ -47,7 +47,14 @@ "bugs": { "url": "https://github.com/malhashemi/workflow-observer/issues" }, - "keywords": ["claude", "workflow", "agents", "observer", "local", "bun"], + "keywords": [ + "claude", + "workflow", + "agents", + "observer", + "local", + "bun" + ], "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/"