fraim 2.0.173 → 2.0.174
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -21,6 +21,7 @@ function runCleanupArtifactsCommand(options) {
|
|
|
21
21
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
22
22
|
}
|
|
23
23
|
exports.cleanupArtifactsCommand = new commander_1.Command('cleanup-artifacts')
|
|
24
|
+
.alias('cleanup-artifact')
|
|
24
25
|
.description('Evaluate and optionally apply artifact retention cleanup for the current FRAIM workspace')
|
|
25
26
|
.option('--user <email>', 'Active user email used only for user-scoped cleanup categories')
|
|
26
27
|
.option('--workspace-root <path>', 'Workspace root containing fraim/config.json')
|
|
@@ -8,6 +8,7 @@ exports.resolveArtifactRetentionConfig = resolveArtifactRetentionConfig;
|
|
|
8
8
|
exports.runArtifactCleanup = runArtifactCleanup;
|
|
9
9
|
exports.planArtifactCleanup = planArtifactCleanup;
|
|
10
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const node_child_process_1 = require("node:child_process");
|
|
11
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
13
|
const project_fraim_paths_1 = require("../core/utils/project-fraim-paths");
|
|
13
14
|
exports.ARTIFACT_RETENTION_CATEGORIES = [
|
|
@@ -96,6 +97,41 @@ function fileMtime(absolutePath) {
|
|
|
96
97
|
return undefined;
|
|
97
98
|
}
|
|
98
99
|
}
|
|
100
|
+
function parseDate(value) {
|
|
101
|
+
if (!value)
|
|
102
|
+
return undefined;
|
|
103
|
+
const parsed = new Date(value);
|
|
104
|
+
return Number.isNaN(parsed.getTime()) ? undefined : parsed;
|
|
105
|
+
}
|
|
106
|
+
function loadGitLastChangeDates(workspaceRoot, relativeDir) {
|
|
107
|
+
const dates = new Map();
|
|
108
|
+
try {
|
|
109
|
+
const output = (0, node_child_process_1.execFileSync)('git', ['log', '--format=__FRAIM_CLEANUP_DATE__%cI', '--name-only', '--', relativeDir], {
|
|
110
|
+
cwd: workspaceRoot,
|
|
111
|
+
encoding: 'utf8',
|
|
112
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
113
|
+
timeout: 10_000,
|
|
114
|
+
maxBuffer: 80 * 1024 * 1024,
|
|
115
|
+
});
|
|
116
|
+
let currentDate;
|
|
117
|
+
for (const rawLine of output.split(/\r?\n/)) {
|
|
118
|
+
const line = rawLine.trim();
|
|
119
|
+
if (!line)
|
|
120
|
+
continue;
|
|
121
|
+
if (line.startsWith('__FRAIM_CLEANUP_DATE__')) {
|
|
122
|
+
currentDate = parseDate(line.slice('__FRAIM_CLEANUP_DATE__'.length));
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const relativePath = line.replace(/\\/g, '/');
|
|
126
|
+
if (currentDate && !dates.has(relativePath))
|
|
127
|
+
dates.set(relativePath, currentDate);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return new Map();
|
|
132
|
+
}
|
|
133
|
+
return dates;
|
|
134
|
+
}
|
|
99
135
|
function readFrontmatter(content) {
|
|
100
136
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
101
137
|
if (!match)
|
|
@@ -124,6 +160,10 @@ function synthesizedDate(absolutePath) {
|
|
|
124
160
|
function userScoped(fileName, activeUserEmail) {
|
|
125
161
|
return fileName.startsWith(`${activeUserEmail}-`);
|
|
126
162
|
}
|
|
163
|
+
function evidenceEligibilityDate(workspaceRoot, absolutePath, gitDates) {
|
|
164
|
+
const relativePath = normalizeRelative(workspaceRoot, absolutePath);
|
|
165
|
+
return gitDates.get(relativePath) ?? fileMtime(absolutePath);
|
|
166
|
+
}
|
|
127
167
|
function collectCandidates(workspaceRoot, activeUserEmail) {
|
|
128
168
|
const candidates = [];
|
|
129
169
|
const add = (category, absolutePath, eligible, reason, eligibilityDate) => {
|
|
@@ -155,23 +195,26 @@ function collectCandidates(workspaceRoot, activeUserEmail) {
|
|
|
155
195
|
add('retrospectives', filePath, false, 'Retrospective has no synthesized frontmatter.');
|
|
156
196
|
}
|
|
157
197
|
const evidenceDir = node_path_1.default.join(workspaceRoot, 'docs', 'evidence');
|
|
198
|
+
const evidenceGitDates = loadGitLastChangeDates(workspaceRoot, 'docs/evidence');
|
|
158
199
|
for (const filePath of listFiles(evidenceDir)) {
|
|
159
200
|
const fileName = node_path_1.default.basename(filePath);
|
|
160
201
|
const isMarkdown = fileName.endsWith('.md');
|
|
161
202
|
const isJson = fileName.endsWith('.json');
|
|
203
|
+
const eligibilityDate = evidenceEligibilityDate(workspaceRoot, filePath, evidenceGitDates);
|
|
162
204
|
if (fileName.includes('cleanup-manifest') || fileName.includes('cleanup_manifest')) {
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
205
|
+
if (isMarkdown || isJson) {
|
|
206
|
+
add('cleanup_manifests', filePath, true, 'Cleanup manifest file.', eligibilityDate);
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
add('evidence', filePath, true, 'Evidence file.', eligibilityDate);
|
|
210
|
+
}
|
|
166
211
|
continue;
|
|
167
212
|
}
|
|
168
|
-
if (!isMarkdown)
|
|
169
|
-
continue;
|
|
170
213
|
if (fileName.endsWith('-feedback.md')) {
|
|
171
|
-
add('feedback', filePath, true, 'Feedback evidence file.',
|
|
214
|
+
add('feedback', filePath, true, 'Feedback evidence file.', eligibilityDate);
|
|
172
215
|
continue;
|
|
173
216
|
}
|
|
174
|
-
add('evidence', filePath, true, 'Evidence file.',
|
|
217
|
+
add('evidence', filePath, true, 'Evidence file.', eligibilityDate);
|
|
175
218
|
}
|
|
176
219
|
return candidates;
|
|
177
220
|
}
|