Skip to content

ci: harden npm release publishing#1918

Merged
liangshuo-1 merged 18 commits into
mainfrom
ci/npm-secure-publishing
Jul 22, 2026
Merged

ci: harden npm release publishing#1918
liangshuo-1 merged 18 commits into
mainfrom
ci/npm-secure-publishing

Conversation

@Tantanz20020918

@Tantanz20020918 Tantanz20020918 commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Migrate npm releases from a long-lived token to trusted publishing, and add fail-closed validation before release side effects.

Changes

  • Add package, lockfile, tag, and latest-main preflight checks
  • Publish through Node 22, pinned npm, OIDC, and the protected npm-production environment
  • Verify run-scoped release assets, checksums, and npm package integrity
  • Fail installation when checksums are missing or invalid
  • Add release workflow and negative-path regression tests

Test Plan

  • make unit-test passed
  • make script-test passed (184/184)
  • validate passed
  • local-eval passed (E2E 0/0; no CLI behavior changes, skillave N/A)
  • acceptance-reviewer passed (4/4 release scenarios, 58/58 assertions)
  • go vet ./..., gofmt -l ., go mod tidy, and incremental lint passed
  • manual verification: node scripts/release-preflight.js, bash -n scripts/tag-release.sh, and git diff --check

Related Issues

N/A

Summary by CodeRabbit

  • Security

    • Installer checksum verification now fails when checksum data is missing, malformed, or invalid instead of allowing installation to continue.
  • Release Process

    • Releases now validate version metadata, tag consistency, and repository state before publishing.
    • Tag creation requires a clean, up-to-date main branch and rejects existing local or remote tags.
    • Publishing now includes automated preflight checks and publishes the package after a successful build.
  • Testing

    • Expanded automated coverage for installation security, release validation, tagging, and publishing workflows.

@Tantanz20020918 Tantanz20020918 added size/L Large or sensitive change across domains or core paths domain/security Security: validation, injection prevention, vulnerability mitigation. labels Jul 16, 2026
@github-actions github-actions Bot added size/XL Architecture-level or global-impact change and removed domain/security Security: validation, injection prevention, vulnerability mitigation. size/L Large or sensitive change across domains or core paths labels Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The release pipeline adds stable version and tag preflight validation, stricter tag creation gates, a preflight-gated publishing job, expanded workflow tests, and fail-closed installer checksum verification.

Changes

Release integrity and publishing

Layer / File(s) Summary
Release metadata and tag gates
package.json, scripts/release-preflight.js, scripts/tag-release.sh, scripts/release-preflight.test.js, Makefile
Release versions, lockfile versions, and optional tags must match stable X.Y.Z values; tagging now requires main, clean metadata, synchronized HEAD, and absent local and remote tags. Related tests and the script-test target are expanded.
Preflight-gated release publishing
.github/workflows/release.yml, scripts/release-workflow.test.js, .github/workflows/pkg-pr-new.yml
Tag pushes run a read-only preflight before publish-release, which builds with GoReleaser, verifies and copies checksums, and publishes npm with write permissions; workflow contract and preview-isolation tests cover these rules.
Fail-closed installer checksums
scripts/install.js, scripts/install.test.js
Missing checksum files and missing or malformed SHA-256 values now raise security errors, with expanded validation tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Suggested labels: feature

Suggested reviewers: liangshuo-1

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: hardened npm release publishing in CI.
Description check ✅ Passed The description follows the template well, with Summary, Changes, Test Plan, and Related Issues all present.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/npm-secure-publishing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.05%. Comparing base (7b98994) to head (2ad67dd).
⚠️ Report is 17 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1918      +/-   ##
==========================================
+ Coverage   74.98%   75.05%   +0.06%     
==========================================
  Files         895      901       +6     
  Lines       94379    95391    +1012     
==========================================
+ Hits        70767    71591     +824     
- Misses      18197    18307     +110     
- Partials     5415     5493      +78     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@4cab173f8b3d2ca49cbad521cea7bbea42c7437f

🧩 Skill update

npx skills add larksuite/cli#ci/npm-secure-publishing -y -g

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

