#!/usr/bin/env python3 import subprocess import os import re import argparse # --- Configuration --- INPUT_FILE = "issues-to-create.md" DEFAULT_PROJECT_TITLE = "Krow" # --- def create_issue(title, body, labels, milestone, project_title=None): """Creates a GitHub issue using the gh CLI.""" command = ["gh", "issue", "create"] command.extend(["--title", title]) command.extend(["--body", body]) if project_title: command.extend(["--project", project_title]) if milestone: command.extend(["--milestone", milestone]) for label in labels: command.extend(["--label", label]) print(f" -> Creating issue: \"{title}\"") try: result = subprocess.run(command, check=True, text=True, capture_output=True) print(result.stdout.strip()) except subprocess.CalledProcessError as e: print(f"❌ ERROR: Failed to create issue '{title}'.") print(f" Stderr: {e.stderr.strip()}") def main(): """Main function to parse the file and create issues.""" parser = argparse.ArgumentParser(description="Bulk create GitHub issues from a markdown file.") parser.add_argument("--project", default=DEFAULT_PROJECT_TITLE, help="GitHub Project title to add issues to.") parser.add_argument("--no-project", action="store_true", help="Do not add issues to any project.") args = parser.parse_args() project_title = args.project if not args.no_project else None print(f"🚀 Starting bulk creation of GitHub issues from '{INPUT_FILE}'...") if project_title: print(f" Target Project: '{project_title}'") else: print(" Target Project: (None)") if subprocess.run(["which", "gh"], capture_output=True).returncode != 0: print("❌ ERROR: GitHub CLI (‘gh’) is not installed.") exit(1) if not os.path.exists(INPUT_FILE): print(f"❌ ERROR: Input file ‘{INPUT_FILE}’ not found.") exit(1) print("✅ Dependencies and input file found.") print(f"2. Reading and parsing ‘{INPUT_FILE}’...") with open(INPUT_FILE, 'r') as f: content = f.read() # Split the content by lines starting with '# ' issue_blocks = re.split(r'\n(?=#\s)', content) for block in issue_blocks: if not block.strip(): continue lines = block.strip().split('\n') title = lines[0].replace('# ', '').strip() labels_line = "" milestone_line = "" body_start_index = 1 # Find all metadata lines (Labels, Milestone) at the beginning of the body for i, line in enumerate(lines[1:]): line_lower = line.strip().lower() if line_lower.startswith('labels:'): labels_line = line.split(':', 1)[1].strip() elif line_lower.startswith('milestone:'): milestone_line = line.split(':', 1)[1].strip() elif line.strip() == "": continue # Ignore blank lines in the metadata header else: # This is the first real line of the body body_start_index = i + 1 break body = "\n".join(lines[body_start_index:]).strip() labels = [label.strip() for label in labels_line.split(',') if label.strip()] milestone = milestone_line if not title: print("⚠️ Skipping block with no title.") continue create_issue(title, body, labels, milestone, project_title) print("\n🎉 Bulk issue creation complete!") if __name__ == "__main__": main()