Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ inputs:
description: Use structured tag extraction with @assignee, #module, and key=value
default: 'false'

warn-overdue:
required: false
description: Emit warnings for TODOs with due dates in the past
default: 'false'

issue-title-template:
required: false
description: Optional path to custom issue title template
Expand Down
8 changes: 7 additions & 1 deletion src/ActionMain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { extractTodosFromDir } from './parser/extractTodosFromDir';
import { extractTodosWithStructuredTagsFromDir } from './parser/extractTodosWithStructuredTagsFromDir'; // 👈 novo
import { TodoItem } from './parser/types';
import { getExistingIssueTitles, createIssueIfNeeded } from './core/issueManager';
import { generateMarkdownReport } from './core/report';
import { generateMarkdownReport, warnOverdueTodos } from './core/report';
import { limitTodos, todoKey } from './core/todoUtils';
import { generateChangelogFromTodos } from './core/changelog';

Expand All @@ -30,6 +30,8 @@ async function run(): Promise<void> {

const useStructured = core.getInput('structured') === 'true';

const warnOverdue = core.getInput('warn-overdue') === 'true';

const todos: TodoItem[] = useStructured
? extractTodosWithStructuredTagsFromDir(workspace)
: extractTodosFromDir(workspace);
Expand All @@ -48,6 +50,10 @@ async function run(): Promise<void> {
return true;
});

if (warnOverdue) {
warnOverdueTodos(uniqueTodos);
}

const issueLimit = parseInt(core.getInput('limit') || '5', 10);
const todosToCreate = limitTodos(uniqueTodos, issueLimit);

Expand Down
16 changes: 16 additions & 0 deletions src/core/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'fs';
import path from 'path';
import { TodoItem } from '../parser/types';
import { todoKey } from './todoUtils';
import * as core from '@actions/core';

const PRIORITY_ORDER = ['high', 'medium', 'low'];

Expand All @@ -13,6 +14,21 @@ function getDue(todo: TodoItem): string {
return todo.metadata?.due ?? '';
}

export function findOverdueTodos(todos: TodoItem[]): TodoItem[] {
const today = new Date().toISOString().split('T')[0];
return todos.filter(t => {
const due = t.metadata?.due;
return typeof due === 'string' && due < today;
});
}

export function warnOverdueTodos(todos: TodoItem[]): void {
const overdue = findOverdueTodos(todos);
for (const todo of overdue) {
core.warning(`\u23F0 Overdue TODO (${todo.metadata!.due}): ${todo.text} (${todo.file}:${todo.line})`);
}
}

function sortTodos(a: TodoItem, b: TodoItem): number {
const pa = getPriority(a);
const pb = getPriority(b);
Expand Down
25 changes: 25 additions & 0 deletions tests/overdueDetection.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, it, expect } from 'vitest';
import { findOverdueTodos } from '../src/core/report';
import { TodoItem } from '../src/parser/types';

function format(date: Date) {
return date.toISOString().split('T')[0];
}

describe('findOverdueTodos', () => {
it('detects todos with past due dates', () => {
const yesterday = format(new Date(Date.now() - 86400000));
const today = format(new Date());
const tomorrow = format(new Date(Date.now() + 86400000));
const todos: TodoItem[] = [
{ tag: 'TODO', text: 'past', file: 'a.ts', line: 1, metadata: { due: yesterday } },
{ tag: 'TODO', text: 'today', file: 'b.ts', line: 2, metadata: { due: today } },
{ tag: 'TODO', text: 'future', file: 'c.ts', line: 3, metadata: { due: tomorrow } },
{ tag: 'TODO', text: 'none', file: 'd.ts', line: 4 }
];

const result = findOverdueTodos(todos);
expect(result.length).toBe(1);
expect(result[0].text).toBe('past');
});
});