@@ -2,6 +2,27 @@ name: Code Quality Checks
22
33on : [pull_request]
44
5+ # How fork PRs are made to work (issue #831)
6+ # ------------------------------------------
7+ # Every job below installs deps via .github/actions/setup-poetry ->
8+ # setup-jfrog, which needs a GitHub OIDC token to authenticate to the JFrog
9+ # PyPI proxy. GitHub withholds that token from fork `pull_request` runs, and
10+ # public PyPI is unreachable from the protected runners — so on a fork PR the
11+ # JFrog path cannot run at all (this is what made every fork check red).
12+ #
13+ # Each job therefore takes a cache-first path:
14+ # 1. restore-deps restores a fully-built .venv prewarmed by
15+ # warm-deps-cache.yml (a trusted workflow that DOES have OIDC).
16+ # 2. On a cache HIT (the normal fork path) we run the tools directly from
17+ # .venv/bin — NOT `poetry run` — because even bootstrapping Poetry
18+ # (`pip install poetry`) would hit JFrog. The restored venv already
19+ # contains pytest/black/mypy and the connector, so no install is needed.
20+ # 3. On a cache MISS (every same-repo PR, or a fork whose lockfiles changed
21+ # before a maintainer re-warmed) we fall back to the original
22+ # setup-poetry (JFrog OIDC) flow, unchanged. Same-repo PRs are unaffected.
23+ #
24+ # Each step branches on the restore step's output, `steps.deps.outputs.cache-hit`.
25+
526permissions :
627 contents : read
728 id-token : write
@@ -26,29 +47,50 @@ jobs:
2647 steps :
2748 - name : Check out repository
2849 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
50+
51+ # --- Fork path: restore a prewarmed .venv and run offline -------------
52+ - name : Restore prewarmed dependencies
53+ id : deps
54+ uses : ./.github/actions/restore-deps
55+ with :
56+ python-version : ${{ matrix.python-version }}
57+ 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 }}
63+ - name : Run tests (offline, from prewarmed venv)
64+ if : steps.deps.outputs.cache-hit == 'true'
65+ run : .venv/bin/python -m pytest tests/unit -m "not realkernel"
66+
67+ # --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) --
2968 - name : Setup Poetry
69+ if : steps.deps.outputs.cache-hit != 'true'
3070 uses : ./.github/actions/setup-poetry
3171 with :
3272 python-version : ${{ matrix.python-version }}
3373 cache-suffix : " ${{ matrix.dependency-version }}-"
3474 - name : Install Python tools for custom versions
35- if : matrix.dependency-version != 'default'
75+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
3676 run : poetry run pip install toml packaging
3777 - name : Generate requirements file
38- if : matrix.dependency-version != 'default'
78+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
3979 run : |
4080 poetry run python scripts/dependency_manager.py ${{ matrix.dependency-version }} --output requirements-${{ matrix.dependency-version }}.txt
4181 echo "Generated requirements for ${{ matrix.dependency-version }} versions:"
4282 cat requirements-${{ matrix.dependency-version }}.txt
4383 - name : Override with custom dependency versions
44- if : matrix.dependency-version != 'default'
84+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
4585 run : poetry run pip install -r requirements-${{ matrix.dependency-version }}.txt
4686 - name : Show installed versions
87+ if : steps.deps.outputs.cache-hit != 'true'
4788 run : |
4889 echo "=== Dependency Version: ${{ matrix.dependency-version }} ==="
4990 poetry run pip list
5091 - name : Run tests
51- run : poetry run python -m pytest tests/unit
92+ if : steps.deps.outputs.cache-hit != 'true'
93+ run : poetry run python -m pytest tests/unit -m "not realkernel"
5294
5395 run-unit-tests-with-arrow :
5496 runs-on :
@@ -69,34 +111,146 @@ jobs:
69111 steps :
70112 - name : Check out repository
71113 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
114+ # libkrb5 is a runtime system lib (not in the venv), so install it on
115+ # both the fork and fallback paths.
72116 - name : Install Kerberos system dependencies
73117 run : |
74118 sudo apt-get update
75119 sudo apt-get install -y libkrb5-dev
120+
121+ # --- Fork path: restore prewarmed pyarrow venv, run offline ------------
122+ - name : Restore prewarmed dependencies
123+ id : deps
124+ uses : ./.github/actions/restore-deps
125+ with :
126+ python-version : ${{ matrix.python-version }}
127+ dependency-version : ${{ matrix.dependency-version }}
128+ 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 }}
134+ - name : Run tests (offline, from prewarmed venv)
135+ if : steps.deps.outputs.cache-hit == 'true'
136+ run : .venv/bin/python -m pytest tests/unit -m "not realkernel"
137+
138+ # --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) ---
76139 - name : Setup Poetry
140+ if : steps.deps.outputs.cache-hit != 'true'
77141 uses : ./.github/actions/setup-poetry
78142 with :
79143 python-version : ${{ matrix.python-version }}
80- install-args : " --all-extras"
144+ # Install ONLY the pyarrow extra (not --all-extras) so this
145+ # tier isolates the "pyarrow present, kernel absent"
146+ # configuration. --all-extras would also pull the kernel wheel,
147+ # making this job redundant with "Unit Tests + Kernel".
148+ install-args : " --extras pyarrow"
81149 cache-suffix : " pyarrow-${{ matrix.dependency-version }}-"
82150 - name : Install Python tools for custom versions
83- if : matrix.dependency-version != 'default'
151+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
84152 run : poetry run pip install toml packaging
85153 - name : Generate requirements file with pyarrow
86- if : matrix.dependency-version != 'default'
154+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
87155 run : |
88156 poetry run python scripts/dependency_manager.py ${{ matrix.dependency-version }} --output requirements-${{ matrix.dependency-version }}-arrow.txt
89157 echo "Generated requirements for ${{ matrix.dependency-version }} versions with PyArrow:"
90158 cat requirements-${{ matrix.dependency-version }}-arrow.txt
91159 - name : Override with custom dependency versions
92- if : matrix.dependency-version != 'default'
160+ if : steps.deps.outputs.cache-hit != 'true' && matrix.dependency-version != 'default'
93161 run : poetry run pip install -r requirements-${{ matrix.dependency-version }}-arrow.txt
94162 - name : Show installed versions
163+ if : steps.deps.outputs.cache-hit != 'true'
95164 run : |
96165 echo "=== Dependency Version: ${{ matrix.dependency-version }} with PyArrow ==="
97166 poetry run pip list
98167 - name : Run tests
99- run : poetry run python -m pytest tests/unit
168+ if : steps.deps.outputs.cache-hit != 'true'
169+ run : poetry run python -m pytest tests/unit -m "not realkernel"
170+
171+ run-unit-tests-with-kernel :
172+ runs-on :
173+ group : databricks-protected-runner-group
174+ labels : linux-ubuntu-latest
175+ strategy :
176+ matrix :
177+ # Kernel wheel is cp310-abi3 (Requires-Python >=3.10), so this
178+ # matrix omits 3.9 — the [kernel] extra is a no-op there.
179+ python-version : ["3.10", "3.11", "3.12", "3.13", "3.14"]
180+
181+ name : " Unit Tests + Kernel (Python ${{ matrix.python-version }})"
182+ # base restore-deps entries omit 3.9-kernel (no kernel wheel there); this
183+ # matrix already starts at 3.10, so every leg has a warmed counterpart.
184+
185+ steps :
186+ - name : Check out repository
187+ uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
188+ - name : Install Kerberos system dependencies
189+ run : |
190+ sudo apt-get update
191+ sudo apt-get install -y libkrb5-dev
192+
193+ # --- Fork path: restore prewarmed kernel venv, run offline ------------
194+ # NOTE: the [kernel] extra pulls databricks-sql-kernel, the one PRIVATE
195+ # (JFrog-only) package. Baking it into the warmed venv is precisely what
196+ # lets a fork exercise the kernel routing without any JFrog credential.
197+ - name : Restore prewarmed dependencies
198+ id : deps
199+ uses : ./.github/actions/restore-deps
200+ with :
201+ python-version : ${{ matrix.python-version }}
202+ 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 }}
208+ - name : Assert the real kernel wheel is installed (offline)
209+ if : steps.deps.outputs.cache-hit == 'true'
210+ 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__)"
211+ - name : Unit tests, realkernel deselected (offline)
212+ if : steps.deps.outputs.cache-hit == 'true'
213+ run : .venv/bin/python -m pytest tests/unit -m "not realkernel"
214+ - name : Drive use_kernel=True through the REAL wheel — routing (offline)
215+ if : steps.deps.outputs.cache-hit == 'true'
216+ run : .venv/bin/python -m pytest tests/unit/test_session.py -m realkernel -v
217+
218+ # --- Fallback path: same-repo PRs / cache miss use JFrog (unchanged) --
219+ - name : Setup Poetry
220+ if : steps.deps.outputs.cache-hit != 'true'
221+ uses : ./.github/actions/setup-poetry
222+ with :
223+ python-version : ${{ matrix.python-version }}
224+ # Install the kernel extra (pulls the published
225+ # databricks-sql-kernel wheel, which transitively brings
226+ # pyarrow). Explicit --extras kernel rather than --all-extras
227+ # so this tier targets the kernel configuration specifically.
228+ install-args : " --extras kernel"
229+ cache-suffix : " kernel-"
230+ - name : Show installed versions
231+ if : steps.deps.outputs.cache-hit != 'true'
232+ run : |
233+ echo "=== with databricks-sql-kernel ==="
234+ poetry run pip list
235+ - name : Assert the real kernel wheel is installed (not a stub)
236+ if : steps.deps.outputs.cache-hit != 'true'
237+ run : |
238+ poetry run 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__)"
239+ - name : Unit tests (kernel wheel present, realkernel deselected)
240+ if : steps.deps.outputs.cache-hit != 'true'
241+ # The bulk of tests/unit fakes databricks_sql_kernel in
242+ # sys.modules, so the real-wheel routing test is deselected here
243+ # and run on its own below (a shared session would shadow the
244+ # real wheel — both real-wheel tests fail loudly if that happens).
245+ run : poetry run python -m pytest tests/unit -m "not realkernel"
246+ - name : Drive use_kernel=True through the REAL wheel (routing)
247+ if : steps.deps.outputs.cache-hit != 'true'
248+ # Separate invocation, explicit file path: never collects the
249+ # fake-module test file, so sys.modules stays unpolluted. This is
250+ # the no-network proof that sql.connect(use_kernel=True) actually
251+ # instantiates the real KernelDatabricksClient (not a stub, not a
252+ # Thrift fallback). Fails loudly if the real wheel is shadowed.
253+ run : poetry run python -m pytest tests/unit/test_session.py -m realkernel -v
100254
101255 check-linting :
102256 runs-on :
@@ -108,11 +262,26 @@ jobs:
108262 steps :
109263 - name : Check out repository
110264 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
265+ - name : Restore prewarmed dependencies
266+ id : deps
267+ uses : ./.github/actions/restore-deps
268+ with :
269+ 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 }}
275+ - name : Black (offline, from prewarmed venv)
276+ if : steps.deps.outputs.cache-hit == 'true'
277+ run : .venv/bin/black --check src
111278 - name : Setup Poetry
279+ if : steps.deps.outputs.cache-hit != 'true'
112280 uses : ./.github/actions/setup-poetry
113281 with :
114282 python-version : ${{ matrix.python-version }}
115283 - name : Black
284+ if : steps.deps.outputs.cache-hit != 'true'
116285 run : poetry run black --check src
117286
118287 check-types :
@@ -125,11 +294,30 @@ jobs:
125294 steps :
126295 - name : Check out repository
127296 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
297+ - name : Restore prewarmed dependencies
298+ id : deps
299+ uses : ./.github/actions/restore-deps
300+ with :
301+ 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 }}
307+ - 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.
311+ run : |
312+ mkdir -p .mypy_cache
313+ .venv/bin/mypy --non-interactive src
128314 - name : Setup Poetry
315+ if : steps.deps.outputs.cache-hit != 'true'
129316 uses : ./.github/actions/setup-poetry
130317 with :
131318 python-version : ${{ matrix.python-version }}
132319 - name : Mypy
320+ if : steps.deps.outputs.cache-hit != 'true'
133321 run : |
134322 mkdir .mypy_cache
135323 poetry run mypy --install-types --non-interactive src
0 commit comments