-
Notifications
You must be signed in to change notification settings - Fork 0
ci_scripts/check_versions.py: gracefully handle existence of upgrade PRs #273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
threexc
wants to merge
2
commits into
main
Choose a base branch
from
tgamblin/fix_check_versions
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ | |
| import subprocess | ||
| import sys | ||
| import os | ||
| import time | ||
| from typing import Dict, List, Optional | ||
| from packaging import version | ||
| from pathlib import Path | ||
|
|
@@ -49,40 +50,49 @@ def read_packages() -> List[str]: | |
| return packages | ||
|
|
||
|
|
||
| def get_registry_latest_version(package: str) -> Optional[str]: | ||
| """Get the latest version available in the riscv64 registry.""" | ||
| try: | ||
| result = subprocess.run([ | ||
| "pip", "index", "versions", package, | ||
| "--index-url", REGISTRY_URL, | ||
| "--platform", "manylinux_2_34_riscv64", | ||
| "--platform", "manylinux_2_35_riscv64", | ||
| "--platform", "manylinux_2_39_riscv64", | ||
| "--python-version", "3.12" | ||
| ], capture_output=True, text=True, timeout=30) | ||
|
|
||
| if result.returncode != 0: | ||
| def get_registry_latest_version(package: str, retries: int = 3) -> Optional[str]: | ||
| """Get the latest version available in the riscv64 registry, retrying on transient failures.""" | ||
| for attempt in range(retries): | ||
| try: | ||
| result = subprocess.run([ | ||
| "pip", "index", "versions", package, | ||
| "--index-url", REGISTRY_URL, | ||
| "--platform", "manylinux_2_34_riscv64", | ||
| "--platform", "manylinux_2_35_riscv64", | ||
| "--platform", "manylinux_2_39_riscv64", | ||
| "--python-version", "3.12" | ||
| ], capture_output=True, text=True, timeout=30) | ||
|
|
||
| if result.returncode != 0: | ||
| if attempt < retries - 1: | ||
| time.sleep(2 * (attempt + 1)) | ||
| continue | ||
| return None | ||
|
|
||
| for line in result.stdout.split('\n'): | ||
| if "Available versions:" in line: | ||
| versions_part = line.split("Available versions:")[1].strip() | ||
| if versions_part: | ||
| versions = [v.strip() for v in versions_part.split(',')] | ||
| return versions[0] if versions else None | ||
| return None | ||
|
|
||
| for line in result.stdout.split('\n'): | ||
| if "Available versions:" in line: | ||
| versions_part = line.split("Available versions:")[1].strip() | ||
| if versions_part: | ||
| versions = [v.strip() for v in versions_part.split(',')] | ||
| return versions[0] if versions else None | ||
| return None | ||
| except (subprocess.TimeoutExpired, subprocess.SubprocessError): | ||
| return None | ||
| except (subprocess.TimeoutExpired, subprocess.SubprocessError): | ||
| if attempt < retries - 1: | ||
| time.sleep(2 * (attempt + 1)) | ||
| return None | ||
|
|
||
|
|
||
| def get_pypi_package_info(package: str) -> Optional[Dict]: | ||
| """Get package information from PyPI API.""" | ||
| try: | ||
| response = requests.get(f"https://pypi.org/pypi/{package}/json", timeout=30) | ||
| response.raise_for_status() | ||
| return response.json() | ||
| except requests.RequestException: | ||
| return None | ||
| def get_pypi_package_info(package: str, retries: int = 3) -> Optional[Dict]: | ||
| """Get package information from PyPI API, retrying on transient failures.""" | ||
| for attempt in range(retries): | ||
| try: | ||
| response = requests.get(f"https://pypi.org/pypi/{package}/json", timeout=30) | ||
| response.raise_for_status() | ||
| return response.json() | ||
| except requests.RequestException: | ||
| if attempt < retries - 1: | ||
| time.sleep(2 * (attempt + 1)) | ||
| return None | ||
|
|
||
|
|
||
| def get_pypi_latest_version(package_info: Dict) -> str: | ||
|
|
@@ -160,8 +170,42 @@ def extract_pr_url(stdout: str) -> Optional[str]: | |
| return None | ||
|
|
||
|
|
||
| def find_open_pr_for_branch(branch: str, retries: int = 3) -> Optional[str]: | ||
| """Return the URL of an open PR with the given head branch, if any. Retries on transient failures.""" | ||
| for attempt in range(retries): | ||
| try: | ||
| result = subprocess.run([ | ||
| "gh", "pr", "list", | ||
| "--repo", REPO, | ||
| "--head", branch, | ||
| "--state", "open", | ||
| "--json", "url", | ||
| "--jq", ".[0].url", | ||
| ], capture_output=True, text=True, timeout=30) | ||
|
|
||
| if result.returncode != 0: | ||
| if attempt < retries - 1: | ||
| time.sleep(2 * (attempt + 1)) | ||
| continue | ||
| return None | ||
|
|
||
| return result.stdout.strip() or None | ||
|
|
||
| except (subprocess.TimeoutExpired, subprocess.SubprocessError): | ||
| if attempt < retries - 1: | ||
| time.sleep(2 * (attempt + 1)) | ||
| return None | ||
|
|
||
|
|
||
| def create_deprecation_pr(package: str, reason: str) -> Optional[str]: | ||
| """Create a pull request to deprecate a package.""" | ||
| branch = f"github-actions/deprecate-{package}" | ||
|
|
||
| existing_pr = find_open_pr_for_branch(branch) | ||
| if existing_pr: | ||
| print(f" [=] PR already open for {package}: {existing_pr}") | ||
| return existing_pr | ||
|
|
||
| try: | ||
| git_run("fetch", "origin") | ||
| git_run("switch", "main") | ||
|
|
@@ -185,8 +229,6 @@ def create_deprecation_pr(package: str, reason: str) -> Optional[str]: | |
| else: | ||
| print(f" [!] No upstream issue found for {package}") | ||
|
|
||
| branch = f"github-actions/deprecate-{package}" | ||
|
|
||
| configure_git_identity() | ||
| git_run("switch", "-c", branch) | ||
|
|
||
|
|
@@ -239,6 +281,8 @@ def create_deprecation_pr(package: str, reason: str) -> Optional[str]: | |
|
|
||
| except subprocess.CalledProcessError as e: | ||
| print(f" [X] Error creating PR for {package}: {e.stderr or e}") | ||
| print(f" [?] Could not confirm whether a PR already exists for {package} " | ||
|
threexc marked this conversation as resolved.
|
||
| "— please check open PRs manually") | ||
| return None | ||
| except Exception as e: | ||
| print(f" [X] Unexpected error creating PR for {package}: {e}") | ||
|
|
@@ -361,14 +405,19 @@ def create_upgrade_pr(package: str, package_info: Dict, new_versions: List[str]) | |
| return None | ||
|
|
||
| latest_version = new_versions[-1] | ||
| branch = f"github-actions/upgrade-{package}-{latest_version}" | ||
|
|
||
| existing_pr = find_open_pr_for_branch(branch) | ||
| if existing_pr: | ||
| print(f" [=] PR already open for {package}: {existing_pr}") | ||
| return existing_pr | ||
|
|
||
| try: | ||
| configure_git_identity() | ||
|
|
||
| git_run("fetch", "origin") | ||
| git_run("switch", "main") | ||
|
|
||
| branch = f"github-actions/upgrade-{package}-{latest_version}" | ||
| git_run("switch", "-c", branch) | ||
|
|
||
| pypi_package_url = get_pypi_package_url(package_info) | ||
|
|
@@ -468,6 +517,8 @@ def create_upgrade_pr(package: str, package_info: Dict, new_versions: List[str]) | |
|
|
||
| except subprocess.CalledProcessError as e: | ||
| print(f" [X] Error creating upgrade PR for {package}: {e.stderr or e}") | ||
| print(f" [?] Could not confirm whether a PR already exists for {package} " | ||
| "— please check open PRs manually") | ||
| return None | ||
| except Exception as e: | ||
| print(f" [X] Unexpected error creating upgrade PR for {package}: {e}") | ||
|
|
@@ -651,9 +702,11 @@ def main(): | |
| if r["status"] in ("need_upgrade", "can_deprecate") and r.get("pr_url") is None | ||
| ] | ||
| if pr_failures: | ||
| print(f"\n[X] PR creation failed for {len(pr_failures)} package(s): " | ||
| print(f"\n[?] Could not create (or confirm an existing) PR for " | ||
| f"{len(pr_failures)} package(s): " | ||
| + ", ".join(r["package"] for r in pr_failures)) | ||
| sys.exit(1) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we are now missing the sys.exit(1) right? |
||
| print(" Please review open PRs to check whether one already exists " | ||
| "for these packages.") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.