Skip to content

Commit 8eb372f

Browse files
authored
Merge branch 'main' into sd-db/chore/dependabot-security-only
2 parents e003c53 + 5194f6c commit 8eb372f

3 files changed

Lines changed: 201 additions & 13 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: "Setup Python Dependencies"
2+
description: |
3+
Restores pre-cached Python dependencies and enables offline mode.
4+
Outputs cache-hit so callers can fall back to setup-jfrog-pypi on miss.
5+
6+
outputs:
7+
cache-hit:
8+
description: "Whether the dependency cache was restored and offline mode enabled"
9+
value: ${{ steps.uv-cache.outputs.cache-matched-key != '' }}
10+
11+
runs:
12+
using: "composite"
13+
steps:
14+
- name: Restore uv and pip cache
15+
id: uv-cache
16+
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
17+
with:
18+
path: |
19+
~/.cache/uv
20+
~/.cache/pip
21+
key: python-deps-${{ hashFiles('uv.lock', 'pyproject.toml') }}-latest
22+
restore-keys: python-deps-${{ hashFiles('uv.lock', 'pyproject.toml') }}-
23+
24+
- name: Restore pre-commit cache
25+
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
26+
with:
27+
path: ~/.cache/pre-commit
28+
key: pre-commit-deps-${{ hashFiles('.pre-commit-config.yaml') }}-latest
29+
restore-keys: pre-commit-deps-${{ hashFiles('.pre-commit-config.yaml') }}-
30+
31+
- name: Enable offline mode
32+
if: steps.uv-cache.outputs.cache-matched-key != ''
33+
shell: bash
34+
run: |
35+
echo "UV_OFFLINE=true" >> "$GITHUB_ENV"
36+
echo "PIP_NO_INDEX=1" >> "$GITHUB_ENV"
37+
mkdir -p ~/.config/pip
38+
printf '[global]\nno-index = true\n' > ~/.config/pip/pip.conf

.github/workflows/main.yml

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# **what?**
22
# Runs code quality checks, unit tests, and verifies python build on
3-
# all code commited to the repository. This workflow should not
4-
# require any secrets since it runs for PRs from forked repos.
5-
# By default, secrets are not passed to workflows running from
6-
# a forked repo.
3+
# all code commited to the repository. Dependencies are served from a
4+
# pre-populated cache (see warmDepsCache.yml) when available, with a
5+
# JFrog OIDC fallback when the cache is cold.
76

87
# **why?**
98
# Ensure code for dbt meets a certain quality standard.
@@ -53,17 +52,19 @@ jobs:
5352

5453
env:
5554
UV_FROZEN: "1"
56-
57-
strategy:
58-
fail-fast: false
55+
UV_CACHE_DIR: /home/runner/.cache/uv
5956

6057
steps:
6158
- name: Check out the repository
6259
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
6360

64-
- name: Setup JFrog PyPI Proxy
65-
uses: ./.github/actions/setup-jfrog-pypi
61+
- name: Setup Python Dependencies
62+
id: deps
63+
uses: ./.github/actions/setup-python-deps
6664

65+
- name: Setup JFrog PyPI Proxy (fallback)
66+
if: steps.deps.outputs.cache-hit != 'true'
67+
uses: ./.github/actions/setup-jfrog-pypi
6768

6869
- name: Set up Python
6970
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
@@ -99,6 +100,7 @@ jobs:
99100

100101
env:
101102
UV_FROZEN: "1"
103+
UV_CACHE_DIR: /home/runner/.cache/uv
102104

103105
strategy:
104106
fail-fast: false
@@ -109,9 +111,13 @@ jobs:
109111
- name: Check out the repository
110112
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
111113

112-
- name: Setup JFrog PyPI Proxy
113-
uses: ./.github/actions/setup-jfrog-pypi
114+
- name: Setup Python Dependencies
115+
id: deps
116+
uses: ./.github/actions/setup-python-deps
114117

118+
- name: Setup JFrog PyPI Proxy (fallback)
119+
if: steps.deps.outputs.cache-hit != 'true'
120+
uses: ./.github/actions/setup-jfrog-pypi
115121

116122
- name: Set up Python ${{ matrix.python-version }}
117123
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
@@ -129,6 +135,7 @@ jobs:
129135

130136
# Only run coverage comment once (not for all python versions)
131137
- name: Coverage Comment
138+
id: coverage_comment
132139
if: matrix.python-version == '3.12' && github.event_name == 'pull_request'
133140
uses: py-cov-action/python-coverage-comment-action@7188638f871f721a365d644f505d1ff3df20d683 # v3
134141
with:
@@ -149,14 +156,19 @@ jobs:
149156

150157
env:
151158
UV_FROZEN: "1"
159+
UV_CACHE_DIR: /home/runner/.cache/uv
152160

153161
steps:
154162
- name: Check out the repository
155163
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
156164

157-
- name: Setup JFrog PyPI Proxy
158-
uses: ./.github/actions/setup-jfrog-pypi
165+
- name: Setup Python Dependencies
166+
id: deps
167+
uses: ./.github/actions/setup-python-deps
159168

169+
- name: Setup JFrog PyPI Proxy (fallback)
170+
if: steps.deps.outputs.cache-hit != 'true'
171+
uses: ./.github/actions/setup-jfrog-pypi
160172

