mirror of
https://github.com/Heretek-AI/IUMBTEMS.git
synced 2026-10-11 21:09:53 +00:00
fix(preset): retarget self-dogfood dev flow to main post-cutover (Refs #281)
This commit is contained in:
@@ -24,7 +24,7 @@ name: E2E feedback (opencode + plugin)
|
||||
# use OPENCODE_API_KEY for Zen/Go keys)
|
||||
on:
|
||||
push:
|
||||
branches: [rewrite]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
model:
|
||||
|
||||
@@ -4,13 +4,13 @@ name: Nightly (OpenCode v2 HEAD)
|
||||
# runs the real-host suite against the `v2` branch HEAD every night and turns
|
||||
# red when the host drifts. It is the mitigation, not a pin.
|
||||
#
|
||||
# Scheduled workflows only run from the default branch, so pre-cutover the job
|
||||
# is exercised through workflow_dispatch (and the push trigger on `rewrite`).
|
||||
# Scheduled workflows only run from the default branch, so the job
|
||||
# is exercised through workflow_dispatch (and the push trigger on `main`).
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * *"
|
||||
push:
|
||||
branches: [rewrite]
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
||||
@@ -75,7 +75,7 @@ spike as proof).
|
||||
registry or capability rows. Prompt changes require the assets snapshot
|
||||
(`bun test -u packages/core/test/assets.test.ts`) and a version bump.
|
||||
- Stage explicit paths; never `git add -A`. Commit per milestone.
|
||||
- Push only `rewrite` during development; never push or merge to `main`
|
||||
without asking. The 1.0 cutover (tag legacy, reviewed merge, npm publish via
|
||||
OIDC) is a separate, human-approved step.
|
||||
- Push only your ticket branch during development; never push or merge to `main`
|
||||
without asking — release lands on `main` through a human-merged PR.
|
||||
Publishing to npm via OIDC is a separate, human-approved step.
|
||||
- After any push, check every workflow (`gh run list`) — not just the obvious one.
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@ the complete normalized `CommandCheck` — `kind`, `parser`, `related`,
|
||||
change to what a gate runs revokes trust. **Humans: re-run `es trust` after
|
||||
upgrading**; previously approved command sets hash differently and the gates
|
||||
refuse to run until the new set is approved. The e2e regression gate now fails
|
||||
closed when no history is sampled (#210).
|
||||
closed when no history is sampled (#210). The self-dogfood preset releases
|
||||
onto `main` post-cutover: the `rewrite` pin, workflow triggers and runbook
|
||||
now target `main` (#281); the `rewrite` branch itself is untouched.
|
||||
|
||||
## 1.6.0 — 2026-10-09
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ suite, and open a draft PR a human reviews. This runbook is that loop.
|
||||
PR through it.
|
||||
- A sealed human key (`es key seal` once); approvals, trust and resume all
|
||||
need the passphrase.
|
||||
- Work on `rewrite`, with a clean tree.
|
||||
- Work on `main`, with a clean tree.
|
||||
|
||||
## The flow
|
||||
|
||||
@@ -22,25 +22,25 @@ suite, and open a draft PR a human reviews. This runbook is that loop.
|
||||
This writes `.factory/config.json` (`audit.phase: required`), the
|
||||
self-dogfood `.factory/gates.json` (`bun install --frozen-lockfile`,
|
||||
`bun run check`, `bun run docs:check`), a `.factory/roadmap.json` seed
|
||||
pinning `baseBranch: rewrite`, and the issue body as
|
||||
pinning `baseBranch: main`, and the issue body as
|
||||
`.factory/notes/idea-<n>.md`.
|
||||
2. **Trust the gates**: `es trust` (human-only). The gate commands run
|
||||
project code; review the hashes before trusting.
|
||||
3. **Grill**: `/grill` (or `es factory begin` plus the grill flow) with a
|
||||
**$15 spend ceiling** (the preset default; raise deliberately). Keep
|
||||
`baseBranch: rewrite` when the grill rewrites the roadmap — the factory
|
||||
never pushes `main` or `rewrite` itself.
|
||||
`baseBranch: main` when the grill rewrites the roadmap — the factory
|
||||
never pushes `main` itself.
|
||||
4. **Approve the frontier and the spec** (`es approve frontier`, then
|
||||
`es approve spec`) — on any Phase 2/4 surface: terminal, TUI or browser.
|
||||
5. **Build ⇄ QA** runs in the phase worktree under bwrap; the runtime cap
|
||||
is mandatory and `.factory/STOP` halts the run at its next step.
|
||||
6. **Release**: the merge gate runs in full; on green the factory pushes
|
||||
only its run branch and opens a **draft PR against `rewrite`**.
|
||||
only its run branch and opens a **draft PR against `main`**.
|
||||
A human reviews and merges. Auto-merge never happens.
|
||||
|
||||
## Reviewing the PR
|
||||
|
||||
- Check the base is `rewrite` and the head is a `factory/…` branch.
|
||||
- Check the base is `main` and the head is a `factory/…` branch.
|
||||
- Read the gate logs under `.factory/runs/<run>/gates/` and the spend
|
||||
against the $15 ceiling.
|
||||
- Merge only when every check — including CodeRabbit and SonarCloud —
|
||||
|
||||
@@ -1105,9 +1105,9 @@ describe("es factory init --preset self-dogfood (#137)", () => {
|
||||
expect(result.out).toContain(".factory/roadmap.json")
|
||||
expect(result.out).toContain("es trust")
|
||||
expect(result.out).toContain("$15")
|
||||
expect(result.out).toContain("rewrite")
|
||||
expect(result.out).toContain('keep baseBranch "main"')
|
||||
const roadmap = JSON.parse(await readFile(factoryLayout(root).roadmap, "utf8"))
|
||||
expect(roadmap.baseBranch).toBe("rewrite")
|
||||
expect(roadmap.baseBranch).toBe("main")
|
||||
expect(await readFile(path.join(root, ".factory/notes/idea-137.md"), "utf8")).toContain("Stub issue")
|
||||
const entries = await recentAuditEntries(root, 5)
|
||||
const seeded = entries.find((entry) => entry.action === "factory.preset")!
|
||||
|
||||
@@ -9,8 +9,8 @@ a human reviews.
|
||||
- `gates.json` — the real merge gate: `bun install --frozen-lockfile`,
|
||||
`bun run check`, `bun run docs:check`. Trust them with `es trust`.
|
||||
- `idea-from-issue.sh` — pulls the issue body as the run idea.
|
||||
- Base branch: `rewrite`, never `main`. `es factory init` seeds
|
||||
`.factory/roadmap.json` with `baseBranch: rewrite`; keep that field when
|
||||
- Base branch: `main`. `es factory init` seeds
|
||||
`.factory/roadmap.json` with `baseBranch: main`; keep that field when
|
||||
the grill rewrites the roadmap.
|
||||
- Spend ceiling: $15 default, set on the frontier during the grill; the
|
||||
runtime cap is mandatory. See `docs/SELF-DOGFOOD.md`.
|
||||
|
||||
@@ -20,8 +20,8 @@ const PRESETS = fileURLToPath(new URL("../../assets/presets/", import.meta.url))
|
||||
|
||||
/** The only preset so far: the factory builds this repo. */
|
||||
export const SELF_DOGFOOD_PRESET = "self-dogfood" as const
|
||||
/** Self-dogfood release PRs target `rewrite`, never `main`. */
|
||||
export const SELF_DOGFOOD_BASE_BRANCH = "rewrite" as const
|
||||
/** Self-dogfood release PRs target `main` (post-1.6.0 cutover, #281). */
|
||||
export const SELF_DOGFOOD_BASE_BRANCH = "main" as const
|
||||
/** Default spend ceiling (USD) the runbook sets on the grill frontier. */
|
||||
export const SELF_DOGFOOD_SPEND_CEILING_USD = 15 as const
|
||||
|
||||
@@ -139,8 +139,8 @@ export const readPresetSeed = (root: string): Promise<PresetSeed | undefined> =>
|
||||
|
||||
/**
|
||||
* The single argv builder every PR opener uses for `gh pr create --draft`.
|
||||
* For a preset-seeded run the base is pinned: `--base main` (or anything
|
||||
* other than the preset's base) is refused here, before anything is pushed.
|
||||
* For a preset-seeded run the base is pinned: anything other than the
|
||||
* preset's base is refused here, before anything is pushed.
|
||||
*/
|
||||
export function buildPrArgs(options: {
|
||||
branch: string
|
||||
@@ -151,7 +151,7 @@ export function buildPrArgs(options: {
|
||||
}): string[] {
|
||||
if (options.preset === SELF_DOGFOOD_PRESET && options.base !== SELF_DOGFOOD_BASE_BRANCH)
|
||||
throw new Error(
|
||||
`refusing --base "${options.base}" for a preset "${SELF_DOGFOOD_PRESET}" run (release PRs target "${SELF_DOGFOOD_BASE_BRANCH}", never "main")`,
|
||||
`refusing --base "${options.base}" for a preset "${SELF_DOGFOOD_PRESET}" run (release PRs target "${SELF_DOGFOOD_BASE_BRANCH}")`,
|
||||
)
|
||||
return [
|
||||
"gh",
|
||||
|
||||
@@ -29,10 +29,9 @@ describe("self-dogfood preset integration (#205)", () => {
|
||||
{ issue: 205, title: "Self-dogfood preset", body: "The factory builds this repo." },
|
||||
{ actor: "human:tester" },
|
||||
)
|
||||
// Current behavior (#281 owns the base-selection question): the seed
|
||||
// pins the release base to `rewrite`.
|
||||
expect(baseBranch).toBe("rewrite")
|
||||
expect(SELF_DOGFOOD_BASE_BRANCH).toBe("rewrite")
|
||||
// Post-cutover (#281): the seed pins the release base to `main`.
|
||||
expect(baseBranch).toBe("main")
|
||||
expect(SELF_DOGFOOD_BASE_BRANCH).toBe("main")
|
||||
expect(written).toEqual([
|
||||
".factory/config.json",
|
||||
".factory/gates.json",
|
||||
@@ -50,7 +49,7 @@ describe("self-dogfood preset integration (#205)", () => {
|
||||
"bun run docs:check",
|
||||
])
|
||||
const roadmap = RoadmapSchema.parse(JSON.parse(await readFile(layout.roadmap, "utf8")))
|
||||
expect(roadmap.baseBranch).toBe("rewrite")
|
||||
expect(roadmap.baseBranch).toBe("main")
|
||||
expect(roadmap.title).toContain("205")
|
||||
expect(await readFile(`${fx.root}/.factory/notes/idea-205.md`, "utf8")).toContain("# #205:")
|
||||
} finally {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Self-dogfood preset (#137): the factory builds this repo. Preset files
|
||||
// validate against their schemas, init seeds the run with the release base
|
||||
// pinned to `rewrite`, and the PR argv never names `main`.
|
||||
// pinned to `main`, and the PR argv always names that base.
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { access, mkdir, mkdtemp, readFile, rm } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
@@ -48,7 +48,7 @@ describe("self-dogfood preset files", () => {
|
||||
})
|
||||
|
||||
describe("initFromPreset", () => {
|
||||
test("seeds control files, a rewrite-based roadmap and the issue idea", async () => {
|
||||
test("seeds control files, a main-based roadmap and the issue idea", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "es-preset-"))
|
||||
try {
|
||||
const { written, baseBranch } = await initFromPreset(root, "self-dogfood", {
|
||||
@@ -56,7 +56,7 @@ describe("initFromPreset", () => {
|
||||
title: "Self-dogfood preset",
|
||||
body: "The factory builds this repo.",
|
||||
})
|
||||
expect(baseBranch).toBe("rewrite")
|
||||
expect(baseBranch).toBe("main")
|
||||
expect(written).toEqual([
|
||||
".factory/config.json",
|
||||
".factory/gates.json",
|
||||
@@ -64,7 +64,7 @@ describe("initFromPreset", () => {
|
||||
".factory/notes/idea-137.md",
|
||||
])
|
||||
const layout = factoryLayout(root)
|
||||
expect(RoadmapSchema.parse(JSON.parse(await readFile(layout.roadmap, "utf8"))).baseBranch).toBe("rewrite")
|
||||
expect(RoadmapSchema.parse(JSON.parse(await readFile(layout.roadmap, "utf8"))).baseBranch).toBe("main")
|
||||
expect(GatesConfigSchema.parse(JSON.parse(await readFile(layout.gates, "utf8"))).commands).toHaveLength(3)
|
||||
expect(await readFile(path.join(root, ".factory/notes/idea-137.md"), "utf8")).toContain(
|
||||
"# #137: Self-dogfood preset",
|
||||
@@ -76,12 +76,12 @@ describe("initFromPreset", () => {
|
||||
})
|
||||
|
||||
describe("self-dogfood PR base", () => {
|
||||
test("the draft PR targets rewrite, never main", () => {
|
||||
expect(SELF_DOGFOOD_BASE_BRANCH).toBe("rewrite")
|
||||
const argv = buildPrArgs({ branch: "factory/x/integration", base: "rewrite", title: "t", body: "b" })
|
||||
test("the draft PR targets main", () => {
|
||||
expect(SELF_DOGFOOD_BASE_BRANCH).toBe("main")
|
||||
const argv = buildPrArgs({ branch: "factory/x/integration", base: "main", title: "t", body: "b" })
|
||||
expect(argv).toContain("--draft")
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["rewrite"])
|
||||
expect(argv).not.toContain("main")
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["main"])
|
||||
expect(argv).not.toContain("rewrite")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -164,22 +164,25 @@ describe("preset-seeded runs pin the release base (S2.2)", () => {
|
||||
}
|
||||
expect(typeof core.buildPrArgs).toBe("function")
|
||||
expect(core.selfDogfoodPrArgs).toBeUndefined()
|
||||
const argv = core.buildPrArgs!({ branch: "factory/x/integration", base: "rewrite", title: "t", body: "b" })
|
||||
const argv = core.buildPrArgs!({ branch: "factory/x/integration", base: "main", title: "t", body: "b" })
|
||||
expect(argv.slice(0, 4)).toEqual(["gh", "pr", "create", "--draft"])
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["rewrite"])
|
||||
expect(argv).not.toContain("main")
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["main"])
|
||||
expect(argv).not.toContain("rewrite")
|
||||
})
|
||||
|
||||
test("the shared argv refuses --base main for a preset run", async () => {
|
||||
test("the shared argv refuses a non-main base for a preset run", async () => {
|
||||
const { buildPrArgs } = (await import("../src/index.ts")) as unknown as {
|
||||
buildPrArgs: (options: { branch: string; base: string; title: string; body: string; preset?: string }) => string[]
|
||||
}
|
||||
expect(() =>
|
||||
buildPrArgs({ branch: "factory/x/integration", base: "main", title: "t", body: "b", preset: "self-dogfood" }),
|
||||
).toThrow(/refusing.*main/)
|
||||
buildPrArgs({ branch: "factory/x/integration", base: "rewrite", title: "t", body: "b", preset: "self-dogfood" }),
|
||||
).toThrow(/refusing.*rewrite/)
|
||||
expect(() =>
|
||||
buildPrArgs({ branch: "factory/x/integration", base: "other", title: "t", body: "b", preset: "self-dogfood" }),
|
||||
).toThrow(/rewrite/)
|
||||
).toThrow(/release PRs target "main"/)
|
||||
expect(
|
||||
buildPrArgs({ branch: "factory/x/integration", base: "main", title: "t", body: "b", preset: "self-dogfood" }),
|
||||
).toContain("--draft")
|
||||
})
|
||||
|
||||
test("the seed marker is part of the run-state schema, not a field the parse strips", () => {
|
||||
@@ -205,7 +208,7 @@ describe("a preset-seeded run end to end (S2.2)", () => {
|
||||
const { gitRepo, frontier, writeReport, writeSpecs } = await import("./helpers.ts")
|
||||
const { Factory, recordApproval, sealHumanKey, unlockHumanKey } = await import("../src/index.ts")
|
||||
const fx = await gitRepo("es-preset-e2e-")
|
||||
await Bun.spawn(["git", "branch", "rewrite"], { cwd: fx.root }).exited
|
||||
await Bun.spawn(["git", "branch", "main"], { cwd: fx.root }).exited
|
||||
await sealHumanKey(PASSPHRASE, fx.state)
|
||||
const signer = await unlockHumanKey(PASSPHRASE, fx.state)
|
||||
await initFromPreset(
|
||||
@@ -239,13 +242,13 @@ describe("a preset-seeded run end to end (S2.2)", () => {
|
||||
// The grill kept baseBranch when it rewrote the roadmap (before approval).
|
||||
const roadmapFile = factoryLayout(fx.root).roadmap
|
||||
const roadmap = JSON.parse(await readFile(roadmapFile, "utf8"))
|
||||
await Bun.write(roadmapFile, `${JSON.stringify({ ...roadmap, baseBranch: "rewrite" }, null, 2)}\n`)
|
||||
await Bun.write(roadmapFile, `${JSON.stringify({ ...roadmap, baseBranch: "main" }, null, 2)}\n`)
|
||||
}
|
||||
await recordApproval(fx.root, { stage: "spec", channel: "cli", approvedBy: "tester", signer })
|
||||
return { fx, factory, opened, begun, writeFrontier }
|
||||
}
|
||||
|
||||
test("a preset-seeded run releases its draft PR on rewrite", async () => {
|
||||
test("a preset-seeded run releases its draft PR on main", async () => {
|
||||
const { fx, factory, opened } = await seedAndReachSpec({ keepBaseBranch: true })
|
||||
try {
|
||||
const { buildPrArgs } = (await import("../src/index.ts")) as unknown as {
|
||||
@@ -258,7 +261,7 @@ describe("a preset-seeded run end to end (S2.2)", () => {
|
||||
}) => string[]
|
||||
}
|
||||
const built = await factory.startBuild("factory")
|
||||
expect(built.baseBranch).toBe("rewrite")
|
||||
expect(built.baseBranch).toBe("main")
|
||||
const dir = (await factory.activeWorktree())!
|
||||
await mkdir(path.join(dir, "test"), { recursive: true })
|
||||
await mkdir(path.join(dir, "src"), { recursive: true })
|
||||
@@ -276,12 +279,12 @@ describe("a preset-seeded run end to end (S2.2)", () => {
|
||||
const done = await factory.release("factory")
|
||||
expect(done.stage).toBe("DONE")
|
||||
expect(opened).toHaveLength(1)
|
||||
expect(opened[0]?.base).toBe("rewrite")
|
||||
expect(opened[0]?.base).toBe("main")
|
||||
expect(opened[0]?.preset).toBe("self-dogfood")
|
||||
const argv = buildPrArgs({ ...opened[0]! })
|
||||
expect(argv).toContain("--draft")
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["rewrite"])
|
||||
expect(argv).not.toContain("main")
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["main"])
|
||||
expect(argv).not.toContain("rewrite")
|
||||
} finally {
|
||||
await fx.cleanup()
|
||||
}
|
||||
@@ -295,7 +298,7 @@ describe("a preset-seeded run end to end (S2.2)", () => {
|
||||
expect(roadmap.baseBranch).toBeUndefined()
|
||||
await expect(factory.startBuild("factory")).rejects.toThrow(/baseBranch/)
|
||||
const state = await factory.read()
|
||||
expect(state?.baseBranch ?? "rewrite").toBe("rewrite")
|
||||
expect(state?.baseBranch ?? "main").toBe("main")
|
||||
} finally {
|
||||
await fx.cleanup()
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const open = (root: string, overrides: { branch: string; base?: string; preset?:
|
||||
ghPrOpener.open({
|
||||
root,
|
||||
branch: overrides.branch,
|
||||
base: overrides.base ?? "rewrite",
|
||||
base: overrides.base ?? "main",
|
||||
title: "t",
|
||||
body: "b",
|
||||
...(overrides.preset !== undefined ? { preset: overrides.preset } : {}),
|
||||
@@ -44,7 +44,7 @@ describe("ghPrOpener.open() branch guard", () => {
|
||||
test("branch === base is refused", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
|
||||
try {
|
||||
await expect(open(root, { branch: "rewrite", base: "rewrite" })).rejects.toThrow(/refusing to push "rewrite"/)
|
||||
await expect(open(root, { branch: "main", base: "main" })).rejects.toThrow(/refusing to push "main"/)
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
}
|
||||
@@ -67,7 +67,7 @@ describe("ghPrOpener.open() branch guard", () => {
|
||||
// (`pr.ts` checks the branch before `git push --set-upstream`).
|
||||
const root = await mkdtemp(path.join(tmpdir(), "es-pr-guard-"))
|
||||
try {
|
||||
await open(root, { branch: "main", base: "rewrite" })
|
||||
await open(root, { branch: "main", base: "main" })
|
||||
expect.unreachable("a non-factory/ branch must be refused")
|
||||
} catch (error) {
|
||||
expect((error as Error).message).toMatch(/refusing to push "main"/)
|
||||
@@ -118,9 +118,9 @@ describe("ghPrOpener.open() branch guard", () => {
|
||||
|
||||
describe("release PR argv", () => {
|
||||
test("a factory branch opens a draft PR against the base", () => {
|
||||
const argv = buildPrArgs({ branch: "factory/run-1/integration", base: "rewrite", title: "t", body: "b" })
|
||||
const argv = buildPrArgs({ branch: "factory/run-1/integration", base: "main", title: "t", body: "b" })
|
||||
expect(argv.slice(0, 4)).toEqual(["gh", "pr", "create", "--draft"])
|
||||
expect(argv.slice(argv.indexOf("--head") + 1, argv.indexOf("--head") + 2)).toEqual(["factory/run-1/integration"])
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["rewrite"])
|
||||
expect(argv.slice(argv.indexOf("--base") + 1, argv.indexOf("--base") + 2)).toEqual(["main"])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// Issue #205 opener half: `ghPrOpener.open` with `preset: self-dogfood` and
|
||||
// `--base main` refuses before anything is pushed. Local bare remote only, no
|
||||
// network and no `gh` invocation: the shared argv builder throws first, so the
|
||||
// test asserts the bare remote holds no new ref afterwards.
|
||||
// a non-`main` base refuses before anything is pushed (#281 retargeted the
|
||||
// preset base to `main`). Local bare remote only, no network and no `gh`
|
||||
// invocation: the shared argv builder throws first, so the test asserts the
|
||||
// bare remote holds no new ref afterwards.
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { mkdtemp, rm } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
@@ -17,8 +18,8 @@ const sh = async (args: string[], cwd: string) => {
|
||||
const showRefs = async (bare: string, cwd: string) =>
|
||||
(await run(["git", "--git-dir", bare, "show-ref"], { cwd })).stdout.trim()
|
||||
|
||||
describe("self-dogfood opener refuses --base main without pushing (#205)", () => {
|
||||
test("preset run against main is refused and the remote is unchanged", async () => {
|
||||
describe("self-dogfood opener refuses a non-main base without pushing (#205, #281)", () => {
|
||||
test("preset run against the old base is refused and the remote is unchanged", async () => {
|
||||
const bare = await mkdtemp(path.join(tmpdir(), "es-205-bare-"))
|
||||
const repo = await mkdtemp(path.join(tmpdir(), "es-205-repo-"))
|
||||
try {
|
||||
@@ -44,12 +45,12 @@ describe("self-dogfood opener refuses --base main without pushing (#205)", () =>
|
||||
ghPrOpener.open({
|
||||
root: repo,
|
||||
branch: "factory/run-205/integration",
|
||||
base: "main",
|
||||
base: "rewrite",
|
||||
title: "dogfood",
|
||||
body: "body",
|
||||
preset: "self-dogfood",
|
||||
}),
|
||||
).rejects.toThrow(/refusing.*main|rewrite/)
|
||||
).rejects.toThrow(/refusing.*rewrite/)
|
||||
|
||||
// Refusal happened before the push: the bare remote is byte-identical.
|
||||
const after = await showRefs(bare, repo)
|
||||
|
||||
@@ -27,7 +27,7 @@ import {
|
||||
import { boot, directiveScript, type Harness } from "@heretek-ai/es-testkit"
|
||||
import { ghPrOpener } from "../src/pr.ts"
|
||||
|
||||
const BASE = "rewrite"
|
||||
const BASE = "main"
|
||||
const BRANCH = "factory/run-test-1/integration"
|
||||
|
||||
interface Remote {
|
||||
@@ -81,8 +81,8 @@ describe("release against a local bare remote", () => {
|
||||
await expect(ghPrOpener.open({ root: fx.root, branch: BASE, base: BASE, title: "t", body: "b" })).rejects.toThrow(
|
||||
`refusing to push "${BASE}"`,
|
||||
)
|
||||
expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/rewrite"])
|
||||
expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
|
||||
expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/main"])
|
||||
expect(await remoteSha(fx.remote, "refs/heads/main")).toBe(fx.baseSha)
|
||||
} finally {
|
||||
await fx.cleanup()
|
||||
}
|
||||
@@ -94,8 +94,8 @@ describe("release against a local bare remote", () => {
|
||||
await expect(
|
||||
ghPrOpener.open({ root: fx.root, branch: "feature/x", base: BASE, title: "t", body: "b" }),
|
||||
).rejects.toThrow(/refusing to push/)
|
||||
expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/rewrite"])
|
||||
expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
|
||||
expect(await remoteBranches(fx.remote)).toEqual(["refs/heads/main"])
|
||||
expect(await remoteSha(fx.remote, "refs/heads/main")).toBe(fx.baseSha)
|
||||
} finally {
|
||||
await fx.cleanup()
|
||||
}
|
||||
@@ -109,10 +109,10 @@ describe("release against a local bare remote", () => {
|
||||
// runs in tests: no `gh` invocation here).
|
||||
await git(fx.root, ["push", "--set-upstream", "origin", `${BRANCH}:${BRANCH}`])
|
||||
expect(await remoteBranches(fx.remote)).toEqual(
|
||||
["refs/heads/factory/run-test-1/integration", "refs/heads/rewrite"].sort(),
|
||||
["refs/heads/factory/run-test-1/integration", "refs/heads/main"].sort(),
|
||||
)
|
||||
expect(await remoteSha(fx.remote, `refs/heads/${BRANCH}`)).toBe(local)
|
||||
expect(await remoteSha(fx.remote, "refs/heads/rewrite")).toBe(fx.baseSha)
|
||||
expect(await remoteSha(fx.remote, "refs/heads/main")).toBe(fx.baseSha)
|
||||
// The release PR itself is a draft against the base.
|
||||
const argv = buildPrArgs({ branch: BRANCH, base: BASE, title: "t", body: "b" })
|
||||
expect(argv.slice(0, 4)).toEqual(["gh", "pr", "create", "--draft"])
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ const ghOk = (args: string[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
const branch = process.env.GITHUB_REF_NAME ?? "rewrite"
|
||||
const branch = process.env.GITHUB_REF_NAME ?? "main"
|
||||
const repo = process.env.GITHUB_REPOSITORY
|
||||
const repoArgs = repo ? ["--repo", repo] : []
|
||||
const listed = ghOut([
|
||||
|
||||
Reference in New Issue
Block a user