Skip to content

Commit 79a22d8

Browse files
committed
Reduce unnecessary PR full-suite CI runs
1 parent 14fb1f0 commit 79a22d8

2 files changed

Lines changed: 94 additions & 3 deletions

File tree

.github/workflows/pr_code_changes.yaml

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@ on:
66
branches:
77
- main
88
paths:
9+
- pyproject.toml
10+
- uv.lock
11+
- modal_app/**
912
- policyengine_us_data/**
1013
- tests/**
1114
- .github/workflows/**
1215
- Makefile
1316

17+
concurrency:
18+
group: pr-code-changes-${{ github.event.pull_request.number }}
19+
cancel-in-progress: true
20+
1421
jobs:
1522
check-fork:
1623
runs-on: ubuntu-latest
@@ -30,6 +37,88 @@ jobs:
3037
fi
3138
echo "✅ PR is from the correct repository"
3239
40+
decide-test-scope:
41+
name: Decide PR test scope
42+
runs-on: ubuntu-latest
43+
needs: check-fork
44+
outputs:
45+
full_suite: ${{ steps.decide.outputs.full_suite }}
46+
reason: ${{ steps.decide.outputs.reason }}
47+
steps:
48+
- uses: actions/checkout@v4
49+
with:
50+
fetch-depth: 0
51+
52+
- id: decide
53+
env:
54+
BASE_SHA: ${{ github.event.pull_request.base.sha }}
55+
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
56+
PR_LABELS_JSON: ${{ toJson(github.event.pull_request.labels.*.name) }}
57+
run: |
58+
python - <<'PY'
59+
import fnmatch
60+
import json
61+
import os
62+
import subprocess
63+
64+
labels = set(json.loads(os.environ["PR_LABELS_JSON"]))
65+
changed_files = subprocess.check_output(
66+
[
67+
"git",
68+
"diff",
69+
"--name-only",
70+
os.environ["BASE_SHA"],
71+
os.environ["HEAD_SHA"],
72+
],
73+
text=True,
74+
).splitlines()
75+
76+
full_suite_label = "full-data-ci"
77+
critical_patterns = [
78+
".github/workflows/pr_code_changes.yaml",
79+
".github/workflows/reusable_test.yaml",
80+
"modal_app/**",
81+
"policyengine_us_data/calibration/**",
82+
"policyengine_us_data/datasets/**",
83+
"policyengine_us_data/db/**",
84+
"policyengine_us_data/storage/download_private_prerequisites.py",
85+
"policyengine_us_data/utils/loss.py",
86+
"policyengine_us_data/utils/mortgage_interest.py",
87+
"policyengine_us_data/utils/soi.py",
88+
"policyengine_us_data/utils/uprating.py",
89+
]
90+
91+
matched_files = [
92+
path
93+
for path in changed_files
94+
if any(fnmatch.fnmatch(path, pattern) for pattern in critical_patterns)
95+
]
96+
97+
if full_suite_label in labels:
98+
full_suite = True
99+
reason = f"label:{full_suite_label}"
100+
elif matched_files:
101+
full_suite = True
102+
reason = f"critical-path:{matched_files[0]}"
103+
else:
104+
full_suite = False
105+
reason = "basic-pytest-only"
106+
107+
with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
108+
output.write(f"full_suite={'true' if full_suite else 'false'}\n")
109+
output.write(f"reason={reason}\n")
110+
111+
summary = [
112+
"### PR test scope",
113+
f"- full suite: `{'true' if full_suite else 'false'}`",
114+
f"- reason: `{reason}`",
115+
]
116+
if matched_files:
117+
summary.append(f"- first matching file: `{matched_files[0]}`")
118+
with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as out:
119+
out.write("\n".join(summary) + "\n")
120+
PY
121+
33122
check-lock-freshness:
34123
name: Check uv.lock freshness
35124
runs-on: ubuntu-latest
@@ -81,10 +170,10 @@ jobs:
81170
run: python -c "from policyengine_core.data import Dataset; print('Core import OK')"
82171

83172
Test:
84-
needs: [check-fork, Lint]
173+
needs: [check-fork, Lint, decide-test-scope]
85174
uses: ./.github/workflows/reusable_test.yaml
86175
with:
87-
full_suite: true
176+
full_suite: ${{ needs.decide-test-scope.outputs.full_suite == 'true' }}
88177
upload_data: false
89178
deploy_docs: false
90-
secrets: inherit
179+
secrets: inherit

changelog.d/cheaper-pr-ci.fixed.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Reduce unnecessary PR CI spend by canceling superseded runs and limiting
2+
the Modal-backed full data build to labeled or high-risk data-pipeline changes.

0 commit comments

Comments
 (0)