Skip to content

workflows: replace softprops/action-gh-release with gh CLI (main)#20

Open
harshita-gupta wants to merge 2 commits intomainfrom
harshitagupta/migrate-gh-release-upload-main
Open

workflows: replace softprops/action-gh-release with gh CLI (main)#20
harshita-gupta wants to merge 2 commits intomainfrom
harshitagupta/migrate-gh-release-upload-main

Conversation

@harshita-gupta
Copy link
Copy Markdown
Member

Summary

Supply-chain hardening: softprops/action-gh-release is a single-maintainer third-party action pinned to the mutable @v1 tag. If that account is compromised or the tag is re-pointed, every workflow depending on it runs whatever the new code does. 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.

This is a follow-up to #18, which migrated build-node-packages.yml. It migrates the remaining three workflows that still used softprops/action-gh-release@v1 on main:

  • .github/workflows/build-node.yml
  • .github/workflows/build-node-fibers.yml
  • .github/workflows/build-node-openssl-fips.yml

After this PR: zero references to third-party release-upload actions on main.

This is the canary PR. If it merges and workflow validation succeeds, I'll open equivalent PRs against v22.21.1, v22.21.1-profiler, v24.13.0, and v20.18.3 (which still use softprops).

Replacement shape

Each softprops step becomes (shell-quoted, set -euo pipefail):

- name: Upload <...> archive to release
  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"

Each job also picks up REPO: ${{ github.repository }} in its job-level env:, matching the pattern from #18.

Behavior deltas vs. softprops/action-gh-release@v1

Based on reading the source of both tools — softprops at the v1 tag SHA (de2c0eb8, src/main.ts + src/github.ts) and gh CLI at the installed v2.90.0 (pkg/cmd/release/{upload,create}/*.go).

# delta class notes
1 softprops creates-if-missing in one call; gh release upload requires release to exist replaceable added view || create preamble
2 softprops has internal 3× retry on create (comment says "presume a race with competing matrix runs"); gh release create has no retry replaceable create || view idiom: the loser of the race falls through via view
3 softprops always deletes-then-uploads on same-name conflict; gh release upload default errors, --clobber matches softprops no-op we pass --clobber
4 softprops always sets name on upload (via updateRelease); gh release upload doesn't touch name preserved via gh release edit --title after upload
5 softprops updateRelease also resets body/draft/prerelease to existing values; gh release edit without those flags is a no-op for them no-op neither approach modifies body; both leave draft/prerelease false
6 softprops glob-expands files:; gh release upload also glob-expands via GlobPaths no-op all call-sites pass a single explicit filename
7 softprops logs warning on missing files (opt-in to fail); gh release upload fails on missing literal path via os.Stat stricter, beneficial no action required
8 tag auto-creation: both create lightweight tag from default-branch HEAD when missing no-op identical
9 retries on upload: softprops has Octokit throttling plugin (1 retry on rate-limit); gh release upload retries 3× on 5xx or network error no-op gh is slightly more robust
10 step outputs url, id, upload_url, assets are not set by run: blocks no-op no consumers of these outputs anywhere in the repo
11 auth: softprops uses GITHUB_TOKEN; gh accepts either GH_TOKEN or GITHUB_TOKEN no-op we keep GITHUB_TOKEN for consistency with #18

Test plan

  • Merge this PR.
  • Manually trigger Build Node via workflow_dispatch on main. Verify the asset appears in the node-${NODE_VERSION}-release release and that the release title is node-${NODE_VERSION}-LATEST.
  • Verify the downstream Build Node-Packages (already on gh CLI from workflows: swap softprops for gh CLI, add CloudFront reachability check, remove --acl #18) succeeds end-to-end.
  • Manually trigger Build node-fibers with prebuilt Node via workflow_dispatch. Verify the fibers archive appears in the same release.
  • Manually trigger Build Node with options around OpenSSL dynamic linking and FIPS via workflow_dispatch (needs a BUILD_REF input). Verify the asset appears in node-${NODE_VERSION}-fips-static-release with title node-${NODE_VERSION}-fips-static-LATEST.
  • Byte-identity check: download one asset uploaded pre-merge via softprops and one uploaded post-merge via gh CLI; confirm sha256sum matches.
  • Concurrency smoke: the matrix runs linux-x64 and linux-arm64 in parallel — confirm no step fails when both try to create the release at the same time.

Out of scope

  • v22.21.1, v22.21.1-profiler, v24.13.0, v20.18.3 branches still use softprops. Equivalent PRs will follow once this canary validates.
  • build-node-packages.yml is not touched here — workflows: swap softprops for gh CLI, add CloudFront reachability check, remove --acl #18 already migrated it (with a simpler pattern that omits the gh release edit --title postamble, because it's not the first uploader and the title is set by this PR's build-node.yml step running earlier in the pipeline).

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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants