-
Notifications
You must be signed in to change notification settings - Fork 18
[issue-951] Clean up GitHub workflows and documentation #952
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
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
dfd95d5
Try to create a copilot PR reviewer
stiber 9d09d53
Updating personal development branch from SharedDevelopment
stiber 727a3fd
Revert "Try to create a copilot PR reviewer"
stiber aa918bc
Converted class diagrams to Mermaid
stiber b9edd07
Deleted automatic generation of images from PlantUML
stiber ece4c97
Converted sequence diagrams to Mermaid
stiber 6f99549
Added non-inheritance relationships to class diagram
stiber d662833
Merge pull request #943 from UWB-Biocomputing/issue-865-converting-Pl…
stiber 81ad517
Merged manual Doxygen GH pages triggering into single workflow script
stiber a9224d7
feat: add manual workflow_dispatch trigger to gh-pages action and upd…
stiber 27d46cc
Updated documentation to replace remaining PlantUML stuff with Mermaid
stiber f9b68fd
Updated action workflows and harmonized them with our implementation …
stiber 808c928
Merge remote-tracking branch 'origin/SharedDevelopment' into StiberDe…
stiber f15cf64
Updated workflows from deprecated Node.js 20 to Node.js 24
stiber 1137292
Split unit and regression testing into separate workflows
stiber 729d64c
Fixed badges
stiber 5d0773a
Updated Zenodo badge for more consistent rendering
stiber 2e198d1
Added shell script and worlflow to close issues referenced in commits…
stiber 1c42120
Minor fixes
stiber e4a2217
Updated workflows to now really only run tests when code changes
stiber 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
Some comments aren't visible on the classic Files Changed page.
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,238 @@ | ||
| #!/usr/bin/env bash | ||
| # ============================================================================== | ||
| # Graphitti - Stale Issue Cleanup Script | ||
| # | ||
| # Description: | ||
| # Scans merged pull requests for referenced issue numbers (e.g. '[issue-123]', | ||
| # 'fixes #123', 'closes #123', 'issue-123') and checks whether those issues | ||
| # are still in the OPEN state on GitHub. | ||
| # | ||
| # In dry-run mode (default), it reports all candidate issues that can be closed. | ||
| # In execute mode (--execute / -x), it closes open issues with a comment linking | ||
| # the merged pull request that resolved them. | ||
| # | ||
| # Prerequisites: | ||
| # - GitHub CLI ('gh') installed and authenticated ('gh auth login') | ||
| # - 'jq' or 'python3' for JSON processing | ||
| # | ||
| # Usage: | ||
| # ./.github/scripts/cleanup_stale_issues.sh [OPTIONS] | ||
| # | ||
| # Options: | ||
| # -d, --dry-run Preview candidate issues without closing them (default) | ||
| # -x, --execute Close open issues associated with merged PRs | ||
| # -l, --limit NUM Number of merged pull requests to inspect (default: 100) | ||
| # -h, --help Display this help message | ||
| # ============================================================================== | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| DRY_RUN=true | ||
| LIMIT=100 | ||
|
|
||
| usage() { | ||
| cat <<EOF | ||
| Usage: $(basename "$0") [OPTIONS] | ||
|
|
||
| Scan merged pull requests on GitHub and close stale open issues. | ||
|
|
||
| Options: | ||
| -d, --dry-run Preview open issues to close without modifying anything (default) | ||
| -x, --execute Close the identified issues on GitHub | ||
| -l, --limit NUM Maximum number of merged PRs to inspect (default: 100) | ||
| -h, --help Show this help message | ||
|
|
||
| Examples: | ||
| $(basename "$0") --dry-run | ||
| $(basename "$0") --execute --limit 200 | ||
| EOF | ||
| exit 0 | ||
| } | ||
|
|
||
| # Parse command line options | ||
| while [[ $# -gt 0 ]]; do | ||
| case "$1" in | ||
| -d|--dry-run) | ||
| DRY_RUN=true | ||
| shift | ||
| ;; | ||
| -x|--execute) | ||
| DRY_RUN=false | ||
| shift | ||
| ;; | ||
| -l|--limit) | ||
| LIMIT="$2" | ||
| shift 2 | ||
| ;; | ||
| -h|--help) | ||
| usage | ||
| ;; | ||
| *) | ||
| echo "Error: Unknown option '$1'" >&2 | ||
| usage | ||
| ;; | ||
| esac | ||
| done | ||
|
|
||
| # Verify GitHub CLI is available and authenticated | ||
| if ! command -v gh &> /dev/null; then | ||
| echo "Error: 'gh' (GitHub CLI) is not installed or not in PATH." >&2 | ||
| echo "Please install it: https://cli.github.com/" >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| if ! gh auth status &> /dev/null; then | ||
| echo "Error: 'gh' is not authenticated. Please run 'gh auth login' first." >&2 | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "============================================================" | ||
| echo " Graphitti Stale Issue Cleanup" | ||
| echo " Mode: $( [ "$DRY_RUN" = true ] && echo "DRY RUN (preview only)" || echo "EXECUTE (closing stale issues)" )" | ||
| echo " Merged PR scan limit: $LIMIT" | ||
| echo "============================================================" | ||
| echo "" | ||
|
|
||
| echo "Fetching merged pull requests from GitHub..." | ||
| PRS_JSON=$(gh pr list --state merged --limit "$LIMIT" --json number,title,body,url,headRefName) | ||
|
|
||
| # Extract issue numbers and map them to the merged PRs | ||
| # Searches for patterns: | ||
| # 1. [issue-123] or [ISSUE-123] | ||
| # 2. issue-123 or issue/123 in branch name or text | ||
| # 3. (fixes|closes|resolves|closed|fixed|resolved) #123 | ||
| # 4. #123 in PR title | ||
|
|
||
| python3 - "$PRS_JSON" "$DRY_RUN" << 'EOF' | ||
| import sys | ||
| import json | ||
| import re | ||
| import subprocess | ||
|
|
||
| from concurrent.futures import ThreadPoolExecutor, as_completed | ||
|
|
||
| prs_json_str = sys.argv[1] | ||
| dry_run = (sys.argv[2] == "True" or sys.argv[2] == "true") | ||
|
|
||
| prs = json.loads(prs_json_str) | ||
|
|
||
| issue_patterns = [ | ||
| re.compile(r'\[issue[-_](\d+)\]', re.IGNORECASE), | ||
| re.compile(r'\bissue[-_/](\d+)\b', re.IGNORECASE), | ||
| re.compile(r'(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+#(\d+)', re.IGNORECASE) | ||
| ] | ||
|
|
||
| # Map issue_number -> list of PR info dicts | ||
| issue_to_prs = {} | ||
|
|
||
| for pr in prs: | ||
| pr_num = pr.get("number") | ||
| pr_title = pr.get("title", "") | ||
| pr_body = pr.get("body", "") or "" | ||
| pr_branch = pr.get("headRefName", "") or "" | ||
| pr_url = pr.get("url", "") | ||
|
|
||
| search_texts = [pr_title, pr_branch, pr_body] | ||
| found_issues = set() | ||
|
|
||
| for text in search_texts: | ||
| for pattern in issue_patterns: | ||
| for match in pattern.finditer(text): | ||
| issue_id = int(match.group(1)) | ||
| # Skip self-referencing PR number if matched via #<num> | ||
| if issue_id != pr_num: | ||
| found_issues.add(issue_id) | ||
| #end if | ||
| #end for match | ||
| #end for pattern | ||
| #end for text | ||
|
|
||
| for issue_id in found_issues: | ||
| if issue_id not in issue_to_prs: | ||
| issue_to_prs[issue_id] = [] | ||
| #end if | ||
| issue_to_prs[issue_id].append({ | ||
| "pr_number": pr_num, | ||
| "pr_title": pr_title, | ||
| "pr_url": pr_url | ||
| }) | ||
| #end for issue_id | ||
| #end for pr | ||
|
|
||
| sorted_issue_ids = sorted(issue_to_prs.keys()) | ||
| print(f"Discovered {len(sorted_issue_ids)} referenced issue candidates in merged PRs.\n") | ||
| print(f"Checking current status of {len(sorted_issue_ids)} candidate issues on GitHub: ", end="", flush=True) | ||
|
|
||
| def check_issue_status(issue_id): | ||
| cmd = ["gh", "issue", "view", str(issue_id), "--json", "number,title,state,url"] | ||
| result = subprocess.run(cmd, capture_output=True, text=True) | ||
| if result.returncode != 0: | ||
| return (issue_id, None) | ||
| #end if | ||
| try: | ||
| issue_data = json.loads(result.stdout) | ||
| return (issue_id, issue_data) | ||
| except Exception: | ||
| return (issue_id, None) | ||
| #end try | ||
| #end def check_issue_status | ||
|
|
||
| open_issues_found = [] | ||
|
|
||
| with ThreadPoolExecutor(max_workers=8) as executor: | ||
| futures = {executor.submit(check_issue_status, i_id): i_id for i_id in sorted_issue_ids} | ||
| for future in as_completed(futures): | ||
| print(".", end="", flush=True) | ||
| issue_id, issue_data = future.result() | ||
| if issue_data and issue_data.get("state") == "OPEN": | ||
| linked_prs = issue_to_prs[issue_id] | ||
| open_issues_found.append((issue_data, linked_prs)) | ||
| #end if | ||
| #end for future | ||
| #end with | ||
|
|
||
| # Sort open issues by issue number for deterministic output | ||
| open_issues_found.sort(key=lambda item: item[0]['number']) | ||
| print("\n") | ||
|
|
||
| if not open_issues_found: | ||
| print("No open stale issues found. All referenced issues in scanned merged PRs are closed.") | ||
| sys.exit(0) | ||
| #end if | ||
|
|
||
| print(f"Found {len(open_issues_found)} OPEN issues associated with merged pull requests:\n") | ||
|
|
||
| for issue_data, linked_prs in open_issues_found: | ||
| issue_num = issue_data['number'] | ||
| issue_title = issue_data['title'] | ||
| issue_url = issue_data['url'] | ||
| pr_references = ", ".join([f"PR #{p['pr_number']} ({p['pr_url']})" for p in linked_prs]) | ||
|
|
||
| print(f" - Issue #{issue_num}: \"{issue_title}\"") | ||
| print(f" URL: {issue_url}") | ||
| print(f" Resolved by: {pr_references}") | ||
|
|
||
| if not dry_run: | ||
| comment_msg = f"Closed automatically by cleanup script: resolved in merged pull request {linked_prs[0]['pr_url']}." | ||
| close_cmd = ["gh", "issue", "close", str(issue_num), "--comment", comment_msg] | ||
| close_result = subprocess.run(close_cmd, capture_output=True, text=True) | ||
| if close_result.returncode == 0: | ||
| print(f" -> Successfully closed Issue #{issue_num}.") | ||
| else: | ||
| print(f" -> Failed to close Issue #{issue_num}: {close_result.stderr.strip()}") | ||
| #end if | ||
| #end if | ||
| print("") | ||
| #end for | ||
|
|
||
| if dry_run: | ||
| print("------------------------------------------------------------") | ||
| print(f"DRY RUN COMPLETE: {len(open_issues_found)} issues identified.") | ||
| print("Run with '--execute' to close these issues.") | ||
| print("------------------------------------------------------------") | ||
| else: | ||
| print("------------------------------------------------------------") | ||
| print(f"CLEANUP COMPLETE: Processed {len(open_issues_found)} issues.") | ||
| print("------------------------------------------------------------") | ||
| #end if | ||
| EOF |
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 |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| name: Auto-Close Merged Issues | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [closed] | ||
| branches: | ||
| - SharedDevelopment | ||
| - master | ||
|
|
||
| permissions: | ||
| issues: write | ||
| pull-requests: read | ||
|
|
||
| jobs: | ||
| close-issues: | ||
| if: github.event.pull_request.merged == true | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Close linked issues | ||
| env: | ||
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GH_REPO: ${{ github.repository }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| PR_TITLE: ${{ github.event.pull_request.title }} | ||
| PR_BODY: ${{ github.event.pull_request.body }} | ||
| PR_BRANCH: ${{ github.event.pull_request.head.ref }} | ||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||
| TARGET_BRANCH: ${{ github.event.pull_request.base.ref }} | ||
| run: | | ||
| python3 - << 'EOF' | ||
| import os | ||
| import re | ||
| import json | ||
| import subprocess | ||
|
|
||
| pr_number = os.environ.get("PR_NUMBER") | ||
| pr_title = os.environ.get("PR_TITLE", "") | ||
| pr_body = os.environ.get("PR_BODY", "") | ||
| pr_branch = os.environ.get("PR_BRANCH", "") | ||
| pr_url = os.environ.get("PR_URL", "") | ||
| target_branch = os.environ.get("TARGET_BRANCH", "") | ||
|
|
||
| search_texts = [pr_title, pr_branch, pr_body] | ||
| issue_patterns = [ | ||
| re.compile(r'\[issue[-_](\d+)\]', re.IGNORECASE), | ||
| re.compile(r'\bissue[-_/](\d+)\b', re.IGNORECASE), | ||
| re.compile(r'(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+#(\d+)', re.IGNORECASE) | ||
| ] | ||
|
|
||
| found_issues = set() | ||
| for text in search_texts: | ||
| for pattern in issue_patterns: | ||
| for match in pattern.finditer(text): | ||
| issue_id = int(match.group(1)) | ||
| if str(issue_id) != str(pr_number): | ||
| found_issues.add(issue_id) | ||
| #end if | ||
| #end for match | ||
| #end for pattern | ||
| #end for text | ||
|
|
||
| if not found_issues: | ||
| print("No linked issues found in PR title, branch, or body.") | ||
| exit(0) | ||
| #end if | ||
|
|
||
| print(f"Identified candidate issue(s): {sorted(found_issues)}") | ||
|
|
||
| for issue_id in sorted(found_issues): | ||
| # Check if issue exists and is currently OPEN | ||
| cmd = ["gh", "issue", "view", str(issue_id), "--json", "number,state,title"] | ||
| res = subprocess.run(cmd, capture_output=True, text=True) | ||
| if res.returncode != 0: | ||
| print(f"Issue #{issue_id} not found or inaccessible; skipping.") | ||
| continue | ||
| #end if | ||
|
|
||
| try: | ||
| data = json.loads(res.stdout) | ||
| if data.get("state") == "OPEN": | ||
| comment = f"Automatically closed: resolved by pull request #{pr_number} ({pr_url}) merged into {target_branch}." | ||
| close_cmd = ["gh", "issue", "close", str(issue_id), "--comment", comment] | ||
| close_res = subprocess.run(close_cmd, capture_output=True, text=True) | ||
| if close_res.returncode == 0: | ||
| print(f"Successfully closed issue #{issue_id} ('{data.get('title')}').") | ||
| else: | ||
| print(f"Failed to close issue #{issue_id}: {close_res.stderr.strip()}") | ||
| #end if | ||
| else: | ||
| print(f"Issue #{issue_id} is already in state '{data.get('state')}'; skipping.") | ||
| #end if | ||
| except Exception as e: | ||
| print(f"Error processing issue #{issue_id}: {e}") | ||
| #end try | ||
| #end for issue_id | ||
| EOF | ||
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
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.