Skip to content

ci: add a beta release channel - #150

Merged
0xisk merged 2 commits into
betafrom
ci/beta-release-pipeline
Aug 31, 2026
Merged

0xisk merged 2 commits into
betafrom
ci/beta-release-pipeline

Conversation

@0xisk

@0xisk 0xisk commented Aug 28, 2026 •

Copy link
Copy Markdown
Member

Lets beta cut prereleases without touching npm's latest tag. Targets beta. pull_request workflows resolve from the base branch, so the publish path only exists once this lands there.

Types of changes

  • New feature (non-breaking change which adds functionality)

Not visible in the diff

  • The publish workflow's pull_request trigger was branches: [main], so a release PR merged into beta published nothing. The workflow_dispatch escape hatch would have published from beta, but with no --tag it would have moved latest to a prerelease. That's why the dist-tag is derived from the version string rather than from the branch: the unsafe path becomes impossible instead of merely discouraged.
  • Yarn 4 has no --preid, and yarn version prerelease off a stable version gives 0.3.2-0, not 0.3.2-beta.0. Hence the relabel step. Verified against yarn 4.10.3: once the identifier exists, prerelease carries it forward (-beta.0 -> -beta.1 -> ... -> -beta.10), and patch on a prerelease drops the suffix, which is the graduation path documented in RELEASING.md.
  • The strategy/branch guard is belt-and-braces on top of that. It fails before the bump so a mis-picked branch costs nothing.

Left out

No branch-protection work: beta is already covered by the active "Main Branch Protection" ruleset (include: [~DEFAULT_BRANCH, refs/heads/beta]), and the compact-npm-prod environment has deployment_branch_policy: null, so auto-merge and the approval gate behave on beta as they do on main.

compact-deployer is not added to the package choice lists — it isn't on this branch.

PR Checklist

  • I have read the Contributing Guide
  • I have added documentation of new methods and any new behavior or changes to existing behavior
  • CI Workflows Are Passing

Release workflows have no test harness here. The bump/normalize and dist-tag logic were both exercised locally against the version strings currently on beta (0.0.3, 0.0.2, 0.3.1) across all seven strategies; every run block is bash -n clean.

Summary by CodeRabbit

  • New Features

    • Added support for beta releases alongside stable releases.
    • Beta versions are published with a dedicated npm beta channel, while stable versions use latest.
    • Added beta-to-stable promotion workflows with branch-specific release validation.
  • Documentation

    • Expanded release guidance covering version bumps, branch selection, approvals, publishing, and promotion procedures.
  • Chores

    • Updated automated checks and release workflows to support both main and beta branches.

Prerelease versions publish under the `beta` npm dist-tag, stable ones
under `latest`. The publish workflow derives the tag from the version
string alone, so a prerelease cannot take over `latest` no matter which
ref triggered the run.

Yarn's `version` has no `--preid`, so a pre* strategy off a stable
version lands a bare counter (0.3.1 -> 0.3.2-0). The bump step relabels
that once to `-beta.0`; yarn carries the identifier forward on every
later prerelease.
@0xisk
0xisk requested review from a team as code owners August 28, 2026 09:10
@coderabbitai

coderabbitai Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ef435f50-06ab-4ed3-8c02-e4100313c154

📥 Commits

Reviewing files that changed from the base of the PR and between 9226f19 and 4b59256.

📒 Files selected for processing (4)
  • .github/workflows/checks.yml
  • .github/workflows/release-publish.yml
  • .github/workflows/release.yml
  • RELEASING.md

Included review availability: 2 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.


Walkthrough

The release automation now supports main and beta channels. It validates branch-specific version strategies, assigns npm dist-tags, publishes with those tags, and documents release and beta-to-stable promotion procedures.

Changes

Release channel automation

Layer / File(s) Summary
Branch and version strategy contracts
.github/workflows/checks.yml, .github/workflows/release.yml, RELEASING.md
The workflows support the beta branch. Release inputs accept prerelease strategies and enforce branch-specific rules.
Version and dist-tag generation
.github/workflows/release.yml
Prerelease versions receive the beta identifier. Release metadata and pull request text include the selected npm dist-tag.
Channel-aware publishing
.github/workflows/release-publish.yml
The publishing workflow detects the base branch, validates release channels, computes beta or latest, and passes the tag to npm publishing.
Release approval and promotion procedure
RELEASING.md
The documentation covers approval, automated publishing, and beta-to-stable promotion.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 4b592

This change adds a beta prerelease channel without moving npm's latest tag, and no actionable merge-blocking risk remains based on the supplied evidence.

Poem

A rabbit checks the beta lane,
Then tags the carrots fresh again.
latest hops from main with care,
beta sprouts from branches there.
Release notes wiggle in delight.

🚥 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 concisely describes the main change: adding a beta release channel to CI and release workflows.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
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.
Full details: Docstring Coverage

Explanation

No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0 files. (4 skipped: 4 unsupported.)

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/beta-release-pipeline

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

@0xisk

0xisk commented Aug 28, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 28, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@0xisk
0xisk merged commit d46a332 into beta Aug 31, 2026
10 checks passed
@0xisk
0xisk deleted the ci/beta-release-pipeline branch August 31, 2026 07:54
0xisk added a commit that referenced this pull request Sep 1, 2026
* ci(codeql): run analysis on beta (#146)

The "Main Branch Protection" ruleset covers main and beta, and its
code_scanning rule requires CodeQL results. CodeQL only triggered on
main, so PRs into beta were blocked waiting on an analysis that never
ran.

* Bump deps to prerelease, add ecdsa tests (#145)

* add tmp runtime dep

* update to 0.18 runtime

* wire sim to 0.18-rc/ledger-9 stack

* fix fmt

* add ecdsa tests

* add pre-release compact install

* fix(simulator): clear the #145 review comments (#147)

* fix(simulator): pin simulated block time to zero

compact-runtime 0.18 defaults `createCircuitContext`'s `time` to
wall-clock, where the hand-rolled 0.16 `QueryContext` left it at 0. Any
suite reading `kernel.blockTime()` silently became non-reproducible.

Threads an explicit `time` from `BaseSimulatorOptions` through to the
runtime factory, defaulting to 0 to restore the previous behaviour.

* refactor(simulator): drop the dead circuit-context helpers

`useCircuitContext` and `useCircuitContextSender` had no callers and were
never exported from the barrel, so they were unreachable before this
migration and got hand-ported to the 0.18 call-tree shape untested.

* fix(simulator): guard context reads before init()

0.18 split construction from state building, so a caller that skipped
`init()` got `Cannot read properties of undefined (reading
'callContext')`. Both `CircuitContextManager.context` and the simulator's
`contractAddress` now throw a named error instead.

* fix(simulator): declare initialState async on IMinimalContract

The canonical interface still promised the sync-only 0.16 return type, so
anything typed against it got the wrong shape and
`CircuitContextManager` carried a private redeclaration to compensate.

* fix(simulator): typecheck the test tree

`tsconfig.types.json` compiled `src/**` only, and its `exclude` named a
`tests` directory that does not exist, so nothing verified the test tree.
Adding a project that covers it surfaced 74 errors, of which the
load-bearing one is in `src/`: 0.18 circuits return
`Promise<CircuitResults>`, which the `ContextlessCircuits` /
`AsyncCircuits` conditionals did not match, collapsing every entry on
`circuits.pure` and `circuits.impure` to `never`.

* src: match the promised circuit shape; keep the anonymous class's
  address field underscore-public for declaration emit
* test: give relative imports the `.js` extension nodenext requires
* wiring: run both projects under `yarn types`, and let turbo see the
  test tree so a regression is not cached away

* fix(simulator): build the setContext fixture as a real 0.18 context

The replacement context was a hand-rolled literal in the pre-0.18 flat
shape, so `setContext`'s only test asserted against a context the runtime
would reject. `toEqual` compared two plain objects and passed anyway.

Rebuilds it through `createCircuitContext` and asserts identity plus the
two fields the test is about. Exporting `BackendDeps` is required for the
same reason: it appears in `create`/`_create`, so an exported binding
assigned from `createSimulator(...)` could not name it (TS4023).

* fix(ci): make the prerelease compiler input functional and verified

* `COMPACTC_VERSION` is now exported to `$GITHUB_ENV`. `VER` was
  step-scoped, so `test/setup.ts` never saw it: overriding the input
  installed one toolchain while the fixtures compiled with the hardcoded
  default.
* The archive is checked against a pinned SHA-256 before it is unpacked
  and executed. The release publishes no checksum file, so the digests
  are pinned per platform here; overriding the version requires passing
  its digest.

* fix(simulator): key the fixture cache on the compiler

The staleness check compared artifact mtime against source mtime only,
so an existing clone kept its 0.31-compiled artifacts and hit a load-time
rejection against the 0.18 runtime. CI never saw it: the `test` turbo
task is uncached and the checkout is clean.

Artifacts now carry a stamp of the compiler version and the zkir flag,
and recompile when it does not match. The flag also moves onto the
fixture list, replacing a `secp256k1` regex over the source that a
mention in a comment was enough to trip.

* test(simulator): collapse the field-to-bytes helpers

Two near-identical copies existed in the test tree. The one in
`SampleZOwnable.test.ts` silently truncated where the runtime helper it
replaced threw, so a wrong value would read as a passing assertion.

Keeps one in the shared test utils, with the range check restored, and
derives `zeroUint8Array` from it. `Signers.ts` keeps its own copy: `src`
cannot import from `test`.

* test(simulator): consume the generated secp256k1 types

The `Ecdsa.compact` re-export is live: the artifact's `index.d.ts`
declares both `Secp256k1EcdsaSignature` and `Secp256k1Point`. The
simulator hand-rolled the signature struct and took the point from the
runtime instead, so a change to the generated shape would not surface
here.

* docs(simulator): correct the sync-era comments

The async migration left the docs describing a synchronous engine: the
dry primitive called "synchronous", `DryBackend` said it wraps a
synchronous result in a resolved promise and preserves dry behaviour
"byte-for-byte", and the `SyncSimulator` name now describes nothing.

Renaming the exported type is a breaking change, so it keeps the name
with a `@remarks` note. Also records that `copyCircuitContext` is
`@internal` upstream.

* ci(turbo): declare COMPACTC_VERSION on the test task

The setup action exports it and `test/setup.ts` reads it, but the task
never declared it. Pass-through happens to work on turbo 2.9, so this
only pins the behaviour against a stricter env mode.

* fix(ci): compile fixtures before the type check

`yarn types` now covers `test/**`, which imports the generated artifacts,
but `checks.yml` runs it before `yarn test` — the only thing that
compiled them. It passed locally only because the artifacts were already
on disk.

Lifts fixture compilation into its own turbo task that `types` depends
on. `test/setup.ts` gains a direct entry point so the task does not have
to go through vitest; vitest's `globalSetup` still calls the same
function.

* ci: add a beta release channel (#150)

* ci: add a beta release channel

Prerelease versions publish under the `beta` npm dist-tag, stable ones
under `latest`. The publish workflow derives the tag from the version
string alone, so a prerelease cannot take over `latest` no matter which
ref triggered the run.

Yarn's `version` has no `--preid`, so a pre* strategy off a stable
version lands a bare counter (0.3.1 -> 0.3.2-0). The bump step relabels
that once to `-beta.0`; yarn carries the identifier forward on every
later prerelease.

* docs: add a beta graduation cycle example

* build: upgrade to compact toolchain 0.34.0 (#152)

0.34.0 is the first stable release carrying the secp256k1 primitives and
ledger 9, so the prerelease-compiler bridge in the setup action is no
longer needed. `setup-compact-action` passes the version straight to
`compact update`, which serves 0.34.0.

Generated artifacts assert the runtime minor at load time, so the
compact-runtime pin moves 0.18.0-rc.1 -> 0.19.0 in lockstep with the
compiler. midnight-js goes to 5.0.0-beta.7 to stay on the current beta
line.

0.34.0 makes `Secp256k1Point` an internally handled type that a contract
can no longer re-export. The Ecdsa fixture drops it from its export list
and `EcdsaSimulator` sources the type from compact-runtime, which is
where the generated artifact now references it.

* fix(simulator): async proxy types and live address binding (#154)

* fix(simulator): type circuit proxies as promise-returning

The 0.18 migration made every circuit proxy async but left
ContextlessCircuits mapping to the bare result, so a consumer typed
`boolean` received `Promise<boolean>` and any truthiness check passed
vacuously. The cast on the Proxy return masked the mismatch from tsc.

* Circuit.ts: map ContextlessCircuits to `Promise<R>`; AsyncCircuits
  becomes an alias so the two cannot drift.
* create-contract.type-test.ts: pin the promise mapping for both the
  0.18 promise shape and the 0.16 sync shape; fails on the old mapping.
* createDrySimulator.ts: narrow the init guard to `=== undefined` and
  note why the private-state cast is sound.
* AbstractSimulator.ts: same cast note.

* fix(simulator): bind live pure evaluation to deployed address

In live mode the local evaluator was built before the LiveContext
resolved, so it kept the dummy address in its circuit context while
the public simulator reported the deployed one. Resolve the context
first, seed the evaluator with the deployed address, and reject an
explicit contractAddress that disagrees with it.

The deployed address now reaches the runtime, so a live harness must
supply a parseable one; the mock worlds in the tests move from a
made-up string to dummyContractAddress().

* docs(release): hyphenate and align prerelease wording

---------

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
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