Skip to content

Commit 7875044

Browse files
committed
ci: fix pr-labels job logic
1 parent bd33a52 commit 7875044

1 file changed

Lines changed: 120 additions & 14 deletions

File tree

.github/workflows/pr-labels.yml

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ jobs:
110110
env:
111111
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112112
PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
113+
PR_BASE_SHA: ${{ steps.pr.outputs.base_sha }}
114+
PR_HEAD_SHA: ${{ steps.pr.outputs.head_sha }}
113115
CODEX_OUTPUT_PATH: ${{ steps.codex-output.outputs.output_file }}
114116
run: |
115117
python - <<'PY'
@@ -120,10 +122,119 @@ jobs:
120122
import re
121123
122124
pr_number = os.environ["PR_NUMBER"]
125+
pr_base_sha = os.environ.get("PR_BASE_SHA")
126+
pr_head_sha = os.environ.get("PR_HEAD_SHA")
123127
codex_output_path = pathlib.Path(os.environ["CODEX_OUTPUT_PATH"])
124128
changed_files_path = pathlib.Path(".tmp/pr-labels/changed-files.txt")
125129
changes_diff_path = pathlib.Path(".tmp/pr-labels/changes.diff")
126130
131+
def read_file_at(commit, path):
132+
if not commit:
133+
return None
134+
try:
135+
return subprocess.check_output(
136+
["git", "show", f"{commit}:{path}"],
137+
text=True,
138+
)
139+
except subprocess.CalledProcessError:
140+
return None
141+
142+
def dependency_lines_for_pyproject(text: str) -> set[int]:
143+
dependency_lines: set[int] = set()
144+
current_section = None
145+
in_project_dependencies = False
146+
147+
for line_number, raw_line in enumerate(text.splitlines(), start=1):
148+
stripped = raw_line.strip()
149+
if stripped.startswith("[") and stripped.endswith("]"):
150+
if stripped.startswith("[[") and stripped.endswith("]]"):
151+
current_section = stripped[2:-2].strip()
152+
else:
153+
current_section = stripped[1:-1].strip()
154+
in_project_dependencies = False
155+
if current_section in ("project.optional-dependencies", "dependency-groups"):
156+
dependency_lines.add(line_number)
157+
continue
158+
159+
if current_section in ("project.optional-dependencies", "dependency-groups"):
160+
dependency_lines.add(line_number)
161+
continue
162+
163+
if current_section != "project":
164+
continue
165+
166+
if in_project_dependencies:
167+
dependency_lines.add(line_number)
168+
if "]" in stripped:
169+
in_project_dependencies = False
170+
continue
171+
172+
if stripped.startswith("dependencies") and "=" in stripped:
173+
dependency_lines.add(line_number)
174+
if "[" in stripped and "]" not in stripped:
175+
in_project_dependencies = True
176+
177+
return dependency_lines
178+
179+
def pyproject_dependency_changed(diff_text: str) -> bool:
180+
base_text = read_file_at(pr_base_sha, "pyproject.toml")
181+
head_text = read_file_at(pr_head_sha, "pyproject.toml")
182+
if base_text is None and head_text is None:
183+
return False
184+
185+
base_dependency_lines = (
186+
dependency_lines_for_pyproject(base_text) if base_text else set()
187+
)
188+
head_dependency_lines = (
189+
dependency_lines_for_pyproject(head_text) if head_text else set()
190+
)
191+
192+
in_pyproject = False
193+
base_line = None
194+
head_line = None
195+
hunk_re = re.compile(r"@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@")
196+
197+
for line in diff_text.splitlines():
198+
if line.startswith("+++ b/"):
199+
current_file = line[len("+++ b/") :].strip()
200+
in_pyproject = current_file == "pyproject.toml"
201+
base_line = None
202+
head_line = None
203+
continue
204+
205+
if not in_pyproject:
206+
continue
207+
208+
if line.startswith("@@ "):
209+
match = hunk_re.match(line)
210+
if not match:
211+
continue
212+
base_line = int(match.group(1))
213+
head_line = int(match.group(2))
214+
continue
215+
216+
if base_line is None or head_line is None:
217+
continue
218+
219+
if line.startswith(" "):
220+
base_line += 1
221+
head_line += 1
222+
continue
223+
224+
if line.startswith("-"):
225+
if base_line in base_dependency_lines:
226+
return True
227+
base_line += 1
228+
continue
229+
230+
if line.startswith("+"):
231+
if head_line in head_dependency_lines:
232+
return True
233+
head_line += 1
234+
continue
235+
236+
return False
237+
127238
changed_files = []
128239
if changed_files_path.exists():
129240
changed_files = [
@@ -137,22 +248,15 @@ jobs:
137248
desired.add("project")
138249
if any(path.startswith("docs/") for path in changed_files):
139250
desired.add("documentation")
140-
if "uv.lock" in changed_files:
141-
desired.add("dependencies")
251+
dependencies_allowed = "uv.lock" in changed_files
252+
diff_text = None
142253
if changes_diff_path.exists():
143254
diff_text = changes_diff_path.read_text()
144-
current_file = None
145-
for line in diff_text.splitlines():
146-
if line.startswith("+++ b/"):
147-
current_file = line[len("+++ b/") :].strip()
148-
continue
149-
if not current_file or not current_file.startswith(".github/workflows/"):
150-
continue
151-
if not line.startswith(("+", "-")):
152-
continue
153-
if re.search(r"uses:\s*(?!\./)(?!\.\/)[^@\s]+/[^@\s]+@", line):
154-
desired.add("dependencies")
155-
break
255+
if "pyproject.toml" in changed_files and pyproject_dependency_changed(diff_text):
256+
dependencies_allowed = True
257+
258+
if dependencies_allowed:
259+
desired.add("dependencies")
156260
157261
allowed = {
158262
"documentation",
@@ -184,6 +288,8 @@ jobs:
184288
pass
185289
186290
for label in codex_labels:
291+
if label == "dependencies" and not dependencies_allowed:
292+
continue
187293
if label in allowed:
188294
desired.add(label)
189295

0 commit comments

Comments
 (0)