From df3dd277e627ce652956ccceb673767096bc37c9 Mon Sep 17 00:00:00 2001 From: Mikola Lysenko Date: Wed, 27 May 2026 13:59:18 -0400 Subject: [PATCH] ci(release): adopt npm staged publishing via OIDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switches the npm-publish job to `npm stage publish` so the GitHub Actions workflow uploads tarballs to npm's staging queue. A maintainer then approves each staged version with 2FA from npmjs.com or the npm CLI before it becomes installable. The previous direct-publish flow was failing with `OIDC permission denied for this action` against npm's post-2026-05-20 trusted-publisher rules, which require the allowed-action checkbox(es) to be explicit. Each of the 15 packages needs a trusted publisher configured on npmjs.com (SocketDev/socket-patch → release.yml) with both `npm publish` and `npm stage publish` allowed; the workflow uses stage-publish for every release. Workflow changes: - Bump actions/setup-node to v6.4.0 + package-manager-cache: false (eliminates the always-auth deprecation warning emitted by v4). - Replace `npm publish --provenance --access public` with `npm stage publish --access public`. OIDC trusted publishing emits provenance automatically, so --provenance is now redundant. - Collect successfully staged package names and write a step summary with org-dashboard and per-package review links, plus a ::notice:: pointing at the staged-packages dashboard, so maintainers can click straight from the workflow run page into the approval UI. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 74 ++++++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0239081..80079bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -279,12 +279,13 @@ jobs: merge-multiple: true - name: Setup Node.js - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: '22.22.1' registry-url: 'https://registry.npmjs.org' + package-manager-cache: false - - name: Update npm for trusted publishing + - name: Update npm for staged publishing run: npm install -g npm@11.15.0 - name: Stage binaries into platform packages @@ -317,34 +318,79 @@ jobs: stage_win socket-patch-i686-pc-windows-msvc npm/socket-patch-win32-ia32 stage_win socket-patch-aarch64-pc-windows-msvc npm/socket-patch-win32-arm64 - - name: Publish platform packages + - name: Stage-publish platform packages + id: stage-platform run: | + : > "${RUNNER_TEMP}/staged-packages.txt" for pkg_dir in npm/socket-patch-*/; do - echo "Publishing ${pkg_dir}..." - npm publish "./${pkg_dir}" --provenance --access public || { - if npm view "@socketsecurity/$(basename "$pkg_dir")@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then + pkg_name="@socketsecurity/$(basename "$pkg_dir")" + echo "Staging ${pkg_name}..." + if npm stage publish "./${pkg_dir}" --access public; then + echo "$pkg_name" >> "${RUNNER_TEMP}/staged-packages.txt" + else + if npm view "${pkg_name}@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then echo "Already published, skipping." else exit 1 fi - } + fi done - - name: Wait for npm registry propagation - run: sleep 30 - - name: Copy README for npm package run: cp README.md npm/socket-patch/README.md - - name: Publish main package + - name: Stage-publish main package run: | - npm publish ./npm/socket-patch --provenance --access public || { - if npm view "@socketsecurity/socket-patch@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then + pkg_name="@socketsecurity/socket-patch" + if npm stage publish ./npm/socket-patch --access public; then + echo "$pkg_name" >> "${RUNNER_TEMP}/staged-packages.txt" + else + if npm view "${pkg_name}@${{ needs.version.outputs.version }}" version >/dev/null 2>&1; then echo "Already published, skipping." else exit 1 fi - } + fi + + - name: Summarize staged versions awaiting approval + if: always() + run: | + STAGED_FILE="${RUNNER_TEMP}/staged-packages.txt" + if [ ! -s "$STAGED_FILE" ]; then + echo "No packages staged this run (all versions already published or publish step failed before staging)." >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + VERSION="${{ needs.version.outputs.version }}" + { + echo "## npm staged versions awaiting approval" + echo "" + echo "Version \`${VERSION}\` is staged on npm. A maintainer must approve each package with 2FA before it becomes installable." + echo "" + echo "**Approve platform packages first**, then the main \`@socketsecurity/socket-patch\` package, so install-time \`optionalDependencies\` resolution sees the platform binaries already live." + echo "" + echo "**Approve from the web** (signs in + 2FA prompts inline):" + echo "" + echo "- Org dashboard: " + echo "" + echo "Per-package review pages:" + echo "" + while IFS= read -r pkg_name; do + [ -z "$pkg_name" ] && continue + # npmjs.com renders staged versions inline on the package page; + # the access page exposes the "Staged" tab and the approve button. + echo "- \`${pkg_name}@${VERSION}\` — " + done < "$STAGED_FILE" + echo "" + echo "**Approve from the CLI** (requires \`npm@11.15.0+\` locally, signed in to the \`socketsecurity\` org with 2FA):" + echo "" + echo '```sh' + echo "npm stage list" + echo "# then, for each stage id printed above (platform packages first, main package last):" + echo "npm stage approve " + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + # Mirror to step log so it's also visible in the raw run output. + echo "::notice title=npm staged versions awaiting approval::Review and approve at https://www.npmjs.com/settings/socketsecurity/staged-packages" pypi-publish: needs: [version, build, tag]