Skip to content

release: goal-loop silent-stall fixes (GOAL-FP-01-17/18/19) #308

release: goal-loop silent-stall fixes (GOAL-FP-01-17/18/19)

release: goal-loop silent-stall fixes (GOAL-FP-01-17/18/19) #308

Workflow file for this run

# ============================================================================
# 🧪 CI · Test
# ----------------------------------------------------------------------------
# Purpose : Run unit + Playwright e2e tests across Linux & Windows
# Trigger : Push to `main`/`dev`, PRs targeting `main`, manual dispatch
# Jobs : unit — `bun turbo test` + config_assistant Go tests on linux
# only (windows dropped — see
# unit-tests matrix comment; free windows-latest runners
# can't fit the suite in a reasonable CI budget)
# e2e — Playwright chromium on linux + windows (matrix)
# Gate : Required status check on the `main` ruleset — full suite gates
# dev → main PRs. Pushes to `dev` also get a full run (dev is the
# integration/testing branch), but feat/fix → dev PRs are gated by
# typecheck only (see ci-typecheck.yml) to keep CI budget sane.
# Notes : `cancel-in-progress: false` — every main/dev push gets a full run
# No trigger on feat/* or fix/* (frequent changes).
# ============================================================================
name: 🧪 CI · Test
on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
workflow_dispatch:
concurrency:
group: ${{ format('{0}-{1}', github.workflow, github.run_id) }}
cancel-in-progress: false
permissions:
contents: read
checks: write
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
unit-tests:
name: Unit Tests (${{ matrix.settings.name }})
strategy:
fail-fast: false
matrix:
settings:
- name: linux
host: ubuntu-latest
# windows dropped: this fork runs on free windows-latest runners
# (not the paid Blacksmith 4vCPU hosts upstream uses), and the
# opencode:test suite (3048 tests, many spawning real CLI
# subprocesses) doesn't fit the standard runner's slower
# process-spawn/IO within a reasonable CI budget. E2E Tests
# (windows) is unaffected and still covers the platform.
runs-on: ${{ matrix.settings.host }}
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"
- name: Setup Go
if: runner.os == 'Linux'
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version-file: config_assistant/go.mod
cache-dependency-path: config_assistant/go.sum
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
# ci-typecheck.yml's Linux job is the designated Linux cache
# saver (runs on every push, fast, low collision risk) — this job
# only restores, to avoid racing on the same {OS}-bun-{hash} key.
save-cache: false
- name: Configure Git Identity
run: |
git config --global user.email "bot@opencode.ai"
git config --global user.name "opencode"
- name: Install ripgrep
# tool.glob tests hit ripgrep; without the system binary, binary.ts
# downloads rg from GitHub releases every run (temp XDG per preload),
# gambling on network stability — ECONNRESET fails the test.
run: sudo apt-get update && sudo apt-get install -y ripgrep
- name: Cache Turbo
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: node_modules/.cache/turbo
key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-${{ github.sha }}
restore-keys: |
turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json') }}-
turbo-${{ runner.os }}-
- name: Run unit tests
# opencode:test alone (244 files / 3048 tests, many spawning real CLI
# subprocesses via cliIt) measured at 803s / 13m24s locally, and this
# job has been seen at 16-17m in CI. turbo buffers a concurrent
# task's output until it completes, so the step is silent the whole
# time it runs — that silence can look like a hang but isn't one.
# 20m was never sized against real data; give it margin above the
# measured baseline instead of racing it.
timeout-minutes: 35
run: GITHUB_ACTIONS=false bun turbo test
env:
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }}
- name: Run config assistant tests
if: runner.os == 'Linux'
working-directory: config_assistant
run: go test ./...
- name: Check generated client
if: runner.os == 'Linux'
working-directory: packages/client
run: bun run check:generated
- name: Check generated SDK
if: runner.os == 'Linux'
working-directory: packages/sdk/js
run: bun run check:generated
- name: Run HttpAPI Exerciser Gates
if: runner.os == 'Linux'
working-directory: packages/opencode
# The exerciser aggregates its report and prints it in one flush, so
# this step is silent while it runs. A latent uninterruptible hang
# (bare Effect.promise in scenario call/cleanup paths that the 30s
# scenario timeout cannot interrupt) froze the effect mode twice on
# 2026-07-27 — with no step timeout that occupies the runner for the
# default 6h. The full gate baseline is ~6m in CI, so 15m is generous;
# test:httpapi:ci adds --progress/--trace to effect mode so the log
# names the exact scenario and phase if it ever hangs again.
# 2026-08-05: a native-level freeze survived every in-process guard
# (timers die with the event loop) and burned the full 15m silently.
# --progress now also arms an out-of-process watchdog that SIGKILLs
# the runner after 120s without progress, naming the last scenario/
# phase — the step timeout stays as the final backstop.
timeout-minutes: 15
run: bun run test:httpapi:ci
e2e-tests:
name: E2E Tests (${{ matrix.settings.name }})
strategy:
fail-fast: false
matrix:
settings:
- name: linux
host: ubuntu-latest
- name: windows
host: windows-latest
runs-on: ${{ matrix.settings.host }}
env:
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
# Playwright 1.59 hangs while extracting Chromium with Node 24.16.
node-version: "24.15"
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
# Only Windows saves (sole Windows job). Linux e2e skips —
# unit-tests (linux) is the single Linux cache saver, avoiding
# intra-run cache key collision on {OS}-bun-{hash}.
save-cache: ${{ matrix.settings.name == 'windows' }}
- name: Read Playwright version
id: playwright-version
run: |
version=$(node -e 'console.log(require("./package.json").workspaces.catalog["@playwright/test"])')
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ github.workspace }}/.playwright-browsers
key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}-chromium
- name: Install Playwright system dependencies
if: runner.os == 'Linux'
working-directory: packages/app
run: bunx playwright install-deps chromium
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
working-directory: packages/app
run: bunx playwright install chromium
- name: Run app e2e tests
run: bun --cwd packages/app test:e2e:local
env:
CI: true
timeout-minutes: 30
- name: Upload Playwright artifacts
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: playwright-${{ matrix.settings.name }}-${{ github.run_attempt }}
if-no-files-found: ignore
retention-days: 7
path: |
packages/app/e2e/test-results
packages/app/e2e/playwright-report