From 357fa6540bb910111a815bcad382203d190dab25 Mon Sep 17 00:00:00 2001 From: forrestlinfeng Date: Thu, 24 Sep 2026 22:29:21 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=8F=91=E5=B8=83=E6=94=B9=E7=94=B1=20ma?= =?UTF-8?q?in=20=E4=B8=8A=E7=9A=84=E7=89=88=E6=9C=AC=E5=8F=B7=E5=8F=98?= =?UTF-8?q?=E5=8C=96=E8=A7=A6=E5=8F=91=EF=BC=8C=E8=80=8C=E4=B8=8D=E6=98=AF?= =?UTF-8?q?=E6=89=93=20tag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原来要先打一个 v* tag 才发布,而 tag 可以被移动,这个 job 又持有 npm token。改成 push 到 main 时比对 package.json 的版本是否已在 npm 上: 不在就发,在就跳过。发布意图因此等同于"改版本号"这一件事,不需要再 记得补一个 tag。 token 收进 npm-publish 这个 environment,与本仓库其他 workflow 隔离, 并写进一个临时的 userconfig 而不是全局 npmrc。provenance 关掉——npm 拒绝来自私有源仓库的 provenance。发布后带重试地回查一次,确认包真的 能从 registry 解析出来,而不是只看 publish 的退出码。 并发组 npm-publish 且不取消进行中的任务:两次连续推送不会让第二次把 第一次的发布腰斩。 Co-Authored-By: Claude Opus 5 --- .github/workflows/publish.yml | 74 +++++++++++++++++++++++------------ README.md | 7 ++-- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 65f71bf..94d9f64 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,22 +1,28 @@ name: Publish -# A tag push publishes. A manual run does everything except publish, so the -# release can be rehearsed from any branch. +# A push to main publishes package.json's version to registry.npmjs.org when +# that version is not on npm yet; bumping the version is the release intent. +# A manual run does everything except publish, so the release can be +# rehearsed from any branch. on: push: - tags: ['v*'] + branches: [main] workflow_dispatch: permissions: contents: read +concurrency: + group: npm-publish + cancel-in-progress: false + jobs: publish: runs-on: ubuntu-latest - permissions: - contents: read - # Required for npm provenance; without it `npm publish --provenance` fails. - id-token: write + # The environment isolates NPM_TOKEN from every other workflow in this repo. + environment: + name: npm-publish + url: https://www.npmjs.com/package/@evomap/dsh-evolver steps: # Pinned to a commit: a tag can be moved, and this job holds the npm token. - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 @@ -24,35 +30,51 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 22 - registry-url: https://registry.npmjs.org - run: npm ci --ignore-scripts --no-audit --no-fund - run: npm test - - name: Check the tag matches the manifest version - if: startsWith(github.ref, 'refs/tags/v') - run: | - tag="${GITHUB_REF_NAME#v}" - manifest="$(node -p "require('./package.json').version")" - if [ "$tag" != "$manifest" ]; then - echo "tag v$tag does not match package.json $manifest" >&2 - exit 1 - fi - - - name: Check this version is not already on npm + - name: Decide whether this version still needs publishing + id: release run: | name="$(node -p "require('./package.json').name")" version="$(node -p "require('./package.json').version")" - if npm view "$name@$version" version >/dev/null 2>&1; then - echo "$name@$version is already published; bump the version" >&2 - exit 1 + if npm view "$name@$version" version --registry https://registry.npmjs.org >/dev/null 2>&1; then + echo "$name@$version is already on npm; nothing to publish" + echo "publish=false" >> "$GITHUB_OUTPUT" + else + echo "publish=true" >> "$GITHUB_OUTPUT" fi + echo "version=$version" >> "$GITHUB_OUTPUT" - run: npm pack --dry-run - - name: Publish - if: startsWith(github.ref, 'refs/tags/v') - run: npm publish --provenance --access public + # Provenance is off because npm rejects provenance from a private source repository. + - name: Publish to npm + if: github.event_name == 'push' && steps.release.outputs.publish == 'true' + run: | + : "${NPM_TOKEN:?NPM_TOKEN is not set on the npm-publish environment}" + npm_config="$(mktemp)" + trap 'rm -f "$npm_config"' EXIT + chmod 600 "$npm_config" + printf '//registry.npmjs.org/:_authToken=%s\n' "$NPM_TOKEN" > "$npm_config" + NPM_CONFIG_USERCONFIG="$npm_config" NPM_CONFIG_PROVENANCE=false \ + npm publish --access public --registry https://registry.npmjs.org + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Verify the package resolves from npm + if: github.event_name == 'push' && steps.release.outputs.publish == 'true' + run: | + name="$(node -p "require('./package.json').name")" + for attempt in 1 2 3 4 5 6; do + if npm view "$name@$VERSION" version --registry https://registry.npmjs.org; then + exit 0 + fi + sleep 10 + done + echo "$name@$VERSION did not appear on npm after publishing" >&2 + exit 1 env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + VERSION: ${{ steps.release.outputs.version }} diff --git a/README.md b/README.md index b976627..7cc9b6b 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ Powered by the [Genome Evolution Protocol](https://evomap.ai) and [`@evomap/evolver`](https://github.com/EvoMap/evolver). > **Status:** pre-release `0.1.0`. Local memory, commands, Skill, and the current -> Proxy-backed asset loop are implemented. The npm package is published only when the -> first matching release tag is created. +> Proxy-backed asset loop are implemented. The npm package is published when a +> `package.json` version that is not yet on npm lands on `main`. ## What it does @@ -174,7 +174,8 @@ npm pack --dry-run ``` CI validates Node 22 and 24 plus the declared DSH compatibility lines. The release workflow -publishes only a tag whose version matches `package.json`; this task does not create that tag. +publishes to npm on every push to `main` whose `package.json` version is not on npm yet, using +the `NPM_TOKEN` secret of the `npm-publish` environment; a manual run rehearses without publishing. ## License