Skip to content

Commit b16b649

Browse files
Configure JFrog Artifactory as PyPI proxy for hardened runners (#1379)
## Summary Hardened GitHub Actions runners block direct access to public registries (PyPI, files.pythonhosted.org). This adds OIDC-based JFrog Artifactory authentication so CI can download Python packages through the `db-pypi` proxy. Uses the same `jfrog/setup-jfrog-cli` action as [CLI PR #4875](databricks/cli#4875). ## What changed - **New composite action** (`.github/actions/setup-jfrog-pypi/action.yml`): Uses `jfrog/setup-jfrog-cli` for OIDC authentication (same action + SHA as the CLI repo), then sets `UV_INDEX_URL` using the token from the action's `oidc-token`/`oidc-user` outputs. - **`test.yml`**: Added `id-token: write` permissions, JFrog setup step, and `uv lock` before `make dev test`. - **`push.yml`**: Added workflow-level `id-token: write` permissions (needed for `workflow_call` to `test.yml`). Added JFrog setup + `uv lock` to `fmt` and `check-manifest` jobs. - **`Makefile`**: Added `fix-lockfile` target. All existing targets are unchanged. ## How it works `uv.lock` stores full registry URLs per package. Setting `UV_INDEX_URL` to JFrog causes `uv sync --locked` to fail (stale lockfile). To work around this, CI runs `uv lock` after JFrog setup, which rewrites the lockfile with JFrog URLs while keeping the same versions. Then `make dev test` / `make dev fmt` work unchanged with `--locked`. The lockfile rewrite is ephemeral (never committed). In the `fmt` job, `git checkout -- '*.lock'` restores the committed lockfile after formatting, then `make fix-lockfile` normalizes any accidentally committed proxy URLs. `git diff --exit-code` catches both unformatted code and dirty lockfiles. ## `make fix-lockfile` Replaces JFrog proxy URLs with their public PyPI equivalents in all `*.lock` files. Prevents proxy URLs from being accidentally committed to the repo. **When to use it**: If you run `uv lock` while `UV_INDEX_URL` points to JFrog (e.g. after sourcing CI env vars), run `make fix-lockfile` before committing to normalize URLs back to public PyPI. **CI enforcement**: The `fmt` job runs this before `git diff --exit-code`, so committed proxy URLs fail CI. ## Out of scope - **`tagging.yml`**: Generated from the Universe codegen template (`openapi/genkit/sync/workflows/tagging.yml`). JFrog setup needs to be upstreamed there. - **`release.yml` / `release-test.yml`**: Need special publish runners per the migration guide. Separate follow-up with the security team. - **Runner migration**: Switching to hardened runner labels can be done as a follow-up. - **`Trigger Tests` CI failure**: Pre-existing IP allowlist issue (DECO-26816). NO_CHANGELOG=true
1 parent 78914fa commit b16b649

4 files changed

Lines changed: 64 additions & 0 deletions

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Setup JFrog PyPI proxy
2+
description: Authenticate to JFrog via OIDC and configure uv to use the db-pypi proxy
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- name: Setup JFrog CLI with OIDC
8+
id: jfrog
9+
uses: jfrog/setup-jfrog-cli@279b1f629f43dd5bc658d8361ac4802a7ef8d2d5 # v4.9.1
10+
env:
11+
JF_URL: https://databricks.jfrog.io
12+
with:
13+
oidc-provider-name: github-actions
14+
15+
- name: Configure uv for JFrog
16+
shell: bash
17+
run: |
18+
# Route uv package resolution through the JFrog PyPI proxy. Hardened
19+
# runners block direct access to pypi.org, so all index queries go
20+
# through this authenticated mirror instead.
21+
echo "UV_INDEX_URL=https://${{ steps.jfrog.outputs.oidc-user }}:${{ steps.jfrog.outputs.oidc-token }}@databricks.jfrog.io/artifactory/api/pypi/db-pypi/simple" >> "$GITHUB_ENV"

.github/workflows/push.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ on:
66
merge_group:
77
types: [checks_requested]
88

9+
permissions:
10+
id-token: write
11+
contents: read
12+
913
jobs:
1014
tests-ubuntu:
1115
uses: ./.github/workflows/test.yml
@@ -39,9 +43,20 @@ jobs:
3943
with:
4044
version: "0.6.5"
4145

46+
- name: Setup JFrog PyPI proxy
47+
uses: ./.github/actions/setup-jfrog-pypi
48+
49+
- name: Re-lock for JFrog
50+
run: uv lock
51+
4252
- name: Format all files
4353
run: make dev fmt
4454

55+
- name: Restore lockfiles and fix proxy URLs
56+
run: |
57+
git checkout -- '*.lock'
58+
make fix-lockfile
59+
4560
- name: Fail on differences
4661
run: git diff --exit-code
4762

@@ -57,5 +72,11 @@ jobs:
5772
with:
5873
version: "0.6.5"
5974

75+
- name: Setup JFrog PyPI proxy
76+
uses: ./.github/actions/setup-jfrog-pypi
77+
78+
- name: Re-lock for JFrog
79+
run: uv lock
80+
6081
- name: Check MANIFEST.in
6182
run: make dev && uv run check-manifest .

.github/workflows/test.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
runs-on: ${{ inputs.os }}
18+
19+
permissions:
20+
id-token: write
21+
contents: read
22+
1823
steps:
1924
- name: Checkout
2025
uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
@@ -28,6 +33,15 @@ jobs:
2833
version: "0.6.5"
2934
python-version: ${{ inputs.pyVersion }}
3035

36+
- name: Setup JFrog PyPI proxy
37+
uses: ./.github/actions/setup-jfrog-pypi
38+
39+
# Re-lock so uv.lock matches the JFrog-configured UV_INDEX_URL.
40+
# Keeps the same versions; only registry URLs change. Ephemeral (not committed).
41+
- name: Re-lock for JFrog
42+
shell: bash
43+
run: uv lock
44+
3145
- name: Run tests
3246
run: make dev test
3347

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,13 @@ benchmark:
3030
coverage: test
3131
open htmlcov/index.html
3232

33+
fix-lockfile:
34+
@# Replace JFrog proxy URLs with public equivalents in lockfiles.
35+
@# Prevents proxy URLs from being accidentally committed.
36+
find . -type f -name '*.lock' -not -path './.github/*' \
37+
-exec sed -i 's|databricks\.jfrog\.io/artifactory/api/pypi/db-pypi/simple|pypi.org/simple|g' {} +
38+
find . -type f -name '*.lock' -not -path './.github/*' \
39+
-exec sed -i 's|databricks\.jfrog\.io/artifactory/api/pypi/db-pypi/packages|files.pythonhosted.org|g' {} +
40+
3341
clean:
3442
rm -fr dist *.egg-info .pytest_cache build htmlcov .venv

0 commit comments

Comments
 (0)