Skip to content

Commit de1ba2a

Browse files
committed
fix: gate release workflow on tests and add build branding guards
Release workflow: - Add test job (typecheck + bun test) that must pass before build/publish - Both build and publish-engine now depend on test job Branding guard tests (upstream-merge-guard.test.ts): - build.ts binary name is 'altimate' not 'opencode' - build.ts user-agent is 'altimate/' not 'opencode/' - build.ts embeds ALTIMATE_ENGINE_VERSION - build.ts reads engine version from pyproject.toml - build.ts creates altimate-code backward-compat symlink - build.ts has sourcemap: "external" - package.json bin entries are correct (no 'opencode') - package.json has no junk fields or echo-stub scripts - bin/opencode does not exist - keepOurs config includes build.ts, publish.ts, bin/**, CHANGELOG.md Publish package test: - Assert 'altimate' bin points to ./bin/altimate - Assert no 'opencode' bin entry exists
1 parent c053bc0 commit de1ba2a

2 files changed

Lines changed: 108 additions & 4 deletions

File tree

.github/workflows/release.yml

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,43 @@ env:
1313
GH_REPO: AltimateAI/altimate-code
1414

1515
jobs:
16+
test:
17+
name: Test
18+
runs-on: ubuntu-latest
19+
timeout-minutes: 60
20+
steps:
21+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
22+
23+
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
24+
with:
25+
bun-version: "1.3.10"
26+
27+
- name: Cache Bun dependencies
28+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
29+
with:
30+
path: ~/.bun/install/cache
31+
key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }}
32+
restore-keys: |
33+
bun-${{ runner.os }}-
34+
35+
- name: Configure git for tests
36+
run: |
37+
git config --global user.name "CI"
38+
git config --global user.email "ci@test.local"
39+
40+
- name: Install dependencies
41+
run: bun install
42+
43+
- name: Typecheck
44+
run: bun turbo typecheck
45+
46+
- name: Run tests
47+
run: bun test
48+
working-directory: packages/opencode
49+
1650
build:
1751
name: Build (${{ matrix.os }})
52+
needs: test
1853
runs-on: ubuntu-latest
1954
timeout-minutes: 60
2055
permissions:
@@ -122,9 +157,10 @@ jobs:
122157
GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
123158

124159
# Engine publish runs without waiting for build — it builds from source and
125-
# doesn't need CLI binary artifacts. This allows it to run in parallel.
160+
# doesn't need CLI binary artifacts. This allows it to run in parallel with build.
126161
publish-engine:
127162
name: Publish engine to PyPI
163+
needs: test
128164
runs-on: ubuntu-latest
129165
timeout-minutes: 60
130166
environment: pypi

packages/opencode/test/branding/upstream-merge-guard.test.ts

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,71 @@ describe("No opencode.ai domain leaks in src/", () => {
266266
})
267267

268268
// ---------------------------------------------------------------------------
269-
// 6. Repository Hygiene
269+
// 6. Build & Package Branding
270+
// ---------------------------------------------------------------------------
271+
describe("Build and package branding", () => {
272+
const buildTs = readText(join(pkgDir, "script", "build.ts"))
273+
const pkg = readJSON(join(pkgDir, "package.json"))
274+
275+
test("build.ts compiles binary as 'altimate' not 'opencode'", () => {
276+
expect(buildTs).toContain("bin/altimate")
277+
expect(buildTs).not.toMatch(/outfile:.*opencode/)
278+
})
279+
280+
test("build.ts user-agent is 'altimate/' not 'opencode/'", () => {
281+
expect(buildTs).toContain("--user-agent=altimate/")
282+
expect(buildTs).not.toContain("--user-agent=opencode/")
283+
})
284+
285+
test("build.ts embeds ALTIMATE_ENGINE_VERSION", () => {
286+
expect(buildTs).toContain("ALTIMATE_ENGINE_VERSION")
287+
})
288+
289+
test("build.ts reads engine version from pyproject.toml", () => {
290+
expect(buildTs).toContain("altimate-engine/pyproject.toml")
291+
})
292+
293+
test("build.ts creates altimate-code backward-compat symlink", () => {
294+
expect(buildTs).toContain("altimate-code")
295+
expect(buildTs).toMatch(/ln -sf altimate|altimate-code\.exe/)
296+
})
297+
298+
test("build.ts has sourcemap: 'external'", () => {
299+
expect(buildTs).toContain('sourcemap: "external"')
300+
})
301+
302+
test("package.json bin has 'altimate' pointing to ./bin/altimate", () => {
303+
expect(pkg.bin.altimate).toBe("./bin/altimate")
304+
})
305+
306+
test("package.json bin has 'altimate-code' pointing to ./bin/altimate-code", () => {
307+
expect(pkg.bin["altimate-code"]).toBe("./bin/altimate-code")
308+
})
309+
310+
test("package.json bin does not have 'opencode' entry", () => {
311+
expect(pkg.bin.opencode).toBeUndefined()
312+
})
313+
314+
test("package.json has no junk fields", () => {
315+
expect(pkg.randomField).toBeUndefined()
316+
})
317+
318+
test("package.json has no echo-stub scripts", () => {
319+
const junkNames = ["random", "clean", "lint", "format", "docs", "deploy"]
320+
for (const name of junkNames) {
321+
if (pkg.scripts?.[name]) {
322+
expect(pkg.scripts[name]).not.toMatch(/^echo /)
323+
}
324+
}
325+
})
326+
327+
test("bin/opencode does not exist", () => {
328+
expect(existsSync(join(pkgDir, "bin", "opencode"))).toBe(false)
329+
})
330+
})
331+
332+
// ---------------------------------------------------------------------------
333+
// 7. Repository Hygiene
270334
// ---------------------------------------------------------------------------
271335
describe("Repository hygiene", () => {
272336
test("__pycache__ is in .gitignore", () => {
@@ -301,7 +365,7 @@ describe("Repository hygiene", () => {
301365
})
302366

303367
// ---------------------------------------------------------------------------
304-
// 7. Config Integrity
368+
// 8. Config Integrity
305369
// ---------------------------------------------------------------------------
306370
describe("Config integrity", () => {
307371
const configTsPath = join(repoRoot, "script", "upstream", "utils", "config.ts")
@@ -313,6 +377,10 @@ describe("Config integrity", () => {
313377
"script/upstream/**",
314378
"packages/opencode/src/altimate/**",
315379
"packages/opencode/src/bridge/**",
380+
"packages/opencode/script/build.ts",
381+
"packages/opencode/script/publish.ts",
382+
"packages/opencode/bin/**",
383+
"CHANGELOG.md",
316384
]
317385
for (const pattern of criticalKeepOurs) {
318386
expect(configTs).toContain(`"${pattern}"`)
@@ -344,7 +412,7 @@ describe("Config integrity", () => {
344412
})
345413

346414
// ---------------------------------------------------------------------------
347-
// 8. altimate_change Marker Integrity
415+
// 9. altimate_change Marker Integrity
348416
// ---------------------------------------------------------------------------
349417
describe("altimate_change marker integrity", () => {
350418
// Files that MUST have altimate_change markers (they contain custom logic in upstream-shared files)

0 commit comments

Comments
 (0)