Skip to content

Commit 806be48

Browse files
committed
ci: retire Trigger: directives; upgrade PRs declare versions in the YAML
check_versions.py appends a bare '- version:' entry to docs/packages/<pkg>.yaml and opens the PR; the PR's own pull_request run builds it, so pr-trigger.yml and the workflow_dispatch calls go away. pr-checks.yml lints changed package YAMLs and requires one for every changed build workflow. Signed-off-by: Ludovic Henry <git@ludovic.dev>
1 parent f35afaf commit 806be48

3 files changed

Lines changed: 45 additions & 137 deletions

File tree

‎.github/workflows/nightly.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
python-version: '3'
3535

3636
- name: Install Python dependencies
37-
run: pip install requests packaging
37+
run: pip install requests packaging pyyaml
3838

3939
- name: Check versions and open PRs
4040
env:

‎.github/workflows/pr-checks.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ jobs:
6969
uv run --quiet ci_scripts/pending_versions.py --check "$(basename "$path" .yaml)" || ret=1
7070
done
7171
for path in $(git diff --name-only --diff-filter=ACMR "$BASE_SHA" "$HEAD_SHA" -- '.github/workflows/build-*.yml' '.github/workflows/test-*.yml'); do
72-
package=$(sed -En 's/^\s+package:\s*([^ #]+).*/\1/p' "$path" | head -n1)
72+
package=$(sed -En 's/^[[:space:]]+package:[[:space:]]*([^ #]+).*/\1/p' "$path" | head -n1)
7373
if [[ -z "$package" ]]; then
7474
echo "::error file=$path::the setup job must call _setup.yml with a package: input"
7575
ret=1

‎ci_scripts/check_versions.py‎

Lines changed: 43 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from packaging import version
2323
from pathlib import Path
2424
import requests
25+
import yaml
2526

2627

2728
REGISTRY_URL = "https://pypi.riseproject.dev/simple/"
@@ -33,7 +34,7 @@
3334
# build — a distinct ABI from "3.14", so it needs its own wheel.
3435
TARGET_PYTHON_VERSIONS = ["3.12", "3.13", "3.14", "3.14t"]
3536

36-
# Cap on how many new versions get dispatched per upgrade PR. When the registry
37+
# Cap on how many new versions get declared per upgrade PR. When the registry
3738
# drifts far behind PyPI, an unbounded loop could kick off dozens of workflow
3839
# runs per package. Three keeps the retry cost bounded while still covering the
3940
# common "we missed one or two point releases" case.
@@ -341,38 +342,16 @@ def create_deprecation_pr(package: str, reason: str) -> Optional[str]:
341342
return None
342343

343344

344-
def dispatch_workflow(workflow: str, ref: str, version_input: str) -> bool:
345-
"""
346-
Dispatch a workflow_dispatch workflow with a version input.
347-
348-
Strips any leading `v`/`V` from version_input; build workflows already
349-
prepend `v` when constructing git refs (e.g. `ref: v${{ env.VERSION }}`),
350-
so passing `v2.5.1` would produce `vv2.5.1`.
351-
"""
352-
version_input = version_input.lstrip("vV")
353-
try:
354-
subprocess.run([
355-
"gh", "workflow", "run", workflow,
356-
"--repo", REPO,
357-
"--ref", ref,
358-
"-f", f"version={version_input}",
359-
], check=True, capture_output=True, text=True)
360-
return True
361-
except subprocess.CalledProcessError as e:
362-
print(f" [!] Failed to dispatch {workflow}: {e.stderr or e}")
363-
return False
364-
365-
366345
def get_new_versions(package_info: Dict, registry_version: str) -> List[str]:
367346
"""
368347
Return sorted list of stable PyPI versions strictly greater than
369348
registry_version. Skips prereleases, dev releases, and fully yanked
370349
versions.
371350
372351
Result is truncated to the newest MAX_NEW_VERSIONS_PER_PR entries so a
373-
stale registry does not spawn dozens of workflow dispatches per package.
374-
Older skipped versions can still be triggered manually by editing the PR
375-
body to add more `Trigger:` directives.
352+
stale registry does not queue dozens of builds per package.
353+
Older skipped versions can still be built by declaring them in
354+
docs/packages/<pkg>.yaml by hand.
376355
"""
377356
try:
378357
reg_ver = version.parse(registry_version)
@@ -397,61 +376,33 @@ def get_new_versions(package_info: Dict, registry_version: str) -> List[str]:
397376
return result[-MAX_NEW_VERSIONS_PER_PR:]
398377

