-
Notifications
You must be signed in to change notification settings - Fork 134
778 lines (696 loc) · 34.3 KB
/
Copy pathci.yml
File metadata and controls
778 lines (696 loc) · 34.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# ---------------------------------------------------------------------------
# Detect which areas of the codebase changed to skip unaffected jobs.
# On push to main, all jobs run unconditionally (safety net).
# ---------------------------------------------------------------------------
changes:
name: Detect Changes
runs-on: ubuntu-latest
timeout-minutes: 2
outputs:
typescript: ${{ steps.filter.outputs.typescript }}
drivers: ${{ steps.filter.outputs.drivers }}
dbt-tools: ${{ steps.filter.outputs.dbt-tools }}
installer: ${{ steps.filter.outputs.installer }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3
id: filter
with:
filters: |
typescript:
# altimate_change start — upstream_fix: typecheck every declared TypeScript workspace
- 'packages/cli/**'
- 'packages/core/**'
- 'packages/dbt-tools/**'
- 'packages/effect-drizzle-sqlite/**'
- 'packages/effect-sqlite-node/**'
- 'packages/http-recorder/**'
- 'packages/llm/**'
- 'packages/opencode/**'
- 'packages/server/**'
- 'packages/tui/**'
# altimate_change end
- 'packages/drivers/**'
- 'packages/plugin/**'
- 'packages/sdk/**'
- 'packages/util/**'
- 'packages/script/**'
- 'bun.lock'
- 'package.json'
- 'tsconfig.json'
drivers:
- 'packages/drivers/src/**'
- 'packages/opencode/src/altimate/native/connections/**'
- 'packages/opencode/test/altimate/drivers-e2e.test.ts'
- 'packages/opencode/test/altimate/drivers-docker-e2e.test.ts'
- 'packages/opencode/test/altimate/drivers-mongodb-e2e.test.ts'
- 'packages/opencode/test/altimate/drivers-clickhouse-e2e.test.ts'
- 'packages/opencode/test/altimate/connections.test.ts'
# Run by the "DuckDB store-open E2E" step in the driver-e2e job.
# Without these, a PR touching only these files skips that job,
# and the main TypeScript job runs them with ALTIMATE_DUCKDB_E2E
# unset, which skips every test in them — no execution anywhere.
- 'packages/opencode/test/altimate/duckdb-open-e2e.test.ts'
- 'packages/opencode/test/altimate/warehouse-test-duckdb-e2e.test.ts'
- 'packages/opencode/test/altimate/duckdb-lock-helper.ts'
- 'packages/opencode/src/altimate/tools/warehouse-test.ts'
- 'packages/drivers/test/**'
# These govern whether the native DuckDB binding is fetched at all
# (`trustedDependencies`, and the pinned version). A change to them
# can break every real-DuckDB test while touching no test file.
- 'package.json'
- 'bun.lock'
- 'packages/drivers/package.json'
dbt-tools:
- 'packages/dbt-tools/**'
installer:
- 'install.ps1'
- 'test/windows/**'
# ---------------------------------------------------------------------------
# altimate_change start — run the tracker-leak guard on every PR.
# `script/check-tracker-leaks.ts` and its tests already existed but were wired
# into no workflow, so nothing enforced them: v0.10.0 shipped three new
# `AI-####` references into tracked files on this PUBLIC repo before a human
# review caught them. It scans the branch name, the commits ahead of
# origin/main, and the diff, so it needs full history and the base ref.
tracker-leaks:
name: Tracker Leaks
runs-on: ubuntu-latest
timeout-minutes: 10
# This job runs pull-request code, so it gets read-only scope and no
# persisted credentials — the checked-out branch must not be able to reach
# the token in `.git/config`. (bot review)
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
persist-credentials: false
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- name: Fetch base branch
# `--depth=0` is not valid git ("depth 0 is not a positive number") and
# failed the job before the guard could run. `fetch-depth: 0` on the
# checkout above already gives full history, so a plain fetch of the
# base ref is all this needs.
run: git fetch origin main
- name: Check for internal tracker references
# `actions/checkout` lands on the synthetic merge commit in detached
# HEAD, so the script's own `rev-parse --abbrev-ref HEAD` yields "HEAD"
# and the branch-name source -- one of the three it documents -- is
# inert. Pass the real head ref explicitly. (review)
env:
PR_BRANCH: ${{ github.head_ref }}
run: bun script/check-tracker-leaks.ts
# altimate_change end
# Main TypeScript tests — excludes driver E2E tests (separate job) and
# cloud credential tests (local-only).
# ---------------------------------------------------------------------------
typescript:
name: TypeScript
needs: changes
if: needs.changes.outputs.typescript == 'true' || github.event_name == 'push'
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.14"
- 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: Install merge tooling deps (script/upstream)
# The script/upstream/ workspace is its own bun project (depends on
# minimatch). Root `bun install` doesn't reach it. Tests in
# test/upstream/bridge-merge-e2e.test.ts shell out to
# `bun run script/upstream/analyze.ts --branding` which requires this.
# Mirrors the install step in the marker-guard job below.
run: bun install
working-directory: script/upstream
# NOTE: there is no "prebuild test CLI" step. We tried pointing the subprocess harness at a
# prebuilt single-file binary (OPENCODE_TEST_CLI) to dodge `bun run src` cold-boot, but the
# compiled binary has a load-triggered hang on the run+mock happy path (it never exits under CPU
# pressure). The robust answer is the dedicated bounded subprocess pass below: `bun run src` (no
# such hang) at --max-concurrency=2, which stays green even under heavy load (~43s locally at
# load 21). Do not reintroduce OPENCODE_TEST_CLI for these tests without fixing that binary hang.
# altimate_change start — deliberate exception: the cold-start-regression job below DOES set
# OPENCODE_TEST_CLI, for exactly one test (test/cli/serve/fresh-start.test.ts). That is safe
# despite the warning above because it is a single bounded cold-start smoke check with its own
# --timeout, not the general run+mock subprocess suite this NOTE is about — the load-triggered
# hang needs sustained CPU pressure across many concurrent mock round-trips to manifest.
# altimate_change end
- name: SDK codegen is reproducible
# The v2 gen tree is committed AND regenerated on every release build
# (script/build.ts, clean: true) with post-codegen patches re-applied.
# A clean checkout must round-trip to itself, or a release ships
# something the review never saw.
working-directory: packages/sdk/js
run: |
# The build mutates the checkout (both gen trees, openapi.json, dist);
# restore it whatever happens so later steps see the committed tree.
# An EXIT trap runs on `bash -e` aborts too.
cleanup() {
git checkout -- src/gen src/v2/gen 2>/dev/null
git clean -qfd src/gen src/v2/gen 2>/dev/null
rm -rf dist openapi.json build.log
}
trap cleanup EXIT
# Prove codegen actually ran: a sentinel comment on the asserted file
# (a comment, because the generator itself imports this client) must
# be gone afterwards. A failure anywhere before the clean:true wipe
# leaves the committed copy — and the sentinel — in place, so it can
# no longer pass the drift check untouched.
sed -i '1i // CI-SENTINEL: not regenerated' src/v2/gen/client/client.gen.ts
build_rc=0; bun script/build.ts > build.log 2>&1 || build_rc=$?
cat build.log
if grep -q 'CI-SENTINEL: not regenerated' src/v2/gen/client/client.gen.ts; then
echo "::error::codegen did not regenerate src/v2/gen/client/client.gen.ts (build exit $build_rc)"
exit 1
fi
drift_rc=0; git diff --exit-code -- src/v2/gen/client/client.gen.ts || drift_rc=$?
# build.ts ends with `bun tsc`, which fails on a clean main until the
# types.gen.ts round-trip is fixed (issue #1148). That ONE failure is
# whitelisted by its EXACT diagnostic set — not merely "tsc failed",
# which would silently whitelist new type regressions in the
# regenerated SDK (script/check-known-tsc-failure.sh, unit-tested) —
# and only when the file was regenerated AND round-trips (the two
# assertions above); any other non-zero build — codegen, a patch,
# prettier — fails the step. Remove the whitelist with #1148.
if [ "$build_rc" -ne 0 ]; then
wl_rc=0; bash script/check-known-tsc-failure.sh build.log || wl_rc=$?
if [ "$wl_rc" -eq 0 ]; then
echo "::warning::build.ts exited $build_rc at tsc with exactly the known #1148 diagnostic (TS2305 FileSystemEntry); codegen+patch asserted above"
elif [ "$wl_rc" -eq 2 ]; then
echo "::error::build.ts exited $build_rc before tsc — not the whitelisted #1148 failure"
exit "$build_rc"
else
echo "::error::tsc failed with diagnostics beyond the whitelisted #1148 set (see output above)"
exit "$build_rc"
fi
fi
exit "$drift_rc"
- name: Run tests
working-directory: packages/opencode
# Cloud E2E tests (Snowflake, BigQuery, Databricks) auto-skip when
# ALTIMATE_CODE_CONN_* env vars are not set. Docker E2E tests auto-skip
# when Docker is not available. No exclusion needed — skipIf handles it.
# --timeout 90000: the full suite (9500+ tests across 379 files) runs in
# one parallel bun process. Under CPU contention a handful of slower tests
# (real fs/spawn/git-bootstrap work) get starved and exceed a tight timeout
# NON-deterministically — different tests each run — failing CI with
# "timed out after Nms" (observed 32s/51s at the old 30s limit). 90s gives
# ~3x headroom over the worst observed, killing the resource-contention
# flakiness without masking genuinely-hung tests.
#
# Bun 1.3.x has a known segfault during process cleanup after all tests
# pass (exit code 143/SIGTERM or 134/SIGABRT). We capture test output and
# check for real failures vs Bun crashes to avoid false CI failures.
shell: bash
run: |
# Redirect bun output to files, then cat for CI visibility (avoids tee/SIGTERM-flush issues).
#
# Two passes:
# - MAIN: the full suite EXCEPT the subprocess tests (OPENCODE_SKIP_SUBPROCESS=1 makes cliIt
# skip them), at default parallelism — fast.
# - SUBPROCESS: test/cli/{acp,smokes,serve} + run-process/mcp-add/help-snapshots, run in a
# DEDICATED pass with --max-concurrency=2 (default 20). These each spawn a real CLI + an
# in-process mock LLM server; at 20-concurrent the runner is CPU-starved and the mock
# round-trips time out (the load-variance flakes). NOTE: do NOT add --parallel=1 — a single
# worker hosts the mock server AND runs the tests on one event loop, so the mock gets starved
# and round-trips time out anyway. Default --parallel spreads the mocks across workers; the
# pass is isolated from the 11k main-suite tests so 2-per-worker is plenty bounded.
# (Bun 1.3.x can segfault during cleanup after all tests pass — we parse the summary, not the
# exit code. --timeout 90000 gives headroom for slow fs/spawn tests.)
SUBPROCESS_PATHS="test/cli/acp/ test/cli/smokes/ test/cli/serve/ test/cli/run/run-process.test.ts test/cli/mcp-add.test.ts test/cli/help/help-snapshots.test.ts"
OPENCODE_SKIP_SUBPROCESS=1 bun test --timeout 90000 > /tmp/test-main.txt 2>&1 || true
bun test --timeout 90000 --max-concurrency=2 $SUBPROCESS_PATHS > /tmp/test-sub.txt 2>&1 || true
echo "===== MAIN SUITE ====="; cat /tmp/test-main.txt
echo "===== SUBPROCESS SUITE (bounded) ====="; cat /tmp/test-sub.txt
# Sum pass/fail across both passes (one summary line each)
sum_field() { awk -v f="$1" '$0 ~ ("^ *[0-9]+ " f "$"){s+=$1} END{print s+0}' /tmp/test-main.txt /tmp/test-sub.txt; }
PASS_COUNT=$(sum_field pass)
FAIL_COUNT=$(sum_field fail)
echo ""
echo "--- Test Summary (main + subprocess) ---"
echo "pass=$PASS_COUNT fail=$FAIL_COUNT"
# Real test failures — always fail CI
if [ "$FAIL_COUNT" != "0" ]; then
echo "::error::$FAIL_COUNT test(s) failed"
exit 1
fi
# Each pass must have produced a summary, else Bun crashed before finishing
if ! grep -qE "^ *[0-9]+ pass$" /tmp/test-main.txt || ! grep -qE "^ *[0-9]+ pass$" /tmp/test-sub.txt; then
echo "::error::Missing test summary in a pass — Bun may have crashed before running tests"
exit 1
fi
if [ "$PASS_COUNT" -gt 0 ] 2>/dev/null; then
exit 0
fi
echo "::error::No test results found"
exit 1
# ---------------------------------------------------------------------------
# Driver E2E tests — only when driver code changes.
# Uses GitHub Actions services (no Docker-in-Docker).
# Cloud tests (Snowflake, BigQuery, Databricks) are NOT run here —
# they require real credentials and are run locally only.
# ---------------------------------------------------------------------------
driver-e2e:
name: Driver E2E
needs: changes
if: needs.changes.outputs.drivers == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: testpass123
ports:
- 15432:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: testpass123
MYSQL_DATABASE: testdb
ports:
- 13306:3306
options: >-
--health-cmd "mysqladmin ping -h 127.0.0.1"
--health-interval 5s
--health-timeout 5s
--health-retries 20
mssql:
image: mcr.microsoft.com/azure-sql-edge:latest
env:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: TestPass123!
ports:
- 11433:1433
options: >-
--health-cmd "/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'TestPass123!' -Q 'SELECT 1' || exit 1"
--health-interval 10s
--health-timeout 10s
--health-retries 20
redshift:
image: postgres:16-alpine
env:
POSTGRES_PASSWORD: testpass123
POSTGRES_DB: dev
ports:
- 15439:5432
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 5s
--health-retries 10
mongodb:
image: mongo:7.0
ports:
- 27017:27017
options: >-
--health-cmd "mongosh --eval 'db.runCommand({ping:1})' --quiet"
--health-interval 5s
--health-timeout 5s
--health-retries 10
clickhouse:
image: clickhouse/clickhouse-server:latest
env:
CLICKHOUSE_DB: testdb
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1
ports:
- 18123:8123
options: >-
--health-cmd "wget --no-verbose --tries=1 --spider http://localhost:8123/ping || exit 1"
--health-interval 5s
--health-timeout 5s
--health-retries 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- 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: Install dependencies
run: bun install
# `packages/drivers` declares no scripts and no job had it as a working
# directory, so its 141 unit tests — including the driver's lock, timeout
# and read-only regressions — never ran in CI at all. The main TypeScript
# job runs `bun test` from `packages/opencode` only.
- name: Run drivers unit suite
run: bun test --timeout 60000
working-directory: packages/drivers
- name: Run local driver E2E (DuckDB, SQLite, PostgreSQL)
run: bun test test/altimate/drivers-e2e.test.ts
working-directory: packages/opencode
env:
TEST_PG_HOST: 127.0.0.1
TEST_PG_PORT: "15432"
TEST_PG_PASSWORD: testpass123
# Needs a dedicated process: four files in test/altimate install a
# top-level mock.module("@altimateai/drivers/duckdb", …), and Bun
# evaluates every test file's top level before running any test, so in a
# whole-directory run these would silently exercise a fake. With
# ALTIMATE_DUCKDB_E2E=1 a missing or mocked driver fails the step rather
# than skipping it.
#
# `--timeout` is explicit because invoking `bun test` directly does not use
# the package's `test` script, so it would otherwise take the CLI default
# of 5000ms — shorter than both the driver's 30s default open budget and
# the lock helper's 30s readiness budget, which would reimpose exactly the
# kind of too-short outer deadline this PR removes.
- name: Run DuckDB store-open E2E (real store, no mocks)
run: bun test --timeout 90000 test/altimate/duckdb-open-e2e.test.ts test/altimate/warehouse-test-duckdb-e2e.test.ts
working-directory: packages/opencode
env:
ALTIMATE_DUCKDB_E2E: "1"
- name: Run Docker driver E2E (MySQL, SQL Server, Redshift)
run: bun test test/altimate/drivers-docker-e2e.test.ts
working-directory: packages/opencode
env:
TEST_MYSQL_HOST: 127.0.0.1
TEST_MYSQL_PORT: "13306"
TEST_MYSQL_PASSWORD: testpass123
TEST_MSSQL_HOST: 127.0.0.1
TEST_MSSQL_PORT: "11433"
TEST_MSSQL_PASSWORD: "TestPass123!"
TEST_REDSHIFT_HOST: 127.0.0.1
TEST_REDSHIFT_PORT: "15439"
TEST_REDSHIFT_PASSWORD: testpass123
- name: Run MongoDB driver E2E
run: bun test test/altimate/drivers-mongodb-e2e.test.ts
working-directory: packages/opencode
env:
TEST_MONGODB_HOST: 127.0.0.1
TEST_MONGODB_PORT: "27017"
- name: Run ClickHouse driver E2E
run: bun test test/altimate/drivers-clickhouse-e2e.test.ts
working-directory: packages/opencode
env:
TEST_CLICKHOUSE_HOST: 127.0.0.1
TEST_CLICKHOUSE_PORT: "18123"
# Cloud tests NOT included — they require real credentials
# Run locally with:
# ALTIMATE_CODE_CONN_SNOWFLAKE_TEST='...' bun test test/altimate/drivers-snowflake-e2e.test.ts
# ALTIMATE_CODE_CONN_BIGQUERY_TEST='...' bun test test/altimate/drivers-bigquery-e2e.test.ts
# ALTIMATE_CODE_CONN_DATABRICKS_TEST='...' bun test test/altimate/drivers-databricks-e2e.test.ts
# ---------------------------------------------------------------------------
# dbt-tools unit tests — fast (< 5s), run on PRs when dbt-tools changes.
# ---------------------------------------------------------------------------
dbt-tools:
name: dbt-tools
needs: changes
if: needs.changes.outputs.dbt-tools == 'true' || github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- name: Install dependencies
run: bun install
- name: Run dbt-tools unit tests
run: bun run test
working-directory: packages/dbt-tools
# ---------------------------------------------------------------------------
# Windows installer (install.ps1) — Pester behavioral tests on real Windows.
# Runs the script as a subprocess (stopping early via -Help / unknown version
# so nothing is downloaded) to cover arg parsing, the WOW64 arch fix, and
# unknown-version rejection. Only when install.ps1 / its tests change.
# ---------------------------------------------------------------------------
windows-installer:
name: Windows Installer (Pester)
needs: changes
if: needs.changes.outputs.installer == 'true' || github.event_name == 'push'
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Install Pester
shell: pwsh
run: Install-Module Pester -MinimumVersion 5.0.0 -Force -Scope CurrentUser -SkipPublisherCheck
- name: Run installer Pester tests
shell: pwsh
run: |
$config = New-PesterConfiguration
$config.Run.Path = "./test/windows/install.Tests.ps1"
$config.Run.Throw = $true
$config.Output.Verbosity = "Detailed"
Invoke-Pester -Configuration $config
# altimate_change start — Windows ripgrep E2E (issue #1072)
# ---------------------------------------------------------------------------
# Real Windows check for ripgrep binary resolution. Downloads and extracts the
# actual archive with PowerShell stripped from PATH, then executes the binary.
# This is the condition that broke grep for 99 Windows machines; no amount of
# unit testing on Linux/macOS covers it.
# ---------------------------------------------------------------------------
windows-ripgrep-e2e:
name: Windows ripgrep E2E
needs: changes
if: needs.changes.outputs.typescript == 'true' || github.event_name == 'push'
runs-on: windows-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
# --ignore-scripts: `tree-sitter-powershell` has no Windows prebuild and its fallback
# compile needs Visual Studio Build Tools, which this runner does not have (see
# anomalyco/opencode#25563). This check only needs the pure-JS dependency graph
# (effect, @zip.js/zip.js, which, xdg-basedir), so skipping lifecycle scripts is enough.
- name: Install dependencies
run: bun install --ignore-scripts
# Run from packages/core so `effect` and the other deps resolve — they are not root deps.
- name: Resolve ripgrep with PowerShell unavailable
working-directory: packages/core
run: bun run script/windows-ripgrep-e2e.ts
# altimate_change end
# ---------------------------------------------------------------------------
# dbt-tools E2E — slow (~3 min), only on push to main.
# Tests dbt CLI fallbacks against real dbt versions (1.8, 1.10, 1.11) and
# real Python environments (venv, uv, system).
# ---------------------------------------------------------------------------
dbt-tools-e2e:
name: "dbt-tools E2E"
needs: changes
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies
run: bun install
- name: Cache dbt venvs
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: packages/dbt-tools/test/.dbt-venvs
key: dbt-venvs-${{ runner.os }}-1.8-1.10-1.11
- name: Cache Python env scenarios
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: packages/dbt-tools/test/.dbt-resolve-envs
key: dbt-resolve-envs-${{ runner.os }}-v1
- name: Set up dbt versions
run: ./test/e2e/setup-versions.sh 1.8 1.10 1.11
working-directory: packages/dbt-tools
- name: Set up Python env scenarios
run: ./test/e2e/setup-resolve.sh venv uv system
working-directory: packages/dbt-tools
- name: Run dbt-tools E2E tests
run: bun run test:e2e
working-directory: packages/dbt-tools
env:
DBT_E2E_VERSIONS: "1.8,1.10,1.11"
DBT_RESOLVE_SCENARIOS: "venv,uv,system"
# ---------------------------------------------------------------------------
# Verdaccio sanity suite — tests the real `npm install -g` flow.
# Only on push to main (too slow for PRs, needs Docker Compose).
# Catches publish-pipeline bugs: missing files, broken symlinks, wrong bin
# field, dependency resolution failures, postinstall script issues.
# ---------------------------------------------------------------------------
sanity-verdaccio:
name: Sanity (Verdaccio)
needs: changes
if: github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- 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: Install dependencies
run: bun install
- name: Build CLI binary
# target-index=1 = linux-x64 (see release.yml matrix)
run: bun run packages/opencode/script/build.ts --target-index=1
env:
OPENCODE_VERSION: 0.0.0-sanity-${{ github.sha }}
OPENCODE_RELEASE: "1"
ALTIMATE_BASE_GATEWAY_URL: https://gateway.test
MODELS_DEV_API_JSON: test/tool/fixtures/models-api.json
- name: Build dbt-tools
run: bun run build
working-directory: packages/dbt-tools
- name: Run Verdaccio sanity suite
run: |
docker compose -f test/sanity/docker-compose.verdaccio.yml up \
--build --abort-on-container-exit --exit-code-from sanity
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# altimate_change start — cold-start config regression, split out of sanity-verdaccio so it also
# runs on PRs. sanity-verdaccio stays push-only by design (Docker Compose, too slow for PRs — see
# its header above); this check only needs the compiled binary, so it gets its own job gated like
# `typescript` above: on PRs that touch TS, and unconditionally on push (safety net).
# ---------------------------------------------------------------------------
cold-start-regression:
name: Cold-start Config Regression
needs: changes
if: needs.changes.outputs.typescript == 'true' || github.event_name == 'push'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- 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: Install dependencies
run: bun install
- name: Build CLI binary
# target-index=1 = linux-x64 (see release.yml matrix)
run: bun run packages/opencode/script/build.ts --target-index=1
env:
OPENCODE_VERSION: 0.0.0-sanity-${{ github.sha }}
OPENCODE_RELEASE: "1"
ALTIMATE_BASE_GATEWAY_URL: https://gateway.test
MODELS_DEV_API_JSON: test/tool/fixtures/models-api.json
# --version and PURE-mode tests never exercise ordinary config installs. This is the
# deliberate exception to the "no OPENCODE_TEST_CLI" NOTE in the `typescript` job above: a
# single bounded cold-start check with its own --timeout, not the general run+mock subprocess
# suite, so the compiled-binary load hang that NOTE warns about cannot stall CI here.
- name: Cold-start config regression (compiled, non-PURE)
working-directory: packages/opencode
env:
OPENCODE_TEST_CLI: ${{ github.workspace }}/packages/opencode/dist/@altimateai/altimate-code-linux-x64/bin/altimate-code
run: bun test test/cli/serve/fresh-start.test.ts --timeout 90000
# altimate_change end
marker-guard:
name: Marker Guard
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@ecf28ddc73e819eb6fa29df6b34ef8921c743461 # v2
with:
bun-version: "1.3.14"
- name: Add upstream remote
run: |
git remote add upstream https://github.com/anomalyco/opencode.git || true
git fetch upstream --quiet --no-tags
- name: Install merge tooling deps
run: bun install
working-directory: script/upstream
- name: Run marker parser tests
run: bun test
working-directory: script/upstream
- name: Run release preflight tests
# Root bunfig.toml pins `bun test` away from the repo root, so run from
# script/ — same pattern as the marker parser tests above.
run: bun test release-preflight.test.ts
working-directory: script
- name: Check for missing altimate_change markers
run: |
if [[ "${{ github.event_name }}" == "push" ]]; then
if [[ "${{ github.event.before }}" == "0000000000000000000000000000000000000000" ]]; then
echo "Initial push (zero-SHA) — skipping marker check"
exit 0
fi
# Squash-merging an `upstream/merge-*` PR lands as a single commit
# on main, so the second-parent / branch-name signal is gone here.
# Detect bridge/merge-upstream commits in the pushed range by subject
# and downgrade to non-strict — the PR-side review already gated this.
if git log --format=%s "${{ github.event.before }}..${{ github.sha }}" \
| grep -qiE '(bridge|merge) upstream'; then
echo "Bridge/upstream-merge commit detected in push range — running marker check in non-strict mode"
bun run script/upstream/analyze.ts --markers --base "${{ github.event.before }}"
else
echo "Push to main — running marker check against pre-push state"
bun run script/upstream/analyze.ts --markers --base "${{ github.event.before }}" --strict
fi
elif [[ "${{ github.head_ref }}" == merge-upstream-* ]] || [[ "${{ github.head_ref }}" == upstream/merge-* ]]; then
echo "Upstream merge PR detected — running marker check in non-strict mode"
bun run script/upstream/analyze.ts --markers --base origin/${{ github.event.pull_request.base.ref }}
else
bun run script/upstream/analyze.ts --markers --base origin/${{ github.event.pull_request.base.ref }} --strict
fi
- name: Branding leak audit (broadened scan, see analyze.ts LEAK_PATTERNS)
# Catches user-visible bare "opencode" strings that markers alone miss —
# yargs describe text, console output, MCP client identity, workflow YAML,
# User-Agent strings, OIDC audience, infrastructure identifiers.
# Caught 13 of 16 v1.4.0 bridge merge regressions in retrospective testing.
run: bun run script/upstream/analyze.ts --branding
- name: Require-markers regression backstop
# Verifies every file in config.requireMarkers (38 files known to hold
# altimate behavioral patches) has at least one altimate_change block.
# If any patches were silently lost (refactor, accidental delete, bridge
# merge oversight), CI fails here BEFORE the bug reaches production.
run: bun run script/upstream/analyze.ts --require-markers --strict