From 2273b5892b0ddd2039dd11196c836d84cd993e9e Mon Sep 17 00:00:00 2001 From: Harshita Gupta Date: Wed, 22 Apr 2026 19:22:04 -0700 Subject: [PATCH 1/2] workflows: replace softprops/action-gh-release with gh CLI Supply-chain hardening: softprops/action-gh-release is a single-maintainer third-party action pinned to the mutable @v1 tag. Replacing it with the first-party `gh` CLI (pre-installed on GitHub-hosted runners, maintained by GitHub) removes that dependency from the release-upload path. Follow-up to #18, which migrated build-node-packages.yml. This migrates the remaining three workflows that still used the action: - build-node.yml - build-node-fibers.yml - build-node-openssl-fips.yml Each Upload step becomes: - view-or-create guard so the first matrix arm creates the release (and the second arm tolerates the race); - `gh release upload --clobber` for the asset (matches softprops's always-delete-then-upload behavior on name collision); - `gh release edit --title` to preserve softprops's behavior of always re-setting the release name on every upload. Each job also picks up `REPO: ${{ github.repository }}` in its env block for consistency with the pattern established in #18. --- .github/workflows/build-node-fibers.yml | 23 +++++++++++++---- .github/workflows/build-node-openssl-fips.yml | 25 +++++++++++++++---- .github/workflows/build-node.yml | 25 +++++++++++++++---- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml index 697163b712a288..bb460e183b9d1c 100644 --- a/.github/workflows/build-node-fibers.yml +++ b/.github/workflows/build-node-fibers.yml @@ -22,6 +22,7 @@ jobs: env: NODE_VERSION: v20.18.3 + REPO: ${{ github.repository }} steps: - name: Debug Matrix Values @@ -78,10 +79,22 @@ jobs: tar -czf "$ARCHIVE_NAME" -C "$(dirname "$FIBERS_DIR")" "$(basename "$FIBERS_DIR")" - name: Upload archive to release - uses: softprops/action-gh-release@v1 - with: - name: node-${{ env.NODE_VERSION }}-LATEST - tag_name: node-${{ env.NODE_VERSION }}-release - files: ${{ env.ARCHIVE_NAME }} + # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) + # instead of softprops/action-gh-release (one-maintainer third-party action). + # The view-or-create guard is race-safe under the matrix and also handles the + # workflow_dispatch case where the release may not yet exist. `--clobber` + # overwrites an existing asset with the same name, matching softprops's + # default. `gh release edit --title` preserves softprops's behavior of always + # re-setting the release name on every upload. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + TAG="node-${NODE_VERSION}-release" + RELEASE_NAME="node-${NODE_VERSION}-LATEST" + if ! gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then + gh release create "$TAG" --title "$RELEASE_NAME" --notes "" --repo "$REPO" \ + || gh release view "$TAG" --repo "$REPO" >/dev/null + fi + gh release upload "$TAG" "$ARCHIVE_NAME" --clobber --repo "$REPO" + gh release edit "$TAG" --title "$RELEASE_NAME" --repo "$REPO" diff --git a/.github/workflows/build-node-openssl-fips.yml b/.github/workflows/build-node-openssl-fips.yml index 524f20d42cb787..e0e1220695db48 100644 --- a/.github/workflows/build-node-openssl-fips.yml +++ b/.github/workflows/build-node-openssl-fips.yml @@ -46,6 +46,7 @@ jobs: env: S3_BUCKET: your-bucket-name AWS_REGION: us-east-1 + REPO: ${{ github.repository }} steps: - name: Checkout Node fork @@ -148,10 +149,24 @@ jobs: path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} - name: Upload Node archive to release - uses: softprops/action-gh-release@v1 - with: - name: node-${{ env.NODE_VERSION }}-fips-static-LATEST - tag_name: node-${{ env.NODE_VERSION }}-fips-static-release - files: ./artifacts/${{ env.NODE_ARCHIVE_LATEST }} + # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) + # instead of softprops/action-gh-release (one-maintainer third-party action). + # The view-or-create guard is race-safe under the matrix: if the sibling + # job creates the release first, `gh release create` fails and the second + # `gh release view` confirms the release now exists. `--clobber` overwrites + # an existing asset with the same name, matching softprops's default. The + # final `gh release edit --title` preserves softprops's behavior of always + # re-setting the release name on every upload. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + TAG="node-${NODE_VERSION}-fips-static-release" + RELEASE_NAME="node-${NODE_VERSION}-fips-static-LATEST" + FILE="./artifacts/${NODE_ARCHIVE_LATEST}" + if ! gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then + gh release create "$TAG" --title "$RELEASE_NAME" --notes "" --repo "$REPO" \ + || gh release view "$TAG" --repo "$REPO" >/dev/null + fi + gh release upload "$TAG" "$FILE" --clobber --repo "$REPO" + gh release edit "$TAG" --title "$RELEASE_NAME" --repo "$REPO" diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml index b74f4af4973deb..c2d9df7842756c 100644 --- a/.github/workflows/build-node.yml +++ b/.github/workflows/build-node.yml @@ -26,6 +26,7 @@ jobs: env: S3_BUCKET: your-bucket-name AWS_REGION: us-east-1 + REPO: ${{ github.repository }} steps: - name: Checkout Node fork @@ -97,10 +98,24 @@ jobs: path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} - name: Upload Node archive to release - uses: softprops/action-gh-release@v1 - with: - name: node-${{ env.NODE_VERSION }}-LATEST - tag_name: node-${{ env.NODE_VERSION }}-release - files: ./artifacts/${{ env.NODE_ARCHIVE_LATEST }} + # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) + # instead of softprops/action-gh-release (one-maintainer third-party action). + # The view-or-create guard is race-safe under the matrix: if the sibling + # job creates the release first, `gh release create` fails and the second + # `gh release view` confirms the release now exists. `--clobber` overwrites + # an existing asset with the same name, matching softprops's default. The + # final `gh release edit --title` preserves softprops's behavior of always + # re-setting the release name on every upload. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + TAG="node-${NODE_VERSION}-release" + RELEASE_NAME="node-${NODE_VERSION}-LATEST" + FILE="./artifacts/${NODE_ARCHIVE_LATEST}" + if ! gh release view "$TAG" --repo "$REPO" >/dev/null 2>&1; then + gh release create "$TAG" --title "$RELEASE_NAME" --notes "" --repo "$REPO" \ + || gh release view "$TAG" --repo "$REPO" >/dev/null + fi + gh release upload "$TAG" "$FILE" --clobber --repo "$REPO" + gh release edit "$TAG" --title "$RELEASE_NAME" --repo "$REPO" From ffae491783e34c77fec1eed95fc2cb75c9171a1b Mon Sep 17 00:00:00 2001 From: Harshita Gupta Date: Wed, 22 Apr 2026 19:33:39 -0700 Subject: [PATCH 2/2] workflows: rewrite upload-step comments to describe current impl The previous comment blocks were framed as migration notes ("instead of softprops..."). Rewrite them as forward-looking descriptions of what each step does and the decisions/pitfalls that matter to a future maintainer: matrix-race behavior, --clobber tradeoff, deterministic-title reasoning. --- .github/workflows/build-node-fibers.yml | 25 ++++++++++++----- .github/workflows/build-node-openssl-fips.yml | 27 ++++++++++++------ .github/workflows/build-node.yml | 28 +++++++++++++------ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml index bb460e183b9d1c..de6db6cd75e46a 100644 --- a/.github/workflows/build-node-fibers.yml +++ b/.github/workflows/build-node-fibers.yml @@ -79,13 +79,24 @@ jobs: tar -czf "$ARCHIVE_NAME" -C "$(dirname "$FIBERS_DIR")" "$(basename "$FIBERS_DIR")" - name: Upload archive to release - # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) - # instead of softprops/action-gh-release (one-maintainer third-party action). - # The view-or-create guard is race-safe under the matrix and also handles the - # workflow_dispatch case where the release may not yet exist. `--clobber` - # overwrites an existing asset with the same name, matching softprops's - # default. `gh release edit --title` preserves softprops's behavior of always - # re-setting the release name on every upload. + # Attach the built node-fibers archive to the same node-${NODE_VERSION}-release + # release that hosts the corresponding Node binaries. + # + # Normally this workflow runs after Build Node via workflow_run, so the + # release already exists by the time we get here. But workflow_dispatch can + # fire it independently (release may not yet exist), and the matrix still + # runs both arms (linux-x64, linux-arm64) in parallel — so both arms can + # race to create the release. The view-or-create guard with the trailing + # `|| gh release view` tolerates "already exists" from a losing racer and + # propagates any other create failure through the final view. + # + # `--clobber` makes re-uploads idempotent on asset name collisions by + # deleting the existing asset before uploading. If the new upload fails + # after the delete, just re-run the job to rebuild. + # + # `gh release edit --title` after upload keeps the release title + # deterministically derived from NODE_VERSION, overriding any manual rename + # in the GitHub UI. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.github/workflows/build-node-openssl-fips.yml b/.github/workflows/build-node-openssl-fips.yml index e0e1220695db48..64ee386bf9b844 100644 --- a/.github/workflows/build-node-openssl-fips.yml +++ b/.github/workflows/build-node-openssl-fips.yml @@ -149,14 +149,25 @@ jobs: path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} - name: Upload Node archive to release - # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) - # instead of softprops/action-gh-release (one-maintainer third-party action). - # The view-or-create guard is race-safe under the matrix: if the sibling - # job creates the release first, `gh release create` fails and the second - # `gh release view` confirms the release now exists. `--clobber` overwrites - # an existing asset with the same name, matching softprops's default. The - # final `gh release edit --title` preserves softprops's behavior of always - # re-setting the release name on every upload. + # Publish the FIPS-static Node build to its own release tag + # (node-${NODE_VERSION}-fips-static-release), separate from the default + # Node release so consumers can select the FIPS variant explicitly rather + # than relying on filename conventions inside a shared release. + # + # This workflow is workflow_dispatch-only, and the matrix has two arms + # (linux-x64, linux-arm64) running in parallel — on the first invocation + # for a new NODE_VERSION both arms race to create the release. The + # view-or-create guard with the trailing `|| gh release view` tolerates + # "already exists" from the losing racer and propagates any other create + # failure through the final view. + # + # `--clobber` makes re-uploads idempotent on asset name collisions by + # deleting the existing asset before uploading. If the new upload fails + # after the delete, just re-run the job. + # + # `gh release edit --title` after upload keeps the release title + # deterministically derived from NODE_VERSION, overriding any manual + # rename in the GitHub UI. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml index c2d9df7842756c..99dd3209f450f9 100644 --- a/.github/workflows/build-node.yml +++ b/.github/workflows/build-node.yml @@ -98,14 +98,26 @@ jobs: path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} - name: Upload Node archive to release - # Use `gh release upload` (first-party GitHub CLI, pre-installed on runners) - # instead of softprops/action-gh-release (one-maintainer third-party action). - # The view-or-create guard is race-safe under the matrix: if the sibling - # job creates the release first, `gh release create` fails and the second - # `gh release view` confirms the release now exists. `--clobber` overwrites - # an existing asset with the same name, matching softprops's default. The - # final `gh release edit --title` preserves softprops's behavior of always - # re-setting the release name on every upload. + # Publish the built Node archive to the node-${NODE_VERSION}-release release. + # This is the first uploader in the pipeline; build-node-packages and + # build-node-fibers attach additional artifacts to the same release later. + # + # Matrix concurrency: both matrix arms (linux-x64, linux-arm64) hit this step + # in parallel. On the first run for a new NODE_VERSION the release does not + # yet exist, so both arms race to create it. The view-or-create guard with + # the trailing `|| gh release view` tolerates "already exists" from the + # losing arm (as long as the release now exists); any other create failure + # propagates through that second view. + # + # `--clobber` makes re-uploads idempotent on asset name collisions: an + # existing asset with the same name is deleted before the new one uploads. + # Acceptable here because the -LATEST assets are always reproducible from + # the build inputs; if the upload fails after the delete, just re-run the + # job. + # + # `gh release edit --title` after upload keeps the release title + # deterministically derived from NODE_VERSION even if someone renames the + # release in the GitHub UI. env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: |