399378

400-
def find_workflow_default_version(content: str) -> Optional[str]:
401-
"""
402-
Return the value of `default:` under `workflow_dispatch.inputs.version`.
403-
404-
Uses a simple heuristic: the first `default:` after `workflow_dispatch:`.
405-
This holds for the current build-<pkg>.yml template.
406-
"""
407-
marker = re.search(r"workflow_dispatch:", content)
408-
if not marker:
409-
return None
410-
m = re.search(
411-
r"^\s*default:\s*(['\"]?)([^'\"\n\r]+?)\1\s*$",
412-
content[marker.end():],
413-
re.MULTILINE,
414-
)
415-
return m.group(2).strip() if m else None
416-
417-
418-
def bump_workflow_version(path: Path, new_version: str) -> bool:
379+
def declare_versions(yaml_file: Path, new_versions: List[str]) -> List[str]:
419380
"""
420-
Update the workflow_dispatch default version in a workflow file, and
421-
replace every quoted literal of the old version elsewhere in the file
422-
(concurrency group, env defaults, job names).
381+
Append a bare `- version:` entry for each version not yet listed in the
382+
package YAML, and return the versions that were added.
423383
424-
Returns True if the file was modified.
384+
An entry without `tag`/`files` is what the build workflow builds, so this
385+
is all an upgrade PR has to change.
425386
"""
426-
content = path.read_text()
427-
current = find_workflow_default_version(content)
428-
if current is None or current == new_version:
429-
return False
430-
updated = re.sub(
431-
r"(['\"])" + re.escape(current) + r"\1",
432-
lambda m: f"{m.group(1)}{new_version}{m.group(1)}",
433-
content,
434-
)
435-
if updated == content:
436-
return False
437-
path.write_text(updated)
438-
return True
387+
content = yaml_file.read_text()
388+
declared = set(re.findall(r"^- version: ['\"]?([^'\"\n]+)", content, re.M))
389+
added = [v for v in new_versions if v not in declared]
390+
if not added:
391+
return []
392+
lines = [content.rstrip("\n")]
393+
for v in added:
394+
lines.append(yaml.safe_dump([{"version": v}], default_flow_style=False).rstrip("\n"))
395+
yaml_file.write_text("\n".join(lines) + "\n")
396+
return added
439397

440398

441399
def create_upgrade_pr(package: str, package_info: Dict, new_versions: List[str]) -> Optional[str]:
442400
"""
443-
Create a PR to trigger builds for each new package version.
444-
445-
- Bumps the `default:` version in build-<pkg>.yml (and test-<pkg>.yml if
446-
present) to the latest new version.
447-
- PR body includes a `Trigger: <package>:<version>` line for every new
448-
version so the pr-trigger workflow (or a human editing the body) can
449-
re-dispatch builds.
450-
- Script also directly dispatches build-<pkg>.yml (and test-<pkg>.yml if
451-
present) once per new version, because PRs authored by GITHUB_TOKEN do
452-
not fire pull_request workflows.
453-
- If build workflow is missing, PR body contains boilerplate asking the
454-
reviewer to create it.
401+
Create a PR that declares each new version in docs/packages/<pkg>.yaml.
402+
403+
The PR's own pull_request run of build-<pkg>.yml builds every version the
404+
YAML declares without a release, so nothing is dispatched here. If the
405+
build workflow is missing, the PR body asks the reviewer to create it.
455406
"""
456407
if not new_versions:
457408
return None
@@ -470,76 +421,45 @@ def create_upgrade_pr(package: str, package_info: Dict, new_versions: List[str])
470421
git_run("fetch", "origin")
471422
git_run("switch", "main")
472423

424+
yaml_file = Path(f"docs/packages/{package}.yaml")
425+
if not yaml_file.exists():
426+
print(f" [!] {yaml_file} does not exist")
427+
return None
428+
473429
git_run("switch", "-c", branch)
474430

