diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..2369aa3 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,195 @@ +# Scripts for Creating GitHub Issues from README TODOs + +这个目录包含了从 README.md 文件中提取 TODO 项并创建 GitHub Issues 的脚本。 + +This directory contains scripts to extract TODO items from README.md and create GitHub Issues. + +## 文件说明 (Files) + +### 1. `create-issues-from-todos.ts` +TypeScript 脚本,用于解析 README.md 并生成不同格式的输出。 + +TypeScript script to parse README.md and generate different output formats. + +**用法 (Usage):** +```bash +# 生成 Markdown 报告 (Generate Markdown report) +npx tsx scripts/create-issues-from-todos.ts markdown + +# 生成 JSON 格式 (Generate JSON format) +npx tsx scripts/create-issues-from-todos.ts json + +# 生成 GitHub CLI 脚本 (Generate GitHub CLI script) +npx tsx scripts/create-issues-from-todos.ts gh > scripts/create-issues.sh +``` + +### 2. `create-issues.cjs` +Node.js 脚本,直接使用 GitHub CLI 创建 Issues。 + +Node.js script to directly create Issues using GitHub CLI. + +**用法 (Usage):** +```bash +# 查看将要创建的 Issues(不实际创建)(Dry run - preview without creating) +node scripts/create-issues.cjs --dry-run + +# 实际创建 Issues (Actually create the issues) +node scripts/create-issues.cjs +``` + +### 3. `create-issues.sh` +自动生成的 Bash 脚本,包含所有 `gh issue create` 命令。 + +Auto-generated Bash script containing all `gh issue create` commands. + +**用法 (Usage):** +```bash +# 直接运行脚本 (Run the script directly) +bash scripts/create-issues.sh +``` + +## 前置要求 (Prerequisites) + +### 方法 1: 使用 Node.js 脚本 (Using Node.js script) + +1. **安装 GitHub CLI:** + ```bash + # macOS + brew install gh + + # Windows + winget install GitHub.cli + + # Linux + sudo apt install gh + ``` + +2. **认证 GitHub CLI:** + ```bash + gh auth login + ``` + +3. **运行脚本:** + ```bash + node scripts/create-issues.cjs --dry-run # 先预览 + node scripts/create-issues.cjs # 实际创建 + ``` + +### 方法 2: 使用生成的 Shell 脚本 (Using generated shell script) + +1. **生成脚本:** + ```bash + npx tsx scripts/create-issues-from-todos.ts gh > scripts/create-issues.sh + chmod +x scripts/create-issues.sh + ``` + +2. **认证 GitHub CLI:** + ```bash + gh auth login + ``` + +3. **运行脚本:** + ```bash + bash scripts/create-issues.sh + ``` + +## TODO 项解析规则 (TODO Parsing Rules) + +脚本会从 README.md 中提取以下格式的 TODO 项: + +The script extracts TODO items in the following format from README.md: + +```markdown +### 🎨 UI/UX 优化 + +- [ ] **优化配置面板的视觉效果** + - 改进卡片阴影和间距 + - 添加更流畅的过渡动画 + - 优化颜色对比度,提升可读性 +``` + +**提取结果 (Extracted as):** +- **标题 (Title)**: `[🎨 UI/UX 优化] 优化配置面板的视觉效果` +- **描述 (Description)**: 缩进的子项列表 (List of indented sub-items) +- **标签 (Labels)**: 根据章节自动分配 (Auto-assigned based on section) + +## 标签映射 (Label Mapping) + +| 章节 (Section) | 标签 (Labels) | +|----------------|---------------| +| 🎨 UI/UX 优化 | `enhancement`, `ui/ux`, `design` | +| 🔧 功能增强 | `enhancement`, `feature` | +| 🏗️ 代码结构优化 | `refactor`, `code-quality` | +| 🧪 测试与质量保证 | `testing`, `quality` | +| 📚 文档完善 | `documentation` | +| 🌐 国际化 | `i18n`, `enhancement` | +| ⚡ 性能优化 | `performance`, `enhancement` | +| 🐛 已知问题修复 | `bug`, `fix` | + +## 统计信息 (Statistics) + +当前 README.md 中包含 **25 个** TODO 项,分布在 **8 个** 不同的类别中。 + +The current README.md contains **25** TODO items across **8** different categories. + +## 注意事项 (Notes) + +1. **避免重复创建 (Avoid Duplicates)**: 运行脚本前,请确认这些 Issues 尚未创建,以避免重复。 + +2. **批量创建限制 (Rate Limiting)**: GitHub API 有速率限制。脚本会在每个 Issue 创建之间添加 1 秒延迟。 + +3. **权限要求 (Permissions)**: 需要对仓库有写入权限才能创建 Issues。 + +4. **标签预创建 (Label Creation)**: 确保仓库中已经创建了相应的标签。如果标签不存在,Issues 仍会创建,但不会附加标签。 + +## 示例输出 (Example Output) + +``` +📖 Reading README.md... + +📝 Found 25 TODO items to convert to issues + +🚀 Creating issues using GitHub CLI (gh)... + +Creating issue 1/25: 优化配置面板的视觉效果 +Creating issue https://github.com/CompPsyUnion/debate-timer/issues/1 +Creating issue 2/25: 统一整体 UI 设计语言 +Creating issue https://github.com/CompPsyUnion/debate-timer/issues/2 +... + +✅ Created 25 issues +``` + +## 故障排除 (Troubleshooting) + +### 问题: gh: command not found +**解决方案**: 安装 GitHub CLI +```bash +brew install gh # macOS +``` + +### 问题: Error: GitHub CLI is not authenticated +**解决方案**: 运行 gh auth login +```bash +gh auth login +``` + +### 问题: Error: could not create issue +**解决方案**: 检查权限和网络连接 +- 确认你有仓库的写入权限 +- 检查网络连接 +- 查看 GitHub CLI 的详细日志:`gh issue create --help` + +## 开发 (Development) + +如果你想修改脚本的行为: + +If you want to modify the script behavior: + +1. 编辑 `create-issues-from-todos.ts` 更改解析逻辑 +2. 编辑 `create-issues.cjs` 更改 Issue 创建逻辑 +3. 运行测试确保脚本正常工作 + +## 许可证 (License) + +MIT License - 与主项目相同 diff --git a/scripts/create-github-issues-README.md b/scripts/create-github-issues-README.md new file mode 100644 index 0000000..e91785e --- /dev/null +++ b/scripts/create-github-issues-README.md @@ -0,0 +1,205 @@ +# GitHub Issues Creator - Standalone Script + +这是一个独立的脚本,用于从 README.md 中提取 TODO 项并自动创建 GitHub Issues。 + +This is a standalone script to extract TODO items from README.md and automatically create GitHub Issues. + +## 特点 (Features) + +- ✅ 自动认证 GitHub (Automatic GitHub authentication) +- ✅ 从 README.md 提取所有 TODO 项 (Extracts all TODO items from README.md) +- ✅ 自动移除标题中的 emoji (Automatically removes emojis from titles) +- ✅ 支持自定义标签 (Supports custom labels) +- ✅ 独立运行,无需其他依赖 (Standalone, no other dependencies) + +## 使用方法 (Usage) + +### 方法 1: 使用环境变量 (Using environment variable) + +```bash +# 设置 token 并运行脚本 +GITHUB_TOKEN=ghp_your_token_here node scripts/create-github-issues.cjs +``` + +### 方法 2: 直接设置环境变量 (Set environment variable directly) + +在运行脚本前设置环境变量: +```bash +export GITHUB_TOKEN=ghp_your_token_here +node scripts/create-github-issues.cjs +``` + +**注意**: 不建议在脚本中硬编码 token,这可能导致 token 意外泄露到版本控制系统中。 + +## 前置要求 (Prerequisites) + +1. **Node.js** (已安装 / Installed) +2. **GitHub CLI (`gh`)** (已安装 / Installed) + ```bash + # macOS + brew install gh + + # Windows + winget install GitHub.cli + + # Linux + sudo apt install gh + ``` +3. **GitHub Personal Access Token (PAT)** 需要以下权限: + - `repo` (完整仓库访问权限) + - 或者 `public_repo` (如果是公开仓库) + +## 创建 GitHub Token + +1. 访问 https://github.com/settings/tokens +2. 点击 "Generate new token" > "Generate new token (classic)" +3. 设置 token 名称,例如: "Issue Creator" +4. 选择权限: + - ✅ `repo` (或 `public_repo`) +5. 点击 "Generate token" +6. 复制生成的 token (以 `ghp_` 开头) + +## 脚本功能 (Script Features) + +### 解析规则 (Parsing Rules) + +脚本会从 README.md 中提取以下格式的 TODO 项: + +```markdown +### 🎨 UI/UX 优化 + +- [ ] **优化配置面板的视觉效果** + - 改进卡片阴影和间距 + - 添加更流畅的过渡动画 +``` + +### Issue 格式 (Issue Format) + +生成的 Issue: +- **标题 (Title)**: `[UI/UX 优化] 优化配置面板的视觉效果` (无 emoji) +- **描述 (Body)**: 所有子项的列表 +- **标签 (Labels)**: 根据章节自动分配 + +### 标签映射 (Label Mapping) + +| 章节 (Section) | 标签 (Labels) | +|----------------|---------------| +| UI/UX 优化 | `enhancement`, `ui/ux`, `design` | +| 功能增强 | `enhancement`, `feature` | +| 代码结构优化 | `refactor`, `code-quality` | +| 测试与质量保证 | `testing`, `quality` | +| 文档完善 | `documentation` | +| 国际化 | `i18n`, `enhancement` | +| 性能优化 | `performance`, `enhancement` | +| 已知问题修复 | `bug`, `fix` | + +## 运行示例 (Example Run) + +```bash +$ GITHUB_TOKEN=ghp_xxx node scripts/create-github-issues.cjs + +📖 GitHub Issues Creator + +============================================================ + +🔐 Authenticating with GitHub... +✅ Authentication successful + +📄 Reading todos from: /path/to/README.md + +📝 Found 25 TODO items + +============================================================ + +🚀 Creating GitHub issues... + +Creating issue 1/25: 优化配置面板的视觉效果 +Creating issue 2/25: 统一整体 UI 设计语言 +Creating issue 3/25: 改进计时器显示效果 +... +Creating issue 25/25: 计时精度优化 + +============================================================ + +✅ Created 25 issues successfully + +🎉 Done! +``` + +## 预期结果 (Expected Results) + +运行此脚本将在 GitHub 仓库中创建 **25 个 Issues**,分布在以下类别: + +1. **UI/UX 优化** (4 issues) + - 优化配置面板的视觉效果 + - 统一整体 UI 设计语言 + - 改进计时器显示效果 + - 响应式设计优化 + +2. **功能增强** (6 issues) + - 使用专业的 JSON 编辑器 + - 配置管理功能 + - 铃声自定义 + - 全屏模式 + - 主题切换 + - 辅助功能 + +3. **代码结构优化** (4 issues) + - 组件拆分 + - 逻辑提取 + - 工具函数提取 + - 添加代码注释 + +4. **测试与质量保证** (3 issues) + - 单元测试 + - 组件测试 + - E2E 测试 + +5. **文档完善** (3 issues) + - 组件文档 + - 用户手册 + - 开发者指南 + +6. **国际化** (1 issue) + - 多语言支持 + +7. **性能优化** (2 issues) + - 代码优化 + - 构建优化 + +8. **已知问题修复** (2 issues) + - TimerConfigPanel 验证改进 + - 计时精度优化 + +## 注意事项 (Notes) + +1. **避免重复**: 运行前请确认这些 Issues 尚未创建 +2. **速率限制**: 脚本在每个 Issue 之间会等待 1 秒,避免触发 GitHub API 速率限制 +3. **Token 安全**: + - 不要将含有 token 的脚本提交到 Git + - 使用后可以考虑撤销该 token + - 使用环境变量是更安全的方式 + +## 故障排除 (Troubleshooting) + +### 问题: gh: command not found +**解决**: 安装 GitHub CLI +```bash +brew install gh # macOS +``` + +### 问题: Authentication failed +**解决**: +- 检查 token 是否正确 +- 确认 token 有 `repo` 权限 +- 确认 token 未过期 + +### 问题: Failed to create issue +**解决**: +- 检查网络连接 +- 确认有仓库的写入权限 +- 检查仓库名称和所有者是否正确 + +## 许可证 (License) + +MIT License - 与主项目相同 diff --git a/scripts/create-github-issues.cjs b/scripts/create-github-issues.cjs new file mode 100755 index 0000000..c3ac162 --- /dev/null +++ b/scripts/create-github-issues.cjs @@ -0,0 +1,289 @@ +#!/usr/bin/env node + +/** + * Create GitHub issues from README todos + * + * This is a standalone script that: + * - Reads TODO items from README.md + * - Creates GitHub issues via GitHub CLI + * - Handles authentication automatically + * - Removes emojis from issue titles + * + * Usage: + * GITHUB_TOKEN=ghp_your_token_here node scripts/create-github-issues.cjs + * + * Or set the token directly in the script (line 27) + */ + +const fs = require('fs'); +const path = require('path'); +const { spawnSync } = require('child_process'); + +// ============================================================================ +// CONFIGURATION +// ============================================================================ + +// Set your GitHub token here or use GITHUB_TOKEN environment variable +const GITHUB_TOKEN = process.env.GITHUB_TOKEN; + +// Repository information (auto-detected from git or set manually) +const REPO_OWNER = 'CompPsyUnion'; +const REPO_NAME = 'debate-timer'; + +// Rate limiting delay between issue creation (in milliseconds) +const RATE_LIMIT_DELAY_MS = 1000; + +// ============================================================================ +// HELPER FUNCTIONS +// ============================================================================ + +// Emoji removal patterns +const EMOJI_RANGES = [ + '\u{1F600}-\u{1F64F}', // Emoticons + '\u{1F300}-\u{1F5FF}', // Misc Symbols and Pictographs + '\u{1F680}-\u{1F6FF}', // Transport and Map + '\u{1F1E0}-\u{1F1FF}', // Regional country flags + '\u{2600}-\u{26FF}', // Misc symbols + '\u{2700}-\u{27BF}', // Dingbats + '\u{1F900}-\u{1F9FF}', // Supplemental Symbols and Pictographs + '\u{1FA00}-\u{1FA6F}', // Chess Symbols + '\u{1FA70}-\u{1FAFF}', // Symbols and Pictographs Extended-A + '\u{FE00}-\u{FE0F}', // Variation Selectors + '\u{200D}' // Zero Width Joiner +]; + +/** + * Remove emoji characters from text + */ +function removeEmojis(text) { + const emojiPattern = new RegExp(`[${EMOJI_RANGES.join('')}]`, 'gu'); + return text + .replace(emojiPattern, '') + .replace(/\s+/g, ' ') + .trim(); +} + +/** + * Parse README.md and extract TODO items + */ +function parseReadmeTodos(readmePath) { + const content = fs.readFileSync(readmePath, 'utf-8'); + const lines = content.split('\n'); + + const todos = []; + let currentSection = ''; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // Detect main section headers (### level) + if (line.startsWith('### ')) { + currentSection = line.replace('### ', '').trim(); + i++; + continue; + } + + // Detect category (bold items that are TODOs) + if (line.match(/^- \[ \] \*\*/)) { + const titleMatch = line.match(/\*\*(.+?)\*\*/); + if (titleMatch) { + const title = titleMatch[1]; + + // Collect description lines + const description = []; + let j = i + 1; + + while (j < lines.length && (lines[j].startsWith(' -') || lines[j].trim() === '')) { + if (lines[j].trim() !== '') { + description.push(lines[j].trim().replace(/^- /, '')); + } + j++; + } + + const labels = getLabelsForSection(currentSection); + + todos.push({ + title: title, + body: description.join('\n'), + labels, + category: currentSection + }); + + i = j; + continue; + } + } + + i++; + } + + return todos; +} + +/** + * Get appropriate labels based on the section + */ +function getLabelsForSection(section) { + const labelMap = { + '🎨 UI/UX 优化': ['enhancement', 'ui/ux', 'design'], + '🎨 UI/UX Optimization': ['enhancement', 'ui/ux', 'design'], + '🔧 功能增强': ['enhancement', 'feature'], + '🔧 Feature Enhancements': ['enhancement', 'feature'], + '🏗️ 代码结构优化': ['refactor', 'code-quality'], + '🏗️ Code Structure Optimization': ['refactor', 'code-quality'], + '🧪 测试与质量保证': ['testing', 'quality'], + '🧪 Testing and Quality Assurance': ['testing', 'quality'], + '📚 文档完善': ['documentation'], + '📚 Documentation Completion': ['documentation'], + '🌐 国际化': ['i18n', 'enhancement'], + '🌐 Internationalization': ['i18n', 'enhancement'], + '⚡ 性能优化': ['performance', 'enhancement'], + '⚡ Performance Optimization': ['performance', 'enhancement'], + '🐛 已知问题修复': ['bug', 'fix'], + '🐛 Known Issue Fixes': ['bug', 'fix'], + }; + + return labelMap[section] || ['enhancement']; +} + +/** + * Authenticate with GitHub CLI using the token + */ +function authenticateGitHub(token) { + console.log('🔐 Authenticating with GitHub...'); + + try { + // Use stdin to pass the token securely + const result = spawnSync('gh', ['auth', 'login', '--with-token'], { + input: token, + encoding: 'utf-8' + }); + + if (result.status === 0) { + console.log('✅ Authentication successful\n'); + return true; + } else { + console.error('❌ Authentication failed'); + console.error(' Please check your token and ensure it has repo permissions'); + return false; + } + } catch (error) { + console.error('❌ Authentication failed:', error.message); + return false; + } +} + +/** + * Create a single issue using GitHub CLI + */ +function createIssue(todo, issueNumber, totalIssues, token) { + // Remove emojis from category and title + const categoryClean = removeEmojis(todo.category); + const titleClean = removeEmojis(todo.title); + const title = `[${categoryClean}] ${titleClean}`; + const labels = todo.labels.join(','); + + console.log(`Creating issue ${issueNumber}/${totalIssues}: ${titleClean}`); + + try { + // Use spawn with argument array to avoid shell injection + const result = spawnSync('gh', [ + 'issue', 'create', + '--repo', `${REPO_OWNER}/${REPO_NAME}`, + '--title', title, + '--body', todo.body, + '--label', labels + ], { + env: { ...process.env, GH_TOKEN: token }, + encoding: 'utf-8' + }); + + if (result.status === 0) { + return true; + } else { + console.error(`❌ Failed to create issue: ${titleClean}`); + if (result.stderr) { + console.error(` Error: ${result.stderr.trim()}`); + } + return false; + } + } catch (error) { + console.error(`❌ Failed to create issue: ${titleClean}`); + console.error(` Error: ${error.message}`); + return false; + } +} + +/** + * Main function + */ +async function main() { + console.log('📖 GitHub Issues Creator\n'); + console.log('='.repeat(60)); + console.log(''); + + // Check if token is provided + if (!GITHUB_TOKEN) { + console.error('❌ Error: GitHub token not provided'); + console.error(' Please set GITHUB_TOKEN environment variable or edit the script'); + console.error(' Example: GITHUB_TOKEN=ghp_xxx node scripts/create-github-issues.cjs'); + process.exit(1); + } + + // Authenticate with GitHub + if (!authenticateGitHub(GITHUB_TOKEN)) { + process.exit(1); + } + + // Parse README.md + const readmePath = path.join(process.cwd(), 'README.md'); + console.log(`📄 Reading todos from: ${readmePath}\n`); + + if (!fs.existsSync(readmePath)) { + console.error(`❌ Error: README.md not found at ${readmePath}`); + process.exit(1); + } + + const todos = parseReadmeTodos(readmePath); + console.log(`📝 Found ${todos.length} TODO items\n`); + console.log('='.repeat(60)); + console.log(''); + + // Create issues + console.log('🚀 Creating GitHub issues...\n'); + + let successCount = 0; + let failCount = 0; + + for (let i = 0; i < todos.length; i++) { + const todo = todos[i]; + const success = createIssue(todo, i + 1, todos.length, GITHUB_TOKEN); + + if (success) { + successCount++; + } else { + failCount++; + } + + // Small delay to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_DELAY_MS)); + } + + // Summary + console.log(''); + console.log('='.repeat(60)); + console.log(''); + console.log(`✅ Created ${successCount} issues successfully`); + if (failCount > 0) { + console.log(`❌ Failed to create ${failCount} issues`); + } + console.log(''); + console.log('🎉 Done!'); +} + +// Run the script +main().catch(error => { + console.error('❌ Error:', error.message); + process.exit(1); +}); diff --git a/scripts/create-issues-from-todos.ts b/scripts/create-issues-from-todos.ts new file mode 100644 index 0000000..8657a0a --- /dev/null +++ b/scripts/create-issues-from-todos.ts @@ -0,0 +1,207 @@ +import { readFileSync } from 'fs'; +import { resolve } from 'path'; + +interface TodoItem { + title: string; + body: string; + labels: string[]; + category: string; +} + +/** + * Parse README.md and extract TODO items with their context + */ +function parseReadmeTodos(readmePath: string): TodoItem[] { + const content = readFileSync(readmePath, 'utf-8'); + const lines = content.split('\n'); + + const todos: TodoItem[] = []; + let currentCategory = ''; + let currentSection = ''; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // Detect main section headers (### level) + if (line.startsWith('### ')) { + currentSection = line.replace('### ', '').trim(); + currentCategory = ''; + i++; + continue; + } + + // Detect category (bold items that are TODOs) + if (line.match(/^- \[ \] \*\*/)) { + // This is a category-level TODO + const titleMatch = line.match(/\*\*(.+?)\*\*/); + if (titleMatch) { + currentCategory = titleMatch[1]; + + // Collect description lines (indented with spaces or dashes) + const description: string[] = []; + let j = i + 1; + + while (j < lines.length && (lines[j].startsWith(' -') || lines[j].trim() === '')) { + if (lines[j].trim() !== '') { + description.push(lines[j].trim().replace(/^- /, '')); + } + j++; + } + + // Determine labels based on section + const labels = getLabelsForSection(currentSection); + + todos.push({ + title: currentCategory, + body: description.join('\n'), + labels, + category: currentSection + }); + + i = j; + continue; + } + } + + // Handle sub-items (nested TODOs with indentation) + if (line.match(/^ - \[ \] /)) { + const subItemTitle = line.replace(/^ - \[ \] /, '').trim(); + + if (currentCategory) { + // This is a sub-item of a category, we'll include it in the body + // Already handled in the category parsing above + } + } + + i++; + } + + return todos; +} + +/** + * Get appropriate labels based on the section + */ +function getLabelsForSection(section: string): string[] { + const labelMap: Record = { + '🎨 UI/UX 优化': ['enhancement', 'ui/ux', 'design'], + '🎨 UI/UX Optimization': ['enhancement', 'ui/ux', 'design'], + '🔧 功能增强': ['enhancement', 'feature'], + '🔧 Feature Enhancements': ['enhancement', 'feature'], + '🏗️ 代码结构优化': ['refactor', 'code-quality'], + '🏗️ Code Structure Optimization': ['refactor', 'code-quality'], + '🧪 测试与质量保证': ['testing', 'quality'], + '🧪 Testing and Quality Assurance': ['testing', 'quality'], + '📚 文档完善': ['documentation'], + '📚 Documentation Completion': ['documentation'], + '🌐 国际化': ['i18n', 'enhancement'], + '🌐 Internationalization': ['i18n', 'enhancement'], + '⚡ 性能优化': ['performance', 'enhancement'], + '⚡ Performance Optimization': ['performance', 'enhancement'], + '🐛 已知问题修复': ['bug', 'fix'], + '🐛 Known Issue Fixes': ['bug', 'fix'], + }; + + return labelMap[section] || ['enhancement']; +} + +/** + * Generate GitHub CLI commands to create issues + */ +function generateGhCommands(todos: TodoItem[]): string { + const commands: string[] = []; + + commands.push('#!/bin/bash'); + commands.push('# Auto-generated script to create GitHub issues from README todos'); + commands.push('# Run this script with: bash create-issues.sh'); + commands.push(''); + + todos.forEach((todo, index) => { + const title = todo.title.replace(/'/g, "'\\''"); + const body = todo.body.replace(/'/g, "'\\''"); + const labels = todo.labels.join(','); + const categoryPrefix = todo.category ? `[${todo.category}] ` : ''; + + commands.push(`echo "Creating issue ${index + 1}/${todos.length}: ${todo.title}"`); + commands.push(`gh issue create \\`); + commands.push(` --title '${categoryPrefix}${title}' \\`); + commands.push(` --body '${body}' \\`); + commands.push(` --label '${labels}'`); + commands.push(''); + }); + + return commands.join('\n'); +} + +/** + * Generate a JSON file with all todos for other tools to consume + */ +function generateJson(todos: TodoItem[]): string { + return JSON.stringify(todos, null, 2); +} + +/** + * Generate a markdown report of all todos + */ +function generateMarkdownReport(todos: TodoItem[]): string { + const lines: string[] = []; + + lines.push('# TODO Items from README'); + lines.push(''); + lines.push(`Total TODO items: ${todos.length}`); + lines.push(''); + + // Group by category + const byCategory = todos.reduce((acc, todo) => { + if (!acc[todo.category]) { + acc[todo.category] = []; + } + acc[todo.category].push(todo); + return acc; + }, {} as Record); + + Object.entries(byCategory).forEach(([category, items]) => { + lines.push(`## ${category}`); + lines.push(''); + items.forEach((item, index) => { + lines.push(`### ${index + 1}. ${item.title}`); + lines.push(''); + if (item.body) { + lines.push(item.body); + lines.push(''); + } + lines.push(`**Labels:** ${item.labels.join(', ')}`); + lines.push(''); + }); + }); + + return lines.join('\n'); +} + +// Main execution +function main() { + const readmePath = resolve(process.cwd(), 'README.md'); + console.error(`Parsing todos from: ${readmePath}`); + + const todos = parseReadmeTodos(readmePath); + console.error(`Found ${todos.length} TODO items`); + + // Output results + const output = process.argv[2] || 'markdown'; + + switch (output) { + case 'json': + console.log(generateJson(todos)); + break; + case 'gh': + console.log(generateGhCommands(todos)); + break; + case 'markdown': + default: + console.log(generateMarkdownReport(todos)); + break; + } +} + +main(); diff --git a/scripts/create-issues.cjs b/scripts/create-issues.cjs new file mode 100644 index 0000000..f8780cd --- /dev/null +++ b/scripts/create-issues.cjs @@ -0,0 +1,207 @@ +#!/usr/bin/env node + +/** + * Create GitHub issues from README todos + * + * This script reads the README.md file, extracts TODO items, + * and creates GitHub issues using either GitHub CLI or Octokit. + * + * Usage: + * node scripts/create-issues.js # Create issues using gh CLI + * node scripts/create-issues.js --use-api # Create issues using Octokit API + * node scripts/create-issues.js --dry-run # Show what would be created + */ + +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +class TodoParser { + constructor(readmePath) { + this.readmePath = readmePath; + this.todos = []; + } + + parse() { + const content = fs.readFileSync(this.readmePath, 'utf-8'); + const lines = content.split('\n'); + + let currentSection = ''; + let currentCategory = ''; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + // Detect main section headers (### level) + if (line.startsWith('### ')) { + currentSection = line.replace('### ', '').trim(); + currentCategory = ''; + i++; + continue; + } + + // Detect category (bold items that are TODOs) + if (line.match(/^- \[ \] \*\*/)) { + const titleMatch = line.match(/\*\*(.+?)\*\*/); + if (titleMatch) { + currentCategory = titleMatch[1]; + + // Collect description lines + const description = []; + let j = i + 1; + + while (j < lines.length && (lines[j].startsWith(' -') || lines[j].trim() === '')) { + if (lines[j].trim() !== '') { + description.push(lines[j].trim().replace(/^- /, '')); + } + j++; + } + + const labels = this.getLabelsForSection(currentSection); + + this.todos.push({ + title: currentCategory, + body: description.join('\n'), + labels, + category: currentSection + }); + + i = j; + continue; + } + } + + i++; + } + + return this.todos; + } + + getLabelsForSection(section) { + const labelMap = { + '🎨 UI/UX 优化': ['enhancement', 'ui/ux', 'design'], + '🎨 UI/UX Optimization': ['enhancement', 'ui/ux', 'design'], + '🔧 功能增强': ['enhancement', 'feature'], + '🔧 Feature Enhancements': ['enhancement', 'feature'], + '🏗️ 代码结构优化': ['refactor', 'code-quality'], + '🏗️ Code Structure Optimization': ['refactor', 'code-quality'], + '🧪 测试与质量保证': ['testing', 'quality'], + '🧪 Testing and Quality Assurance': ['testing', 'quality'], + '📚 文档完善': ['documentation'], + '📚 Documentation Completion': ['documentation'], + '🌐 国际化': ['i18n', 'enhancement'], + '🌐 Internationalization': ['i18n', 'enhancement'], + '⚡ 性能优化': ['performance', 'enhancement'], + '⚡ Performance Optimization': ['performance', 'enhancement'], + '🐛 已知问题修复': ['bug', 'fix'], + '🐛 Known Issue Fixes': ['bug', 'fix'], + }; + + return labelMap[section] || ['enhancement']; + } +} + +class IssueCreator { + constructor(todos, options = {}) { + this.todos = todos; + this.dryRun = options.dryRun || false; + this.useApi = options.useApi || false; + } + + async createIssues() { + console.log(`\n📝 Found ${this.todos.length} TODO items to convert to issues\n`); + + if (this.dryRun) { + console.log('🔍 DRY RUN MODE - No issues will be created\n'); + this.todos.forEach((todo, index) => { + console.log(`${index + 1}. [${todo.category}] ${todo.title}`); + console.log(` Labels: ${todo.labels.join(', ')}`); + console.log(` Body: ${todo.body.substring(0, 50)}...`); + console.log(''); + }); + return; + } + + if (this.useApi) { + await this.createIssuesViaApi(); + } else { + await this.createIssuesViaGhCli(); + } + } + + async createIssuesViaGhCli() { + console.log('🚀 Creating issues using GitHub CLI (gh)...\n'); + + // Check if gh is authenticated + try { + execSync('gh auth status', { stdio: 'ignore' }); + } catch (error) { + console.error('❌ Error: GitHub CLI is not authenticated.'); + console.error(' Please run: gh auth login'); + process.exit(1); + } + + let successCount = 0; + let failCount = 0; + + for (let i = 0; i < this.todos.length; i++) { + const todo = this.todos[i]; + const issueNumber = i + 1; + const title = `[${todo.category}] ${todo.title}`; + const labels = todo.labels.join(','); + + console.log(`Creating issue ${issueNumber}/${this.todos.length}: ${todo.title}`); + + try { + const command = `gh issue create --title "${title.replace(/"/g, '\\"')}" --body "${todo.body.replace(/"/g, '\\"')}" --label "${labels}"`; + execSync(command, { stdio: 'inherit' }); + successCount++; + } catch (error) { + console.error(`❌ Failed to create issue: ${todo.title}`); + failCount++; + } + + // Small delay to avoid rate limiting + await new Promise(resolve => setTimeout(resolve, 1000)); + } + + console.log(`\n✅ Created ${successCount} issues`); + if (failCount > 0) { + console.log(`❌ Failed to create ${failCount} issues`); + } + } + + async createIssuesViaApi() { + console.log('🚀 Creating issues using Octokit API...\n'); + console.error('❌ Error: Octokit is not installed.'); + console.error(' Please install it with: npm install @octokit/rest'); + console.error(' Or use GitHub CLI instead: node scripts/create-issues.js'); + process.exit(1); + } +} + +async function main() { + const args = process.argv.slice(2); + const dryRun = args.includes('--dry-run'); + const useApi = args.includes('--use-api'); + + const readmePath = path.join(process.cwd(), 'README.md'); + + if (!fs.existsSync(readmePath)) { + console.error(`❌ Error: README.md not found at ${readmePath}`); + process.exit(1); + } + + console.log('📖 Reading README.md...'); + const parser = new TodoParser(readmePath); + const todos = parser.parse(); + + const creator = new IssueCreator(todos, { dryRun, useApi }); + await creator.createIssues(); +} + +main().catch(error => { + console.error('❌ Error:', error.message); + process.exit(1); +}); diff --git a/scripts/create-issues.sh b/scripts/create-issues.sh new file mode 100755 index 0000000..7dde5cb --- /dev/null +++ b/scripts/create-issues.sh @@ -0,0 +1,205 @@ +#!/bin/bash +# Auto-generated script to create GitHub issues from README todos +# Run this script with: bash create-issues.sh + +echo "Creating issue 1/25: 优化配置面板的视觉效果" +gh issue create \ + --title '[🎨 UI/UX 优化] 优化配置面板的视觉效果' \ + --body '改进卡片阴影和间距 +添加更流畅的过渡动画 +优化颜色对比度,提升可读性' \ + --label 'enhancement,ui/ux,design' + +echo "Creating issue 2/25: 统一整体 UI 设计语言" +gh issue create \ + --title '[🎨 UI/UX 优化] 统一整体 UI 设计语言' \ + --body '统一按钮样式(大小、圆角、阴影) +统一字体大小和行高 +制定完整的设计系统文档' \ + --label 'enhancement,ui/ux,design' + +echo "Creating issue 3/25: 改进计时器显示效果" +gh issue create \ + --title '[🎨 UI/UX 优化] 改进计时器显示效果' \ + --body '添加更多的动画效果(如数字滚动) +优化时间变色的渐变效果 +添加进度条指示器' \ + --label 'enhancement,ui/ux,design' + +echo "Creating issue 4/25: 响应式设计优化" +gh issue create \ + --title '[🎨 UI/UX 优化] 响应式设计优化' \ + --body '优化平板设备显示 +优化手机端显示 +添加触摸手势支持' \ + --label 'enhancement,ui/ux,design' + +echo "Creating issue 5/25: 使用专业的 JSON 编辑器" +gh issue create \ + --title '[🔧 功能增强] 使用专业的 JSON 编辑器' \ + --body '集成 Monaco Editor 或 CodeMirror +添加语法高亮 +添加自动补全和错误提示' \ + --label 'enhancement,feature' + +echo "Creating issue 6/25: 配置管理功能" +gh issue create \ + --title '[🔧 功能增强] 配置管理功能' \ + --body '[ ] 导出配置为 JSON 文件 +[ ] 从 JSON 文件导入配置 +[ ] 保存多个配置预设 +[ ] 配置模板库(不同辩论赛制)' \ + --label 'enhancement,feature' + +echo "Creating issue 7/25: 铃声自定义" +gh issue create \ + --title '[🔧 功能增强] 铃声自定义' \ + --body '[ ] 上传自定义铃声文件 +[ ] 调整铃声音量 +[ ] 测试铃声功能' \ + --label 'enhancement,feature' + +echo "Creating issue 8/25: 全屏模式" +gh issue create \ + --title '[🔧 功能增强] 全屏模式' \ + --body '[ ] 添加全屏切换按钮 +[ ] 优化全屏模式下的布局 +[ ] 支持 ESC 键退出全屏' \ + --label 'enhancement,feature' + +echo "Creating issue 9/25: 主题切换" +gh issue create \ + --title '[🔧 功能增强] 主题切换' \ + --body '[ ] 明暗主题切换 +[ ] 多种配色方案 +[ ] 自定义主题编辑器' \ + --label 'enhancement,feature' + +echo "Creating issue 10/25: 辅助功能" +gh issue create \ + --title '[🔧 功能增强] 辅助功能' \ + --body '[ ] 添加 ARIA 标签支持 +[ ] 键盘导航优化 +[ ] 屏幕阅读器支持' \ + --label 'enhancement,feature' + +echo "Creating issue 11/25: 组件拆分" +gh issue create \ + --title '[🏗️ 代码结构优化] 组件拆分' \ + --body '[ ] 将 `debateTimer.vue` (400+ 行) 拆分为更小的子组件 +[ ] 提取 `StageDisplay` 组件 +[ ] 提取 `TimerDisplay` 组件 +[ ] 提取 `NavigationControls` 组件' \ + --label 'refactor,code-quality' + +echo "Creating issue 12/25: 逻辑提取" +gh issue create \ + --title '[🏗️ 代码结构优化] 逻辑提取' \ + --body '[ ] 创建 `useTimer` composable 管理计时逻辑 +[ ] 创建 `useBell` composable 管理铃声逻辑 +[ ] 创建 `useKeyboard` composable 管理键盘事件' \ + --label 'refactor,code-quality' + +echo "Creating issue 13/25: 工具函数提取" +gh issue create \ + --title '[🏗️ 代码结构优化] 工具函数提取' \ + --body '[ ] 将铃声生成逻辑提取到 `utils/audio.ts` +[ ] 将时间格式化逻辑提取到 `utils/time.ts`' \ + --label 'refactor,code-quality' + +echo "Creating issue 14/25: 添加代码注释" +gh issue create \ + --title '[🏗️ 代码结构优化] 添加代码注释' \ + --body '[ ] 为所有公共函数添加 JSDoc 注释 +[ ] 为复杂逻辑添加行内注释 +[ ] 创建组件使用示例文档' \ + --label 'refactor,code-quality' + +echo "Creating issue 15/25: 单元测试" +gh issue create \ + --title '[🧪 测试与质量保证] 单元测试' \ + --body '[ ] 为工具函数编写测试 +[ ] 为 composables 编写测试 +[ ] 使用 Vitest 搭建测试环境' \ + --label 'testing,quality' + +echo "Creating issue 16/25: 组件测试" +gh issue create \ + --title '[🧪 测试与质量保证] 组件测试' \ + --body '[ ] 为关键组件编写测试 +[ ] 使用 Vue Test Utils +[ ] 测试键盘快捷键功能' \ + --label 'testing,quality' + +echo "Creating issue 17/25: E2E 测试" +gh issue create \ + --title '[🧪 测试与质量保证] E2E 测试' \ + --body '[ ] 使用 Playwright 或 Cypress +[ ] 测试完整的计时流程 +[ ] 测试配置保存与加载' \ + --label 'testing,quality' + +echo "Creating issue 18/25: 组件文档" +gh issue create \ + --title '[📚 文档完善] 组件文档' \ + --body '[ ] 为每个组件编写使用说明 +[ ] 添加 Props、Events、Slots 文档 +[ ] 创建组件 API 参考' \ + --label 'documentation' + +echo "Creating issue 19/25: 用户手册" +gh issue create \ + --title '[📚 文档完善] 用户手册' \ + --body '[ ] 编写详细的用户操作指南 +[ ] 添加常见问题解答 (FAQ) +[ ] 制作使用视频教程' \ + --label 'documentation' + +echo "Creating issue 20/25: 开发者指南" +gh issue create \ + --title '[📚 文档完善] 开发者指南' \ + --body '[ ] 编写贡献指南 (CONTRIBUTING.md) +[ ] 代码规范说明 +[ ] Git 提交规范' \ + --label 'documentation' + +echo "Creating issue 21/25: 多语言支持" +gh issue create \ + --title '[🌐 国际化] 多语言支持' \ + --body '[ ] 集成 vue-i18n +[ ] 提取所有中文文本 +[ ] 添加英文翻译 +[ ] 支持语言切换' \ + --label 'i18n,enhancement' + +echo "Creating issue 22/25: 代码优化" +gh issue create \ + --title '[⚡ 性能优化] 代码优化' \ + --body '[ ] 使用 `computed` 优化重复计算 +[ ] 使用 `v-memo` 优化列表渲染 +[ ] 懒加载大型组件' \ + --label 'performance,enhancement' + +echo "Creating issue 23/25: 构建优化" +gh issue create \ + --title '[⚡ 性能优化] 构建优化' \ + --body '[ ] 配置代码分割 +[ ] 优化资源加载 +[ ] 添加 PWA 支持' \ + --label 'performance,enhancement' + +echo "Creating issue 24/25: TimerConfigPanel 验证改进" +gh issue create \ + --title '[🐛 已知问题修复] TimerConfigPanel 验证改进' \ + --body '[ ] 在保存失败时显示具体错误信息 +[ ] 改进表单验证提示 +[ ] 添加数据格式校验' \ + --label 'bug,fix' + +echo "Creating issue 25/25: 计时精度优化" +gh issue create \ + --title '[🐛 已知问题修复] 计时精度优化' \ + --body '[ ] 处理浏览器后台标签页计时偏差 +[ ] 使用 Web Worker 提高精度' \ + --label 'bug,fix' +