feat: Add script for automated GitHub issue creation with environment variable validation and issue management

This commit is contained in:
Christian Krakau-Louis
2025-04-16 15:39:21 +02:00
parent 55d767a84a
commit 9815a1a3e0
2 changed files with 316 additions and 0 deletions
+67
View File
@@ -0,0 +1,67 @@
# GitHub Issue Creation
This directory contains scripts for creating GitHub issues for the LeagueLedger project.
## Create GitHub Issues Script
The `create_github_issues.py` script allows you to automatically create structured GitHub issues for your project features.
### Prerequisites
1. A GitHub account
2. A personal access token with "repo" scope (Create one at https://github.com/settings/tokens)
3. Python 3.6+ installed with the requests library
### Setup
1. Update the script with your GitHub information:
```python
REPO_OWNER = "YOUR_GITHUB_USERNAME" # Replace with your GitHub username
REPO_NAME = "LeagueLedger" # Replace with your repository name
```
2. Set your GitHub token as an environment variable:
**Windows:**
```cmd
set GITHUB_TOKEN=your_token_here
```
**Mac/Linux:**
```bash
export GITHUB_TOKEN=your_token_here
```
### Usage
Run the script from the command line:
```bash
python scripts/create_github_issues.py
```
This will create issues for the profile management features:
- Add profile picture update functionality
- Implement username change capability
- Create account deletion process
- Add profile privacy settings
- Implement social media integration for profiles
### Customizing Issues
You can customize the issues by editing the `PROFILE_ISSUES` list in the script. Each issue consists of:
- `title`: The issue title
- `body`: Markdown-formatted issue description
- `labels`: List of labels to apply to the issue
### Best Practices
- Always use clear titles and detailed descriptions
- Include requirements and acceptance criteria in each issue
- Use labels to categorize issues for better organization
- Mention related areas of code that might be affected
## Adding More Issue Sets
To add more feature sets beyond profile management, create new issue lists following the same format and add them to the script.
+249
View File
@@ -0,0 +1,249 @@
#!/usr/bin/env python3
"""
Script to create GitHub issues for LeagueLedger project.
"""
import requests
import os
import json
import sys
import re
from typing import Dict, List, Any
from dotenv import load_dotenv
from pathlib import Path
# Load environment variables from .env file
env_path = Path(__file__).parent / '.env'
load_dotenv(dotenv_path=env_path)
# GitHub repository information
GITHUB_API_URL = "https://api.github.com"
REPO_OWNER = os.getenv("REPO_OWNER")
REPO_NAME = os.getenv("REPO_NAME")
# GitHub authentication - use a personal access token
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
def validate_env():
"""Validate that all required environment variables are set."""
missing_vars = []
if not GITHUB_TOKEN:
missing_vars.append("GITHUB_TOKEN")
if not REPO_OWNER:
missing_vars.append("REPO_OWNER")
if not REPO_NAME:
missing_vars.append("REPO_NAME")
if missing_vars:
print("Error: The following environment variables are missing in your .env file:")
for var in missing_vars:
print(f"- {var}")
print("\nPlease update your .env file in the scripts directory.")
return False
return True
def get_all_issues():
"""Get all open issues from the repository."""
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues"
params = {
"state": "open",
"per_page": 100
}
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
response = requests.get(url, params=params, headers=headers)
if response.status_code == 200:
return response.json()
else:
print(f"Failed to fetch issues: {response.status_code}")
print(response.text)
return []
def close_issue(issue_number):
"""Close an issue by its number."""
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues/{issue_number}"
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
data = {
"state": "closed"
}
response = requests.patch(url, json=data, headers=headers)
if response.status_code == 200:
print(f"Successfully closed issue #{issue_number}")
return True
else:
print(f"Failed to close issue #{issue_number}: {response.status_code}")
print(response.text)
return False
def delete_all_issues():
"""Close all open issues in the repository."""
issues = get_all_issues()
if not issues:
print("No open issues found.")
return
print(f"Found {len(issues)} open issues. Closing all...")
for issue in issues:
close_issue(issue["number"])
print("Finished closing issues.")
def create_github_issue(issue_data: Dict[str, Any]) -> Dict[str, Any]:
"""Create a GitHub issue using the GitHub API."""
url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/issues"
headers = {
"Authorization": f"token {GITHUB_TOKEN}",
"Accept": "application/vnd.github.v3+json"
}
response = requests.post(url, json=issue_data, headers=headers)
if response.status_code == 201:
print(f"Successfully created issue: {issue_data['title']}")
return response.json()
else:
print(f"Failed to create issue: {response.status_code}")
print(response.text)
return {}
def parse_todo_file(file_path):
"""Parse the TODO.md file and extract the issues."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
except Exception as e:
print(f"Error reading TODO.md file: {e}")
return []
# Split the content by sections (## headings)
sections = re.split(r'^## ', content, flags=re.MULTILINE)
# The first section is the title and intro, skip it
sections = sections[1:]
all_issues = []
for section in sections:
# Get the section title from the first line
title_line = section.split('\n', 1)[0].strip()
section_content = section.split('\n', 1)[1].strip() if len(section.split('\n', 1)) > 1 else ""
# Create a main issue for the section
section_issue = {
"title": title_line,
"body": f"## {title_line}\n\nThis issue tracks the progress of all {title_line.lower()} tasks.\n\n### Tasks\n{section_content}",
"labels": ["epic", title_line.lower().replace(' & ', '-').replace(' ', '-')]
}
all_issues.append(section_issue)
# Parse individual tasks in the section
tasks = parse_section_tasks(section_content, title_line)
all_issues.extend(tasks)
return all_issues
def parse_section_tasks(section_content, section_title):
"""Parse individual tasks from a section."""
issues = []
# Extract tasks at the top level (directly under the section)
tasks = re.findall(r'^- \[ \] (.+)$', section_content, re.MULTILINE)
for task in tasks:
# Skip tasks that are groups (they have subtasks and usually end with a colon)
if task.strip().endswith(':') or task.strip().startswith('**'):
continue
issue = {
"title": task.strip(),
"body": f"## Description\nImplement the following task from {section_title}:\n\n{task.strip()}\n\n## Related\nPart of the {section_title} epic.",
"labels": ["task", section_title.lower().replace(' & ', '-').replace(' ', '-')]
}
issues.append(issue)
# Extract grouped tasks (subtasks under headings like "User Management")
groups = re.findall(r'- \[ \] \*\*(.*?)\*\*\s*\n((?: - \[ \].*\n)*)', section_content, re.MULTILINE)
for group_name, group_tasks_text in groups:
# Create a group issue
group_issue = {
"title": f"{group_name}",
"body": f"## Description\nImplement {group_name} features for {section_title}.\n\n## Tasks\n{group_tasks_text}\n\n## Related\nPart of the {section_title} epic.",
"labels": ["group", section_title.lower().replace(' & ', '-').replace(' ', '-'), group_name.lower().replace(' ', '-')]
}
issues.append(group_issue)
# Add individual subtasks
subtasks = re.findall(r' - \[ \] (.*)', group_tasks_text)
for subtask in subtasks:
subtask_issue = {
"title": f"{group_name}: {subtask.strip()}",
"body": f"## Description\nImplement the following {group_name} feature:\n\n{subtask.strip()}\n\n## Related\nPart of the {group_name} group in the {section_title} epic.",
"labels": ["subtask", section_title.lower().replace(' & ', '-').replace(' ', '-'), group_name.lower().replace(' ', '-')]
}
issues.append(subtask_issue)
return issues
def main():
"""Main function to create all the issues."""
print("LeagueLedger GitHub Issue Creator")
print("=================================")
# Validate environment variables
if not validate_env():
sys.exit(1)
print(f"Repository: {REPO_OWNER}/{REPO_NAME}")
# Delete existing issues
delete_option = input("Do you want to close all existing open issues? (y/n): ").lower()
if delete_option == 'y':
delete_all_issues()
# Parse TODO.md and create issues
todo_path = Path(__file__).parent.parent / 'TODO.md'
if not todo_path.exists():
print(f"Error: TODO.md file not found at {todo_path}")
sys.exit(1)
print(f"Parsing TODO.md at {todo_path}...")
issues = parse_todo_file(todo_path)
print(f"Found {len(issues)} issues to create.")
create_option = input(f"Do you want to create {len(issues)} GitHub issues? (y/n): ").lower()
if create_option != 'y':
print("Operation cancelled.")
sys.exit(0)
# Create issues
created_issues = []
for issue in issues:
result = create_github_issue(issue)
if result:
created_issues.append(result)
# Show results
if created_issues:
print("\nSuccessfully created issues:")
for issue in created_issues:
print(f"- #{issue['number']} {issue['title']}: {issue['html_url']}")
print("\nCompleted creating GitHub issues.")
if __name__ == "__main__":
main()