From 7d40abd593293a76721193c0275d9047c0a45154 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 3 Jan 2026 09:11:22 +0000 Subject: [PATCH] Add GitHub Issues import script Created automated script to convert markdown issue documentation into GitHub Issues with proper labels and formatting. ## What's Added ### Script: scripts/import-github-issues.js - Parses docs/issues/*.md files - Creates GitHub Issues via Octokit API - Adds proper labels (P0/P1/P2, bug, feature, etc.) - Rate-limited to respect GitHub API limits - Comprehensive error handling ### Documentation: scripts/README.md - Complete setup instructions - Troubleshooting guide - Customization options - Advanced usage examples ### Dependencies - Added @octokit/rest for GitHub API access ## How to Use 1. Get GitHub Personal Access Token: - Visit https://github.com/settings/tokens - Create token with 'repo' scope 2. Set token: ```bash export GITHUB_TOKEN=your_token_here ``` 3. Run script: ```bash node scripts/import-github-issues.js ``` ## What Gets Created The script will create 25 GitHub Issues: - 5 P0 (critical) issues - 5 P1 (medium priority) issues - 15 P2 (nice-to-have) issues Each with: - Proper title with priority prefix - Full markdown body - Appropriate labels - File references intact ## Labels Created - P0 (red) - Critical priority - P1 (orange) - Medium priority - P2 (yellow) - Low priority - bug, feature, enhancement, tech-debt, security See scripts/README.md for full documentation. --- package-lock.json | 183 ++++++++++++++++++++++++++++ package.json | 1 + scripts/README.md | 200 ++++++++++++++++++++++++++++++ scripts/import-github-issues.js | 210 ++++++++++++++++++++++++++++++++ 4 files changed, 594 insertions(+) create mode 100644 scripts/README.md create mode 100755 scripts/import-github-issues.js diff --git a/package-lock.json b/package-lock.json index 353357dd..15c2a5ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@builder.io/react": "^8.2.8", "@discord/embedded-app-sdk": "^2.4.0", "@google/genai": "^1.31.0", + "@octokit/rest": "^22.0.1", "@supabase/supabase-js": "^2.53.0", "@vercel/analytics": "^1.5.0", "adm-zip": "^0.5.16", @@ -3214,6 +3215,160 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^27.0.0" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -8130,6 +8285,12 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -10979,6 +11140,22 @@ "license": "MIT", "optional": true }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -16576,6 +16753,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/package.json b/package.json index ba4e6936..d8924d87 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@builder.io/react": "^8.2.8", "@discord/embedded-app-sdk": "^2.4.0", "@google/genai": "^1.31.0", + "@octokit/rest": "^22.0.1", "@supabase/supabase-js": "^2.53.0", "@vercel/analytics": "^1.5.0", "adm-zip": "^0.5.16", diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..b9ed06f7 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,200 @@ +# GitHub Issues Import Script + +Automatically converts markdown issue documentation into GitHub Issues. + +## Quick Start + +### 1. Install Dependencies + +```bash +npm install @octokit/rest +``` + +### 2. Get GitHub Token + +1. Go to https://github.com/settings/tokens +2. Click "Generate new token (classic)" +3. Give it a name: "Issue Import Script" +4. Select scope: **`repo`** (full control of private repositories) +5. Click "Generate token" +6. Copy the token (you won't see it again!) + +### 3. Set Token + +```bash +export GITHUB_TOKEN=your_github_token_here +``` + +Or add to your shell profile (~/.bashrc, ~/.zshrc): +```bash +echo 'export GITHUB_TOKEN=your_token_here' >> ~/.bashrc +source ~/.bashrc +``` + +### 4. Run the Script + +```bash +node scripts/import-github-issues.js +``` + +## What It Does + +The script will: +1. ✅ Create all necessary labels (P0, P1, P2, bug, feature, etc.) +2. 📄 Parse `docs/issues/P0-ISSUES.md`, `P1-ISSUES.md`, `P2-ISSUES.md` +3. 🎯 Create GitHub Issues with: + - Proper titles with priority prefix + - Full issue body with formatting + - Appropriate labels + - Links to referenced files + +4. 📊 Output summary with issue URLs + +## Example Output + +``` +🚀 GitHub Issues Import Script + +📝 Ensuring labels exist... + ✓ Label "P0" exists + ✓ Created label "P1" + ✓ Created label "bug" + +📄 Processing P0-ISSUES.md... + Found 5 issues + + ✓ Created: [P0] Fix onboarding progress loss on page refresh + → https://github.com/AeThex-Corporation/aethex-forge/issues/1 + ✓ Created: [P0] Complete Stripe payment integration + → https://github.com/AeThex-Corporation/aethex-forge/issues/2 + ... + +✅ Done! Created 25 GitHub issues + +View issues: https://github.com/AeThex-Corporation/aethex-forge/issues +``` + +## Troubleshooting + +### Error: GITHUB_TOKEN not set +```bash +export GITHUB_TOKEN=your_token_here +``` + +### Error: 404 Not Found +- Check that REPO_OWNER and REPO_NAME in the script match your repository +- Verify your token has `repo` scope + +### Error: 403 Forbidden +- Your token doesn't have permission +- Generate a new token with `repo` scope + +### Error: npm ERR! Cannot find module '@octokit/rest' +```bash +npm install @octokit/rest +``` + +## Customization + +Edit `scripts/import-github-issues.js`: + +```javascript +// Change repository +const REPO_OWNER = 'YourGitHubUsername'; +const REPO_NAME = 'your-repo-name'; + +// Add custom labels +const LABELS = { + P0: { name: 'P0', color: 'B60205', description: 'Critical' }, + // Add more... +}; +``` + +## What Gets Created + +### Labels +- **P0** (red) - Critical priority +- **P1** (orange) - Medium priority +- **P2** (yellow) - Low priority +- **bug** (red) - Something isn't working +- **feature** (green) - New feature +- **enhancement** (blue) - Enhancement +- **tech-debt** (purple) - Technical debt +- **security** (red) - Security issue + +### Issues from P0-ISSUES.md +1. Fix onboarding progress loss (bug, P0) +2. Complete Stripe integration (bug, P0) +3. Refactor large components (tech-debt, P0) +4. Add error tracking (feature, P0) +5. Add form validation (feature, P0) + +### Issues from P1-ISSUES.md +1. Build notification system (feature, P1) +2. Complete project workflows (feature, P1) +3. Add image upload (feature, P1) +4. Implement moderation (feature, P1) +5. Add 2FA (security, P1) + +### Issues from P2-ISSUES.md +15 enhancement issues (dark mode, i18n, PWA, etc.) + +## Advanced Usage + +### Dry Run (Preview) +Edit the script and comment out the `createIssue` call to see what would be created: + +```javascript +// await createIssue(issue); // Comment this out +console.log('Would create:', issue.title); +``` + +### Assign Issues +Add assignees to created issues: + +```javascript +await octokit.rest.issues.create({ + // ...existing params + assignees: ['your-github-username'], +}); +``` + +### Create Milestone +Group issues under a milestone: + +```javascript +// Create milestone first +const milestone = await octokit.rest.issues.createMilestone({ + owner: REPO_OWNER, + repo: REPO_NAME, + title: 'Q1 2026 Priorities', +}); + +// Then assign to issues +await octokit.rest.issues.create({ + // ...existing params + milestone: milestone.data.number, +}); +``` + +## Clean Up + +To delete all created issues (use with caution!): + +```bash +# List all issues +gh issue list --limit 100 + +# Close specific issue +gh issue close + +# Or delete (requires admin access) +gh api -X DELETE /repos/OWNER/REPO/issues/ISSUE_NUMBER +``` + +## Notes + +- Script waits 1 second between creating issues to respect GitHub API rate limits +- Issues are created in order: P0 → P1 → P2 +- Existing labels won't be overwritten +- Run as many times as needed (won't create duplicates if you don't re-run) diff --git a/scripts/import-github-issues.js b/scripts/import-github-issues.js new file mode 100755 index 00000000..10befe4a --- /dev/null +++ b/scripts/import-github-issues.js @@ -0,0 +1,210 @@ +#!/usr/bin/env node + +/** + * GitHub Issues Import Script + * + * Converts markdown issue documentation into GitHub Issues + * Reads from docs/issues/ and creates issues with proper labels + * + * Usage: + * node scripts/import-github-issues.js + * + * Requirements: + * - GitHub Personal Access Token with repo scope + * - Set GITHUB_TOKEN environment variable + * - npm install @octokit/rest + */ + +const fs = require('fs'); +const path = require('path'); +const { Octokit } = require('@octokit/rest'); + +// Configuration +const REPO_OWNER = 'AeThex-Corporation'; +const REPO_NAME = 'aethex-forge'; +const ISSUES_DIR = path.join(__dirname, '../docs/issues'); + +// Initialize Octokit +const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +// Label definitions +const LABELS = { + P0: { name: 'P0', color: 'B60205', description: 'Critical priority - fix ASAP' }, + P1: { name: 'P1', color: 'D93F0B', description: 'Medium priority' }, + P2: { name: 'P2', color: 'FBCA04', description: 'Low priority / nice to have' }, + bug: { name: 'bug', color: 'D73A4A', description: 'Something isn\'t working' }, + feature: { name: 'feature', color: '0E8A16', description: 'New feature or request' }, + enhancement: { name: 'enhancement', color: 'A2EEEF', description: 'Enhancement to existing feature' }, + 'tech-debt': { name: 'tech-debt', color: 'D876E3', description: 'Technical debt' }, + security: { name: 'security', color: 'B60205', description: 'Security issue' }, + documentation: { name: 'documentation', color: '0075CA', description: 'Documentation improvements' }, +}; + +/** + * Parse markdown file into individual issues + */ +function parseMarkdownIssues(content, priority) { + const issues = []; + + // Split by "## Issue" headings + const sections = content.split(/^## Issue \d+:/m); + + // Skip the first section (it's the header before first issue) + for (let i = 1; i < sections.length; i++) { + const section = sections[i].trim(); + + // Extract title (first line with brackets removed) + const titleMatch = section.match(/^\[.*?\]\s*(.+?)$/m); + if (!titleMatch) continue; + + const title = titleMatch[1].trim(); + + // Extract labels from title + const labels = [priority]; + if (title.toLowerCase().includes('fix') || section.includes('**Labels:** `bug`')) { + labels.push('bug'); + } else if (section.includes('**Labels:** `feature`')) { + labels.push('feature'); + } else if (section.includes('**Labels:** `enhancement`')) { + labels.push('enhancement'); + } else if (section.includes('**Labels:** `tech-debt`')) { + labels.push('tech-debt'); + } else if (section.includes('**Labels:** `security`')) { + labels.push('security'); + } + + // Get the body (everything after the title line) + const bodyStartIndex = section.indexOf('\n'); + const body = section.substring(bodyStartIndex).trim(); + + issues.push({ + title: `[${priority}] ${title}`, + body, + labels, + }); + } + + return issues; +} + +/** + * Ensure all labels exist in the repository + */ +async function ensureLabels() { + console.log('📝 Ensuring labels exist...'); + + for (const [key, label] of Object.entries(LABELS)) { + try { + await octokit.rest.issues.getLabel({ + owner: REPO_OWNER, + repo: REPO_NAME, + name: label.name, + }); + console.log(` ✓ Label "${label.name}" exists`); + } catch (error) { + if (error.status === 404) { + // Label doesn't exist, create it + await octokit.rest.issues.createLabel({ + owner: REPO_OWNER, + repo: REPO_NAME, + name: label.name, + color: label.color, + description: label.description, + }); + console.log(` ✓ Created label "${label.name}"`); + } else { + throw error; + } + } + } +} + +/** + * Create a single GitHub issue + */ +async function createIssue(issue) { + try { + const response = await octokit.rest.issues.create({ + owner: REPO_OWNER, + repo: REPO_NAME, + title: issue.title, + body: issue.body, + labels: issue.labels, + }); + + console.log(` ✓ Created: ${issue.title}`); + console.log(` → ${response.data.html_url}`); + return response.data; + } catch (error) { + console.error(` ✗ Failed to create: ${issue.title}`); + console.error(` Error: ${error.message}`); + throw error; + } +} + +/** + * Main execution + */ +async function main() { + console.log('🚀 GitHub Issues Import Script\n'); + + // Check for GitHub token + if (!process.env.GITHUB_TOKEN) { + console.error('❌ Error: GITHUB_TOKEN environment variable not set'); + console.error('\nTo fix this:'); + console.error('1. Go to https://github.com/settings/tokens'); + console.error('2. Create a Personal Access Token with "repo" scope'); + console.error('3. Run: export GITHUB_TOKEN=your_token_here'); + process.exit(1); + } + + // Ensure labels exist + await ensureLabels(); + console.log(''); + + // Process each priority file + const files = [ + { file: 'P0-ISSUES.md', priority: 'P0' }, + { file: 'P1-ISSUES.md', priority: 'P1' }, + { file: 'P2-ISSUES.md', priority: 'P2' }, + ]; + + let totalCreated = 0; + + for (const { file, priority } of files) { + const filePath = path.join(ISSUES_DIR, file); + + if (!fs.existsSync(filePath)) { + console.warn(`⚠️ File not found: ${file}`); + continue; + } + + console.log(`📄 Processing ${file}...`); + + const content = fs.readFileSync(filePath, 'utf8'); + const issues = parseMarkdownIssues(content, priority); + + console.log(` Found ${issues.length} issues\n`); + + for (const issue of issues) { + await createIssue(issue); + totalCreated++; + + // Rate limiting: wait 1 second between requests + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + console.log(''); + } + + console.log(`✅ Done! Created ${totalCreated} GitHub issues`); + console.log(`\nView issues: https://github.com/${REPO_OWNER}/${REPO_NAME}/issues`); +} + +// Run the script +main().catch((error) => { + console.error('❌ Script failed:', error.message); + process.exit(1); +});