fix: eliminate stale file race condition and fix error misclassification #1464
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} | |
| steps: | |
| - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3 | |
| id: filter | |
| with: | |
| filters: | | |
| typescript: | |
| - 'packages/opencode/**' | |
| - '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' | |
| dbt-tools: | |
| - 'packages/dbt-tools/**' | |
| # --------------------------------------------------------------------------- | |
| # 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.10" | |
| - 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 "[email protected]" | |
| - name: Install dependencies | |
| run: bun install | |
| - 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 30000: matches package.json "test" script; prevents 5s default | |
| # from cutting off tests that run bun install or bootstrap git instances. | |
| # | |
| # 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 file, then cat it for CI visibility. | |
| # This avoids tee/pipe issues where SIGTERM kills tee before flush. | |
| bun test --timeout 30000 > /tmp/test-output.txt 2>&1 || true | |
| cat /tmp/test-output.txt | |
| # Extract pass/fail counts from Bun test summary (e.g., " 5362 pass") | |
| PASS_COUNT=$(awk '/^ *[0-9]+ pass$/{print $1}' /tmp/test-output.txt || true) | |
| FAIL_COUNT=$(awk '/^ *[0-9]+ fail$/{print $1}' /tmp/test-output.txt || true) | |
| echo "" | |
| echo "--- Test Summary ---" | |
| echo "pass=${PASS_COUNT:-none} fail=${FAIL_COUNT:-none}" | |
| # Real test failures — always fail CI | |
| if [ -n "$FAIL_COUNT" ] && [ "$FAIL_COUNT" != "0" ]; then | |
| echo "::error::$FAIL_COUNT test(s) failed" | |
| exit 1 | |
| fi | |
| # Tests passed (we have a pass count and zero/no failures) | |
| if [ -n "$PASS_COUNT" ] && [ "$PASS_COUNT" -gt 0 ] 2>/dev/null; then | |
| exit 0 | |
| fi | |
| # No test summary at all — Bun crashed before running tests | |
| echo "::error::No test results found in output — Bun may have crashed before running tests" | |
| 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.10" | |
| - 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: 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 | |
| - 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.10" | |
| - name: Install dependencies | |
| run: bun install | |
| - name: Run dbt-tools unit tests | |
| run: bun run test | |
| working-directory: packages/dbt-tools | |
| # --------------------------------------------------------------------------- | |
| # 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.10" | |
| - 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.10" | |
| - 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" | |
| 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 }} | |
| 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.10" | |
| - 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: 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 | |
| echo "Push to main — running marker check against pre-push state" | |
| bun run script/upstream/analyze.ts --markers --base "${{ github.event.before }}" --strict | |
| 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 ${{ github.event.pull_request.base.ref }} | |
| else | |
| bun run script/upstream/analyze.ts --markers --base ${{ github.event.pull_request.base.ref }} --strict | |
| fi |