Skip to content

Commit 37320a5

Browse files
ci: make fork offline CI robust — 3.9 skip, mypy flags, warmer matrix (#831) (#851)
Follow-ups from the fork verification run (#849) once the cache finally hit. The offline restore mechanism works (10 legs green offline incl. all kernel legs); these fix the remaining offline-path failures: 1. Python 3.9 can't be provisioned offline — the protected-runner image preinstalls 3.10-3.14 but not 3.9 (EOL), so setup-python tries to DOWNLOAD it and fails with no network on a fork. That hard-failed the 3.9 legs and, under fail-fast, cancelled the working 3.10-3.14 siblings. Move setup-python into restore-deps with continue-on-error, probe the restored interpreter, and export venv-usable; callers gate their offline run on it and skip 3.9 neutrally (3.9 fork coverage is in the merge queue) instead of failing. Keeps 3.9 in the matrix per maintainer preference without letting it sink the other legs. 2. check-types offline ran , which mypy rejects without --install-types ('--non-interactive is only supported with --install-types'). The warmer already primed the stubs into the venv, so run plain . 3. The warmer excluded 3.14-min but the PR matrix runs 3.14-min pyarrow (PR excludes only 3.12-min/3.13-min) — that leg missed the cache and fell back to JFrog. Align the warmer's excludes to the PR matrix exactly. Refs #831 Co-authored-by: Isaac Signed-off-by: Vikrant Puppala <vikrant.puppala@databricks.com>
1 parent f90efac commit 37320a5

3 files changed

Lines changed: 63 additions & 49 deletions

File tree

.github/actions/restore-deps/action.yml

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
11
name: Restore Prewarmed Dependencies
22
description: |
33
Restores a fully-built in-project .venv from the "forkvenv-*" cache written by
4-
warm-deps-cache.yml, so FORK PRs run offline without touching JFrog (#831).
4+
warm-deps-cache.yml, provisions the matching Python interpreter, and probes
5+
that the restored venv actually runs — so FORK PRs run offline without JFrog
6+
(#831).
57
6-
Outputs cache-hit. When true, the caller must SKIP setup-poetry (the .venv is
7-
already complete) and only install Poetry itself. When false — a same-repo PR,
8-
or a fork whose lockfiles changed and haven't been warmed — the caller falls
9-
back to setup-poetry (JFrog OIDC), which works in every trusted context and on
10-
cache-miss for same-repo PRs.
8+
Outputs:
9+
- cache-hit: "true" if a warmed .venv was restored. When false — a same-repo
10+
PR, or a fork whose lockfiles changed and haven't been re-warmed — the
11+
caller falls back to setup-poetry (JFrog OIDC), which works in every
12+
trusted context and on cache-miss for same-repo PRs.
13+
- venv-usable: "true" if the restored venv's interpreter runs offline. It is
14+
false when the interpreter can't be provisioned without network — the
15+
protected-runner image preinstalls 3.10–3.14 but not 3.9 (EOL), so on 3.9
16+
setup-python would try to download the interpreter and fail on a fork.
17+
Callers gate their offline run on venv-usable=='true' and skip neutrally
18+
otherwise (3.9 fork coverage comes from the merge queue), which also keeps
19+
a 3.9 leg from cancelling its 3.10–3.14 siblings under fail-fast.
1120
1221
inputs:
1322
python-version:
@@ -26,6 +35,9 @@ outputs:
2635
cache-hit:
2736
description: "true if a warmed .venv was restored (install can be skipped)"
2837
value: ${{ steps.restore.outputs.cache-matched-key != '' }}
38+
venv-usable:
39+
description: "true if the restored venv interpreter runs offline (run tests) — false means skip neutrally"
40+
value: ${{ steps.probe.outputs.usable }}
2941

3042
runs:
3143
using: composite
@@ -38,16 +50,37 @@ runs:
3850
# The warmer appends a timestamp to the key (caches are immutable), so
3951
# there is no fixed exact key to hit. restore-keys does a PREFIX match
4052
# and returns the most-recently-created entry for this lockfile +
41-
# matrix leg — exactly the freshest warmed venv.
53+
# matrix leg — exactly the freshest warmed venv. The lockfile hash MUST
54+
# be computed here on the PRISTINE poetry.lock (before any `poetry
55+
# lock` rewrites it) to match the warmer's key — see warm-deps-cache.yml.
4256
key: forkvenv-${{ runner.os }}-${{ inputs.python-version }}-${{ inputs.dependency-version }}-${{ inputs.extras || 'base' }}-${{ hashFiles('**/poetry.lock') }}-never
4357
restore-keys: |
4458
forkvenv-${{ runner.os }}-${{ inputs.python-version }}-${{ inputs.dependency-version }}-${{ inputs.extras || 'base' }}-${{ hashFiles('**/poetry.lock') }}-
4559
46-
- name: Report restore outcome
60+
- name: Set up Python for the restored venv
61+
# Only meaningful on a cache hit — the restored venv symlinks its
62+
# interpreter to a hostedtoolcache path this step provisions.
63+
# continue-on-error: 3.9 isn't preinstalled on the runner image, so this
64+
# would try to DOWNLOAD it and fail offline on a fork. Tolerate that and
65+
# let the probe decide, rather than hard-fail (and cancel siblings).
66+
if: steps.restore.outputs.cache-matched-key != ''
67+
continue-on-error: true
68+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
69+
with:
70+
python-version: ${{ inputs.python-version }}
71+
72+
- name: Probe restored venv interpreter
73+
id: probe
4774
shell: bash
4875
run: |
49-
if [ -n "${{ steps.restore.outputs.cache-matched-key }}" ]; then
50-
echo "Restored warmed .venv (${{ steps.restore.outputs.cache-matched-key }}) — install will be skipped."
76+
if [ -n "${{ steps.restore.outputs.cache-matched-key }}" ] && .venv/bin/python --version >/dev/null 2>&1; then
77+
echo "usable=true" >> "$GITHUB_OUTPUT"
78+
echo "Restored venv is usable offline: $(.venv/bin/python --version 2>&1)"
5179
else
52-
echo "No warmed .venv for this lockfile/leg — caller falls back to setup-poetry (JFrog)."
80+
echo "usable=false" >> "$GITHUB_OUTPUT"
81+
if [ -n "${{ steps.restore.outputs.cache-matched-key }}" ]; then
82+
echo "::notice::Prewarmed venv interpreter (Python ${{ inputs.python-version }}) is unavailable offline on this runner; the caller will skip its offline run for this leg. Covered in the merge queue."
83+
else
84+
echo "No warmed .venv for this lockfile/leg — caller falls back to setup-poetry (JFrog)."
85+
fi
5386
fi

.github/workflows/code-quality-checks.yml

Lines changed: 15 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,17 @@ jobs:
4949
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
5050

5151
# --- Fork path: restore a prewarmed .venv and run offline -------------
52+
# restore-deps also provisions the interpreter and probes it; venv-usable
53+
# is false when the interpreter can't be provisioned offline (3.9), in
54+
# which case we skip the offline run neutrally instead of failing.
5255
- name: Restore prewarmed dependencies
5356
id: deps
5457
uses: ./.github/actions/restore-deps
5558
with:
5659
python-version: ${{ matrix.python-version }}
5760
dependency-version: ${{ matrix.dependency-version }}
58-
- name: Set up Python (cache hit — needed for the restored venv interpreter)
59-
if: steps.deps.outputs.cache-hit == 'true'
60-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
61-
with:
62-
python-version: ${{ matrix.python-version }}
6361
- name: Run tests (offline, from prewarmed venv)
64-
if: steps.deps.outputs.cache-hit == 'true'
62+
if: steps.deps.outputs.venv-usable == 'true'
6563
run: .venv/bin/python -m pytest tests/unit -m "not realkernel"
6664

6765
# --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) --
@@ -126,13 +124,8 @@ jobs:
126124
python-version: ${{ matrix.python-version }}
127125
dependency-version: ${{ matrix.dependency-version }}
128126
extras: pyarrow
129-
- name: Set up Python (cache hit — needed for the restored venv interpreter)
130-
if: steps.deps.outputs.cache-hit == 'true'
131-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
132-
with:
133-
python-version: ${{ matrix.python-version }}
134127
- name: Run tests (offline, from prewarmed venv)
135-
if: steps.deps.outputs.cache-hit == 'true'
128+
if: steps.deps.outputs.venv-usable == 'true'
136129
run: .venv/bin/python -m pytest tests/unit -m "not realkernel"
137130

138131
# --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) ---
@@ -200,19 +193,14 @@ jobs:
200193
with:
201194
python-version: ${{ matrix.python-version }}
202195
extras: kernel
203-
- name: Set up Python (cache hit — needed for the restored venv interpreter)
204-
if: steps.deps.outputs.cache-hit == 'true'
205-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
206-
with:
207-
python-version: ${{ matrix.python-version }}
208196
- name: Assert the real kernel wheel is installed (offline)
209-
if: steps.deps.outputs.cache-hit == 'true'
197+
if: steps.deps.outputs.venv-usable == 'true'
210198
run: .venv/bin/python -c "import databricks_sql_kernel as k; assert k.__file__, 'kernel wheel missing __file__ — not the real wheel'; print('real kernel wheel:', k.__file__)"
211199
- name: Unit tests, realkernel deselected (offline)
212-
if: steps.deps.outputs.cache-hit == 'true'
200+
if: steps.deps.outputs.venv-usable == 'true'
213201
run: .venv/bin/python -m pytest tests/unit -m "not realkernel"
214202
- name: Drive use_kernel=True through the REAL wheel — routing (offline)
215-
if: steps.deps.outputs.cache-hit == 'true'
203+
if: steps.deps.outputs.venv-usable == 'true'
216204
run: .venv/bin/python -m pytest tests/unit/test_session.py -m realkernel -v
217205

218206
# --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) --
@@ -267,13 +255,8 @@ jobs:
267255
uses: ./.github/actions/restore-deps
268256
with:
269257
python-version: ${{ matrix.python-version }}
270-
- name: Set up Python (cache hit — needed for the restored venv interpreter)
271-
if: steps.deps.outputs.cache-hit == 'true'
272-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
273-
with:
274-
python-version: ${{ matrix.python-version }}
275258
- name: Black (offline, from prewarmed venv)
276-
if: steps.deps.outputs.cache-hit == 'true'
259+
if: steps.deps.outputs.venv-usable == 'true'
277260
run: .venv/bin/black --check src
278261
- name: Setup Poetry
279262
if: steps.deps.outputs.cache-hit != 'true'
@@ -299,18 +282,15 @@ jobs:
299282
uses: ./.github/actions/restore-deps
300283
with:
301284
python-version: ${{ matrix.python-version }}
302-
- name: Set up Python (cache hit — needed for the restored venv interpreter)
303-
if: steps.deps.outputs.cache-hit == 'true'
304-
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
305-
with:
306-
python-version: ${{ matrix.python-version }}
307285
- name: Mypy (offline, from prewarmed venv)
308-
if: steps.deps.outputs.cache-hit == 'true'
309-
# --install-types would reach the index; the warmed venv already has
310-
# the stub packages, so run mypy without it on the offline path.
286+
if: steps.deps.outputs.venv-usable == 'true'
287+
# The warmer already ran `mypy --install-types` to bake the stub
288+
# packages into the venv, so just run mypy plainly here. (mypy rejects
289+
# --non-interactive unless --install-types is also given, and
290+
# --install-types would reach the index — neither is wanted offline.)
311291
run: |
312292
mkdir -p .mypy_cache
313-
.venv/bin/mypy --non-interactive src
293+
.venv/bin/mypy src
314294
- name: Setup Poetry
315295
if: steps.deps.outputs.cache-hit != 'true'
316296
uses: ./.github/actions/setup-poetry

.github/workflows/warm-deps-cache.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,14 @@ jobs:
9090
dependency-version: ["default", "min"]
9191
extras: ["", "pyarrow", "kernel"]
9292
exclude:
93-
# min deps are only exercised on 3.9/3.10/3.11 in the PR matrix.
93+
# MUST match code-quality-checks.yml's excludes exactly, or a PR leg
94+
# with no warmed cache entry misses and falls back to JFrog (which
95+
# forks can't reach). The PR matrix excludes ONLY 3.12-min and
96+
# 3.13-min — 3.14-min IS a real PR leg, so it must be warmed.
9497
- python-version: "3.12"
9598
dependency-version: "min"
9699
- python-version: "3.13"
97100
dependency-version: "min"
98-
- python-version: "3.14"
99-
dependency-version: "min"
100101
# The kernel wheel is cp310-abi3 (Requires-Python >=3.10); the
101102
# [kernel] extra is a no-op on 3.9, so there's no kernel leg to warm.
102103
- python-version: "3.9"

0 commit comments

Comments
 (0)