|
| 1 | +--- |
| 2 | +name: portal-ponder-release |
| 3 | +description: Cut a new @subsquid/ponder release — the Portal fork of ponder, versioned <ponder-version>-sqd.<rev>. Bump/confirm the target ponder version in versions.json, trigger the release workflow (manual dispatch or a v<version> tag), watch it build+test+publish to npm via Trusted Publishing, then flip the versions.json row to published and write GitHub release notes for the v<version> tag. Use when the user asks to "release", "publish", "cut a version", "ship", or "re-cut a fork revision" for @subsquid/ponder / portal-ponder. |
| 4 | +--- |
| 5 | + |
| 6 | +# @subsquid/ponder release |
| 7 | + |
| 8 | +End-to-end release procedure for **`@subsquid/ponder`** — the drop-in Portal fork of `ponder`. |
| 9 | +A release is `ponder@<version>` + the Portal layer (`portal/`), published to npm as |
| 10 | +`@subsquid/ponder@<version>-sqd.<rev>`. The bin stays `ponder`, so it is a drop-in install. |
| 11 | + |
| 12 | +The fork is **generated, not hand-maintained**: [`scripts/sync-upstream.sh <ver>`](../../../scripts/sync-upstream.sh) |
| 13 | +clones `ponder@<ver>`, drops in the two Portal modules + the per-version `portal/wiring/<ver>.patch`, |
| 14 | +renames the package, and builds. The publishable artifact lives in the generated tree at |
| 15 | +`packages/core`, **not** in this repo — this repo only carries the Portal layer. |
| 16 | + |
| 17 | +Publishing runs on **npm Trusted Publishing** (OIDC, no `NPM_TOKEN`) via |
| 18 | +[`.github/workflows/release.yml`](../../../.github/workflows/release.yml). The workflow guards that |
| 19 | +the requested version is declared in [`versions.json`](../../../versions.json) before it builds. |
| 20 | + |
| 21 | +Full background: [`PUBLISHING.md`](../../../PUBLISHING.md) and `versions.json` are the sources of truth. |
| 22 | + |
| 23 | +> **Scope:** this releases `@subsquid/ponder` only. Do not confuse the **git tag** (`v<ponder-version>`, |
| 24 | +> e.g. `v0.16.6`) with the **npm version** (`<ponder-version>-sqd.<rev>`, e.g. `0.16.6-sqd.1`) — see |
| 25 | +> [The two version forms](#the-two-version-forms). |
| 26 | +
|
| 27 | +## Preconditions |
| 28 | + |
| 29 | +Confirm before starting: |
| 30 | + |
| 31 | +- The user named a **ponder version** to release (e.g. `0.16.6`). If not, ask. It must already exist |
| 32 | + in `versions.json` with a `portal/wiring/<ver>.patch`. If it doesn't, this is an *add a version* |
| 33 | + task first — see [Adding a new ponder version](#adding-a-new-ponder-version). |
| 34 | +- The target row's wiring patch **applies + builds + tests green** locally: |
| 35 | + `SYNC_REV=<rev> scripts/sync-upstream.sh <ver> --test`. CI runs this for every row on each push, |
| 36 | + so a green `ci.yml` on the branch is a good proxy — but re-run it if the patch or Portal modules |
| 37 | + changed since the last CI run. |
| 38 | +- You know the **fork revision** (`rev`). First cut of a ponder version is `1`. Bump it (`2`, `3`, …) |
| 39 | + only to re-cut a fork-side fix on the **same** ponder version — npm permanently retires an |
| 40 | + unpublished version number, so `0.16.6-sqd.1` can never be re-published after a bad build. |
| 41 | +- A **trusted publisher** for `@subsquid/ponder` is configured on npmjs.com pointing at |
| 42 | + `subsquid-labs/portal-ponder` → `release.yml`. One-time setup — see |
| 43 | + [First-time setup](#first-time-setup-trusted-publishing). Without it, the publish step 404s. |
| 44 | + |
| 45 | +## The two version forms |
| 46 | + |
| 47 | +This trips people up. There are two distinct strings: |
| 48 | + |
| 49 | +| Form | Example | Where it lives | |
| 50 | +|------|---------|----------------| |
| 51 | +| **git tag** | `v0.16.6` | what you push to trigger the workflow; the bare ponder version, prefixed `v` | |
| 52 | +| **npm version** | `0.16.6-sqd.1` | what actually publishes; ponder version + `-sqd.<rev>` | |
| 53 | + |
| 54 | +The workflow strips `v` off the tag → `0.16.6`, guards it against `versions.json` (which stores |
| 55 | +**bare** ponder versions), then `sync-upstream.sh` appends `-sqd.<rev>` at build time. |
| 56 | + |
| 57 | +**Consequence:** tag `v0.16.6`, never `v0.16.6-sqd.1` — the `-sqd.1` form would fail the |
| 58 | +`versions.json` guard (no such row). And the **tag-push path always publishes `rev` = 1** |
| 59 | +(`SYNC_REV` defaults to `1` on a tag push). To publish `rev` ≥ 2, you **must** use the manual |
| 60 | +`workflow_dispatch` and pass the `rev` input — a tag push can't express a revision bump. |
| 61 | + |
| 62 | +## Steps |
| 63 | + |
| 64 | +### 1. Pick the trigger |
| 65 | + |
| 66 | +Two ways in, same `publish` job: |
| 67 | + |
| 68 | +- **Manual (preferred, and required for rev ≥ 2):** Actions → **release** → *Run workflow* → |
| 69 | + enter `version` (e.g. `0.16.6`) and `rev` (e.g. `1`). From the CLI: |
| 70 | + |
| 71 | + ```sh |
| 72 | + gh workflow run release.yml --repo subsquid-labs/portal-ponder -f version=0.16.6 -f rev=1 |
| 73 | + ``` |
| 74 | + |
| 75 | +- **Tag push (rev 1 only):** push a `v<version>` tag on the release branch. |
| 76 | + |
| 77 | + ```sh |
| 78 | + git tag v0.16.6 |
| 79 | + git push origin v0.16.6 |
| 80 | + ``` |
| 81 | + |
| 82 | + The workflow triggers on `tags: ['v*.*.*']`. This path is `rev` = 1 by construction. |
| 83 | + |
| 84 | +### 2. Watch the workflow |
| 85 | + |
| 86 | +```sh |
| 87 | +gh run watch --repo subsquid-labs/portal-ponder --exit-status |
| 88 | +``` |
| 89 | + |
| 90 | +Or grab the latest run explicitly: |
| 91 | + |
| 92 | +```sh |
| 93 | +RUN_ID=$(gh run list --repo subsquid-labs/portal-ponder --workflow=release.yml --limit 1 --json databaseId --jq '.[0].databaseId') |
| 94 | +gh run watch "$RUN_ID" --repo subsquid-labs/portal-ponder --exit-status |
| 95 | +``` |
| 96 | + |
| 97 | +The single `publish` job runs: resolve version → **guard against `versions.json`** → |
| 98 | +`sync-upstream.sh <ver> --test` (apply layer, build, run Portal unit tests) → `npm publish`. If the |
| 99 | +publish step fails on Trusted Publishing, the npm-side config is missing/misconfigured — surface the |
| 100 | +error, don't retry blindly. See [Failure modes](#failure-modes). |
| 101 | + |
| 102 | +### 3. Confirm the publish and dist-tag |
| 103 | + |
| 104 | +The workflow tags `latest` **only** for the newest ponder version in `versions.json`; publishing an |
| 105 | +older version (e.g. `0.15.17` after `0.16.6`) publishes it under `ponder-<version>` so it does not |
| 106 | +clobber the default `npm install`. Verify where it landed: |
| 107 | + |
| 108 | +```sh |
| 109 | +npm view @subsquid/ponder dist-tags |
| 110 | +npm view @subsquid/ponder@<version>-sqd.<rev> version # confirm the exact build exists |
| 111 | +``` |
| 112 | + |
| 113 | +`latest` should point at the newest ponder version's `-sqd` build; an older release should sit under |
| 114 | +`ponder-<version>` and users pin the exact `@subsquid/ponder@<version>-sqd.<rev>`. |
| 115 | + |
| 116 | +### 4. Flip the versions.json row to published |
| 117 | + |
| 118 | +After a successful publish, update the row in [`versions.json`](../../../versions.json): set |
| 119 | +`"status": "published"` (and `"published": true` if not already), bump `"rev"` if you cut a new |
| 120 | +revision, and trim the `note` to reflect reality (drop stale "publish candidate / BROKEN seed" |
| 121 | +caveats once they no longer apply). Commit: |
| 122 | + |
| 123 | +```sh |
| 124 | +git add versions.json |
| 125 | +git commit -m "chore(release): @subsquid/ponder <version>-sqd.<rev> published" |
| 126 | +git push |
| 127 | +``` |
| 128 | + |
| 129 | +This is a real state change other releases and CI read — don't skip it. |
| 130 | + |
| 131 | +### 5. Write the GitHub release notes |
| 132 | + |
| 133 | +**Required — every release gets one.** `release.yml` publishes to npm only; it does **not** create a |
| 134 | +GitHub Release, so this is a manual step you always do. Write curated, Portal-layer-scoped highlights |
| 135 | +(what the fork changed vs plain `ponder@<version>`: the Portal backfill seam, wiring touch-points, |
| 136 | +correctness fixes, tests) — **not** a restatement of ponder's own changelog. Use |
| 137 | +[release-template.md](release-template.md) as the shape, and source highlights from the commits since |
| 138 | +the previous `-sqd` cut. Apply from a file to avoid heredoc escaping around code fences: |
| 139 | + |
| 140 | +```sh |
| 141 | +# Tag-push release: the v<version> tag already exists — just attach the release. |
| 142 | +gh release create v<version> --repo subsquid-labs/portal-ponder \ |
| 143 | + --title "@subsquid/ponder <version>-sqd.<rev>" --notes-file notes.md |
| 144 | + |
| 145 | +# If the release already exists (e.g. re-editing notes): |
| 146 | +gh release edit v<version> --repo subsquid-labs/portal-ponder --notes-file notes.md |
| 147 | +``` |
| 148 | + |
| 149 | +**Manual-dispatch releases have no tag** (a `workflow_dispatch` run doesn't push one). `gh release |
| 150 | +create` will create the `v<version>` tag for you, but it defaults to the branch HEAD — pin it to the |
| 151 | +exact commit that was released with `--target`: |
| 152 | + |
| 153 | +```sh |
| 154 | +gh release create v<version> --repo subsquid-labs/portal-ponder \ |
| 155 | + --target <released-commit-sha> \ |
| 156 | + --title "@subsquid/ponder <version>-sqd.<rev>" --notes-file notes.md |
| 157 | +``` |
| 158 | + |
| 159 | +For a re-cut (`rev` ≥ 2), the `v<version>` tag from the earlier revision already exists — don't move |
| 160 | +it. Either append the revision's notes to the existing release, or create a distinct |
| 161 | +`v<version>-sqd.<rev>` release tag for that revision. |
| 162 | + |
| 163 | +### 6. Print the summary |
| 164 | + |
| 165 | +```sh |
| 166 | +echo "npm: @subsquid/ponder@<version>-sqd.<rev>" |
| 167 | +echo "tag: https://github.com/subsquid-labs/portal-ponder/releases/tag/v<version>" |
| 168 | +npm view @subsquid/ponder dist-tags |
| 169 | +``` |
| 170 | + |
| 171 | +## Re-cutting a fork revision (same ponder version) |
| 172 | + |
| 173 | +A bad build on `<version>-sqd.1` can't be re-published under the same number. To ship a fork-side fix |
| 174 | +on the **same** ponder version: |
| 175 | + |
| 176 | +1. Fix the Portal layer (`portal/*.ts` or `portal/wiring/<ver>.patch`) and land it on the branch. |
| 177 | +2. `SYNC_REV=2 scripts/sync-upstream.sh <ver> --test` — confirm green. |
| 178 | +3. Bump the row's `"rev"` in `versions.json`. |
| 179 | +4. Release via **manual dispatch** with `rev=2` (a tag push can't — it's always rev 1). |
| 180 | + |
| 181 | +## Adding a new ponder version |
| 182 | + |
| 183 | +If the requested ponder version has no `versions.json` row / wiring patch, it can't be released yet. |
| 184 | +Per [`PUBLISHING.md`](../../../PUBLISHING.md): |
| 185 | + |
| 186 | +1. Author `portal/wiring/<ver>.patch` — the 4 touch-points (`internal/types.ts`, `config/index.ts`, |
| 187 | + `build/config.ts`, `runtime/historical.ts`). See an existing patch for the shape. |
| 188 | +2. `scripts/sync-upstream.sh <ver> --test` — applies, builds, Portal tests pass. |
| 189 | +3. Add a `{ "ponder": "<ver>", "wiring": "wiring/<ver>.patch", "rev": 1, "status": "verified" }` row |
| 190 | + and add `<ver>` to `compat.tested`. |
| 191 | +4. Commit (CI proves the seam on every push). Then release with the steps above. |
| 192 | + |
| 193 | +## First-time setup (Trusted Publishing) |
| 194 | + |
| 195 | +Publishing uses OIDC — there is no `NPM_TOKEN`. Once, before the first automated release, a maintainer |
| 196 | +with publish rights on the `@subsquid` scope configures a trusted publisher on npmjs.com: |
| 197 | + |
| 198 | +- `@subsquid/ponder` → **Settings** → **Trusted Publisher** → GitHub Actions. |
| 199 | +- Repository: `subsquid-labs/portal-ponder` |
| 200 | +- Workflow filename: `release.yml` |
| 201 | + |
| 202 | +If npm requires the package to exist before a trusted publisher can be added, **seed it with one |
| 203 | +manual publish**, then configure the publisher — every release after that is tokenless: |
| 204 | + |
| 205 | +```sh |
| 206 | +SYNC_REV=1 SYNC_WORKDIR=/tmp/sqd-fork scripts/sync-upstream.sh <ver> --test # build + test locally |
| 207 | +cd /tmp/sqd-fork/<ver>/packages/core && npm publish --access public --tag latest # requires npm login |
| 208 | +``` |
| 209 | + |
| 210 | +When the repo is public, add `--provenance` to the publish step for a signed provenance attestation. |
| 211 | + |
| 212 | +## Failure modes |
| 213 | + |
| 214 | +- **Guard fails: "`<ver>` not in versions.json"**: the version has no row / wiring patch. Add it first |
| 215 | + — see [Adding a new ponder version](#adding-a-new-ponder-version). Don't hand-edit the workflow to |
| 216 | + skip the guard. |
| 217 | +- **Tag pushed as `v<ver>-sqd.<rev>`**: it fails the `versions.json` guard (rows are bare ponder |
| 218 | + versions). Delete the tag, push `v<ver>` instead. For a revision bump, use manual dispatch with the |
| 219 | + `rev` input, not a tag. |
| 220 | +- **`sync-upstream.sh` fails (patch won't apply / build breaks)**: the wiring patch drifted against |
| 221 | + that ponder version, or the Portal modules broke the build. Reproduce locally with |
| 222 | + `SYNC_REV=<rev> scripts/sync-upstream.sh <ver> --test`, fix the patch/modules, re-run. This is the |
| 223 | + same step CI runs, so a red `ci.yml` points at the same fix. |
| 224 | +- **Publish fails on Trusted Publishing (404/401)**: the trusted publisher for `@subsquid/ponder` is |
| 225 | + missing or points at the wrong repo/workflow. See |
| 226 | + [First-time setup](#first-time-setup-trusted-publishing). Don't fall back to a token-based |
| 227 | + `npm publish` unless the user explicitly asks. |
| 228 | +- **npm >= 11.5.1 required**: OIDC publishing needs a recent npm; node 22 ships an older one. The |
| 229 | + workflow already does `npm install -g npm@latest` before publishing — if you publish locally, do |
| 230 | + the same or the OIDC step is unavailable. |
| 231 | +- **Version already published**: `0.16.6-sqd.1` exists on npm and can't be re-published. Bump the |
| 232 | + `rev` and re-cut via manual dispatch — see |
| 233 | + [Re-cutting a fork revision](#re-cutting-a-fork-revision-same-ponder-version). |
| 234 | +- **`latest` clobbered by an older release**: publishing an older ponder version should route to |
| 235 | + `ponder-<version>`, not `latest` — the workflow computes the max from `versions.json`. If `latest` |
| 236 | + drifted (e.g. a manual publish), repoint it: `npm dist-tag add @subsquid/ponder@<newest-sqd> latest` |
| 237 | + (requires npm auth — a manual recovery outside the OIDC workflow). |
0 commit comments