Skip to content

Commit 7b4cd92

Browse files
authored
Merge pull request #24 from LonoxX/develop
fix: issue and PR deletion
2 parents d5dbcc2 + 448528b commit 7b4cd92

6 files changed

Lines changed: 123 additions & 58 deletions

File tree

src/file-manager.ts

Lines changed: 62 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,25 @@ export class FileManager {
269269
);
270270

271271
for (const file of files) {
272-
const fileNumberString = extractNumberFromFilename(
273-
file.name,
274-
repo.issueNoteTemplate || "Issue - {number}"
275-
);
272+
// Try to get number from frontmatter first (most reliable)
273+
const properties = extractProperties(this.app, file);
274+
let fileNumberString: string | null = null;
275+
276+
if (properties.number) {
277+
fileNumberString = properties.number.toString();
278+
} else {
279+
// Fallback: try to extract from filename
280+
fileNumberString = extractNumberFromFilename(
281+
file.name,
282+
repo.issueNoteTemplate || "Issue - {number}"
283+
);
284+
}
276285

277286
if (!fileNumberString) {
278-
// If we can't extract a number, skip this file
287+
// If we can't determine the issue number, log a warning but skip
288+
this.noticeManager.debug(
289+
`Could not determine issue number for file: ${file.name}. Consider adding a 'number' property to the frontmatter.`
290+
);
279291
continue;
280292
}
281293

@@ -289,17 +301,22 @@ export class FileManager {
289301
let deleteReason = "";
290302

291303
if (correspondingIssue) {
292-
if (correspondingIssue.state === "closed") {
293-
shouldDelete = true;
294-
deleteReason = `Deleted closed issue ${fileNumberString} from ${repo.repository}`;
304+
if (correspondingIssue.state === "closed" && correspondingIssue.closed_at) {
305+
// Check if issue has been closed longer than the configured days
306+
const closedDate = new Date(correspondingIssue.closed_at);
307+
const cutoffDate = new Date();
308+
cutoffDate.setDate(cutoffDate.getDate() - this.settings.cleanupClosedIssuesDays);
309+
310+
if (closedDate < cutoffDate) {
311+
shouldDelete = true;
312+
const daysClosed = Math.floor((Date.now() - closedDate.getTime()) / (1000 * 60 * 60 * 24));
313+
deleteReason = `Deleted issue ${fileNumberString} from ${repo.repository} (closed ${daysClosed} days ago, threshold: ${this.settings.cleanupClosedIssuesDays} days)`;
314+
}
295315
}
296316
} else {
297317
shouldDelete = true;
298-
deleteReason = `Deleted issue ${fileNumberString} from ${repo.repository} as it's no longer tracked (closed > 30 days or deleted)`;
299-
}
300-
301-
if (shouldDelete) {
302-
const properties = extractProperties(this.app, file);
318+
deleteReason = `Deleted issue ${fileNumberString} from ${repo.repository} as it's no longer tracked (closed > ${this.settings.cleanupClosedIssuesDays} days or deleted)`;
319+
} if (shouldDelete) {
303320
const allowDelete = properties.allowDelete
304321
? String(properties.allowDelete)
305322
.toLowerCase()
@@ -333,13 +350,25 @@ export class FileManager {
333350
);
334351

335352
for (const file of files) {
336-
const fileNumberString = extractNumberFromFilename(
337-
file.name,
338-
repo.pullRequestNoteTemplate || "Pull Request - {number}"
339-
);
353+
// Try to get number from frontmatter first (most reliable)
354+
const properties = extractProperties(this.app, file);
355+
let fileNumberString: string | null = null;
356+
357+
if (properties.number) {
358+
fileNumberString = properties.number.toString();
359+
} else {
360+
// Fallback: try to extract from filename
361+
fileNumberString = extractNumberFromFilename(
362+
file.name,
363+
repo.pullRequestNoteTemplate || "Pull Request - {number}"
364+
);
365+
}
340366

341367
if (!fileNumberString) {
342-
// If we can't extract a number, skip this file
368+
// If we can't determine the PR number, log a warning but skip
369+
this.noticeManager.debug(
370+
`Could not determine PR number for file: ${file.name}. Consider adding a 'number' property to the frontmatter.`
371+
);
343372
continue;
344373
}
345374

@@ -352,17 +381,22 @@ export class FileManager {
352381
let deleteReason = "";
353382

354383
if (correspondingPR) {
355-
if (correspondingPR.state === "closed") {
356-
shouldDelete = true;
357-
deleteReason = `Deleted closed pull request ${fileNumberString} from ${repo.repository}`;
384+
if (correspondingPR.state === "closed" && correspondingPR.closed_at) {
385+
// Check if PR has been closed longer than the configured days
386+
const closedDate = new Date(correspondingPR.closed_at);
387+
const cutoffDate = new Date();
388+
cutoffDate.setDate(cutoffDate.getDate() - this.settings.cleanupClosedIssuesDays);
389+
390+
if (closedDate < cutoffDate) {
391+
shouldDelete = true;
392+
const daysClosed = Math.floor((Date.now() - closedDate.getTime()) / (1000 * 60 * 60 * 24));
393+
deleteReason = `Deleted pull request ${fileNumberString} from ${repo.repository} (closed ${daysClosed} days ago, threshold: ${this.settings.cleanupClosedIssuesDays} days)`;
394+
}
358395
}
359396
} else {
360397
shouldDelete = true;
361-
deleteReason = `Deleted pull request ${fileNumberString} from ${repo.repository} as it's no longer tracked (closed > 30 days or deleted)`;
362-
}
363-
364-
if (shouldDelete) {
365-
const properties = extractProperties(this.app, file);
398+
deleteReason = `Deleted pull request ${fileNumberString} from ${repo.repository} as it's no longer tracked (closed > ${this.settings.cleanupClosedIssuesDays} days or deleted)`;
399+
} if (shouldDelete) {
366400
const allowDelete = properties.allowDelete
367401
? String(properties.allowDelete)
368402
.toLowerCase()
@@ -660,6 +694,7 @@ export class FileManager {
660694
// Fallback to default template
661695
return `---
662696
title: "${escapeYamlString(issue.title)}"
697+
number: ${issue.number}
663698
status: "${issue.state}"
664699
created: "${
665700
this.settings.dateFormat !== ""
@@ -721,6 +756,7 @@ ${this.formatComments(comments, this.settings.escapeMode)}
721756
// Fallback to default template
722757
return `---
723758
title: "${escapeYamlString(pr.title)}"
759+
number: ${pr.number}
724760
status: "${pr.state}"
725761
created: "${
726762
this.settings.dateFormat !== ""

src/util/templateUtils.ts

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -452,38 +452,62 @@ export function extractNumberFromFilename(filename: string, template: string): s
452452
// Remove .md extension if present
453453
const baseFilename = filename.replace(/\.md$/, '');
454454

455-
// Create a regex pattern from the template
456-
// Replace {number} with a capture group and escape other special regex characters
457-
let pattern = escapeRegExp(template);
458-
459-
// Replace template variables with regex patterns
460-
pattern = pattern.replace(/\\?\{number\}/g, '(\\d+)');
461-
pattern = pattern.replace(/\\?\{title\}/g, '.*?');
462-
pattern = pattern.replace(/\\?\{title_yaml\}/g, '.*?');
463-
pattern = pattern.replace(/\\?\{status\}/g, '\\w+');
464-
pattern = pattern.replace(/\\?\{author\}/g, '[^\\s]+');
465-
pattern = pattern.replace(/\\?\{assignee\}/g, '[^\\s]*');
466-
pattern = pattern.replace(/\\?\{repository\}/g, '[^\\s]+');
467-
pattern = pattern.replace(/\\?\{owner\}/g, '[^\\s]+');
468-
pattern = pattern.replace(/\\?\{repoName\}/g, '[^\\s]+');
469-
pattern = pattern.replace(/\\?\{type\}/g, '\\w+');
470-
pattern = pattern.replace(/\\?\{state\}/g, '\\w+');
471-
pattern = pattern.replace(/\\?\{milestone\}/g, '[^\\s]*');
472-
473-
// Handle date patterns
474-
pattern = pattern.replace(/\\?\{created(?::[^}]+)?\}/g, '[\\d\\-T:Z\\s]+');
475-
pattern = pattern.replace(/\\?\{updated(?::[^}]+)?\}/g, '[\\d\\-T:Z\\s]+');
476-
pattern = pattern.replace(/\\?\{closed(?::[^}]+)?\}/g, '[\\d\\-T:Z\\s]*');
477-
478-
// Handle array patterns (labels, assignees)
479-
pattern = pattern.replace(/\\?\{labels(?::[^}]+)?\}/g, '.*?');
480-
pattern = pattern.replace(/\\?\{assignees(?::[^}]+)?\}/g, '.*?');
481-
482-
// Handle conditional blocks {condition:content}
483-
pattern = pattern.replace(/\\?\{\w+:.*?\}/g, '.*?');
484-
485-
// Handle remaining unmatched variables as generic matches
486-
pattern = pattern.replace(/\\?\{[^}]+\}/g, '.*?');
455+
// First, try a simple approach: if the template is just "{number}", extract any number
456+
if (template === "{number}") {
457+
const match = baseFilename.match(/^(\d+)$/);
458+
return match ? match[1] : null;
459+
}
460+
461+
// Create a regex pattern from the template by replacing variables BEFORE escaping
462+
let pattern = template;
463+
464+
// Replace template variables with regex patterns (before escaping special chars)
465+
// {number} is the only one we want to capture
466+
pattern = pattern.replace(/\{number\}/g, '<<<NUMBER>>>');
467+
468+
// Replace other variables with patterns that match their likely content
469+
// {title} and {title_yaml} can contain almost anything except file-system forbidden chars
470+
pattern = pattern.replace(/\{title\}/g, '<<<TITLE>>>');
471+
pattern = pattern.replace(/\{title_yaml\}/g, '<<<TITLE>>>');
472+
473+
// Simple word-based patterns
474+
pattern = pattern.replace(/\{status\}/g, '<<<WORD>>>');
475+
pattern = pattern.replace(/\{type\}/g, '<<<WORD>>>');
476+
pattern = pattern.replace(/\{state\}/g, '<<<WORD>>>');
477+
478+
// Username/repo patterns (no spaces)
479+
pattern = pattern.replace(/\{author\}/g, '<<<NOSPACE>>>');
480+
pattern = pattern.replace(/\{assignee\}/g, '<<<OPTIONAL_NOSPACE>>>');
481+
pattern = pattern.replace(/\{repository\}/g, '<<<NOSPACE>>>');
482+
pattern = pattern.replace(/\{owner\}/g, '<<<NOSPACE>>>');
483+
pattern = pattern.replace(/\{repoName\}/g, '<<<NOSPACE>>>');
484+
pattern = pattern.replace(/\{milestone\}/g, '<<<OPTIONAL_NOSPACE>>>');
485+
486+
// Date patterns
487+
pattern = pattern.replace(/\{created(?::[^}]+)?\}/g, '<<<DATE>>>');
488+
pattern = pattern.replace(/\{updated(?::[^}]+)?\}/g, '<<<DATE>>>');
489+
pattern = pattern.replace(/\{closed(?::[^}]+)?\}/g, '<<<OPTIONAL_DATE>>>');
490+
491+
// Array patterns (can be comma-separated, etc)
492+
pattern = pattern.replace(/\{labels(?::[^}]+)?\}/g, '<<<ANY>>>');
493+
pattern = pattern.replace(/\{assignees(?::[^}]+)?\}/g, '<<<ANY>>>');
494+
495+
// Handle conditional blocks and any remaining variables
496+
pattern = pattern.replace(/\{\w+:[^}]*\}/g, '<<<ANY>>>');
497+
pattern = pattern.replace(/\{[^}]+\}/g, '<<<ANY>>>');
498+
499+
// Now escape special regex characters in the remaining static parts
500+
pattern = escapeRegExp(pattern);
501+
502+
// Replace our placeholders with actual regex patterns
503+
pattern = pattern.replace(/<<<NUMBER>>>/g, '(\\d+)');
504+
pattern = pattern.replace(/<<<TITLE>>>/g, '(.+?)'); // More permissive for titles
505+
pattern = pattern.replace(/<<<WORD>>>/g, '[A-Za-z0-9_-]+');
506+
pattern = pattern.replace(/<<<NOSPACE>>>/g, '[A-Za-z0-9_-]+');
507+
pattern = pattern.replace(/<<<OPTIONAL_NOSPACE>>>/g, '[A-Za-z0-9_-]*');
508+
pattern = pattern.replace(/<<<DATE>>>/g, '[\\d\\-T:Z\\s]+');
509+
pattern = pattern.replace(/<<<OPTIONAL_DATE>>>/g, '[\\d\\-T:Z\\s]*');
510+
pattern = pattern.replace(/<<<ANY>>>/g, '.*?');
487511

488512
// Create the regex and try to match
489513
try {
@@ -495,6 +519,7 @@ export function extractNumberFromFilename(filename: string, template: string): s
495519
}
496520
} catch (error) {
497521
console.warn(`Failed to parse filename "${filename}" with template "${template}":`, error);
522+
console.warn(`Generated regex pattern: ${pattern}`);
498523
}
499524

500525
return null;

templates/default-issue-template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: "{title_yaml}"
3+
number: {number}
34
status: "{status}"
45
created: "{created}"
56
url: "{url}"

templates/default-pr-template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: "{title_yaml}"
3+
number: {number}
34
status: "{status}"
45
created: "{created}"
56
url: "{url}"

templates/detailed-template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: "{title_yaml}"
3+
number: {number}
34
status: "{status}"
45
type: "{type}"
56
repository: "{repository}"

templates/minimal-template.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
title: "{title_yaml}"
3+
number: {number}
34
status: "{status}"
45
created: "{created}"
56
url: "{url}"

0 commit comments

Comments
 (0)