161173
- name: Set up Python
162174
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Warm Python Dependency Cache
2+
#
3+
# Pre-downloads all Python dependencies via JFrog Artifactory and saves them
4+
# to the GitHub Actions cache. PR workflows (including fork PRs, which cannot
5+
# authenticate to JFrog) restore this cache and build fully offline.
6+
#
7+
# Triggers:
8+
# - push to main when dependency files change (keeps cache fresh)
9+
# - daily schedule (prevents 7-day GitHub Actions cache eviction)
10+
# - manual dispatch (with optional PR number to warm cache for a fork's deps)
11+
12+
name: Warm Python Dependency Cache
13+
14+
on:
15+
push:
16+
branches: [main]
17+
paths:
18+
- "uv.lock"
19+
- "pyproject.toml"
20+
- ".pre-commit-config.yaml"
21+
pull_request: # TEMPORARY: remove after testing
22+
schedule:
23+
- cron: "0 6 * * *" # Daily at 06:00 UTC
24+
workflow_dispatch:
25+
inputs:
26+
pr_number:
27+
description: "PR number to warm cache for (reads lockfiles from the PR branch). Leave empty to warm from main."
28+
required: false
29+
type: string
30+
31+
permissions:
32+
id-token: write
33+
contents: read
34+
pull-requests: read
35+
36+
jobs:
37+
warm-cache:
38+
runs-on:
39+
group: databricks-protected-runner-group
40+
labels: linux-ubuntu-latest
41+
42+
env:
43+
UV_FROZEN: "1"
44+
# Pin cache dir so setup-uv doesn't override it to a temp path.
45+
# Must match the path in setup-python-deps/action.yml cache restore.
46+
UV_CACHE_DIR: /home/runner/.cache/uv
47+
48+
steps:
49+
- name: Checkout main branch
50+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
51+
52+
- name: Overlay PR dependency files
53+
if: inputs.pr_number != ''
54+
shell: bash
55+
run: |
56+
set -euo pipefail
57+
58+
PR_DATA=$(curl -sLS \
59+
-H "Accept: application/vnd.github+json" \
60+
-H "Authorization: Bearer ${{ github.token }}" \
61+
"https://api.github.com/repos/${{ github.repository }}/pulls/${{ inputs.pr_number }}")
62+
63+
FORK_REPO=$(echo "$PR_DATA" | jq -r '.head.repo.full_name')
64+
FORK_REF=$(echo "$PR_DATA" | jq -r '.head.ref')
65+
66+
echo "Warming cache for PR #${{ inputs.pr_number }} from ${FORK_REPO}@${FORK_REF}"
67+
68+
# Fetch only lockfiles from the fork — .github/actions/ always
69+
# comes from main to prevent code injection from forks.
70+
git remote add fork "https://github.com/${FORK_REPO}.git"
71+
git fetch --depth=1 fork "${FORK_REF}"
72+
git checkout FETCH_HEAD -- uv.lock pyproject.toml .pre-commit-config.yaml
73+
74+
- name: Setup JFrog PyPI Proxy
75+
uses: ./.github/actions/setup-jfrog-pypi
76+
77+
- name: Set up Python
78+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
79+
with:
80+
python-version: "3.10"
81+
82+
- name: Install uv
83+
uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4
84+
85+
- name: Install Hatch
86+
uses: pypa/hatch@257e27e51a6a5616ed08a39a408a21c35c9931bc # install
87+
88+
- name: Install Python versions for test matrix
89+
run: uv python install 3.10 3.11 3.12 3.13
90+
91+
- name: Create all hatch environments (populates uv cache)
92+
run: |
93+
set -euo pipefail
94+
hatch env create default
95+
hatch env create test.py3.10
96+
hatch env create test.py3.11
97+
hatch env create test.py3.12
98+
hatch env create test.py3.13
99+
hatch env create verify
100+
101+
- name: Warm pre-commit cache
102+
run: hatch run pre-commit install-hooks
103+
104+
- name: Build and warm pip cache for verify environment
105+
run: |
106+
set -euo pipefail
107+
hatch -v build
108+
# Run verify to populate ~/.cache/pip/ with runtime transitive deps.
109+
# Allow failure — we only care about populating the cache.
110+
hatch run verify:check-all || true
111+
112+
- name: Generate cache key
113+
id: cache-key
114+
shell: bash
115+
run: |
116+
# Hash first so consumers can prefix-match on the hash alone.
117+
# Timestamp suffix ensures each run creates a new immutable entry;
118+
# consumers pick the latest timestamp for a given hash.
119+
TIMESTAMP=$(date -u +%Y%m%d%H%M%S)
120+
LOCK_HASH="${{ hashFiles('uv.lock', 'pyproject.toml') }}"
121+
echo "python-deps-key=python-deps-${LOCK_HASH}-${TIMESTAMP}" >> "$GITHUB_OUTPUT"
122+
123+
PRECOMMIT_HASH="${{ hashFiles('.pre-commit-config.yaml') }}"
124+
echo "pre-commit-key=pre-commit-deps-${PRECOMMIT_HASH}-${TIMESTAMP}" >> "$GITHUB_OUTPUT"
125+
126+
- name: Save uv and pip cache
127+
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
128+
with:
129+
path: |
130+
~/.cache/uv
131+
~/.cache/pip
132+
key: ${{ steps.cache-key.outputs.python-deps-key }}
133+
134+
- name: Save pre-commit cache
135+
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
136+
with:
137+
path: ~/.cache/pre-commit
138+
key: ${{ steps.cache-key.outputs.pre-commit-key }}

0 commit comments

Comments
 (0)