fix(ci): validate cached dbt e2e environments; pin the uv venv to the system Python
Issue for this PR
Closes #1257
Type of change
- Bug fix (CI)
What does this PR do?
Main's dbt-tools E2E job fails on every run. Both of its caches hold venvs whose bin/python links to an interpreter outside the cached directory, so after a restore bin/dbt exists but does not run:
- The scenario cache holds a uv venv linked to a uv-managed Python that fresh runners lack: 3
dbt resolver e2e > uvtests fail with ENOENT. - The version cache (built 2026-05-20) no longer runs either. Setup prints
already installed (unknown), and the multi-version suite finds no working dbt and skips every test with a warning. Main runs 11 E2E tests; with that suite running it is 48.
actions/cache saves only on a miss, so the same broken environments come back on every run.
- Setup scripts (
setup-resolve.sh,setup-versions.sh): a cached environment counts only if itsdbt --versionsucceeds (and, for the version venvs, reports the requested version); otherwise the script prints↻ <name> cache is stale (dbt does not run) — rebuilding...and rebuilds. Both build onDBT_E2E_PYTHONwhen set, and the uv venv is created with--pythonso it links to that interpreter. - Versions test (
dbt-versions.test.ts): whenDBT_E2E_VERSIONSis set (CI sets it), a requested version without a working dbt that reports that version fails the run instead of skipping. Each version'sbeforeAllnow callsconfigure()with its own venv. The tests never reset the dbt binarydbt-clicaches, so every version after the first ran the first one's dbt; it surfaced as 4 dbt 1.11 failures (a DuckDB file written by 1.11 that 1.10 cannot read). - Workflow (
ci.yml): both cache keys include the setup-python version, so a new 3.11 patch release starts fresh caches instead of restoring dead venvs; the scenario key also moves to-v2. Both setup steps getDBT_E2E_PYTHONfrom setup-python. The job also runs on PRs that touchpackages/dbt-tools/**, with a read-only token and no persisted credentials, so a fix to its own setup is proven before merge.
How did you verify your code works?
Linux, Python 3.11.15, uv 0.11.1, from packages/dbt-tools with CI's env vars and no dbt on PATH:
- Fresh build of dbt 1.8/1.10/1.11 and the venv/uv scenarios, then
bun run test:e2e: 48 pass, 0 fail. Without theconfigure()call: 4 dbt 1.11 failures. - CI's broken state, reproduced by pointing the 1.8 venv's
bin/pythonat a missing interpreter: main's setup script printedalready installed (unknown)and main's test ran 0 tests and exited 0. The new test fails; the new setup script rebuilt 1.8 and reused it on the next run. The same holds when1.8holds a working 1.10, and for adbtthat prints its version and exits 1. - Same for uv (dangling
uv/.venv/bin/python): rebuilt. Also checked on macOS with pyenv Python 3.9: stale uv venv rebuilt, resolver tests 23 pass. - dbt-tools unit tests 82 pass; strict marker check and branding audit pass; workflow YAML parses.
On CI (this PR, Python 3.11.17): the first run missed both new keys, built every environment fresh and saved the caches; a later run restored both, each cached dbt passed the run-and-version check, and nothing was rebuilt. Both runs: 48 pass / 0 fail including the new version check. Caches saved by a PR run are scoped to the PR, so main builds its own on the first push after merge and restores from the second.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
🤖 Generated with Claude Code
https://claude.ai/code/session_0174HUYiaceApqHP7EKMNgnb
Summary by CodeRabbit
-
Tests
- End-to-end checks run for relevant changes in both pull requests and pushes.
- Requested dbt versions are checked against available environments, and runs fail when a requested version is unavailable.
- Cached Python environments are validated before use and rebuilt when cached tools are unusable.
- Environment setup uses the selected Python interpreter for more reliable results after cache restoration.
-
Chores
- Updated environment cache versioning.
- Restricted CI checkout access to read-only permissions.
[!NOTE]
Low Risk
Changes are limited to CI workflow and E2E setup scripts; they do not alter shipped application or resolver runtime behavior in production.
Overview
Fixes dbt-tools E2E failures where a restored GitHub Actions cache looked healthy (.done present, bin/dbt on disk) but dbt exited with ENOENT because the uv venv pointed at a uv-managed Python outside the cached tree.
setup-resolve.sh now treats a cache hit as valid only after dbt --version succeeds (with optional timeout); stale caches are rebuilt with a clear message. uv venvs are created with --python "$REAL_PYTHON", and DBT_E2E_PYTHON lets CI prefer setup-python’s interpreter over the image default. All scenarios share a cached_or_rebuild helper instead of trusting .done alone.
.github/workflows/ci.yml bumps the resolve-env cache key v1 → v2, passes DBT_E2E_PYTHON into setup, and runs dbt-tools E2E on PRs that touch packages/dbt-tools (not only on push to main). The job uses read-only contents and persist-credentials: false on checkout, matching other PR-scoped jobs.
Reviewed by Cursor Bugbot for commit b443456e768c7e1a25b686e49cad59e988a02117. Bugbot is set up for automated code reviews on this repo. Configure here.