Skip to content

Commit f17eb99

Browse files
authored
fix: gate release workflow on tests and add build branding guards (#157)
* 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 * fix: scope release test job to release-critical tests only Scope the release workflow test job to branding and install tests only, with --timeout 30000 for consistency with package.json. Prevents unrelated flaky tests from blocking releases while still catching branding regressions and packaging issues. * fix: fix pr-management duplicate check and update TEAM_MEMBERS - Replace broken `curl altimate.ai/install` with `npm install -g @altimateai/altimate-code` (altimate.ai/install returns HTML, not a shell script) - Replace upstream TEAM_MEMBERS with actual AltimateAI collaborators so internal PRs skip the duplicate check * fix: address code review findings — regex precedence and case-sensitive TEAM_MEMBERS - Split symlink guard regex into two explicit assertions to prevent false positives (Unix symlink + Windows .exe checked independently) - Lowercase TEAM_MEMBERS entries and make grep case-insensitive (-i) since GitHub logins are case-insensitive
1 parent 1caa711 commit f17eb99

File tree

4 files changed

+144
-23
lines changed

4 files changed

+144
-23
lines changed

.github/TEAM_MEMBERS

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
1-
adamdotdevin
2-
Brendonovich
3-
fwang
4-
Hona
5-
iamdavidhill
6-
jayair
7-
jlongster
8-
kitlangton
9-
kommander
10-
MrMushrooooom
11-
nexxeln
12-
R44VC0RP
13-
rekram1-node
14-
RhysSullivan
15-
thdxr
1+
aidtya
2+
aloks98
3+
altimateanas
4+
anandgupta42
5+
ankitksharma
6+
anusha-sharma
7+
arora-saurabh448
8+
dvanaken
9+
frasermarlow
10+
gaurpulkit
11+
govindpawa
12+
jontsai
13+
kulvirgit
14+
mdesmet
15+
mhallida
16+
ppradnesh
17+
rakendd
18+
ralphstodomingo
19+
ravik-aai
20+
robertmaybin
21+
sahrizvi
22+
sanjaykr5
23+
saravmajestic
24+
sgvarsh
25+
shreyastelkar
26+
sourabhchrs93
27+
suryaiyer95
28+
tshreyas
29+
vivekvenkatareddy
30+
yukthagv

.github/workflows/pr-management.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ jobs:
1919
- name: Check team membership
2020
id: team-check
2121
run: |
22-
LOGIN="${{ github.event.pull_request.user.login }}"
23-
if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qxF "$LOGIN" .github/TEAM_MEMBERS; then
22+
LOGIN=$(echo "${{ github.event.pull_request.user.login }}" | tr '[:upper:]' '[:lower:]')
23+
if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qixF "$LOGIN" .github/TEAM_MEMBERS; then
2424
echo "is_team=true" >> "$GITHUB_OUTPUT"
2525
echo "Skipping: $LOGIN is a team member or bot"
2626
else
@@ -35,9 +35,9 @@ jobs:
3535
if: steps.team-check.outputs.is_team != 'true'
3636
run: bun install
3737

38-
- name: Install opencode
38+
- name: Install altimate-code
3939
if: steps.team-check.outputs.is_team != 'true'
40-
run: curl -fsSL https://altimate.ai/install | bash
40+
run: npm install -g @altimateai/altimate-code
4141

4242
- name: Build prompt
4343
if: steps.team-check.outputs.is_team != 'true'

.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 release-critical tests
47+
run: bun test --timeout 30000 test/branding/ test/install/
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: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,73 @@ 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+
// Unix: symlink
295+
expect(buildTs).toContain("ln -sf altimate")
296+
// Windows: copy
297+
expect(buildTs).toContain("altimate-code.exe")
298+
})
299+
300+
test("build.ts has sourcemap: 'external'", () => {
301+
expect(buildTs).toContain('sourcemap: "external"')
302+
})
303+
304+
test("package.json bin has 'altimate' pointing to ./bin/altimate", () => {
305+
expect(pkg.bin.altimate).toBe("./bin/altimate")
306+
})
307+
308+
test("package.json bin has 'altimate-code' pointing to ./bin/altimate-code", () => {
309+
expect(pkg.bin["altimate-code"]).toBe("./bin/altimate-code")
310+
})
311+
312+
test("package.json bin does not have 'opencode' entry", () => {
313+
expect(pkg.bin.opencode).toBeUndefined()
314+
})
315+
316+
test("package.json has no junk fields", () => {
317+
expect(pkg.randomField).toBeUndefined()
318+
})
319+
320+
test("package.json has no echo-stub scripts", () => {
321+
const junkNames = ["random", "clean", "lint", "format", "docs", "deploy"]
322+
for (const name of junkNames) {
323+
if (pkg.scripts?.[name]) {
324+
expect(pkg.scripts[name]).not.toMatch(/^echo /)
325+
}
326+
}
327+
})
328+
329+
test("bin/opencode does not exist", () => {
330+
expect(existsSync(join(pkgDir, "bin", "opencode"))).toBe(false)
331+
})
332+
})
333+
334+
// ---------------------------------------------------------------------------
335+
// 7. Repository Hygiene
270336
// ---------------------------------------------------------------------------
271337
describe("Repository hygiene", () => {
272338
test("__pycache__ is in .gitignore", () => {
@@ -301,7 +367,7 @@ describe("Repository hygiene", () => {
301367
})
302368

303369
// ---------------------------------------------------------------------------
304-
// 7. Config Integrity
370+
// 8. Config Integrity
305371
// ---------------------------------------------------------------------------
306372
describe("Config integrity", () => {
307373
const configTsPath = join(repoRoot, "script", "upstream", "utils", "config.ts")
@@ -313,6 +379,10 @@ describe("Config integrity", () => {
313379
"script/upstream/**",
314380
"packages/opencode/src/altimate/**",
315381
"packages/opencode/src/bridge/**",
382+
"packages/opencode/script/build.ts",
383+
"packages/opencode/script/publish.ts",
384+
"packages/opencode/bin/**",
385+
"CHANGELOG.md",
316386
]
317387
for (const pattern of criticalKeepOurs) {
318388
expect(configTs).toContain(`"${pattern}"`)
@@ -344,7 +414,7 @@ describe("Config integrity", () => {
344414
})
345415

346416
// ---------------------------------------------------------------------------
347-
// 8. altimate_change Marker Integrity
417+
// 9. altimate_change Marker Integrity
348418
// ---------------------------------------------------------------------------
349419
describe("altimate_change marker integrity", () => {
350420
// Files that MUST have altimate_change markers (they contain custom logic in upstream-shared files)

0 commit comments

Comments
 (0)