diff --git a/.github/TEAM_MEMBERS b/.github/TEAM_MEMBERS index 22c9a923d3..34b7b23648 100644 --- a/.github/TEAM_MEMBERS +++ b/.github/TEAM_MEMBERS @@ -1,15 +1,30 @@ -adamdotdevin -Brendonovich -fwang -Hona -iamdavidhill -jayair -jlongster -kitlangton -kommander -MrMushrooooom -nexxeln -R44VC0RP -rekram1-node -RhysSullivan -thdxr +aidtya +aloks98 +altimateanas +anandgupta42 +ankitksharma +anusha-sharma +arora-saurabh448 +dvanaken +frasermarlow +gaurpulkit +govindpawa +jontsai +kulvirgit +mdesmet +mhallida +ppradnesh +rakendd +ralphstodomingo +ravik-aai +robertmaybin +sahrizvi +sanjaykr5 +saravmajestic +sgvarsh +shreyastelkar +sourabhchrs93 +suryaiyer95 +tshreyas +vivekvenkatareddy +yukthagv diff --git a/.github/workflows/pr-management.yml b/.github/workflows/pr-management.yml index 529dd315b7..06eca8f3ae 100644 --- a/.github/workflows/pr-management.yml +++ b/.github/workflows/pr-management.yml @@ -19,8 +19,8 @@ jobs: - name: Check team membership id: team-check run: | - LOGIN="${{ github.event.pull_request.user.login }}" - if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qxF "$LOGIN" .github/TEAM_MEMBERS; then + LOGIN=$(echo "${{ github.event.pull_request.user.login }}" | tr '[:upper:]' '[:lower:]') + if [ "$LOGIN" = "opencode-agent[bot]" ] || grep -qixF "$LOGIN" .github/TEAM_MEMBERS; then echo "is_team=true" >> "$GITHUB_OUTPUT" echo "Skipping: $LOGIN is a team member or bot" else @@ -35,9 +35,9 @@ jobs: if: steps.team-check.outputs.is_team != 'true' run: bun install - - name: Install opencode + - name: Install altimate-code if: steps.team-check.outputs.is_team != 'true' - run: curl -fsSL https://altimate.ai/install | bash + run: npm install -g @altimateai/altimate-code - name: Build prompt if: steps.team-check.outputs.is_team != 'true' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 608dfa0253..ca23d23306 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,8 +13,43 @@ env: GH_REPO: AltimateAI/altimate-code jobs: + test: + name: Test + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + + - uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2 + with: + bun-version: "1.3.10" + + - name: Cache Bun dependencies + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 + with: + path: ~/.bun/install/cache + key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} + restore-keys: | + bun-${{ runner.os }}- + + - name: Configure git for tests + run: | + git config --global user.name "CI" + git config --global user.email "ci@test.local" + + - name: Install dependencies + run: bun install + + - name: Typecheck + run: bun turbo typecheck + + - name: Run release-critical tests + run: bun test --timeout 30000 test/branding/ test/install/ + working-directory: packages/opencode + build: name: Build (${{ matrix.os }}) + needs: test runs-on: ubuntu-latest timeout-minutes: 60 permissions: @@ -122,9 +157,10 @@ jobs: GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} # Engine publish runs without waiting for build — it builds from source and - # doesn't need CLI binary artifacts. This allows it to run in parallel. + # doesn't need CLI binary artifacts. This allows it to run in parallel with build. publish-engine: name: Publish engine to PyPI + needs: test runs-on: ubuntu-latest timeout-minutes: 60 environment: pypi diff --git a/packages/opencode/test/branding/upstream-merge-guard.test.ts b/packages/opencode/test/branding/upstream-merge-guard.test.ts index 54f8a5f24f..0fde091f59 100644 --- a/packages/opencode/test/branding/upstream-merge-guard.test.ts +++ b/packages/opencode/test/branding/upstream-merge-guard.test.ts @@ -266,7 +266,73 @@ describe("No opencode.ai domain leaks in src/", () => { }) // --------------------------------------------------------------------------- -// 6. Repository Hygiene +// 6. Build & Package Branding +// --------------------------------------------------------------------------- +describe("Build and package branding", () => { + const buildTs = readText(join(pkgDir, "script", "build.ts")) + const pkg = readJSON(join(pkgDir, "package.json")) + + test("build.ts compiles binary as 'altimate' not 'opencode'", () => { + expect(buildTs).toContain("bin/altimate") + expect(buildTs).not.toMatch(/outfile:.*opencode/) + }) + + test("build.ts user-agent is 'altimate/' not 'opencode/'", () => { + expect(buildTs).toContain("--user-agent=altimate/") + expect(buildTs).not.toContain("--user-agent=opencode/") + }) + + test("build.ts embeds ALTIMATE_ENGINE_VERSION", () => { + expect(buildTs).toContain("ALTIMATE_ENGINE_VERSION") + }) + + test("build.ts reads engine version from pyproject.toml", () => { + expect(buildTs).toContain("altimate-engine/pyproject.toml") + }) + + test("build.ts creates altimate-code backward-compat symlink", () => { + // Unix: symlink + expect(buildTs).toContain("ln -sf altimate") + // Windows: copy + expect(buildTs).toContain("altimate-code.exe") + }) + + test("build.ts has sourcemap: 'external'", () => { + expect(buildTs).toContain('sourcemap: "external"') + }) + + test("package.json bin has 'altimate' pointing to ./bin/altimate", () => { + expect(pkg.bin.altimate).toBe("./bin/altimate") + }) + + test("package.json bin has 'altimate-code' pointing to ./bin/altimate-code", () => { + expect(pkg.bin["altimate-code"]).toBe("./bin/altimate-code") + }) + + test("package.json bin does not have 'opencode' entry", () => { + expect(pkg.bin.opencode).toBeUndefined() + }) + + test("package.json has no junk fields", () => { + expect(pkg.randomField).toBeUndefined() + }) + + test("package.json has no echo-stub scripts", () => { + const junkNames = ["random", "clean", "lint", "format", "docs", "deploy"] + for (const name of junkNames) { + if (pkg.scripts?.[name]) { + expect(pkg.scripts[name]).not.toMatch(/^echo /) + } + } + }) + + test("bin/opencode does not exist", () => { + expect(existsSync(join(pkgDir, "bin", "opencode"))).toBe(false) + }) +}) + +// --------------------------------------------------------------------------- +// 7. Repository Hygiene // --------------------------------------------------------------------------- describe("Repository hygiene", () => { test("__pycache__ is in .gitignore", () => { @@ -301,7 +367,7 @@ describe("Repository hygiene", () => { }) // --------------------------------------------------------------------------- -// 7. Config Integrity +// 8. Config Integrity // --------------------------------------------------------------------------- describe("Config integrity", () => { const configTsPath = join(repoRoot, "script", "upstream", "utils", "config.ts") @@ -313,6 +379,10 @@ describe("Config integrity", () => { "script/upstream/**", "packages/opencode/src/altimate/**", "packages/opencode/src/bridge/**", + "packages/opencode/script/build.ts", + "packages/opencode/script/publish.ts", + "packages/opencode/bin/**", + "CHANGELOG.md", ] for (const pattern of criticalKeepOurs) { expect(configTs).toContain(`"${pattern}"`) @@ -344,7 +414,7 @@ describe("Config integrity", () => { }) // --------------------------------------------------------------------------- -// 8. altimate_change Marker Integrity +// 9. altimate_change Marker Integrity // --------------------------------------------------------------------------- describe("altimate_change marker integrity", () => { // Files that MUST have altimate_change markers (they contain custom logic in upstream-shared files)