|
1 | | -// src/ActionMain.ts |
2 | | -import * as core from '@actions/core'; |
3 | | -import * as github from '@actions/github'; |
4 | | -import path from 'path'; |
5 | | -import fs from 'fs'; |
6 | | -import { extractTodosFromDir } from './parser/extractTodosFromDir'; |
7 | | -import { TodoItem } from './parser/types'; |
8 | | -import { getExistingIssueTitles, createIssueIfNeeded } from './core/issueManager'; |
9 | | -import { generateMarkdownReport } from './core/report'; |
10 | | -import { limitTodos, todoKey } from './core/todoUtils'; |
11 | | -import { generateChangelogFromTodos } from './core/changelog'; |
12 | | - |
13 | | -async function run(): Promise<void> { |
14 | | - try { |
15 | | - const token = core.getInput('repo-token', { required: true }); |
16 | | - const generateReport = core.getInput('report') === 'true'; |
17 | | - const titleTemplatePath = core.getInput('issue-title-template'); |
18 | | - const bodyTemplatePath = core.getInput('issue-body-template'); |
19 | | - const workspace = process.env.GITHUB_WORKSPACE || '.'; |
20 | | - |
21 | | - const todos: TodoItem[] = extractTodosFromDir(workspace); |
22 | | - const octokit = github.getOctokit(token); |
23 | | - const { owner, repo } = github.context.repo; |
24 | | - |
25 | | - core.info(`🔍 Found ${todos.length} TODOs`); |
26 | | - |
27 | | - const existingTitles = await getExistingIssueTitles(octokit, owner, repo); |
28 | | - |
29 | | - const seenKeys = new Set<string>(); |
30 | | - const uniqueTodos = todos.filter(todo => { |
31 | | - const key = todoKey(todo); |
32 | | - if (seenKeys.has(key)) return false; |
33 | | - seenKeys.add(key); |
34 | | - return true; |
35 | | - }); |
36 | | - |
37 | | - const todosToCreate = limitTodos(uniqueTodos, 5); |
| 1 | +// src/core/changelog.ts |
| 2 | +import { TodoItem } from '../parser/types'; |
| 3 | +import { classifyTodoText } from './classifier'; |
| 4 | + |
| 5 | +function formatGroupHeader(tag: string, semantic: string, metadataKey?: string, metadataValue?: string): string { |
| 6 | + const parts = [tag]; |
| 7 | + if (semantic) parts.push(semantic); |
| 8 | + if (metadataKey && metadataValue) parts.push(`${metadataKey}:${metadataValue}`); |
| 9 | + return `## ${parts.join(' · ')}`; |
| 10 | +} |
38 | 11 |
|
39 | | - for (const todo of todosToCreate) { |
40 | | - await createIssueIfNeeded( |
41 | | - octokit, |
42 | | - owner, |
43 | | - repo, |
44 | | - todo, |
45 | | - existingTitles, |
46 | | - titleTemplatePath, |
47 | | - bodyTemplatePath |
48 | | - ); |
| 12 | +export function generateChangelogFromTodos(todos: TodoItem[]): string { |
| 13 | + type GroupKey = string; |
| 14 | + const groups: Record<GroupKey, TodoItem[]> = {}; |
| 15 | + |
| 16 | + for (const todo of todos) { |
| 17 | + const semantics = classifyTodoText(todo.text); |
| 18 | + const metadataEntries = Object.entries(todo.metadata || {}) || [['', '']]; |
| 19 | + const tag = todo.tag.toUpperCase(); |
| 20 | + |
| 21 | + for (const semantic of semantics.length ? semantics : ['']) { |
| 22 | + for (const [metaKey, metaValue] of metadataEntries.length ? metadataEntries : [['', '']]) { |
| 23 | + const key = JSON.stringify({ tag, semantic, metaKey, metaValue }); |
| 24 | + if (!groups[key]) groups[key] = []; |
| 25 | + groups[key].push(todo); |
| 26 | + } |
49 | 27 | } |
| 28 | + } |
| 29 | + |
| 30 | + const output: string[] = ['# 📝 Changelog (from TODOs)', '']; |
50 | 31 |
|
51 | | - if (generateReport) { |
52 | | - generateMarkdownReport(todos); |
53 | | - core.info('📝 Generated TODO_REPORT.md'); |
| 32 | + for (const key of Object.keys(groups)) { |
| 33 | + const { tag, semantic, metaKey, metaValue } = JSON.parse(key); |
| 34 | + output.push(formatGroupHeader(tag, semantic, metaKey, metaValue)); |
54 | 35 |
|
55 | | - const changelog = generateChangelogFromTodos(todos); |
56 | | - fs.writeFileSync('CHANGELOG.md', changelog, 'utf8'); |
57 | | - core.info('📦 Generated CHANGELOG.md'); |
| 36 | + for (const todo of groups[key]) { |
| 37 | + output.push(`- ${todo.text} (\`${todo.file}:${todo.line}\`)`); |
58 | 38 | } |
59 | 39 |
|
60 | | - } catch (error: any) { |
61 | | - core.setFailed(`Action failed: ${error.message}`); |
| 40 | + output.push(''); |
62 | 41 | } |
63 | | -} |
64 | | - |
65 | | -run(); |
66 | 42 |
|
| 43 | + return output.join('\n'); |
| 44 | +} |
0 commit comments