Skip to content

chore: add automated package publishing workflow#12371

Merged
kaladinlight merged 2 commits into
developfrom
chore/auto-publish-packages
May 21, 2026
Merged

chore: add automated package publishing workflow#12371
kaladinlight merged 2 commits into
developfrom
chore/auto-publish-packages

Conversation

@kaladinlight
Copy link
Copy Markdown
Member

@kaladinlight kaladinlight commented May 21, 2026

Description

Adds a new GitHub Actions workflow that publishes ShapeShift's public packages to npm automatically, removing the need for manual pnpm publish runs from a maintainer's laptop.

What's in this PR

New workflow: .github/workflows/publish-packages.yml

  • Triggers on push to main and on workflow_dispatch
  • Builds packages, then runs pnpm -r publish filtered to packages/* excluding @shapeshiftoss/web and @shapeshiftoss/hdwallet-*
  • pnpm publish skips any version already on the registry, so re-running with no bumps is a clean no-op
  • After publish, pushes per-package git tags <name>-v<version> for anything that actually shipped
  • Authenticates via npm Trusted Publishers (OIDC) - no NPM_TOKEN secret needed; each package is configured on npmjs.com to trust this repo + workflow filename

Package cleanups bundled in

  • @shapeshiftoss/affiliate-dashboard and @shapeshiftoss/public-api marked private: true — they're service/app packages, not libraries; they shouldn't be on npm
  • @shapeshiftoss/contracts version cleaned from 1.0.5-coderabbit-fix.2 to 1.0.6 (matches what's already on npm; first workflow run no-ops for contracts)
  • @shapeshiftoss/swap-widget bumped from 0.1.0 to 0.2.0 (real publish; first workflow run will ship this)

Authoring flow going forward

Bump the version in packages/<name>/package.json as part of any normal PR. When the change reaches main, the workflow ships it. For urgent publishes that can't wait for the next web release, trigger the Publish Packages workflow manually from the Actions tab.

One-time setup tasks (done out-of-band)

  • Trusted Publisher configured on npmjs.com for each publishable package (caip, chain-adapters, contracts, errors, swapper, swap-widget, types, unchained-client, utils) — pointing at shapeshift/web + publish-packages.yml
  • Existing-version git tags backfilled (caip-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: main and workflow_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 publish errors loudly with Trusted publisher configuration not found or similar — no silent partial state.

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

None.

Testing

Engineering

Hard to dry-run without merging, since the workflow only triggers on main. Two natural verification points after merge:

  1. First push to main without a new bump — should run, snapshot zero pending publishes, no-op cleanly, no new tags
  2. First push to main with the bundled swap-widget 0.2.0 bump (this PR) — should publish @shapeshiftoss/swap-widget@0.2.0 to npm and push swap-widget-v0.2.0 tag; contracts@1.0.6 is already on npm and will skip cleanly

If the workflow fails for any package, the error message will name the package + reason; fix in a follow-up.

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

No user-facing changes. Affects internal release tooling only.

Screenshots (if applicable)

N/A

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Added automated GitHub Actions workflow to publish workspace packages from main with safe concurrency and tag synchronization
    • Marked affiliate-dashboard and public-api packages as private
    • Updated package versions: contracts → 1.0.6 and swap-widget → 0.2.0

Review Change Stack

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>
@kaladinlight kaladinlight requested a review from a team as a code owner May 21, 2026 17:49
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

Walkthrough

This 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.

Changes

Package Publishing and Release Automation

Layer / File(s) Summary
Workflow triggers and concurrency
.github/workflows/publish-packages.yml
Workflow entry (name, push to main, workflow_dispatch) and a concurrency group (publish-packages) to serialize publish runs.
Publish job: setup, build, publish
.github/workflows/publish-packages.yml
Job requests id-token: write, checks out full history, enables pnpm, sets Node from .nvmrc (upgrades npm for Trusted Publisher OIDC), installs deps, builds packages, and runs pnpm -r publish with workspaceConcurrency=1 and filters to packages/* excluding @shapeshiftoss/web and @shapeshiftoss/hdwallet-*.
Post-publish tagging
.github/workflows/publish-packages.yml
Shell step iterates packages/*/package.json, skips private/web/hdwallet packages, derives <short_name>-v<version> tags, verifies versions exist on npm, creates annotated tags for missing local tags, and pushes newly created tags.
Package version and visibility updates
packages/affiliate-dashboard/package.json, packages/contracts/package.json, packages/public-api/package.json, packages/swap-widget/package.json
Marks affiliate-dashboard and public-api as private; bumps @shapeshiftoss/contracts to 1.0.6; bumps @shapeshiftoss/swap-widget to 0.2.0.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through workflows, OIDC in paw,
No tokens to hide, just trust that I saw,
Publish each package with careful pace,
Tag every release so trace stays in place,
A tiny rabbit celebrates CI's grace!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding a GitHub Actions workflow for automated package publishing.
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 chore/auto-publish-packages

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 and usage tips.

Copy link
Copy Markdown
Contributor

@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 (4)
.github/workflows/publish-packages.yml (4)

42-63: ⚖️ Poor tradeoff

Consider pinning third-party actions to commit SHAs.

Static analysis (zizmor unpinned-uses) flags actions/checkout@v4, actions/setup-node@v4, and actions/cache@v4 as unpinned. For a release-critical workflow with contents: write + id-token: write that publishes to npm, the hardened pattern is to pin to a full commit SHA (with the @v4 as a trailing comment for readability) so a tag move can't silently swap the action behind OIDC trust.

The companion artipacked warning on the checkout step is intentional here — the persisted credential is needed downstream for git 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 value

Push only the tags created in this run.

git push origin --tags blanket-pushes every local tag. With fetch-depth: 0 the 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 manual git tag in 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 value

Consider restricting workflow_dispatch to main.

workflow_dispatch allows 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 win

Remove redundant node_modules cache; rely on the pnpm store cache from actions/setup-node

In .github/workflows/publish-packages.yml, actions/setup-node@v4 is already configured with cache: 'pnpm' (caches pnpm’s global content-addressable store). Caching node_modules directly is unnecessary for pnpm and can introduce avoidable mismatch/link issues—especially with the broad restore-keys fallback.

♻️ 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

📥 Commits

Reviewing files that changed from the base of the PR and between 720b9c8 and c0c65b1.

📒 Files selected for processing (5)
  • .github/workflows/publish-packages.yml
  • packages/affiliate-dashboard/package.json
  • packages/contracts/package.json
  • packages/public-api/package.json
  • packages/swap-widget/package.json

Comment thread .github/workflows/publish-packages.yml Outdated
- 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>
Copy link
Copy Markdown
Contributor

@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.

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 win

Restrict workflow_dispatch to the main branch.

workflow_dispatch allows a user to select any branch as the dispatch ref in the GitHub UI, after which actions/checkout@v4 checks out that ref and the job proceeds to build and publish from arbitrary code. Because the Trusted Publisher config is matched on repo + 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-main code 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

📥 Commits

Reviewing files that changed from the base of the PR and between c0c65b1 and 172a2e9.

📒 Files selected for processing (1)
  • .github/workflows/publish-packages.yml

@kaladinlight kaladinlight merged commit be1cb72 into develop May 21, 2026
4 checks passed
@kaladinlight kaladinlight deleted the chore/auto-publish-packages branch May 21, 2026 18:30
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.

1 participant