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.
This commit is contained in:
parent
a1ff55c41d
commit
7d40abd593
4 changed files with 594 additions and 0 deletions
183
package-lock.json
generated
183
package-lock.json
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
200
scripts/README.md
Normal file
200
scripts/README.md
Normal file
|
|
@ -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 <issue-number>
|
||||
|
||||
# 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)
|
||||
210
scripts/import-github-issues.js
Executable file
210
scripts/import-github-issues.js
Executable file
|
|
@ -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);
|
||||
});
|
||||
Loading…
Reference in a new issue