Skip to content

ci: publish to npm and ClawHub on merge to main#103

Merged
kaghni merged 9 commits intomainfrom
feat/cd-publish-npm-clawhub
May 7, 2026
Merged

ci: publish to npm and ClawHub on merge to main#103
kaghni merged 9 commits intomainfrom
feat/cd-publish-npm-clawhub

Conversation

@kaghni
Copy link
Copy Markdown
Collaborator

@kaghni kaghni commented May 7, 2026

Summary

  • Adds a publish job to release.yml that fires after the existing version-bump flow and publishes @deeplake/hivemind to npm and the openclaw bundle to ClawHub
  • Gated by a production GitHub Environment with required reviewer — each version still gets explicit per-publish approval (so this CD does not bypass the existing manual gate, just routes it through GitHub's UI)
  • npm publish uses --provenance --access public (Sigstore via GitHub OIDC), so even if NPM_TOKEN later leaks, consumers can verify the package was built by this workflow on this commit
  • Adds a npm pack --dry-run regression guard (scripts/pack-check.mjs, wired into both ci.yml and the publish job) that hard-fails if a PR widens the files allowlist to include .npmrc, .env, .github/, secrets/, etc.

Token-leak defenses, layered

  1. Environment-scoped secretsNPM_TOKEN and CLAWHUB_TOKEN are stored on the production environment, not the repo. PRs from forks and unprotected branches cannot read them; only the gated publish job can.
  2. Minimum-privilege permissions — workflow stays contents: read at top, only the publish job adds id-token: write (needed for OIDC provenance). No pull-requests, no broader contents: write.
  3. Ephemeral .npmrcactions/setup-node@v4 writes a temporary ~/.npmrc referencing ${NODE_AUTH_TOKEN}; passed only on the npm publish step's env:. The runner's $HOME is discarded when the job ends.
  4. Sigstore provenance — defense-in-depth even if NPM_TOKEN leaks. npm view @deeplake/hivemind --json | jq .dist.attestations will show the signature.
  5. GitHub auto-masking — anything flowing through ${{ secrets.* }} is *** in logs. We don't set -x or echo env on token-bearing steps.
  6. Concurrency guardhivemind-publish group prevents two merges from racing two publishes of the same version.
  7. Tarball regression guardscripts/pack-check.mjs runs in PR CI, so a broken files array trips on PR review, never on a release run where tokens are reachable.

Required one-time setup before merging

The repo owner must do these in GitHub UI before the first publish run reaches the gate:

  1. Settings → Environments → New environment named production
    • Required reviewers: add your account
    • (Optional) Deployment branches: restrict to main
  2. Add environment secrets to that environment:
  3. Settings → Actions → General → Workflow permissions: confirm id-token: write is allowed (some org policies disable it by default).

How it fires after merge

push to main
   ↓
release job  (existing)
   ├─ first run:  bump patch + push back ──► retriggers
   └─ bump-commit run: GitHub Release ──► publish job (new)
                                              ↓
                                       [GATE: production env approval]
                                              ↓
                                       npm publish --provenance
                                       clawhub login --token
                                       clawhub package publish ./openclaw

The publish job's if: requires startsWith(head_commit.message, 'chore: bump version'), so it runs exactly once per released version.

Test plan

  • One-time setup steps above are done before merging this PR
  • On first merge, reject the production environment gate to confirm the gate works without publishing
  • On second merge, approve the gate; verify npm view @deeplake/hivemind version matches the GitHub Release tag
  • npm view @deeplake/hivemind --json | jq .dist.attestations shows a sigstore attestation (Provenance badge on npmjs.com)
  • clawhub:hivemind is installable and verify-install.sh passes against it
  • gh run view --log <run-id> | grep -E 'npm_[A-Za-z0-9_]{20,}|CLAWHUB_[A-Za-z0-9]{20,}' returns nothing

Confidence

Confidence: 80%, because the npm side is grounded in real package.json + release.yml + standard provenance/Environments practice, and the ClawHub side is grounded in clawhub --help output (clawhub login --token + clawhub package publish <folder> are real subcommands of the published clawhub v0.12.3 package).

Untested:

  • Whether clawhub package publish ./openclaw auto-detects the package name hivemind from openclaw/openclaw.plugin.json's id field, or whether --name hivemind needs to be passed explicitly
  • Whether ClawHub has a "trusted publisher" config that would force --manual-override-reason "Automated CD" to be added — first real publish will surface this if so
  • Whether id-token: write is allowed by the org's current Actions permissions (rules 3 above), since policy may differ per repo
  • The interaction between the existing release job's git push and the publish job's checkout — current architecture splits across two workflow invocations (the bump-push retriggers), so the publish job sees the bumped commit on ref: main, but worth visually confirming on the first real run before approving the gate
  • Whether existing per-bundle E2E (verify-install.sh against installed @deeplake/hivemind) should be wired into the publish job before npm publish; currently the human reviewer at the gate is the E2E checkpoint

Summary by CodeRabbit

  • Chores
    • Enhanced CI pipeline with additional validation during pull request testing to safeguard package integrity.
    • Implemented automated publishing workflow that handles both npm and ClawHub distribution with environment-based access controls.
    • Added validation tooling to verify package contents before publishing.

Extends release.yml with a `publish` job that fires after the
existing version-bump-and-tag flow. The job is gated by a
`production` GitHub Environment so each version still requires an
explicit human approval before tokens are reachable, and is
restricted to runs whose head commit is the bump commit so the
published artifact matches the GitHub Release tag.

npm publish runs with `--provenance --access public`. The job
declares `id-token: write` so npm can sign via Sigstore using the
GitHub OIDC token — even if NPM_TOKEN later leaks, consumers can
verify the package was built by this workflow on this commit.

ClawHub publish uses `clawhub login --token` + `clawhub package
publish ./openclaw`. CLAWHUB_TOKEN is passed only as job-step env
on the relevant steps; the runner's $HOME is ephemeral, GitHub
auto-masks `secrets.*` in logs, and the step does not echo or pipe
the token anywhere.

Token-leak defenses also include:
  - environment-scoped secrets (forks/PRs cannot read them)
  - workflow `permissions: contents: read` at top-level, only
    `id-token: write` added on the publish job
  - `npm pack --dry-run` regression guard in both ci.yml and the
    publish job (scripts/pack-check.mjs) — refuses if a future PR
    widens the `files` allowlist to include `.npmrc`, `.env`,
    `.github/`, secrets/, etc.
  - concurrency group `hivemind-publish` so two simultaneous
    merges cannot race two publishes of the same version

One-time setup the repo owner must do before this lands:
  - Settings → Environments → New environment `production`
    + required reviewer
    + (optional) restrict to `main` branch
  - Add environment secrets `NPM_TOKEN` (granular, scoped to
    @deeplake/hivemind) and `CLAWHUB_TOKEN`
  - Settings → Actions → General → ensure id-token is allowed
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ac55b9f1-251b-4484-9d46-2041021dd492

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR introduces a security gate that prevents sensitive files (e.g., .npmrc, .env, .github, .git) from being included in the npm package tarball. A new scripts/pack-check.mjs script validates npm pack output against forbidden regex patterns, integrated into both CI and release workflows.

Changes

NPM Package Tarball Security Gate

Layer / File(s) Summary
Script Implementation
scripts/pack-check.mjs
New Node CLI that runs npm pack --dry-run --json, parses file list, and fails if any paths match forbidden patterns (secrets, .github, .git, etc.).
Script Wiring
package.json
Adds pack:check npm script invoking node scripts/pack-check.mjs.
CI Integration
.github/workflows/ci.yml
Adds "Pack-check" step to test job that runs npm run pack:check after bundle verification, gating the job on forbidden file detection.
Release Integration
.github/workflows/release.yml
Adds new publish job that runs pack-check before npm publishing (with provenance); also orchestrates ClawHub bundle publication and writes step summary.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A tarball so clean, no secrets inside,
The pack-check script guards with regex pride,
No .npmrc sneaks, no .env takes flight,
CI gates and release flows—all locked tight! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding npm and ClawHub publication automation to the release workflow on merge to main.
Description check ✅ Passed The description comprehensively covers all template sections: Summary clearly explains what the PR does, Version Bump section is addressed (no version bump needed as this is infrastructure), and Test plan is detailed with concrete verification steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cd-publish-npm-clawhub

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

@claude
Copy link
Copy Markdown

claude Bot commented May 7, 2026

Claude finished @kaghni's task in 3m 44s —— View job


Review

Three significant issues — left as inline comments:

  • publish job will never fire — release pushes with default GITHUB_TOKEN, which by design doesn't re-trigger workflows; so the head_commit.message bump-commit retrigger that gates publish never happens. (#discussion_r3198227512)
  • ref: main + manual gate is racy — main can advance during the production-gate pause, so approval and publish target different commits. (#discussion_r3198227771)
  • Pack-check is duplicated — inline node block re-implements scripts/pack-check.mjs; the two copies will drift. (#discussion_r3198227996)

Token-leak defenses, provenance setup, and pack-check.mjs itself look solid.
· feat/cd-publish-npm-clawhub

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 7, 2026

Coverage Report

No src/*.ts files changed in this PR.

Generated for commit 89000e9.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
scripts/pack-check.mjs (1)

17-22: 💤 Low value

Consider adding error handling for npm pack failures.

If npm pack fails (e.g., invalid package.json) or returns unexpected JSON, line 21 will throw an unhandled exception with a cryptic error. Wrapping the execution and parse in a try-catch with a clear error message would improve debuggability.

🛡️ Optional: Add error handling
-const raw = execFileSync('npm', ['pack', '--dry-run', '--json'], {
-  encoding: 'utf8',
-  stdio: ['ignore', 'pipe', 'inherit'],
-});
-const entries = JSON.parse(raw)[0].files.map((f) => f.path);
+let entries;
+try {
+  const raw = execFileSync('npm', ['pack', '--dry-run', '--json'], {
+    encoding: 'utf8',
+    stdio: ['ignore', 'pipe', 'inherit'],
+  });
+  const parsed = JSON.parse(raw);
+  if (!parsed?.[0]?.files) {
+    throw new Error('Unexpected npm pack output structure');
+  }
+  entries = parsed[0].files.map((f) => f.path);
+} catch (err) {
+  console.error('pack-check failed to run npm pack:', err.message);
+  process.exit(1);
+}
🤖 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/pack-check.mjs` around lines 17 - 22, Wrap the call to execFileSync
and the subsequent JSON.parse (the logic that produces raw and entries) in a
try-catch so failures from npm pack or malformed JSON are caught; in the catch,
surface a clear error using the same runtime logger or throw a new Error that
includes context like "npm pack --dry-run failed" and the original error
message/stack, and ensure the script exits non-zero instead of letting the
unhandled exception from JSON.parse (which happens before entries/ hits are
computed) leak; specifically update the block that defines raw, entries and hits
(references: execFileSync, raw, entries, FORBIDDEN) to perform the exec and
parse inside the try and handle/report errors cleanly.
.github/workflows/release.yml (1)

209-210: 💤 Low value

Consider pinning the clawhub CLI version.

Installing without a version (npm install -g clawhub) means future runs may use a different CLI version with potentially breaking changes. Pinning ensures reproducible publishes.

♻️ Suggested fix
       - name: Install ClawHub CLI
-        run: npm install -g clawhub
+        run: npm install -g clawhub@0
🤖 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 around lines 209 - 210, The workflow step
named "Install ClawHub CLI" currently runs `npm install -g clawhub` which can
install different CLI versions over time; change this to pin a specific version
(e.g., replace the command with `npm install -g clawhub@<VERSION>` or use an
input/environment variable like CLAWHUB_VERSION to control the exact release) so
the `Install ClawHub CLI` step always installs a reproducible, explicit CLI
version.
🤖 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 180-198: The inline Pack-check step contains buggy regexes and
duplicates logic from scripts/pack-check.mjs; replace the entire inline node
script in the "Pack-check (refuse forbidden filenames in tarball)" step with a
call to the canonical script (e.g. run "npm run pack:check" or execute "node
scripts/pack-check.mjs") so the workflow reuses the existing pack:check
implementation and avoids the regex bug and duplication.

---

Nitpick comments:
In @.github/workflows/release.yml:
- Around line 209-210: The workflow step named "Install ClawHub CLI" currently
runs `npm install -g clawhub` which can install different CLI versions over
time; change this to pin a specific version (e.g., replace the command with `npm
install -g clawhub@<VERSION>` or use an input/environment variable like
CLAWHUB_VERSION to control the exact release) so the `Install ClawHub CLI` step
always installs a reproducible, explicit CLI version.

In `@scripts/pack-check.mjs`:
- Around line 17-22: Wrap the call to execFileSync and the subsequent JSON.parse
(the logic that produces raw and entries) in a try-catch so failures from npm
pack or malformed JSON are caught; in the catch, surface a clear error using the
same runtime logger or throw a new Error that includes context like "npm pack
--dry-run failed" and the original error message/stack, and ensure the script
exits non-zero instead of letting the unhandled exception from JSON.parse (which
happens before entries/ hits are computed) leak; specifically update the block
that defines raw, entries and hits (references: execFileSync, raw, entries,
FORBIDDEN) to perform the exec and parse inside the try and handle/report errors
cleanly.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a66cf427-0f0f-45bc-b5a7-b9520417d356

📥 Commits

Reviewing files that changed from the base of the PR and between 630b75f and a436373.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • package.json
  • scripts/pack-check.mjs

Comment thread .github/workflows/release.yml Outdated
Comment on lines +180 to +198
- name: Pack-check (refuse forbidden filenames in tarball)
run: |
npm pack --dry-run --json > /tmp/pack.json
node -e "
const entries = JSON.parse(require('fs').readFileSync('/tmp/pack.json','utf8'))[0].files.map(f => f.path);
const forbidden = entries.filter(p =>
/(^|\/)\.npmrc$/.test(p) ||
/(^|\/)\.env(\$|\.)/.test(p) ||
/(^|\/)secrets?(\/|\$)/.test(p) ||
/(^|\/)\.github(\/|\$)/.test(p) ||
/(^|\/)\.git(\/|\$)/.test(p)
);
if (forbidden.length) {
console.error('Refusing to publish — forbidden filenames in tarball:');
for (const f of forbidden) console.error(' ' + f);
process.exit(1);
}
console.log('Pack-check OK — ' + entries.length + ' files, no forbidden patterns');
"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: Inline pack-check has broken regex patterns and duplicates logic.

Two issues here:

  1. Regex bug: The inline patterns use \$ (literal dollar sign) instead of $ (end-of-string anchor). For example, line 187's /(^|\/)\.env(\$|\.)/.test(p) won't match .env files correctly because \$ matches a literal $ character, not end-of-string. Compare to scripts/pack-check.mjs line 11: /(^|\/)\.env($|\.)/ which uses the correct $.

  2. Code duplication: This inline logic duplicates scripts/pack-check.mjs, creating maintenance risk. If forbidden patterns are updated in the script, this inline version will diverge.

Simply call the existing script:

🐛 Proposed fix: Use the existing pack:check script
       - name: Pack-check (refuse forbidden filenames in tarball)
-        run: |
-          npm pack --dry-run --json > /tmp/pack.json
-          node -e "
-            const entries = JSON.parse(require('fs').readFileSync('/tmp/pack.json','utf8'))[0].files.map(f => f.path);
-            const forbidden = entries.filter(p =>
-              /(^|\/)\.npmrc$/.test(p) ||
-              /(^|\/)\.env(\$|\.)/.test(p) ||
-              /(^|\/)secrets?(\/|\$)/.test(p) ||
-              /(^|\/)\.github(\/|\$)/.test(p) ||
-              /(^|\/)\.git(\/|\$)/.test(p)
-            );
-            if (forbidden.length) {
-              console.error('Refusing to publish — forbidden filenames in tarball:');
-              for (const f of forbidden) console.error('  ' + f);
-              process.exit(1);
-            }
-            console.log('Pack-check OK — ' + entries.length + ' files, no forbidden patterns');
-          "
+        run: npm run pack:check
🤖 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 around lines 180 - 198, The inline Pack-check
step contains buggy regexes and duplicates logic from scripts/pack-check.mjs;
replace the entire inline node script in the "Pack-check (refuse forbidden
filenames in tarball)" step with a call to the canonical script (e.g. run "npm
run pack:check" or execute "node scripts/pack-check.mjs") so the workflow reuses
the existing pack:check implementation and avoids the regex bug and duplication.

Comment thread .github/workflows/release.yml Outdated
# forks and unprotected branches cannot reach them.
name: Publish to npm + ClawHub
needs: release
if: "${{ startsWith(github.event.head_commit.message, 'chore: bump version') }}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This if: will never match — the publish job will never run.

The existing release job (line 17) pushes the bump commit using secrets.GITHUB_TOKEN, and per GitHub's loop-prevention rule pushes made with GITHUB_TOKEN do not trigger new workflow runs. So there is no second workflow run whose head_commit.message starts with chore: bump version — your PR description's "bump-commit run: GitHub Release ──► publish job (new)" arrow is the path that doesn't actually fire here.

Fix: gate publish on a job output from release instead, and have release set that output only when it actually created a Release (e.g. inside steps.check.outputs.exists == 'false'):

Suggested change
if: "${{ startsWith(github.event.head_commit.message, 'chore: bump version') }}"
if: needs.release.outputs.published == 'true'

(also add outputs: to the release job declaration and set published: 'true' from the Create-Release step). Alternative: use a PAT or GitHub App token on the actions/checkout at line 17 so the bump push can re-trigger the workflow — but the job-output approach avoids needing a second token entirely.

Comment thread .github/workflows/release.yml Outdated
# Pull the latest main HEAD: the push event's SHA may point at
# the pre-bump commit, but the `release` job above just pushed
# the bump commit — that's the version we want to publish.
ref: main
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Race window: ref: main plus the production environment gate means the reviewer approves a specific commit, but during the approval pause main can advance — and on resume this checkout pulls the new tip and publishes a version the reviewer never saw. Pin to the trigger SHA instead (which, on the bump-commit retrigger, is the bump commit you want anyway):

Suggested change
ref: main
ref: ${{ github.sha }}

Comment thread .github/workflows/release.yml Outdated
Comment on lines +180 to +198
- name: Pack-check (refuse forbidden filenames in tarball)
run: |
npm pack --dry-run --json > /tmp/pack.json
node -e "
const entries = JSON.parse(require('fs').readFileSync('/tmp/pack.json','utf8'))[0].files.map(f => f.path);
const forbidden = entries.filter(p =>
/(^|\/)\.npmrc$/.test(p) ||
/(^|\/)\.env(\$|\.)/.test(p) ||
/(^|\/)secrets?(\/|\$)/.test(p) ||
/(^|\/)\.github(\/|\$)/.test(p) ||
/(^|\/)\.git(\/|\$)/.test(p)
);
if (forbidden.length) {
console.error('Refusing to publish — forbidden filenames in tarball:');
for (const f of forbidden) console.error(' ' + f);
process.exit(1);
}
console.log('Pack-check OK — ' + entries.length + ' files, no forbidden patterns');
"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pack-check is duplicated — this inline node block re-implements scripts/pack-check.mjs (already wired as npm run pack:check in ci.yml). Two copies of the forbidden-filename list will drift; a future PR adding a pattern to one and missing the other defeats the regression-guard. Call the script:

Suggested change
- name: Pack-check (refuse forbidden filenames in tarball)
run: |
npm pack --dry-run --json > /tmp/pack.json
node -e "
const entries = JSON.parse(require('fs').readFileSync('/tmp/pack.json','utf8'))[0].files.map(f => f.path);
const forbidden = entries.filter(p =>
/(^|\/)\.npmrc$/.test(p) ||
/(^|\/)\.env(\$|\.)/.test(p) ||
/(^|\/)secrets?(\/|\$)/.test(p) ||
/(^|\/)\.github(\/|\$)/.test(p) ||
/(^|\/)\.git(\/|\$)/.test(p)
);
if (forbidden.length) {
console.error('Refusing to publish — forbidden filenames in tarball:');
for (const f of forbidden) console.error(' ' + f);
process.exit(1);
}
console.log('Pack-check OK — ' + entries.length + ' files, no forbidden patterns');
"
- name: Pack-check (refuse forbidden filenames in tarball)
run: npm run pack:check

kaghni and others added 6 commits May 7, 2026 00:37
…check

Fixes three review comments on PR #103:

1. publish job would never fire — pushes made by the release job use
   the default GITHUB_TOKEN, which GitHub's loop-prevention rule says
   does NOT retrigger workflows. The previous gate (head_commit.message
   starts with 'chore: bump version') therefore never matched on
   automated runs. Replace with a job output: release sets
   outputs.published='true' only when Create GitHub Release ran; publish
   gates on needs.release.outputs.published=='true' and runs in the same
   workflow.

2. ref: main on the publish checkout was racy — during the production
   environment-gate pause, main can advance, so an approval at commit A
   could resume and publish commit B. Capture the bump SHA via
   'git rev-parse HEAD' in a new release-job step and pin the checkout
   to needs.release.outputs.sha.

3. Inline pack-check in the publish job duplicated scripts/pack-check.mjs
   and its regex used \$ (literal dollar) instead of $ (end anchor),
   breaking .env matching. Replaced with 'npm run pack:check' which
   reuses the canonical script wired into ci.yml.
Exercises the full publish pipeline without uploading: validates
NPM_TOKEN via 'npm whoami', runs 'npm publish --dry-run --provenance'
(catches 2FA-required classic tokens via EOTP before the real run),
validates CLAWHUB_TOKEN via 'clawhub whoami', and runs
'clawhub package publish ./openclaw --dry-run'.

Uses the same 'production' environment as the real publish job so a
successful smoke-test also proves the environment + secrets are
wired correctly. No bump, no Release, no public artifact.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants