Last active
September 29, 2025 06:45
-
-
Save Kotauror/1d596de16eef400b69c435b6b9ad5e5a to your computer and use it in GitHub Desktop.
Check the status of issues mentioned in merge PRs
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 script: | |
| - finds all "Staging release" PRs | |
| - gets all issues linked in them | |
| - for all issues - checks their status | |
| and prints: | |
| - status of issues per PR | |
| - summary of all open issues in the PRs | |
| ''' | |
| import requests | |
| import re | |
| import time | |
| import os | |
| TOKEN = os.getenv("GH_TOKEN") | |
| OWNER = os.getenv("CUR_COMPANY_OWNER") | |
| REPO = os.getenv("PR_SCRIPT_REPO_NAME") | |
| URL_PATTERN = os.getenv("F_ISSUE_URL_PATTERN") | |
| HEADERS = { | |
| "Authorization": f"Bearer {TOKEN}", | |
| "Accept": "application/vnd.github+json" | |
| } | |
| def search_release_prs(): | |
| """Search all PRs containing 'release' in title/body, paginated.""" | |
| url = f"https://api.github.com/search/issues?q=repo:{OWNER}/{REPO}+is:pr+release&per_page=100" | |
| prs = [] | |
| while url: | |
| r = requests.get(url, headers=HEADERS) | |
| r.raise_for_status() | |
| data = r.json() | |
| prs.extend(data["items"]) | |
| url = r.links.get("next", {}).get("url") | |
| time.sleep(0.2) # avoid hitting rate limits | |
| return prs | |
| def extract_issue_ids(text, pr_num): | |
| print(f"Extracting issues for PR #{pr_num}") | |
| return re.findall(URL_PATTERN, text or "") | |
| def get_issue_state(issue_number): | |
| """Check if an issue is open/closed.""" | |
| url = f"https://api.github.com/repos/{OWNER}/{REPO}/issues/{issue_number}" | |
| r = requests.get(url, headers=HEADERS) | |
| if r.status_code == 404: # might not exist | |
| return "not found" | |
| r.raise_for_status() | |
| data = r.json() | |
| return data.get("state", "unknown") | |
| def main(): | |
| prs = search_release_prs() | |
| results = [] | |
| processed_issues = {} # {issue_number: state} | |
| for pr in prs: | |
| pr_num = pr["number"] | |
| pr_title = pr["title"] | |
| pr_url = pr["html_url"] | |
| # get full PR body | |
| pr_url_api = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{pr_num}" | |
| r = requests.get(pr_url_api, headers=HEADERS) | |
| r.raise_for_status() | |
| pr_data = r.json() | |
| pr_body = pr_data.get("body", "") | |
| issues = extract_issue_ids(pr_body, pr_num) | |
| # collect issues + fetch states once | |
| for issue in issues: | |
| if issue not in processed_issues: # fetch only once | |
| state = get_issue_state(issue) | |
| processed_issues[issue] = state | |
| time.sleep(0.2) # rate limit safety | |
| results.append({ | |
| "pr_number": pr_num, | |
| "title": pr_title, | |
| "url": pr_url, | |
| "issues": issues | |
| }) | |
| # Print PR details | |
| for r in results: | |
| print(f"\nPR #{r['pr_number']}: {r['title']} ({r['url']})") | |
| if r["issues"]: | |
| for issue in r["issues"]: | |
| state = processed_issues[issue] | |
| color_light = "🟢" if state == "open" else "🟣" | |
| print(f" - {color_light} Issue #{issue}: {state}: https://github.com/{OWNER}/{REPO}/issues/{issue}") | |
| else: | |
| print(" - No linked issues") | |
| # Summary of open issues | |
| open_issues = [i for i, state in processed_issues.items() if state == "open"] | |
| print(f"\n\n✅ Summary: {len(open_issues)} open issues across release PRs:") | |
| for issue in sorted(open_issues, key=int): | |
| print(f"- 🟢 Issue #{issue} → https://github.com/{OWNER}/{REPO}/issues/{issue}") | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment