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.
210 lines
5.9 KiB
JavaScript
Executable file
210 lines
5.9 KiB
JavaScript
Executable file
#!/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);
|
|
});
|