From ccf2b46482f58bb4fee29f6e201e43fa392c6834 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Thu, 21 May 2026 21:34:33 +0200 Subject: [PATCH] ci(release): per-platform idempotent npm publish + spam-filter retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous 'napi prepublish -t npm' step was atomic — one failure killed the whole step, and on retry it tried to republish already-uploaded versions which 403'd. v0.1.2 left 3 of 4 platform packages and the wasm package on npm but the main 'wickra' package and 'wickra-win32-x64-msvc' never got out. Replace it with a small bash loop that: - Walks every npm// directory. - Skips the publish if 'npm view @' confirms the version is already on the registry (idempotent re-runs). - Tolerates per-package failures (sets rc but always returns 0 from the helper) so spam-filter blocks on one platform don't take down the others. A 30-second retry handles transient rate-limit spam blocks. - Publishes the main 'wickra' meta-package as a separate step with the same skip-existing guard. Same publishing semantics, just deconstructed into individually recoverable steps. --- .github/workflows/release.yml | 50 +++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b952364..7a34c303 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -188,18 +188,58 @@ jobs: working-directory: bindings/node run: npx napi artifacts --dir artifacts - - name: Prepublish platform packages to npm + # Publish each platform package individually so one failure doesn't kill + # the others. Skip versions that are already on npm. Tolerate spam-filter + # 403s with a one-time retry after a short delay (spam detection is + # often rate-limit-based and clears on the next request). + - name: Publish platform packages (idempotent) working-directory: bindings/node - run: npx napi prepublish -t npm --skip-gh-release env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + set +e + version=$(node -p "require('./package.json').version") + publish_dir() { + local dir=$1 + local pkg=$(basename "$dir") + local pkgname="wickra-$pkg" + local existing + existing=$(npm view "$pkgname@$version" version 2>/dev/null) + if [ "$existing" = "$version" ]; then + echo "::notice::skip $pkgname@$version (already on npm)" + return 0 + fi + echo "::group::publish $pkgname@$version" + (cd "$dir" && npm publish --access public) + local rc=$? + echo "::endgroup::" + if [ "$rc" -ne 0 ]; then + echo "::warning::first attempt of $pkgname failed (rc=$rc); retrying after 30s" + sleep 30 + (cd "$dir" && npm publish --access public) + rc=$? + fi + if [ "$rc" -ne 0 ]; then + echo "::warning::$pkgname could not be published; the main package will skip the missing optional dep" + fi + return 0 + } + for dir in npm/*/; do + publish_dir "$dir" + done - - name: Publish main package to npm + - name: Publish main package to npm (idempotent) working-directory: bindings/node - run: npm publish --access public env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: | + version=$(node -p "require('./package.json').version") + existing=$(npm view "wickra@$version" version 2>/dev/null) + if [ "$existing" = "$version" ]; then + echo "::notice::skip wickra@$version (already on npm)" + else + npm publish --access public + fi # -------------------------------------------------------------------------- # WASM: wasm-pack build + npm publish (as `wickra-wasm`)