diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ac471c2c1..77523504f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -63,6 +63,17 @@ jobs: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}- turbo-${{ runner.os }}- + - name: Install ripgrep (Windows) + # Fork: the file-search index is built by a background ripgrep walk + # whose errors are swallowed (core/src/filesystem/search.ts forks it + # with orDie). On GitHub-hosted Windows runners the first-use + # download+extract of rg.exe is slow/fragile, which left the index + # permanently empty and failed httpapi-file's search-endpoints test on + # every run. Pre-installing ripgrep makes binary.ts's which("rg.exe") + # short-circuit the download entirely. + if: runner.os == 'Windows' + run: choco install ripgrep --no-progress -y + - name: Run unit tests # Fork: upstream runs on Blacksmith runners; GitHub-hosted windows-latest # needs well over 20 minutes for the spawn-heavy opencode suite. @@ -78,6 +89,10 @@ jobs: - name: Run HttpApi exerciser gates if: runner.os == 'Linux' + # Fork: the three exerciser modes finish in a few minutes; without a + # step timeout a wedged run (seen once in effect mode) holds the job + # until the 6-hour GitHub ceiling. + timeout-minutes: 30 working-directory: packages/opencode run: bun run test:httpapi diff --git a/DIVERGENCES.md b/DIVERGENCES.md index b0ec9815c7..eb2babcd7e 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -49,9 +49,9 @@ upstream maintainers — see README → Syncing with upstream for why and how. | `.github/TEAM_MEMBERS` | Deleted | Upstream's team roster. The five workflows that read it (pr-standards, pr-management, triage, duplicate-issues, compliance-close) fetch it from a `dev` branch this fork doesn't have, so they are already inert here. | Keep deleted. | | `packages/script/src/index.ts` | Guard the `.github/TEAM_MEMBERS` read: fall back to an empty team list when the file is absent instead of throwing at module load | This module is imported by the publish path (`publish.ts`); the deleted `TEAM_MEMBERS` (row above) made `bun run release` crash with ENOENT. `Script.team` has no consumers in the fork. | Keep our `exists()` guard; take upstream's surrounding changes. | | `.github/workflows/` — 22 upstream workflows deleted (beta, close-issues, close-prs, compliance-close, containers, deploy, docs-locale-sync, docs-update, duplicate-issues, generate, nix-hashes, notify-discord, opencode, pr-management, pr-standards, publish, publish-github-action, publish-vscode, release-github-action, review, stats, triage) | Deleted | All inert or upstream-only here: gated on `anomalyco/opencode`, publish/deploy to upstream infra, need upstream secrets (`OPENCODE_APP_SECRET`, `OPENCODE_API_KEY`, marketplace/Discord/PostHog tokens), trigger only on a `dev` branch the fork lacks, or read the deleted `TEAM_MEMBERS`. | Keep deleted; delete new upstream workflows too unless they're generic self-contained CI. | -| `.github/workflows/{typecheck,test,nix-eval,storybook}.yml` | Triggers `dev` → `main` (incl. test.yml's concurrency-group ref check); runners `blacksmith-*` → `ubuntu-latest`/`windows-latest`; test.yml unit-test step `timeout-minutes` 20 → 45 | Fork's default branch is `main`; Blacksmith runners aren't installed here, so jobs queued forever. GitHub-hosted `windows-latest` needs well over 20 minutes for the spawn-heavy opencode suite. | Keep our branches + runners + timeout; take upstream's other changes. | +| `.github/workflows/{typecheck,test,nix-eval,storybook}.yml` | Triggers `dev` → `main` (incl. test.yml's concurrency-group ref check); runners `blacksmith-*` → `ubuntu-latest`/`windows-latest`; test.yml unit-test step `timeout-minutes` 20 → 45; test.yml exerciser step gains `timeout-minutes: 30`; test.yml Windows unit job pre-installs ripgrep via choco | Fork's default branch is `main`; Blacksmith runners aren't installed here, so jobs queued forever. GitHub-hosted `windows-latest` needs well over 20 minutes for the spawn-heavy opencode suite. A wedged exerciser run otherwise holds the job for GitHub's 6-hour ceiling. The file-search index is built by a background ripgrep walk with swallowed errors; Windows's first-use rg.exe download left it permanently empty, failing httpapi-file's search test every run. | Keep our branches + runners + timeouts; take upstream's other changes. | | `packages/core/test/ripgrep.test.ts` | `120_000` timeout on both `it.live` tests | First use downloads + extracts the ripgrep binary; GitHub-hosted Windows runners blow bun's 5s default. | Keep our timeouts; take upstream's test changes. | -| `packages/opencode/test/cli/run/run-process.test.ts`, `packages/opencode/test/cli/acp/lifecycle.test.ts`, `packages/opencode/test/server/httpapi-file.test.ts`, `packages/opencode/test/server/httpapi-sdk.test.ts` | Slow-runner headroom: unknown-model test `timeoutMs`/bound 15s → 30s (test timeout 60s); ACP stdin-EOF exit timeout 5s → 30s; file-search-index polls 5s → 30s | Loaded GitHub-hosted runners spawn processes and build the lazy file index slower than upstream's Blacksmith runners; 5–15s budgets flake. Hang regressions are still caught (a hang exceeds the bound; polls return as soon as ready). | Keep our numbers; take upstream's test changes. | +| `packages/opencode/test/cli/run/run-process.test.ts`, `packages/opencode/test/cli/acp/lifecycle.test.ts`, `packages/opencode/test/server/httpapi-file.test.ts`, `packages/opencode/test/server/httpapi-sdk.test.ts` | Slow-runner headroom: unknown-model test `timeoutMs`/bound 15s → 30s (test timeout 60s); ACP stdin-EOF exit timeout 5s → 30s; file-search-index poll 5s → 120s with a 150s test timeout (the search-endpoints test pays ripgrep's first-use download on Windows runners, like ripgrep.test.ts) | Loaded GitHub-hosted runners spawn processes, fetch ripgrep, and build the lazy file index slower than upstream's Blacksmith runners; 5–30s budgets flake or fail outright. Hang regressions are still caught (a hang exceeds the bound; polls return as soon as ready). | Keep our numbers; take upstream's test changes. | | `packages/opencode/test/tool/external-directory.test.ts`, `packages/opencode/test/tool/read.test.ts` | Windows path-variant tests keep the drive letter in the `alt` form (drop the `.replace(/^[A-Za-z]:/, "")` step) | A drive-less path is drive-relative: it resolves against the CWD's drive (D: on GitHub-hosted Windows) while the temp dir lives on C:, so the file genuinely isn't there. Slash + case variants still exercise normalization. | Keep our `alt` construction; take upstream's test changes. | | `packages/opencode/test/server/httpapi-exercise/environment.ts` | Exercise config/data dirs `config/opencode`,`data/opencode` → `…/codev-code`; seeds `config.json` with `{"share":"manual"}` | Follows the `global.ts` app-dir rename (row above); the fork's `share: "disabled"` default (config.ts row) would 500 the share/unshare scenarios, so the isolated environment opts back in (network stays stubbed by `OPENCODE_DISABLE_SHARE`). | Keep our dir names + seeded config; take upstream's surrounding changes. | | `sdks/vscode/package.json`, `sdks/vscode/README.md` | `description` "opencode for VS Code" → "CoDev Code for VS Code"; README title → "CoDev Code VS Code Extension" | Manifest branding. `name`, `displayName`, command IDs/titles and README body (which names actual UI elements) stay upstream — the fork doesn't publish this extension; revisit before shipping it. | Keep our description + title; take upstream's other fields/body. | diff --git a/README.md b/README.md index e2208036e5..1810dc53a9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Releases publish the unscoped — therefore **public** — [`codev-code`](https: ```sh npm login # once per machine -bun release # from the repo root: builds all platforms (~15-20 min), then publishes +bun release # builds all platforms, then publishes ``` Variants: diff --git a/packages/opencode/test/server/httpapi-file.test.ts b/packages/opencode/test/server/httpapi-file.test.ts index 31e77534ec..7b31f1e5dd 100644 --- a/packages/opencode/test/server/httpapi-file.test.ts +++ b/packages/opencode/test/server/httpapi-file.test.ts @@ -52,35 +52,41 @@ describe("file HttpApi", () => { expect(await status.json()).toEqual([]) }) - test("serves search endpoints", async () => { - await using tmp = await tmpdir({ git: true }) - await Bun.write(path.join(tmp.path, "hello.txt"), "needle") + test( + "serves search endpoints", + async () => { + await using tmp = await tmpdir({ git: true }) + await Bun.write(path.join(tmp.path, "hello.txt"), "needle") - const [text, symbols] = await Promise.all([ - request(FilePaths.findText, tmp.path, { pattern: "needle" }), - request(FilePaths.findSymbol, tmp.path, { query: "hello" }), - ]) - const files = await Effect.runPromise( - pollWithTimeout( - Effect.promise(async () => { - const response = await request(FilePaths.findFile, tmp.path, { query: "hello", type: "file" }) - const body = await response.json() - return body.includes("hello.txt") ? { response, body } : undefined - }), - "file search index was not ready", - // Lazy indexing on loaded GitHub-hosted Windows runners can exceed - // the 5s default; the poll returns as soon as the index is ready. - "30 seconds", - ), - ) + const [text, symbols] = await Promise.all([ + request(FilePaths.findText, tmp.path, { pattern: "needle" }), + request(FilePaths.findSymbol, tmp.path, { query: "hello" }), + ]) + const files = await Effect.runPromise( + pollWithTimeout( + Effect.promise(async () => { + const response = await request(FilePaths.findFile, tmp.path, { query: "hello", type: "file" }) + const body = await response.json() + return body.includes("hello.txt") ? { response, body } : undefined + }), + "file search index was not ready", + // Lazy indexing needs ripgrep, whose first use downloads + extracts + // the binary — on GitHub-hosted Windows runners that alone can blow + // a 30s budget (see ripgrep.test.ts). The poll returns as soon as + // the index is ready. + "120 seconds", + ), + ) - expect(text.status).toBe(200) - expect(await text.json()).toContainEqual(expect.objectContaining({ line_number: 1 })) + expect(text.status).toBe(200) + expect(await text.json()).toContainEqual(expect.objectContaining({ line_number: 1 })) - expect(files.response.status).toBe(200) - expect(files.body).toContain("hello.txt") + expect(files.response.status).toBe(200) + expect(files.body).toContain("hello.txt") - expect(symbols.status).toBe(200) - expect(await symbols.json()).toEqual([]) - }) + expect(symbols.status).toBe(200) + expect(await symbols.json()).toEqual([]) + }, + 150_000, + ) })