-
-
Notifications
You must be signed in to change notification settings - Fork 16
Open
Description
Hi folks,
When creating my own application, I saw that there were a lot of open application
issues, and a lot were obviously invalid. I had a quick sit down with Cursor / claude and put together a script to help clean those up for you:
check_stars.py
#!/usr/bin/env python3
import subprocess
import json
import re
import sys
import os
from rich.console import Console
from rich.progress import Progress, TextColumn, BarColumn, TaskProgressColumn
from rich.table import Table
from rich.markdown import Markdown
console = Console()
def run_gh_command(command, handle_pagination=False):
"""Run a GitHub CLI command and return the output as JSON."""
try:
if handle_pagination and "api" in command:
# For API calls that might need pagination
all_results = []
page = 1
per_page = 100
while True:
# Add pagination parameters
paginated_command = command.copy()
if "?" in paginated_command[2]:
paginated_command[2] += f"&page={page}&per_page={per_page}"
else:
paginated_command[2] += f"?page={page}&per_page={per_page}"
result = subprocess.run(paginated_command, capture_output=True, text=True, check=True)
if not result.stdout.strip():
break
page_results = json.loads(result.stdout)
if not page_results or (isinstance(page_results, list) and len(page_results) == 0):
break
if isinstance(page_results, list):
all_results.extend(page_results)
else:
# If it's not a list, just return the result
return page_results
# If we got fewer results than per_page, we've reached the end
if len(page_results) < per_page:
break
page += 1
return all_results
else:
# For non-paginated commands
result = subprocess.run(command, capture_output=True, text=True, check=True)
if not result.stdout.strip():
return None
return json.loads(result.stdout)
except subprocess.CalledProcessError as e:
# For repository not found errors, return None instead of exiting
if "Not Found (HTTP 404)" in e.stderr and "repos/" in command[2]:
return None
console.print(f"[red]Error running command:[/red] {' '.join(command)}")
console.print(f"[red]Error message:[/red] {e.stderr}")
return None
except json.JSONDecodeError:
console.print(f"[red]Error parsing JSON from command:[/red] {' '.join(command)}")
console.print(f"[red]Output:[/red] {result.stdout}")
return None
def extract_repo_url(comment_body):
"""Extract GitHub repository URL from comment body."""
if not comment_body:
return None
pattern = r"Repository URL to your project\s*\n\s*(https://github\.com/[^\s/]+/[^\s/]+)"
match = re.search(pattern, comment_body)
if match:
return match.group(1)
return None
def get_repo_stars(repo_url):
"""Get the number of stars for a GitHub repository."""
if not repo_url:
return None
# Extract owner and repo name from URL
parts = repo_url.strip('/').split('/')
if len(parts) < 5:
return None
owner = parts[-2]
repo = parts[-1]
command = ["gh", "api", f"repos/{owner}/{repo}"]
try:
repo_info = run_gh_command(command)
if repo_info:
return repo_info.get("stargazers_count")
return None
except Exception as e:
console.print(f"[red]Error fetching stars for {repo_url}: {e}[/red]")
return None
def generate_markdown_table(results):
"""Generate a markdown table from the results."""
markdown = "# CORS.sh Application Status\n\n"
markdown += "## Repository Star Analysis Results\n\n"
# Table header
markdown += "| Issue # | Title | Repository | Stars | Valid | Reason |\n"
markdown += "|---------|-------|------------|-------|-------|--------|\n"
# Sort by validity and stars
sorted_results = sorted(
results.values(),
key=lambda x: (
x["valid"],
x.get("stars", 0) if isinstance(x.get("stars", 0), int) else -1
),
reverse=True
)
# Table rows
for result in sorted_results:
stars_display = str(result.get("stars", "N/A"))
valid_display = "✅" if result["valid"] else "❌"
# Escape pipe characters in markdown table
title = result['issue_title'].replace('|', '\\|')
# Fix for NoneType error - ensure repo_url is a string
repo_url = result.get('repo_url', 'N/A')
if repo_url is None:
repo_url = 'N/A'
else:
repo_url = repo_url.replace('|', '\\|')
reason = result['reason'].replace('|', '\\|')
markdown += f"| #{result['issue_number']} | {title} | {repo_url} | {stars_display} | {valid_display} | {reason} |\n"
# Summary stats
valid_count = sum(1 for r in results.values() if r["valid"])
markdown += f"\n## Summary\n\n{valid_count} valid repositories out of {len(results)} issues\n"
return markdown
def generate_close_issues_script(results):
"""Generate a shell script to close invalid issues."""
script = "#!/bin/bash\n\n"
script += "# Script to close invalid issues\n\n"
script += "# This script was automatically generated\n\n"
# Filter for invalid issues
invalid_issues = [r for r in results.values() if not r["valid"]]
script += f"echo 'Closing {len(invalid_issues)} invalid issues...'\n\n"
for issue in invalid_issues:
issue_number = issue["issue_number"]
reason = issue["reason"]
# Create a comment explaining why the issue is being closed
comment = f"This application is being closed because: {reason}."
if "stars" in reason and "need 10+" in reason:
comment += " Please reapply when your repository has at least 10 stars."
elif "Repository not found" in reason:
comment += " The repository URL provided is not accessible or does not exist."
elif "No repository URL found" in reason:
comment += " No valid GitHub repository URL was found in the issue or comments."
# Escape quotes in the comment for shell
escaped_comment = comment.replace('"', '\\"')
# Add the command to close the issue with a comment
script += f'echo "Closing issue #{issue_number}..."\n'
script += f'gh issue close gridaco/cors.sh/{issue_number} --comment "{escaped_comment}"\n'
script += 'echo "Done"\n\n'
script += "echo 'All invalid issues have been closed.'\n"
return script
def get_all_issues_with_label(repo, label):
"""Get all issues with a specific label, handling pagination."""
console.print(f"Fetching all issues with '{label}' label from {repo}...")
# Use the API directly to get all issues with the label
issues = run_gh_command([
"gh", "api",
f"repos/{repo}/issues?state=open&labels={label}&per_page=100"
], handle_pagination=True)
if not issues:
return []
# Extract the fields we need
return [{"number": issue["number"], "title": issue["title"]} for issue in issues]
def main():
# Check if gh CLI is installed
try:
subprocess.run(["gh", "--version"], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
console.print("[red]GitHub CLI (gh) is not installed or not working properly.[/red]")
console.print("Please install it from https://cli.github.com/")
sys.exit(1)
# Check if user is authenticated with gh
try:
subprocess.run(["gh", "auth", "status"], capture_output=True, check=True)
except subprocess.CalledProcessError:
console.print("[red]You are not authenticated with GitHub CLI.[/red]")
console.print("Please run 'gh auth login' first.")
sys.exit(1)
# Get all open issues with the "application" label
issues = get_all_issues_with_label("gridaco/cors.sh", "application")
if not issues:
console.print("[yellow]No open issues with 'application' label found.[/yellow]")
return
console.print(f"Found [green]{len(issues)}[/green] open issues with 'application' label. Analyzing...")
results = {}
with Progress(
TextColumn("[progress.description]{task.description}"),
BarColumn(),
TaskProgressColumn(),
console=console
) as progress:
task = progress.add_task("[cyan]Processing issues...", total=len(issues))
for issue in issues:
issue_number = issue["number"]
issue_title = issue["title"]
progress.update(task, description=f"[cyan]Processing issue #{issue_number}[/cyan]")
# Get all comments for this issue
all_comments = run_gh_command(["gh", "api", f"repos/gridaco/cors.sh/issues/{issue_number}/comments"], handle_pagination=True)
# Check if there are any comments
first_comment_body = None
if all_comments and len(all_comments) > 0:
first_comment_body = all_comments[0].get("body", "")
# Get the issue body
issue_data = run_gh_command(["gh", "api", f"repos/gridaco/cors.sh/issues/{issue_number}"])
issue_body = issue_data.get("body", "") if issue_data else ""
# Try to find repo URL in first comment, then in issue body
repo_url = None
if first_comment_body:
repo_url = extract_repo_url(first_comment_body)
if not repo_url and issue_body:
repo_url = extract_repo_url(issue_body)
# Initialize status dict for this issue
status = {
"issue_number": issue_number,
"issue_title": issue_title,
"repo_url": repo_url,
"valid": False,
"reason": "No repository URL found"
}
if repo_url:
stars = get_repo_stars(repo_url)
status["stars"] = stars
if stars is None:
status["reason"] = "Repository not found"
elif isinstance(stars, int):
if stars >= 10:
status["valid"] = True
status["reason"] = f"{stars} stars (valid)"
else:
status["reason"] = f"Only {stars} stars (need 10+)"
else:
status["reason"] = "Unknown star count"
results[issue_number] = status
progress.advance(task)
# Print summary table to console
table = Table(title="Repository Star Analysis Results")
table.add_column("Issue #", justify="right", style="cyan")
table.add_column("Title", style="magenta")
table.add_column("Repository", style="blue")
table.add_column("Stars", justify="right")
table.add_column("Valid", justify="center")
table.add_column("Reason", style="green")
# Sort by validity and stars
sorted_results = sorted(
results.values(),
key=lambda x: (
x["valid"],
x.get("stars", 0) if isinstance(x.get("stars", 0), int) else -1
),
reverse=True
)
for result in sorted_results:
stars_display = str(result.get("stars", "N/A"))
valid_display = "✅" if result["valid"] else "❌"
# Ensure repo_url is a string for display
repo_url_display = result.get("repo_url", "N/A")
if repo_url_display is None:
repo_url_display = "N/A"
table.add_row(
f"#{result['issue_number']}",
result["issue_title"],
repo_url_display,
stars_display,
valid_display,
result["reason"]
)
console.print(table)
# Print summary stats
valid_count = sum(1 for r in results.values() if r["valid"])
console.print(f"\n[bold]Summary:[/bold] {valid_count} valid repositories out of {len(results)} issues")
# Generate and save markdown table
markdown_content = generate_markdown_table(results)
with open("application_statuses.md", "w") as f:
f.write(markdown_content)
console.print(f"\n[green]Markdown table saved to[/green] application_statuses.md")
# Generate and save close issues shell script
close_issues_script = generate_close_issues_script(results)
with open("close_issues.sh", "w") as f:
f.write(close_issues_script)
# Make the script executable
os.chmod("close_issues.sh", 0o755)
console.print(f"[green]Close issues script saved to[/green] close_issues.sh")
console.print("[yellow]Run ./close_issues.sh to close invalid issues[/yellow]")
if __name__ == "__main__":
main()
I also got it to generate a bash script with GitHub CLI commands to close the invalid issues with a reason, so that a maintainer can do that in a single command:
close_issues.sh
#!/bin/bash
# Script to close invalid issues
# This script was automatically generated
echo 'Closing 33 invalid issues...'
echo "Closing issue #146..."
gh issue close gridaco/cors.sh/146 --comment "This application is being closed because: Repository not found. The repository URL provided is not accessible or does not exist."
echo "Done"
echo "Closing issue #143..."
gh issue close gridaco/cors.sh/143 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #140..."
gh issue close gridaco/cors.sh/140 --comment "This application is being closed because: Only 0 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #133..."
gh issue close gridaco/cors.sh/133 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #126..."
gh issue close gridaco/cors.sh/126 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #125..."
gh issue close gridaco/cors.sh/125 --comment "This application is being closed because: Only 1 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #124..."
gh issue close gridaco/cors.sh/124 --comment "This application is being closed because: Repository not found. The repository URL provided is not accessible or does not exist."
echo "Done"
echo "Closing issue #110..."
gh issue close gridaco/cors.sh/110 --comment "This application is being closed because: Only 8 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #95..."
gh issue close gridaco/cors.sh/95 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #87..."
gh issue close gridaco/cors.sh/87 --comment "This application is being closed because: Only 4 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #84..."
gh issue close gridaco/cors.sh/84 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #82..."
gh issue close gridaco/cors.sh/82 --comment "This application is being closed because: Only 0 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #79..."
gh issue close gridaco/cors.sh/79 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #78..."
gh issue close gridaco/cors.sh/78 --comment "This application is being closed because: Only 0 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #72..."
gh issue close gridaco/cors.sh/72 --comment "This application is being closed because: Repository not found. The repository URL provided is not accessible or does not exist."
echo "Done"
echo "Closing issue #71..."
gh issue close gridaco/cors.sh/71 --comment "This application is being closed because: Repository not found. The repository URL provided is not accessible or does not exist."
echo "Done"
echo "Closing issue #70..."
gh issue close gridaco/cors.sh/70 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #69..."
gh issue close gridaco/cors.sh/69 --comment "This application is being closed because: Only 2 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #59..."
gh issue close gridaco/cors.sh/59 --comment "This application is being closed because: Only 0 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #58..."
gh issue close gridaco/cors.sh/58 --comment "This application is being closed because: Only 1 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #57..."
gh issue close gridaco/cors.sh/57 --comment "This application is being closed because: Repository not found. The repository URL provided is not accessible or does not exist."
echo "Done"
echo "Closing issue #56..."
gh issue close gridaco/cors.sh/56 --comment "This application is being closed because: Only 7 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #37..."
gh issue close gridaco/cors.sh/37 --comment "This application is being closed because: Only 5 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #36..."
gh issue close gridaco/cors.sh/36 --comment "This application is being closed because: Only 7 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #33..."
gh issue close gridaco/cors.sh/33 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #32..."
gh issue close gridaco/cors.sh/32 --comment "This application is being closed because: Only 0 stars (need 10+). Please reapply when your repository has at least 10 stars."
echo "Done"
echo "Closing issue #29..."
gh issue close gridaco/cors.sh/29 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #23..."
gh issue close gridaco/cors.sh/23 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #21..."
gh issue close gridaco/cors.sh/21 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #17..."
gh issue close gridaco/cors.sh/17 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #16..."
gh issue close gridaco/cors.sh/16 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #11..."
gh issue close gridaco/cors.sh/11 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo "Closing issue #9..."
gh issue close gridaco/cors.sh/9 --comment "This application is being closed because: No repository URL found. No valid GitHub repository URL was found in the issue or comments."
echo "Done"
echo 'All invalid issues have been closed.'
The results are below: 18 valid repositories out of 51 issues
Note that it's kind of strict about following the issue template, and also expecting a GitHub repo and only that. So maybe handle with some care 😅
Hope this helps a little!
Issue # | Title | Repository | Stars | Valid | Reason |
---|---|---|---|---|---|
#94 | Apply for OSS Program: RSSHub Radar | https://github.com/DIYgod/RSSHub-Radar | 6177 | ✅ | 6177 stars (valid) |
#109 | Github HTML Preview | https://github.com/htmlpreview/htmlpreview.github.com | 1595 | ✅ | 1595 stars (valid) |
#137 | Food Ordering App Open Source Project | https://github.com/kubesre/docker-registry-mirrors | 1384 | ✅ | 1384 stars (valid) |
#149 | [Application] for my OSS Project: sra-explorer.info | https://github.com/ewels/sra-explorer | 213 | ✅ | 213 stars (valid) |
#77 | Mastertex | https://github.com/gridaco/cors.sh | 209 | ✅ | 209 stars (valid) |
#142 | Syncify | https://github.com/panoply/syncify | 145 | ✅ | 145 stars (valid) |
#89 | Nujan IDE | https://github.com/nujan-io/nujan-ide | 145 | ✅ | 145 stars (valid) |
#76 | Application for OSS program for Lyrixed | https://github.com/Nuzair46/Lyrixed | 123 | ✅ | 123 stars (valid) |
#128 | Syntithenai Chat | https://github.com/syntithenai/hermod | 94 | ✅ | 94 stars (valid) |
#129 | seasick/openscad-web-gui | https://github.com/seasick/openscad-web-gui | 64 | ✅ | 64 stars (valid) |
#138 | tipitaka.lk | https://github.com/pathnirvana/tipitaka.lk | 48 | ✅ | 48 stars (valid) |
#74 | NBN Availability Extension | https://github.com/LukePrior/nbn-availability-extension | 24 | ✅ | 24 stars (valid) |
#66 | Apply for OSS Program - Schedule Helper [Open-Source Club @ University of Florida] | https://github.com/ufosc/Schedule_Helper | 22 | ✅ | 22 stars (valid) |
#103 | TTV Tools | https://github.com/ephellon/twitch-tools | 21 | ✅ | 21 stars (valid) |
#73 | NostrOP OSS application | https://github.com/franzos/nostr-ts | 21 | ✅ | 21 stars (valid) |
#116 | Food Ordering App Open Source Project | https://github.com/jain-rithik/Zaika | 17 | ✅ | 17 stars (valid) |
#144 | AuthLeu | https://github.com/jlcvp/AuthLeu | 13 | ✅ | 13 stars (valid) |
#123 | Free request | https://github.com/BradPerbs/GPT-Rewriter | 13 | ✅ | 13 stars (valid) |
#110 | BZ-Next WebGL BZFlag tools and client | https://github.com/bz-next/bz-next | 8 | ❌ | Only 8 stars (need 10+) |
#56 | OOS Application for osu-cyberball | https://github.com/CuddleBunny/osu-cyberball | 7 | ❌ | Only 7 stars (need 10+) |
#36 | GhotBypasser [OSS Project Appication] | https://github.com/megz15/ghotbypasser | 7 | ❌ | Only 7 stars (need 10+) |
#37 | CORS for | https://github.com/TomRadford/shootdrop | 5 | ❌ | Only 5 stars (need 10+) |
#87 | NFTJukebox | https://github.com/Martibis/nft-jukebox-frontend | 4 | ❌ | Only 4 stars (need 10+) |
#69 | Application for aceess to free pro-plan of cors.sh | https://github.com/InventorsDev/Team-Orange | 2 | ❌ | Only 2 stars (need 10+) |
#125 | So Many Current Players | https://github.com/infntyrepeating/soManyCurrentPlayers | 1 | ❌ | Only 1 stars (need 10+) |
#58 | Requesting a proxy | https://github.com/godjooyoung/sailing-bamboo | 1 | ❌ | Only 1 stars (need 10+) |
#143 | odigo | N/A | N/A | ❌ | No repository URL found |
#140 | MyPort | https://github.com/cpxdev-frontend/cp-myport | 0 | ❌ | Only 0 stars (need 10+) |
#133 | Suggest keyword | N/A | N/A | ❌ | No repository URL found |
#126 | q | N/A | N/A | ❌ | No repository URL found |
#95 | [Application] for my OSS Project | N/A | N/A | ❌ | No repository URL found |
#84 | GD Web | N/A | N/A | ❌ | No repository URL found |
#82 | need cros proxy | https://github.com/Alaminpramanik/Eco | 0 | ❌ | Only 0 stars (need 10+) |
#79 | Teste Request Cors Broken | N/A | N/A | ❌ | No repository URL found |
#78 | News summary | https://github.com/truongduongmau/tin-tuc | 0 | ❌ | Only 0 stars (need 10+) |
#70 | zone | N/A | N/A | ❌ | No repository URL found |
#59 | mini-project | https://github.com/soyunhan/Together_TeamProject | 0 | ❌ | Only 0 stars (need 10+) |
#33 | OSS program application | N/A | N/A | ❌ | No repository URL found |
#32 | [Application] for my OSS Project | https://github.com/lawrenceshava/roadside | 0 | ❌ | Only 0 stars (need 10+) |
#29 | Hytale Tracker | N/A | N/A | ❌ | No repository URL found |
#23 | Open Source Application | N/A | N/A | ❌ | No repository URL found |
#21 | george.barbu.cc | N/A | N/A | ❌ | No repository URL found |
#17 | Application for my OSS Project | N/A | N/A | ❌ | No repository URL found |
#16 | Personal portfolio - https://lukebottle.github.io/luke-milner/ | N/A | N/A | ❌ | No repository URL found |
#11 | FFO Application: https://sketchy-bot.github.io/sketch/ | N/A | N/A | ❌ | No repository URL found |
#9 | FFO Application: api.enime.moe | N/A | N/A | ❌ | No repository URL found |
#146 | Filter | https://github.com/SaurabhBhatiaodesk/filtersync | None | ❌ | Repository not found |
#124 | web based china "vpn" | https://github.com/techniixcom/wbcvpn.git | None | ❌ | Repository not found |
#72 | Kothar Dashboard | https://github.com/rbdiwash/KotharDashboard | None | ❌ | Repository not found |
#71 | keywordtool | https://github.com/Gemy-Dev/keywordtool.github.io | None | ❌ | Repository not found |
#57 | Al Yamamah web app sponsorship | https://github.com/YazeedAlKhalaf/alyamamah | None | ❌ | Repository not found |
softmarshmallow
Metadata
Metadata
Assignees
Labels
No labels