PR Quality Summary

CI did not complete successfully. Use the failed check links below to decide whether this PR needs a code change or a rerun.

Failed checks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
scripts/release-preflight.test.js (1)

326-355: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise each release gate behaviorally, not only through source-text ordering.

Add subprocess failure cases for a non-main branch, staged/unstaged metadata, and HEAD != FETCH_HEAD, asserting that no tag query, creation, or push occurs. The fake diff command will need configurable exit statuses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-preflight.test.js` around lines 326 - 355, Extend the release
preflight tests around the existing tag-release subprocess harness to execute
failure cases for a non-main branch, staged or unstaged package metadata, and
HEAD differing from FETCH_HEAD. Make the fake diff command support configurable
exit statuses, assert each gate rejects the release, and verify no tag query,
creation, or push command is reached in every case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 78-83: Update the release artifact naming in the upload and
corresponding download steps to use only github.run_id, removing
github.run_attempt so reruns reference the same artifact. Apply this
consistently to the artifact references near the upload/download steps at the
indicated locations, and add overwrite: true to the upload configuration if the
producing job can rerun.

In `@scripts/release-preflight.js`:
- Line 8: Update the version validation around STABLE_VERSION_PATTERN and the
release:check flow to reject any major, minor, or patch component exceeding
Number.MAX_SAFE_INTEGER, matching node-semver behavior. Apply the same
semver-compatible bound check to package fields and the generated --tag vX.Y.Z
value while preserving acceptance of valid stable versions.

In `@scripts/tag-release.sh`:
- Line 15: Bind all Git operations in the release script, including
CURRENT_BRANCH and the commands in the affected preflight, tagging, and push
flow, to the repository root derived from SCRIPT_DIR rather than the caller’s
working directory. Preserve existing release behavior while ensuring absolute
invocations and non-root working directories operate on the script’s checkout.
Add a regression test that invokes the script from a non-root cwd and verifies
Git operations target that repository.

---

Nitpick comments:
In `@scripts/release-preflight.test.js`:
- Around line 326-355: Extend the release preflight tests around the existing
tag-release subprocess harness to execute failure cases for a non-main branch,
staged or unstaged package metadata, and HEAD differing from FETCH_HEAD. Make
the fake diff command support configurable exit statuses, assert each gate
rejects the release, and verify no tag query, creation, or push command is
reached in every case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f45e910c-1f08-4585-b41d-4f892df52801

📥 Commits

Reviewing files that changed from the base of the PR and between 6558657 and 12b7f7a.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (11)
  • .github/workflows/release.yml
  • Makefile
  • package.json
  • scripts/install.js
  • scripts/install.test.js
  • scripts/release-preflight.js
  • scripts/release-preflight.test.js
  • scripts/release-workflow.test.js
  • scripts/tag-release.sh
  • scripts/verify-release-assets.js
  • scripts/verify-release-assets.test.js

Comment thread .github/workflows/release.yml Outdated
Comment thread scripts/release-preflight.js
Comment thread scripts/tag-release.sh

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 159-164: Update the existing release-state parsing in
.github/workflows/release.yml at lines 159-164 to validate both draft and
prerelease fields, rejecting the release when either state is true while
preserving the current error-and-exit behavior. Add a contract assertion in
scripts/release-workflow.test.js at lines 166-177 covering rejection of an
existing prerelease release.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ebb1004-d0d3-404d-8bba-cc086ce174f0

📥 Commits

Reviewing files that changed from the base of the PR and between 12b7f7a and 15e4175.

📒 Files selected for processing (2)
  • .github/workflows/release.yml
  • scripts/release-workflow.test.js

Comment thread .github/workflows/release.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
scripts/verify-release-assets.js (1)

86-92: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify array operations.

archiveEntries is only used to generate archiveNames, and sorting is performed twice. You can chain these operations and eliminate the redundant intermediate sort.

♻️ Proposed refactor
-  const archiveEntries = entries
-    .filter((entry) => entry.isFile() && isReleaseArchive(entry.name))
-    .sort((left, right) => left.name.localeCompare(right.name));
-
-  const archiveNames = archiveEntries
-    .map((entry) => entry.name)
-    .sort();
+  const archiveNames = entries
+    .filter((entry) => entry.isFile() && isReleaseArchive(entry.name))
+    .map((entry) => entry.name)
+    .sort();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify-release-assets.js` around lines 86 - 92, In the archive-name
construction flow, remove the intermediate archiveEntries sort and chain the
file filtering, name mapping, and final sorting directly into archiveNames.
Preserve the existing file and release-archive filters and the alphabetical
ordering of the resulting names.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@scripts/verify-release-assets.js`:
- Around line 86-92: In the archive-name construction flow, remove the
intermediate archiveEntries sort and chain the file filtering, name mapping, and
final sorting directly into archiveNames. Preserve the existing file and
release-archive filters and the alphabetical ordering of the resulting names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d62d8ab0-1689-4c9e-8823-2a359a162388

📥 Commits

Reviewing files that changed from the base of the PR and between 964c571 and ac1e09e.

📒 Files selected for processing (6)
  • .github/workflows/release.yml
  • scripts/release-preflight.js
  • scripts/release-preflight.test.js
  • scripts/release-workflow.test.js
  • scripts/verify-release-assets.js
  • scripts/verify-release-assets.test.js
💤 Files with no reviewable changes (3)
  • .github/workflows/release.yml
  • scripts/release-workflow.test.js
  • scripts/release-preflight.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/release-preflight.js

@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/XL Architecture-level or global-impact change labels Jul 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/release-preflight.js (1)

19-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify property access with optional chaining.

Since the project uses Node 22, you can use optional chaining (?.) to make these nested property accesses much more concise and easier to read.

♻️ Proposed refactor
-  const packageVersion = packageJson && packageJson.version;
-  const lockVersion = packageLockJson && packageLockJson.version;
-  const lockRootVersion = packageLockJson && packageLockJson.packages &&
-    packageLockJson.packages[""] && packageLockJson.packages[""].version;
+  const packageVersion = packageJson?.version;
+  const lockVersion = packageLockJson?.version;
+  const lockRootVersion = packageLockJson?.packages?.[""]?.version;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-preflight.js` around lines 19 - 22, Update the version
assignments for packageVersion, lockVersion, and lockRootVersion to use optional
chaining instead of repeated && checks, preserving their current undefined
behavior when any parent property is absent.
.github/workflows/release.yml (1)

21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inconsistent setup-node versions across jobs. preflight pins actions/setup-node@…# v4 while publish-release uses # v6. Both target Node 22.14.0, so behavior is equivalent, but aligning the two avoids drift and matches the contract test that expects v6.

Also applies to: 64-64

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml at line 21, Align the actions/setup-node
version in the preflight job with the v6 pin already used by publish-release,
updating the referenced commit and version comment while preserving the existing
Node 22.14.0 configuration.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/release.yml:
- Line 21: Align the actions/setup-node version in the preflight job with the v6
pin already used by publish-release, updating the referenced commit and version
comment while preserving the existing Node 22.14.0 configuration.

In `@scripts/release-preflight.js`:
- Around line 19-22: Update the version assignments for packageVersion,
lockVersion, and lockRootVersion to use optional chaining instead of repeated &&
checks, preserving their current undefined behavior when any parent property is
absent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fc0a26e-722e-4ee7-896e-7df3206cf56e

📥 Commits

Reviewing files that changed from the base of the PR and between ac1e09e and e50820b.

📒 Files selected for processing (6)
  • .github/workflows/release.yml
  • Makefile
  • scripts/release-preflight.js
  • scripts/release-preflight.test.js
  • scripts/release-workflow.test.js
  • scripts/tag-release.sh
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile

@github-actions github-actions Bot added size/M Single-domain feat or fix with limited business impact and removed size/L Large or sensitive change across domains or core paths labels Jul 21, 2026
@liangshuo-1
liangshuo-1 merged commit 4eefe32 into main Jul 22, 2026
23 checks passed
@liangshuo-1
liangshuo-1 deleted the ci/npm-secure-publishing branch July 22, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants