Skip to content

Prepare a coordinated release ​

Language: English | 简体中文

This maintainer guide cuts one immutable version across every public module. scripts/modules.sh --public is the executable release inventory; it derives complete membership from go.work and the public subset from externally importable production packages across the supported source sets. The release script derives dependency order from the modules' go.mod files. Do not create tags or edit dependency versions by hand.

Inventory derivation is a fallible query, not a best-effort filter. Every supported source set must be readable before the script publishes any names; failure and an empty public set stop the release. Consumers first capture a successful inventory and only then iterate it, while diagnostics remain on standard error. This distinction prevents an unavailable toolchain, dependency graph, or checkout from being mistaken for a repository with nothing to release.

Know what is released ​

The public release train contains core, components, markdown, highlight, latex, mermaid, ptytest, and ssh. Every public module receives the same version even when its own files did not change. examples and internal are tested but never tagged. This prose list explains the current train; tooling must consume scripts/modules.sh --public rather than copy it.

Before v1, every exported API may change. From v1 onward, the release must preserve Go source compatibility with the previous v1 release. The pinned gorelease check enforces that boundary. Independently, pinned apidiff compares the working API with the preceding immutable tag on every change. Every incompatible exported API change must appear by exact name in the Unreleased migration ledger, so review can distinguish an intentional contract decision from deletion or reshaping driven by repository reachability.

Prepare the repository ​

Complete these steps on main:

  1. Choose the next canonical X.Y.Z version
  2. Move the entries under Unreleased into ## [X.Y.Z] — YYYY-MM-DD
  3. Leave a new empty Unreleased section for later work
  4. Run the complete CI workflow and resolve every failure
  5. Confirm main is clean and matches origin/main

CI checks the newest version's migration ledger while any public module lacks that version's tag. Once the entire train is tagged, it checks Unreleased again.

Do not place a replace directive in any module. A workspace replacement is not part of a published module and would make local verification test a different graph from the one consumers receive.

Inspect the dry run ​

Run the release command without --execute:

sh
scripts/release.sh X.Y.Z

The dry run performs the local gate, checks first-phase API compatibility, derives dependency phases, and prints every dependency bump and tag. It does not modify the repository or push a tag.

Review these facts in its output:

  • Every public module appears exactly once in a tag phase
  • A module is tagged after every Oolong module it imports
  • The proposed changelog section exists
  • Every incompatible exported API change appears by exact API name in its module's migration ledger for the proposed version
  • gorelease reports the expected pre-1.0 break or v1 compatibility result
  • No local or remote tag already uses the version

Execute once ​

After reviewing the plan, run:

sh
scripts/release.sh X.Y.Z --execute

The script asks for the version again when attached to a terminal. It then updates downstream requirements phase by phase, tests each published dependency graph, commits and pushes dependency bumps, runs compatibility at the last safe point, prepares all annotated module tags in that phase, and pushes each tag separately. GitHub does not create tag push events when more than three tags are pushed at once; individual pushes let every module trigger its release contract check. A failed push stops the release before later tags or dependency phases are published.

During the release, Oolong module downloads use the Git origin directly and exclude only that module namespace from checksum-database lookups. Proxy and checksum lookups can cache the absence of a tag that the release has just pushed; they do not own publication. Other dependencies and downloaded toolchains keep checksum verification. Dependency resolution errors remain visible.

Do not interrupt the process after the first tag is pushed. A tag recorded by the Go proxy or checksum database is immutable, even if it is later deleted from GitHub.

Verify the result ​

The script verifies every remote tag and prints the commit it names. The Go proxy is eventually consistent; query it once later instead of polling it during the release:

sh
GOPROXY=https://proxy.golang.org GOFLAGS=-mod=mod \
  go list -m github.com/Tangerg/oolong/core@vX.Y.Z

Check one downstream module as well, then create GitHub release notes from the same changelog section. Do not publish a second set of release notes with different behavioral claims.

Full workspace CI runs on branches and pull requests. Module tags trigger the separate release contract workflow, which validates the tagged module against its published dependencies. A phase's snapshot can still contain unfinished higher modules, so workspace checks belong to the branch and must pass again on the final dependency-bump commit.

API comparison runs in release.sh before each tag is published. The tag workflow uses gorelease -base=none -version=... to validate module usability; asking it to suggest a next version after publication would depend on proxy cache timing.

Confirm that every module tag has a successful release contract run. A tag's existence alone does not establish that its checks ran. If a tag has no run, use release-contract.yml with that actual tag as the ref and its module and version as inputs. For a failed run whose external blocker has cleared, rerun its failed jobs. Keep the original tag unchanged; running on main checks different source and dependency requirements.

Recover from a partial release ​

Never move, delete, or recreate a published tag. If execution stops after any tag reaches the remote:

  1. Record which module tags and dependency-bump commits were pushed
  2. Fix the cause on main
  3. Move the remaining release notes to the next patch version
  4. Run a new coordinated release for every public module

The next version supersedes the partial one. If a published module version is unsafe to select, retract it in that module's next go.mod release and explain the reason in the changelog.

Released under the Apache 2.0 License.