mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-14 03:56:55 +03:00
342d8f1e3b
the windows playwright legs still fail on dev pushes often enough that a release gate over them would stall shipping. unit tests and builds still cover both OSes, and dev pushes and nightly runs still cover the windows browser legs.
356 lines
15 KiB
YAML
356 lines
15 KiB
YAML
name: check-test
|
|
|
|
# Pull requests run the fast tier: the affected unit tests on Linux. The full tier — unit tests on Windows, the
|
|
# compiled builds, and the Playwright suites on both OSes — runs on pushes to dev and beta and on nightly and manual
|
|
# runs. A stable release also runs it through release.yml's gate, with the browser suites on Linux only.
|
|
on:
|
|
push:
|
|
branches:
|
|
- dev
|
|
- beta
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, ready_for_review]
|
|
workflow_dispatch:
|
|
inputs:
|
|
quarantine:
|
|
description: Run only the quarantined browser tests (packages/app/e2e/QUARANTINE.md)
|
|
type: boolean
|
|
default: false
|
|
workflow_call:
|
|
inputs:
|
|
quarantine:
|
|
description: Run only the quarantined browser tests (packages/app/e2e/QUARANTINE.md)
|
|
type: boolean
|
|
default: false
|
|
release_gate:
|
|
description: Run the browser suites on Linux only
|
|
type: boolean
|
|
default: false
|
|
|
|
concurrency:
|
|
# A new push to a pull request cancels its stale run. Every push and manual run keeps its own group, so each
|
|
# dev and beta commit gets a verdict and a quarantine run never cancels a normal one.
|
|
group: ${{ case(github.event_name == 'pull_request', format('{0}-{1}', github.workflow, github.event.pull_request.number), format('{0}-{1}', github.workflow, github.run_id)) }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
checks: write
|
|
|
|
env:
|
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
# Push and pull request runs test only what changed. Manual runs and the release gate test everything: they
|
|
# have no base commit to diff against.
|
|
AFFECTED: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
|
|
# Pull requests diff against the merge's first parent; pushes diff against the previous dev head.
|
|
TURBO_SCM_BASE: ${{ github.event_name == 'pull_request' && format('{0}^1', github.sha) || github.event.before }}
|
|
TURBO_SCM_HEAD: ${{ github.sha }}
|
|
|
|
jobs:
|
|
# Only the browser suites need this, and pull requests run no browser suites.
|
|
affected:
|
|
name: affected packages
|
|
if: ${{ !github.event.pull_request.draft && github.event_name != 'pull_request' }}
|
|
runs-on: blacksmith-4vcpu-ubuntu-2404
|
|
outputs:
|
|
e2e: ${{ steps.packages.outputs.app }}
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
with:
|
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Bun
|
|
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
|
|
with:
|
|
bun-version-file: package.json
|
|
|
|
- name: Find affected packages
|
|
id: packages
|
|
run: |
|
|
if [ "$AFFECTED" != "true" ]; then
|
|
echo "app=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
# packages/stories hosts the component tests, but no app package depends on it.
|
|
bun x turbo@2.10.2 ls --affected --filter=@opencode/app --filter=@opencode/stories --output=json > affected.json
|
|
bun -e 'const result = await Bun.file("affected.json").json(); console.log(`app=${result.packages.count > 0}`)' >> "$GITHUB_OUTPUT"
|
|
|
|
unit:
|
|
name: unit (${{ matrix.settings.name }})
|
|
if: ${{ !inputs.quarantine && !github.event.pull_request.draft }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# Pull requests run Linux only; Windows runs the same tests on pushes, nightly runs and the release gate.
|
|
settings: ${{ fromJSON(github.event_name == 'pull_request' && '[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404"}]' || '[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404"},{"name":"windows","host":"blacksmith-4vcpu-windows-2025"}]') }}
|
|
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 }}
|
|
# Pull requests diff against the merge's first parent; pushes diff against the previous dev head.
|
|
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
|
|
|
|
- name: Setup Node
|
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "24"
|
|
|
|
- name: Setup Bun
|
|
uses: ./.github/actions/setup-bun
|
|
|
|
- name: Test Effect simplification rules
|
|
if: runner.os == 'Linux'
|
|
run: bun run test:effect-simplification-rules
|
|
|
|
- name: Check Effect simplifications
|
|
if: runner.os == 'Linux'
|
|
run: bun run lint:effect-simplifications
|
|
|
|
- name: Configure git identity
|
|
run: |
|
|
git config --global user.email "bot@opencode.ai"
|
|
git config --global user.name "opencode"
|
|
|
|
- name: Run unit tests
|
|
timeout-minutes: 20
|
|
run: |
|
|
# The runners have four vCPUs, and each Bun test process performs its own concurrent work.
|
|
if [ "$AFFECTED" = "true" ]; then
|
|
GITHUB_ACTIONS=false bun turbo test --affected --concurrency=3
|
|
exit 0
|
|
fi
|
|
GITHUB_ACTIONS=false bun turbo test --concurrency=3
|
|
env:
|
|
OPENCODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }}
|
|
|
|
- name: Verify packed codemode package
|
|
if: runner.os == 'Linux'
|
|
working-directory: packages/codemode
|
|
run: bun run pack
|
|
|
|
- name: Verify packed workerd SDK
|
|
if: runner.os == 'Linux'
|
|
timeout-minutes: 15
|
|
run: |
|
|
if [ "$AFFECTED" = "true" ]; then
|
|
bun turbo verify:package --affected --filter=@opencode/sdk
|
|
exit 0
|
|
fi
|
|
bun turbo verify:package --filter=@opencode/sdk
|
|
|
|
- name: Check generated client
|
|
if: runner.os == 'Linux'
|
|
working-directory: packages/client
|
|
run: bun run check:generated
|
|
|
|
- name: Check generated OpenAPI document
|
|
if: runner.os == 'Linux'
|
|
working-directory: packages/protocol
|
|
run: bun run check:generated
|
|
|
|
build:
|
|
name: build (${{ matrix.settings.name }})
|
|
# Compiling the CLI and smoke-testing its service lifecycle is part of the full tier.
|
|
if: ${{ !inputs.quarantine && github.event_name != 'pull_request' && !github.event.pull_request.draft }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
settings:
|
|
- name: linux
|
|
host: blacksmith-4vcpu-ubuntu-2404
|
|
- name: windows
|
|
host: blacksmith-4vcpu-windows-2025
|
|
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 }}
|
|
fetch-depth: 0
|
|
|
|
- name: Setup Bun
|
|
uses: ./.github/actions/setup-bun
|
|
|
|
- name: Verify compiled service lifecycle
|
|
timeout-minutes: 10
|
|
working-directory: packages/cli
|
|
env:
|
|
NODE_OPTIONS: ${{ runner.os == 'Windows' && '--max-old-space-size=4096' || '' }}
|
|
run: |
|
|
bun run script/build.ts --single --skip-install
|
|
bun run script/service-smoke.ts
|
|
|
|
# Unit tests import workspace sources and never build the libraries they use, so this checks those builds. The
|
|
# CLI and app closures hold every library a tested package depends on. The builds are tsc and Bun.build with no
|
|
# platform-specific steps, and the libraries publish from Linux, so Windows skips them.
|
|
- name: Build workspace libraries
|
|
if: ${{ !cancelled() && runner.os == 'Linux' }}
|
|
timeout-minutes: 10
|
|
run: |
|
|
if [ "$AFFECTED" = "true" ]; then
|
|
bun turbo build --affected --concurrency=3 --filter='@opencode/cli^...' --filter='@opencode/app^...'
|
|
exit 0
|
|
fi
|
|
bun turbo build --concurrency=3 --filter='@opencode/cli^...' --filter='@opencode/app^...'
|
|
|
|
- name: Setup Node build runtime
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
|
|
with:
|
|
node-version: "26.4.0"
|
|
|
|
- name: Verify Node build
|
|
if: ${{ !cancelled() }}
|
|
timeout-minutes: 15
|
|
working-directory: packages/cli
|
|
env:
|
|
NODE_OPTIONS: ${{ runner.os == 'Windows' && '--max-old-space-size=4096' || '' }}
|
|
run: |
|
|
bun run script/build-node.ts --single --skip-install --outdir=dist/node
|
|
bun run script/service-smoke.ts --node
|
|
|
|
e2e-suite:
|
|
name: e2e (${{ matrix.settings.name }}, ${{ matrix.suite.id }}${{ matrix.quarantine && ', quarantine' || '' }})
|
|
needs: affected
|
|
if: needs.affected.outputs.e2e == 'true'
|
|
# Quarantined tests are known red; their legs report without failing the e2e checks.
|
|
continue-on-error: ${{ matrix.quarantine }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# The release gate runs the Linux browser suites only: the Windows legs still fail on dev pushes often
|
|
# enough that a stable release must not wait for them. Pushes and nightly runs cover Windows.
|
|
settings: ${{ fromJSON(inputs.release_gate && '[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404"}]' || '[{"name":"linux","host":"blacksmith-4vcpu-ubuntu-2404"},{"name":"windows","host":"blacksmith-4vcpu-windows-2025"}]') }}
|
|
quarantine: ${{ fromJSON(inputs.quarantine && '[true]' || '[false]') }}
|
|
# Playwright splits whole spec files across shards by test count, not duration. Every app-N
|
|
# entry must use the same total, and the entries must cover 1..total. The service worker suite
|
|
# reuses shard 2's production build. Each component suite runs once per OS in its own leg, so the
|
|
# two suites' story servers and test time are not added up on one runner.
|
|
suite:
|
|
- id: app-1
|
|
shard: 1/2
|
|
- id: app-2
|
|
shard: 2/2
|
|
- id: session-ui-components
|
|
- id: app-components
|
|
runs-on: ${{ matrix.settings.host }}
|
|
env:
|
|
PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers
|
|
# Beta only (packages/app/e2e/QUARANTINE.md): gate legs skip quarantined tests, retry once and stop after 8
|
|
# failures; quarantine legs run only those tests, to the end. Other branches keep the config defaults.
|
|
PLAYWRIGHT_ARGS: ${{ matrix.quarantine && '--grep @quarantine --pass-with-no-tests --retries=1' || github.ref == 'refs/heads/beta' && '--grep-invert @quarantine --max-failures=8 --retries=1' || '' }}
|
|
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
|
|
|
|
- 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 against production build
|
|
if: matrix.suite.shard
|
|
run: bun --cwd packages/app test:e2e:built --shard=${{ matrix.suite.shard }} ${{ env.PLAYWRIGHT_ARGS }}
|
|
env:
|
|
CI: true
|
|
timeout-minutes: ${{ (matrix.quarantine || github.ref == 'refs/heads/beta') && 10 || 15 }}
|
|
|
|
- name: Verify service worker precaching and upgrades
|
|
if: matrix.suite.shard == '2/2' && !matrix.quarantine
|
|
working-directory: packages/app
|
|
run: bunx playwright test --config e2e/service-worker/playwright.config.ts
|
|
timeout-minutes: 5
|
|
|
|
- name: Run session UI component tests
|
|
if: matrix.suite.id == 'session-ui-components'
|
|
run: bun --cwd packages/session-ui test:components ${{ env.PLAYWRIGHT_ARGS }}
|
|
env:
|
|
CI: true
|
|
timeout-minutes: ${{ (matrix.quarantine || github.ref == 'refs/heads/beta') && 10 || 15 }}
|
|
|
|
- name: Run app component tests
|
|
if: matrix.suite.id == 'app-components'
|
|
run: bun --cwd packages/app test:components ${{ env.PLAYWRIGHT_ARGS }}
|
|
env:
|
|
CI: true
|
|
timeout-minutes: ${{ (matrix.quarantine || github.ref == 'refs/heads/beta') && 10 || 15 }}
|
|
|
|
- name: Upload Playwright artifacts
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: playwright-${{ matrix.settings.name }}-${{ matrix.suite.id }}${{ matrix.quarantine && '-quarantine' || '' }}-${{ github.run_attempt }}
|
|
if-no-files-found: ignore
|
|
retention-days: 7
|
|
path: |
|
|
packages/app/e2e/test-results
|
|
packages/app/e2e/playwright-report
|
|
packages/session-ui/component-tests/test-results
|
|
packages/session-ui/component-tests/playwright-report
|
|
packages/app/component-tests/test-results
|
|
packages/app/component-tests/playwright-report
|
|
|
|
# One check per OS for the browser suites: it passes only when every leg above passed, or when no leg was
|
|
# needed because the changed packages have no browser tests.
|
|
e2e:
|
|
name: e2e (${{ matrix.os }})
|
|
needs: [affected, e2e-suite]
|
|
if: ${{ !cancelled() && !inputs.quarantine && github.event_name != 'pull_request' && !github.event.pull_request.draft }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [linux, windows]
|
|
# Aggregates results only; these do not need a paid runner.
|
|
runs-on: ubuntu-24.04
|
|
steps:
|
|
- name: Check browser test results
|
|
env:
|
|
AFFECTED: ${{ needs.affected.result }}
|
|
SUITES: ${{ needs.e2e-suite.result }}
|
|
EXPECTED: ${{ needs.affected.outputs.e2e == 'true' && 'success' || 'skipped' }}
|
|
run: |
|
|
echo "affected packages: $AFFECTED, browser test legs: $SUITES (expected $EXPECTED)"
|
|
test "$AFFECTED" = success && test "$SUITES" = "$EXPECTED"
|