475431
pypi_package_url = get_pypi_package_url(package_info)
476432
build_workflow = Path(f".github/workflows/build-{package}.yml")
477-
test_workflow = Path(f".github/workflows/test-{package}.yml")
478-
479-
bumped: List[Path] = []
480-
if build_workflow.exists() and bump_workflow_version(build_workflow, latest_version):
481-
git_run("add", str(build_workflow))
482-
bumped.append(build_workflow)
483-
if test_workflow.exists() and bump_workflow_version(test_workflow, latest_version):
484-
git_run("add", str(test_workflow))
485-
bumped.append(test_workflow)
486-
487-
if bumped:
488-
commit_message = f"{package}: upgrade to v{latest_version}"
489-
git_run("commit", "-m", commit_message)
490-
else:
491-
# This block should only run if either:
492-
#
493-
# 1. No workflows exist for the package
494-
# 2. The workflows don't need a bump for the detected version
495-
#
496-
# In the latter case, we want an empty PR to indicate that a new
497-
# build should be triggered to add the latest version to the
498-
# registry. Trigger directives are added for the empty PR so we can
499-
# run a test build and ensure no tweaks need to be made before
500-
# running the workflow from main.
501-
commit_message = f"DO NOT MERGE: {package}: trigger builds for v{latest_version}"
502-
git_run("commit", "--allow-empty", "-m", commit_message)
503433

434+
added = declare_versions(yaml_file, new_versions)
435+
if not added:
436+
print(f" [=] {package}: {', '.join(new_versions)} already declared in {yaml_file}")
437+
return None
438+
439+
git_run("add", str(yaml_file))
440+
git_run("commit", "-m", f"{package}: upgrade to v{latest_version}")
504441
git_run("push", "origin", branch)
505442

506-
versions_list = ", ".join(f"v{v}" for v in new_versions)
443+
versions_list = ", ".join(f"v{v}" for v in added)
507444
body_lines = [
508445
f"Automatically generated PR to upgrade {package} — new versions detected: {versions_list}.",
509446
"",
510447
f"Link to [PyPI]({pypi_package_url}).",
511448
"",
449+
f"Declares {versions_list} in `{yaml_file}`; the build workflow builds every declared version that has no release yet.",
450+
"",
512451
]
513452

514-
if bumped:
515-
bumped_list = ", ".join(f"`{p}`" for p in bumped)
516-
body_lines += [
517-
f"Bumped default `version` input to v{latest_version} in {bumped_list}.",
518-
"",
519-
]
520-
521453
if build_workflow.exists():
522454
body_lines += [
523-
"Build (and test, if present) workflows dispatched automatically for each version listed below.",
524-
"",
525-
"Trigger directives (re-dispatch by editing the PR body):",
526-
"",
527-
]
528-
body_lines += [f"Trigger: {package}:{v}" for v in new_versions]
529-
body_lines += [
530-
"",
531-
"If all builds succeed without modification, merge this PR and re-trigger the workflow(s) from `main` to publish.",
532-
"If changes are needed, force-push this branch and re-dispatch.",
455+
"If all builds succeed without modification, merge this PR: the push to `main` publishes the wheels and opens the docs PR.",
456+
"If changes are needed, force-push this branch.",
533457
]
534458
else:
535459
body_lines += [
536460
f"No build workflow found at `.github/workflows/build-{package}.yml`.",
537461
"Please create one (and optionally a matching `test-*.yml`) before merging.",
538-
"",
539-
"Once the workflow exists, edit this PR body to re-fire the `Trigger:` directives:",
540-
"",
541462
]
542-
body_lines += [f"Trigger: {package}:{v}" for v in new_versions]
543463

544464
body = "\n".join(body_lines) + "\n"
545465

@@ -553,19 +473,7 @@ def create_upgrade_pr(package: str, package_info: Dict, new_versions: List[str])
553473
"--body", body,
554474
], capture_output=True, text=True, check=True)
555475

556-
pr_url = extract_pr_url(result.stdout) or f"PR created for {package} (URL not found in output)"
557-
558-
if build_workflow.exists():
559-
for v in new_versions:
560-
print(f" [+] Dispatching build-{package}.yml on {branch} for v{v}")
561-
dispatch_workflow(f"build-{package}.yml", branch, v)
562-
if test_workflow.exists():
563-
print(f" [+] Dispatching test-{package}.yml on {branch} for v{v}")
564-
dispatch_workflow(f"test-{package}.yml", branch, v)
565-
else:
566-
print(f" [!] No build workflow for {package}; skipping dispatch")
567-
568-
return pr_url
476+
return extract_pr_url(result.stdout) or f"PR created for {package} (URL not found in output)"
569477

570478
except subprocess.CalledProcessError as e:
571479
print(f" [X] Error creating upgrade PR for {package}: {e.stderr or e}")

0 commit comments

Comments
 (0)