chore: add automated package publishing workflow#12371
Conversation
Adds .github/workflows/publish-packages.yml that publishes any public, non-hdwallet package whose version in package.json isn't yet on npm. Triggers on push to main and on workflow_dispatch. Authenticates via npm Trusted Publishers (OIDC), so no NPM_TOKEN secret is required. Per-package <name>-v<version> tags are pushed after each successful publish. Existing-version tags were backfilled manually before this commit. Also marks two service/app packages as private so they're not picked up by the publish flow: - @shapeshiftoss/affiliate-dashboard (dashboard app, not a library) - @shapeshiftoss/public-api (HTTP service, not a library) And cleans up two version strings in preparation for the workflow's first runs: - contracts: 1.0.5-coderabbit-fix.2 -> 1.0.6 (matches what's already on npm; no-op publish on first run) - swap-widget: 0.1.0 -> 0.2.0 (real bump; ships on first run) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis PR adds a new GitHub Actions workflow to publish workspace packages from main using npm Trusted Publisher OIDC, enforces sequential publish concurrency, creates annotated git tags for published versions missing local tags, and updates four package manifests for privacy or version bumps. ChangesPackage Publishing and Release Automation
Sequence Diagram(s)sequenceDiagram
participant GitHubActions as GitHub Actions
participant Repo as Repository (checkout)
participant PNPM as pnpm
participant Npm as npm Registry
participant Git as Git
GitHubActions->>Repo: checkout (full history)
GitHubActions->>PNPM: setup pnpm, install deps, build packages
GitHubActions->>Npm: pnpm -r publish (workspaceConcurrency=1, filters)
Npm-->>GitHubActions: publish responses
GitHubActions->>Git: create & push annotated tags for published versions
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
.github/workflows/publish-packages.yml (4)
42-63: ⚖️ Poor tradeoffConsider pinning third-party actions to commit SHAs.
Static analysis (zizmor
unpinned-uses) flagsactions/checkout@v4,actions/setup-node@v4, andactions/cache@v4as unpinned. For a release-critical workflow withcontents: write+id-token: writethat publishes to npm, the hardened pattern is to pin to a full commit SHA (with the@v4as a trailing comment for readability) so a tag move can't silently swap the action behind OIDC trust.The companion
artipackedwarning on the checkout step is intentional here — the persisted credential is needed downstream forgit push origin --tags, so leaving it on is fine.Example pattern (verify SHAs against the upstream release tags):
- uses: actions/checkout@<sha> # v4.x.x - uses: actions/setup-node@<sha> # v4.x.x - uses: actions/cache@<sha> # v4.x.x🤖 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/publish-packages.yml around lines 42 - 63, The workflow uses unpinned third-party actions (actions/checkout@v4, actions/setup-node@v4, actions/cache@v4); replace those floating tags with the corresponding full commit SHAs (keeping the v4 comment for readability) so tags can’t be moved under OIDC/contents: write/ id-token: write trust, e.g. change uses: actions/checkout@v4 → uses: actions/checkout@<full-sha> # v4.x.x, and do the same for actions/setup-node and actions/cache after verifying the SHAs against the upstream release tags; retain any intentional settings needed for git push (persist-credentials) as-is.
108-138: 💤 Low valuePush only the tags created in this run.
git push origin --tagsblanket-pushes every local tag. Withfetch-depth: 0the local tag set matches remote plus the new ones, so today this is effectively a no-op for pre-existing tags — but it's fragile: any local-only tag (bootstrap leftover, future manualgit tagin another step, etc.) would also be pushed. Tracking the new tags explicitly is cheap and self-documenting.♻️ Suggested change
- tagged=0 + new_tags=() for pkg_json in packages/*/package.json; do ... echo "Tagging $tag at $(git rev-parse --short HEAD)" git tag -a "$tag" -m "$tag" - tagged=$((tagged + 1)) + new_tags+=("$tag") done - if [ "$tagged" -gt 0 ]; then - git push origin --tags + if [ "${`#new_tags`[@]}" -gt 0 ]; then + git push origin "${new_tags[@]}" else echo "No new tags to push." fi🤖 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/publish-packages.yml around lines 108 - 138, The script currently pushes all local tags with git push origin --tags which can inadvertently publish unrelated local tags; modify the tagging logic in the loop that computes tag="${short_name}-v${version}" and runs git tag -a "$tag" so that each created tag is recorded (e.g., append to a tags_to_push array or a temp file), incrementing tagged as before, then replace the blanket git push origin --tags with a push of only those recorded tags (e.g., git push origin "${tags_to_push[@]}" or reading the temp file and pushing each) so only the tags created in this run are pushed.
23-26: 💤 Low valueConsider restricting
workflow_dispatchtomain.
workflow_dispatchallows manually running from any ref. While the npm-side Trusted Publisher config may restrict by branch, adding a job-level guard provides defense-in-depth and prevents accidental dispatches against feature branches from attempting publish/tag operations.♻️ Suggested guard
jobs: publish: name: Publish runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' permissions:🤖 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/publish-packages.yml around lines 23 - 26, The workflow currently exposes workflow_dispatch for any ref; add a job-level guard to prevent manual runs from non-main refs by adding a conditional (e.g., on the publish/tag job that performs npm publish) such as if: github.ref == 'refs/heads/main' so the job that runs the publish/tag steps only executes when the ref is main even when triggered via workflow_dispatch; locate the job that runs the publish/tag operations (the job name that invokes npm publish or creates tags) and add that if condition adjacent to its existing configuration.
58-63: ⚡ Quick winRemove redundant
node_modulescache; rely on the pnpm store cache fromactions/setup-nodeIn
.github/workflows/publish-packages.yml,actions/setup-node@v4is already configured withcache: 'pnpm'(caches pnpm’s global content-addressable store). Cachingnode_modulesdirectly is unnecessary for pnpm and can introduce avoidable mismatch/link issues—especially with the broadrestore-keysfallback.♻️ Suggested change
- - name: Cache node_modules - uses: actions/cache@v4 - with: - path: node_modules - key: ${{ runner.os }}-node-modules-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: ${{ runner.os }}-node-modules- - - name: Install Dependencies run: pnpm install --frozen-lockfile🤖 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/publish-packages.yml around lines 58 - 63, Remove the redundant "Cache node_modules" step that uses actions/cache@v4: delete the entire step (the name "Cache node_modules" and its uses/with block including path, key, and restore-keys) so the workflow relies solely on actions/setup-node@v4 with cache: 'pnpm' (which caches the pnpm store); ensure no other steps depend on that node_modules cache key or restore-keys fallback.
🤖 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/publish-packages.yml:
- Around line 71-72: Replace the non-deterministic "npm install -g npm@latest"
step used in the "Upgrade npm for trusted publishing" job with a pinned
known-good CLI version (e.g., npm@11.5.1) to ensure reproducible publishes and
satisfy Trusted Publisher OIDC requirements; update the step that currently runs
"npm install -g npm@latest" to install the pinned version instead so pnpm's
publish upload behavior is stable across runs.
---
Nitpick comments:
In @.github/workflows/publish-packages.yml:
- Around line 42-63: The workflow uses unpinned third-party actions
(actions/checkout@v4, actions/setup-node@v4, actions/cache@v4); replace those
floating tags with the corresponding full commit SHAs (keeping the v4 comment
for readability) so tags can’t be moved under OIDC/contents: write/ id-token:
write trust, e.g. change uses: actions/checkout@v4 → uses:
actions/checkout@<full-sha> # v4.x.x, and do the same for actions/setup-node
and actions/cache after verifying the SHAs against the upstream release tags;
retain any intentional settings needed for git push (persist-credentials) as-is.
- Around line 108-138: The script currently pushes all local tags with git push
origin --tags which can inadvertently publish unrelated local tags; modify the
tagging logic in the loop that computes tag="${short_name}-v${version}" and runs
git tag -a "$tag" so that each created tag is recorded (e.g., append to a
tags_to_push array or a temp file), incrementing tagged as before, then replace
the blanket git push origin --tags with a push of only those recorded tags
(e.g., git push origin "${tags_to_push[@]}" or reading the temp file and pushing
each) so only the tags created in this run are pushed.
- Around line 23-26: The workflow currently exposes workflow_dispatch for any
ref; add a job-level guard to prevent manual runs from non-main refs by adding a
conditional (e.g., on the publish/tag job that performs npm publish) such as if:
github.ref == 'refs/heads/main' so the job that runs the publish/tag steps only
executes when the ref is main even when triggered via workflow_dispatch; locate
the job that runs the publish/tag operations (the job name that invokes npm
publish or creates tags) and add that if condition adjacent to its existing
configuration.
- Around line 58-63: Remove the redundant "Cache node_modules" step that uses
actions/cache@v4: delete the entire step (the name "Cache node_modules" and its
uses/with block including path, key, and restore-keys) so the workflow relies
solely on actions/setup-node@v4 with cache: 'pnpm' (which caches the pnpm
store); ensure no other steps depend on that node_modules cache key or
restore-keys fallback.
🪄 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
Run ID: 0ee2be57-e4b0-4df5-b468-18babd1f8f7a
📒 Files selected for processing (5)
.github/workflows/publish-packages.ymlpackages/affiliate-dashboard/package.jsonpackages/contracts/package.jsonpackages/public-api/package.jsonpackages/swap-widget/package.json
- Pin npm to 11.5.1 instead of @latest for deterministic publish behavior - Push only the tags created in this run instead of blanket --tags, so any stray local tag on the runner can't leak to origin Skipped: - SHA-pinning third-party actions — rest of the repo uses floating @v4 tags; pinning here would be inconsistent - Restricting workflow_dispatch to main — the manual dispatch is the escape hatch for shipping faster than the release cycle, which requires running from develop or hotfix branches - Removing the node_modules cache — pr.yml and bootstrap.yml both use the same dual-cache pattern; this matches project convention Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/publish-packages.yml (1)
23-26:⚠️ Potential issue | 🟠 Major | ⚡ Quick winRestrict
workflow_dispatchto themainbranch.
workflow_dispatchallows a user to select any branch as the dispatch ref in the GitHub UI, after whichactions/checkout@v4checks out that ref and the job proceeds to build and publish from arbitrary code. Because the Trusted Publisher config is matched onrepo + workflow filename(not branch), OIDC will still authenticate successfully and publish whatever was just built. That makes the manual trigger a viable path to ship non-maincode to npm.The push-trigger side is already constrained to
main, so a job-level guard closes the gap with minimal effort. PR notes call this out as deliberately skipped — flagging here so the trade-off is visible alongside the OIDC publish path.🔒 Suggested guard at the job level
jobs: publish: name: Publish runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' permissions: contents: write # push per-package git tags id-token: write # mint OIDC token for npm Trusted Publisher auth🤖 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/publish-packages.yml around lines 23 - 26, The workflow currently exposes workflow_dispatch without branch restriction; add a job-level guard to prevent manual dispatch from running on non-main refs by adding an if conditional to the publish job (e.g., the top-level job like publish/deploy) such as: if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main' so that runs from push still work but manual dispatches only proceed when the ref is main; update the job that performs checkout/publish (reference the job name that calls actions/checkout@v4 and the workflow_dispatch trigger) to include this if condition.
🤖 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.
Outside diff comments:
In @.github/workflows/publish-packages.yml:
- Around line 23-26: The workflow currently exposes workflow_dispatch without
branch restriction; add a job-level guard to prevent manual dispatch from
running on non-main refs by adding an if conditional to the publish job (e.g.,
the top-level job like publish/deploy) such as: if: github.event_name !=
'workflow_dispatch' || github.ref == 'refs/heads/main' so that runs from push
still work but manual dispatches only proceed when the ref is main; update the
job that performs checkout/publish (reference the job name that calls
actions/checkout@v4 and the workflow_dispatch trigger) to include this if
condition.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0244349d-3cdc-4088-b750-939be5323cd3
📒 Files selected for processing (1)
.github/workflows/publish-packages.yml
Description
Adds a new GitHub Actions workflow that publishes ShapeShift's public packages to npm automatically, removing the need for manual
pnpm publishruns from a maintainer's laptop.What's in this PR
New workflow:
.github/workflows/publish-packages.ymlpushtomainand onworkflow_dispatchpnpm -r publishfiltered topackages/*excluding@shapeshiftoss/weband@shapeshiftoss/hdwallet-*pnpm publishskips any version already on the registry, so re-running with no bumps is a clean no-op<name>-v<version>for anything that actually shippedNPM_TOKENsecret needed; each package is configured on npmjs.com to trust this repo + workflow filenamePackage cleanups bundled in
@shapeshiftoss/affiliate-dashboardand@shapeshiftoss/public-apimarkedprivate: true— they're service/app packages, not libraries; they shouldn't be on npm@shapeshiftoss/contractsversion cleaned from1.0.5-coderabbit-fix.2to1.0.6(matches what's already on npm; first workflow run no-ops for contracts)@shapeshiftoss/swap-widgetbumped from0.1.0to0.2.0(real publish; first workflow run will ship this)Authoring flow going forward
Bump the version in
packages/<name>/package.jsonas part of any normal PR. When the change reachesmain, the workflow ships it. For urgent publishes that can't wait for the next web release, trigger thePublish Packagesworkflow manually from the Actions tab.One-time setup tasks (done out-of-band)
caip,chain-adapters,contracts,errors,swapper,swap-widget,types,unchained-client,utils) — pointing atshapeshift/web+publish-packages.ymlcaip-v8.16.7,chain-adapters-v11.3.9,errors-v1.1.6,swapper-v17.7.3,swap-widget-v0.1.0,types-v8.6.7,unchained-client-v10.14.10,utils-v1.0.5)Issue (if applicable)
closes #
Risk
Low. The workflow only runs on
push: mainandworkflow_dispatch, so it doesn't affect PR or develop CI. Failures are isolated to the workflow itself (won't block the web app deploy, which lives in a separate workflow). Worst case if something is misconfigured:pnpm publisherrors loudly withTrusted publisher configuration not foundor similar — no silent partial state.None.
Testing
Engineering
Hard to dry-run without merging, since the workflow only triggers on
main. Two natural verification points after merge:swap-widget0.2.0 bump (this PR) — should publish@shapeshiftoss/swap-widget@0.2.0to npm and pushswap-widget-v0.2.0tag;contracts@1.0.6is already on npm and will skip cleanlyIf the workflow fails for any package, the error message will name the package + reason; fix in a follow-up.
Operations
No user-facing changes. Affects internal release tooling only.
Screenshots (if applicable)
N/A
🤖 Generated with Claude Code
Summary by CodeRabbit