From 441107b2bf0310b361f0dcae6b2d3bac3cbc7c04 Mon Sep 17 00:00:00 2001 From: minhn4 Date: Tue, 14 Jul 2026 17:24:41 +0700 Subject: [PATCH] test(ci): retry the Windows unit job once to absorb runner flakes The spawn-heavy opencode suite has several process/timing-sensitive tests (snapshot-tool-race, skill discovery, flock contention, ...) that flake individually on loaded GitHub-hosted Windows runners. bun has no per-test retry (--only-failures does not persist across invocations), so a single flake failed the whole unit job; different tests flaked on different runs. Wrap the unit-test step in a 2-attempt loop. Turbo cache-hits the packages that already passed, so the retry only re-runs the package that flaked, which clears a transient flake. Bump the step timeout 45 -> 60 to cover one re-run of the ~27min opencode suite. Each failed attempt emits a ::warning:: so flakes stay visible instead of being silently masked. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/test.yml | 19 ++++++++++++++++--- DIVERGENCES.md | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 931aa3eb74..d9f12b8ef3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,9 +76,22 @@ jobs: - 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. - timeout-minutes: 45 - run: GITHUB_ACTIONS=false bun turbo test + # needs well over 20 minutes for the spawn-heavy opencode suite. That suite + # also has several process-spawn/timing-sensitive tests (snapshot-tool-race, + # skill discovery, flock contention, ...) that flake individually on loaded + # GitHub-hosted runners, and bun has no per-test retry (`--only-failures` + # does not persist across invocations), so one flake fails the whole job. + # Retry once: turbo cache-hits the packages that already passed, so the retry + # only re-runs the package that flaked — usually clearing a transient flake. + # Timeout is 60 (not 45) to cover one re-run of the ~27min opencode suite. + timeout-minutes: 60 + run: | + for attempt in 1 2; do + if GITHUB_ACTIONS=false bun turbo test; then exit 0; fi + echo "::warning::unit tests failed on attempt ${attempt}; retrying" >&2 + done + echo "unit tests still failing after 2 attempts" >&2 + exit 1 env: CODEV_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }} diff --git a/DIVERGENCES.md b/DIVERGENCES.md index 5f4135469a..8f413f524b 100644 --- a/DIVERGENCES.md +++ b/DIVERGENCES.md @@ -126,7 +126,7 @@ from the squash message first, or use a plain merge. | `.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; 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. | +| `.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 wrapped in a 2-attempt retry loop (`bun turbo test`, re-run once on failure) with `timeout-minutes` 20 → 60; 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, and several process-spawn/timing-sensitive tests (snapshot-tool-race, skill discovery, flock contention, ...) flake individually there; bun has no per-test retry (`--only-failures` doesn't persist across invocations), so one flake failed the whole job. The retry re-runs only the flaked package (turbo cache-hits the rest); 60 min covers one re-run of the ~27min 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 + retry loop + 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`, `packages/opencode/test/effect/runner.test.ts`, `packages/opencode/test/session/prompt.test.ts`, `packages/opencode/test/project/instance-bootstrap.test.ts` | Slow-runner headroom: unknown-model test `timeoutMs`/bound 15s → 30s (test timeout 60s); the two unknown-stream-finish `codev run` subprocess tests ("unknown stream finish preserves partial output…", "--format json records partial output for an unknown stream finish") `timeoutMs` 30s → 60s and test timeout 60s → 120s (pure-upstream spawn tests that pay `codev run` cold-start; neither asserts on duration, so headroom only); 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); Runner fiber-coordination `Effect.timeout` guards 250ms/1s → 10s (pure hang-detectors on already-unblocked awaits; Windows runners starve the event loop past 250ms); prompt.test.ts's two `sleep`-based loop/shell tests ("loop waits while shell runs…", "shell completion resumes queued loop callers") moved from `it.instance` to the file's own `unix` helper so they skip on Windows like their `unixNoLLMServer` siblings (`sleep` isn't on Windows, so the shell fiber hangs to the budget — a genuine hang, not slowness); instance-bootstrap's first test 30s → 60s (pays instance cold-start) | Loaded GitHub-hosted runners spawn processes, fetch ripgrep, and build the lazy file index slower than upstream's Blacksmith runners; 250ms–30s budgets flake or fail outright, and Unix-shell tests can't run on Windows at all. Hang regressions are still caught (a hang exceeds the bound; polls return as soon as ready). | Keep our numbers and the `unix` gating; 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. |