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
65 changes: 65 additions & 0 deletions .github/scripts/release-rehearsal.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Rehearse a release pull request: build the Go adapter the way it will be built in one.
//
// Release Please rewrites every line marked `x-release-please-version` to the version being released,
// and that version's tag does not exist until the release PR merges. Builds that pass on main can
// therefore fail on the release PR (0.6.0 did: workspace mode still read gateway@v0.6.0's go.mod).
// This applies Release Please's own version regex to the marked lines, with a version that can never
// be tagged, vets the adapter, and restores the files.
//
// It also refuses drift: go.work's replacement must name the version gateway/kube/go.mod requires.

import { execFileSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";

// Verbatim from release-please src/updaters/generic.ts.
const VERSION_REGEX = /(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(-(?<preRelease>[\w.]+))?(\+(?<build>[-\w.]+))?/;
const MARKER = "x-release-please-version";
// A v0 version (the module path has no /vN suffix) that will never be tagged.
const REHEARSED = "0.999.0";
const CORE = "github.com/ConfigButler/krm-stream/gateway v";

const files = ["gateway/kube/go.mod", "go.work"];
const originals = new Map(files.map((f) => [f, readFileSync(f, "utf8")]));

const coreVersion = (file) => {
const line = originals
.get(file)
.split("\n")
.find((l) => l.includes(CORE) && l.includes(MARKER));
if (!line) throw new Error(`${file}: no ${MARKER} line for the core module`);
return line.slice(line.indexOf(CORE) + CORE.length).split(/\s/)[0];
};

const required = coreVersion("gateway/kube/go.mod");
const replaced = coreVersion("go.work");
if (required !== replaced) {
console.error(
`::error file=go.work::go.work replaces core v${replaced}, but gateway/kube/go.mod requires v${required}. ` +
"They must name the same version; Release Please moves both.",
);
process.exit(1);
}

try {
for (const [file, text] of originals) {
const rehearsed = text
.split("\n")
.map((line) => (line.includes(MARKER) ? line.replace(VERSION_REGEX, REHEARSED) : line))
.join("\n");
writeFileSync(file, rehearsed);
}
if (!readFileSync("gateway/kube/go.mod", "utf8").includes(`${CORE}${REHEARSED}`)) {
throw new Error("the rehearsal did not rewrite the adapter's core requirement");
}
const run = (...args) => execFileSync("go", args, { cwd: "gateway/kube", stdio: "inherit" });
try {
run("vet", "-tags", "e2e", "./...");
run("build", "./...");
} catch {
console.error(`::error::the adapter does not build as a release PR for v${REHEARSED} would build it (see go's output above)`);
process.exitCode = 1;
}
if (!process.exitCode) console.log(`the adapter builds as a release PR for v${REHEARSED} would build it`);
} finally {
for (const [file, text] of originals) writeFileSync(file, text);
}
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,15 @@ jobs:
working-directory: gateway/kube
args: --build-tags e2e

# A release PR requires a core version whose tag does not exist until it merges, which main
# never does. 0.6.0's release PR failed here for that reason alone. Rehearse it on every change:
# Release Please's own regex, a version that is never tagged, then vet and build.
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "24"
- name: rehearse a release PR (its core version is not tagged yet)
run: node .github/scripts/release-rehearsal.mjs

# Can a STRANGER actually use this? Nothing else in this repo can answer that.
#
# gateway/kube once carried `replace ... => ../`, and a replace applies ONLY to the main module. So
Expand Down
7 changes: 7 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ tasks:
- task: fixtures-check
- task: lint
- task: test
# After the suites, not beside them: it rewrites go.work while it runs.
- task: release-rehearsal
- task: e2e-wire
- task: e2e-browser
# Last, because nothing else proves it: what we upload contains what the exports map advertises.
Expand Down Expand Up @@ -112,6 +114,11 @@ tasks:
cmds:
- go test ./...

release-rehearsal:
desc: "Build the Go adapter as a release PR would, with a core version that is not tagged yet."
cmds:
- node .github/scripts/release-rehearsal.mjs

test-client:
desc: "TypeScript: the client + its half of the conformance suite (node --test, no deps)."
deps: [fixtures, _client-deps]
Expand Down
5 changes: 4 additions & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ Only `@configbutler/krm-stream` is maintained on npm. Do not publish to the unsc

`gateway/kube/go.mod` requires the core module at the release it ships with. Every release pull
request rewrites that line to the version being released (its `x-release-please-version` marker),
so a released adapter always resolves at least its own core. Do not edit it, and do not pin a
so a released adapter always resolves at least its own core. The matching `replace` in `go.work` moves
with it: a release PR names a core version that is not tagged until it merges, and that replace is
what lets the adapter build in it. `task release-rehearsal`, run by `task verify` and CI, builds the
adapter in exactly that state. Do not edit either line, and do not pin a
pseudo-version when an adapter change needs unreleased core API: `go.work` wires the two modules
together in this checkout, and CI's `a stranger can go get this` job builds an unreleased adapter
with the core from the same commit. Someone using an unreleased commit does the same:
Expand Down
8 changes: 8 additions & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ use (
./gateway
./gateway/kube
)

// The adapter requires the core at the release it ships with, and a release PR names a version whose
// tag does not exist until it merges. Workspace mode still reads the required version's go.mod, so
// without this every build of the adapter in a release PR fails ("unknown revision gateway/vX.Y.Z").
// This points that exact version at the working tree. Go refuses an unversioned replace of a
// workspace module, so the version is spelled out, and Release Please moves it together with
// gateway/kube/go.mod; CI fails if the two disagree.
replace github.com/ConfigButler/krm-stream/gateway v0.5.0 => ./gateway // x-release-please-version
4 changes: 4 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
{
"type": "generic",
"path": "go.mod"
},
{
"type": "generic",
"path": "/go.work"
}
]
}
Expand Down
Loading