Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
{
"$schema": "https://unpkg.com/@changesets/config@3.0.5/schema.json",
"changelog": "@changesets/cli/changelog",
"changelog": [
"@changesets/changelog-github",
{
"repo": "0xsequence/sequence.js"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"access": "public",
"baseBranch": "master",
"updateInternalDependencies": "patch",
"ignore": ["@0xsequence/wallet-primitives-cli", "docs", "web"]
Expand Down
2 changes: 2 additions & 0 deletions .changeset/quick-roses-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
2 changes: 1 addition & 1 deletion .github/actions/install-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ runs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: .nvmrc

- name: Setup PNPM
uses: pnpm/action-setup@v3
Expand Down
13 changes: 13 additions & 0 deletions .github/workflows/changeset-check-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: Changeset check

on:
pull_request:
branches: [master]

permissions:
contents: read
pull-requests: write

jobs:
check:
uses: 0xPolygon/pipelines/.github/workflows/apps-changeset-check.yml@main
126 changes: 126 additions & 0 deletions .github/workflows/npm-release-trigger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Release

# Staged npm publishing via changesets + OIDC trusted publishing. See PUBLISHING.md.
# The v2 branch has its own copy of this file (same name, so the same npm trusted publisher
# applies) that stages under the `v2-legacy` dist-tag.

on:
push:
branches: [master]
workflow_dispatch:
inputs:
snapshot_tag:
description: 'npm dist-tag for snapshot (e.g. canary, pre-3.0.14). Non-semver values only.'
required: true
default: 'snapshot'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

# Default-deny: each job declares its own permissions.
permissions: {}

jobs:
release:
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
pull-requests: write
id-token: write # required for npm OIDC trusted publishing
steps:
# Nothing pushes over git (changesets github-api mode writes through the API with the
# app token below), so do not persist the read-only job token into .git/config.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- name: Install npm with staged-publishing support
run: npm install --global npm@11.15.0
# The release gates run the wallet tests, which need a local anvil fork.
- uses: foundry-rs/foundry-toolchain@908c540300062bd5a7e473851cdb4282204cee09 # v1.9.1
with:
version: v1.5.0
# The release PR changes package manifests without regenerating the lockfile. Link
# co-released workspace packages locally so the not-yet-public versions still install.
# --ignore-scripts: releasing needs no install-time script, and this job holds id-token: write.
- run: pnpm install --no-frozen-lockfile --config.link-workspace-packages=true --ignore-scripts
- name: Detect release PR merge
id: pending
run: |
if find .changeset -name '*.md' ! -name README.md | grep -q .; then
echo "changesets=true" >> "$GITHUB_OUTPUT"
else
echo "changesets=false" >> "$GITHUB_OUTPUT"
fi
# Only a merged release PR (no pending changesets) stages packages. Run the gates before
# the release bot token exists so build and test code never sees it.
- name: Release gates
if: steps.pending.outputs.changesets == 'false'
run: pnpm run ci:release-gates
# Mint the app token last, scoped to what changesets/action uses, so only that step has it.
- uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
id: app-token
with:
app-id: ${{ secrets.CHANGESET_RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.CHANGESET_RELEASE_BOT_APP_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write
- name: Create release PR or stage versioned packages
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1.9.0
with:
title: 'changesets: Release / Deploy'
commit: 'release: version packages'
commitMode: github-api
# When a merged release PR has consumed all changesets, this command packs with
# pnpm (rewriting workspace: ranges) and stages the tarballs with npm. Its
# `New tag:` output lets the action create package tags and GitHub Releases.
publish: pnpm run ci:stage-release
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}

snapshot:
if: github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
pull-requests: read # changelog-github PR lookups
id-token: write # required for npm OIDC trusted publishing
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: .nvmrc
cache: pnpm
registry-url: 'https://registry.npmjs.org'
- name: Install npm with staged-publishing support
run: npm install --global npm@11.15.0
- uses: foundry-rs/foundry-toolchain@908c540300062bd5a7e473851cdb4282204cee09 # v1.9.1
with:
version: v1.5.0
- run: pnpm install --no-frozen-lockfile --config.link-workspace-packages=true --ignore-scripts
- name: Version snapshot
env:
GITHUB_TOKEN: ${{ github.token }}
SNAPSHOT_TAG: ${{ inputs.snapshot_tag }}
run: node scripts/prepare-npm-snapshot.mjs
- name: Release gates
run: pnpm run ci:release-gates
- name: Stage snapshot packages
env:
NPM_STAGE_CREATE_GIT_TAGS: 'false'
NPM_STAGE_TAG: ${{ inputs.snapshot_tag }}
run: pnpm run ci:stage-release
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
- name: Start Anvil in background
run: anvil --fork-url https://nodes.sequence.app/arbitrum &
- run: pnpm build
- run: pnpm test:release
- run: pnpm test

# NOTE: if you'd like to see example of how to run
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
99 changes: 99 additions & 0 deletions PUBLISHING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Publishing

> **Publishing is CI-only. Never publish or stage from a local machine.** Do not run `changeset version`,
> `changeset publish`, `npm publish`, `npm stage publish`, `pnpm publish`, or the internal `ci:stage-release`
> script yourself. A local release bypasses the signed release commit and npm OIDC trusted publishing. Your job as
> a contributor is to land a changeset (see "Day-to-day") and let CI prepare the release; an npm maintainer
> performs the final 2FA approval.

Releases are driven by [changesets](https://github.com/changesets/changesets). Packages are versioned
independently (there is no `fixed` group); `updateInternalDependencies: patch` bumps dependents when a workspace
dependency is released.

## Day-to-day: add a changeset

Every PR that changes files inside a published package must include a changeset. From the repo root:

```bash
pnpm exec changeset
```

Pick the packages and bump type and write a user-facing changelog entry. For changes with no consumer impact
(internal refactors, chores), record an empty changeset instead:

```bash
pnpm exec changeset add --empty
```

The `Changeset check` CI job fails a PR that touches a package without one.

## Release flow (automated)

1. Merging PRs with changesets into `master` triggers the `Release` workflow
(`.github/workflows/npm-release-trigger.yml`), which opens or updates a **`changesets: Release / Deploy`** PR.
That PR applies the pending changesets, bumps package versions, and updates each `CHANGELOG.md`.
2. Review and merge that Release PR.
3. On merge, the same workflow uses npm `11.15.0` and runs the release gates (`ci:release-gates`: build,
typecheck, lint, release script tests, and the full test suite against an anvil fork) before the release bot
token is minted, so build and test code never sees it. It then packs every publishable package whose version
is not yet on npm with pnpm, which rewrites `workspace:` ranges to real versions, and fails if any `workspace:`
range remains.
4. The workflow sends the tarballs to `npm stage publish` under the `latest` dist-tag via **OIDC trusted
publishing** (no `NPM_TOKEN`). After each package stages, Changesets pushes its package tag (e.g.
`@0xsequence/wallet-core@3.0.14`) and creates a GitHub Release from its `CHANGELOG.md` entry.
5. An npm maintainer inspects and approves every staged package with 2FA. **Approve in the order listed in the
workflow run summary** (dependencies first, e.g. `wallet-primitives` before `wallet-core` before `wallet-wdk`)
so each approved package's dependencies are already installable.

Version-bump commits are signed by GitHub's GPG key (`commitMode: github-api`). The package tags and GitHub
Releases record that CI staged the exact release artifacts; they do not mean npm approval has finished.

### Approve a staged release

On npmjs.com, open each package's **Staged Packages** tab and approve, or from the CLI:

```bash
npm stage list @0xsequence/wallet-core
npm stage view <stage-id>
npm stage approve <stage-id> # prompts for 2FA
```

If one tarball stages and a later tarball fails, do not merge another release. The workflow creates the
successful packages' tags before failing; after fixing the cause, rerun that workflow and the release helper will
skip the tagged stages. If staging succeeded but its package tag was not created, reject that partial stage with
`npm stage reject <stage-id>` before retrying because staging reserves the package version.

## Prerelease / snapshot builds

To prepare a throwaway prerelease under a non-`latest` npm dist-tag, manually dispatch the **Release** workflow —
this is still CI, not a local release:

1. GitHub → **Actions** → **Release** → **Run workflow** (on `master` or a feature branch).
2. Set the **`snapshot_tag`** input to a non-semver dist-tag (e.g. `canary`, `pre-3.0.14`).

The workflow versions **every** publishable package as `0.0.0-<tag>-<timestamp>` on the runner (never committed),
so snapshot packages only depend on each other, then packs and stages them. The snapshot path skips git tags and
GitHub Releases, so **`snapshot_tag` must be a lowercase, non-version tag other than `latest` or `v2-legacy`**. An
npm maintainer must approve the stages before consumers can install them with
`pnpm add @0xsequence/wallet-wdk@<tag>`.

## v2 (`v2-legacy`)

The `v2` branch has its own `npm-release-trigger.yml` with the same flow, except it stages under the
**`v2-legacy`** dist-tag so v2 releases never move `latest` (which belongs to v3). The workflow file name is the
same on both branches, so one trusted publisher configuration per package covers both.

## One-time setup (admins)

- **npm trusted publishers:** run `./scripts/configure-npm-trust.sh` (runs npm 11.15.0 via `npx`; needs an npm login with
publish rights, and 2FA). It registers `0xsequence/sequence.js` + `npm-release-trigger.yml` as a **stage-only**
trusted publisher (`--allow-stage-publish`, no `--allow-publish`) for every package on `master` and `v2`, so CI
can stage but never publish directly. Afterwards, set each package's publishing access to "Require two-factor
authentication and disallow tokens" and revoke old automation tokens.
- **GitHub:** the release workflow authenticates as the **0xsequence Changesets Release Bot** GitHub App, owned
by the `0xsequence` org (Polygon's `0xpolygon-changesets-release-bot` is private to `0xPolygon` and cannot be
installed here). The app needs repository permissions **Contents: read & write** and **Pull requests: read &
write**, no webhook, and is installed on this repository only. Its App ID and a private key are stored as the
repository secrets `CHANGESET_RELEASE_BOT_APP_ID` and `CHANGESET_RELEASE_BOT_APP_PRIVATE_KEY`. The app (not the
default `GITHUB_TOKEN`) is required so the release PR triggers CI and its commits are signed. To rotate the key,
generate a new one in the app settings, update the secret, then delete the old key.
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
"test:anvil": "anvil --fork-url https://nodes.sequence.app/arbitrum",
"clean": "turbo clean",
"deps:lint": "syncpack lint --dependency-types prod,dev",
"deps:fix": "syncpack fix"
"deps:fix": "syncpack fix",
"test:release": "node --test scripts/*.test.mjs",
"ci:stage-release": "node scripts/stage-npm-packages.mjs",
"ci:release-gates": "pnpm build && pnpm typecheck && pnpm lint && pnpm test:release && pnpm test:local"
},
"devDependencies": {
"@changesets/changelog-github": "^1.0.1",
"@changesets/cli": "^2.29.8",
"lefthook": "^2.1.6",
"prettier": "^3.8.3",
"rimraf": "^6.1.3",
"semver": "^7",
"syncpack": "^14.3.1",
"turbo": "^2.9.14",
"typescript": "^6.0.3"
Expand Down
6 changes: 5 additions & 1 deletion packages/services/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@0xsequence/api",
"version": "3.0.13",
"description": "api sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/api",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/api"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"publishConfig": {
Expand Down
6 changes: 5 additions & 1 deletion packages/services/builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@0xsequence/builder",
"version": "3.0.13",
"description": "builder sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/builder",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/builder"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"publishConfig": {
Expand Down
6 changes: 5 additions & 1 deletion packages/services/guard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@0xsequence/guard",
"version": "3.0.13",
"description": "guard sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/guard",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/guard"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"type": "module",
Expand Down
5 changes: 5 additions & 0 deletions packages/services/identity-instrument/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
"name": "@0xsequence/identity-instrument",
"version": "3.0.13",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/identity-instrument"
},
"type": "module",
"publishConfig": {
"access": "public"
Expand Down
6 changes: 5 additions & 1 deletion packages/services/indexer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@0xsequence/indexer",
"version": "3.0.13",
"description": "indexer sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/indexer",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/indexer"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"publishConfig": {
Expand Down
6 changes: 5 additions & 1 deletion packages/services/marketplace/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
"name": "@0xsequence/marketplace",
"version": "3.0.13",
"description": "marketplace sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/marketplace",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/marketplace"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"publishConfig": {
Expand Down
6 changes: 5 additions & 1 deletion packages/services/metadata/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
"access": "public"
},
"description": "metadata sub-package for Sequence",
"repository": "https://github.com/0xsequence/sequence.js/tree/master/packages/services/metadata",
"repository": {
"type": "git",
"url": "git+https://github.com/0xsequence/sequence.js.git",
"directory": "packages/services/metadata"
},
"author": "Sequence Platforms ULC",
"license": "Apache-2.0",
"type": "module",
Expand Down
Loading
Loading