spec-up-t 1.4.1-beta.3 → 1.5.0
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.
- package/assets/compiled/body.js +2 -2
- package/assets/compiled/head.css +2 -2
- package/assets/css/embedded-libraries/bootstrap.min.css +1 -1
- package/assets/css/header-navbar.css +4 -4
- package/assets/js/github-issues.js +3 -3
- package/examples/read-console-messages.js +102 -0
- package/gulpfile.js +42 -1
- package/index.js +49 -1
- package/package.json +2 -1
- package/src/health-check.js +47 -629
- package/src/install-from-boilerplate/config-scripts-keys.js +1 -1
- package/src/markdown-it/README.md +2 -14
- package/src/markdown-it/index.js +1 -7
- package/src/parsers/template-tag-parser.js +19 -3
- package/src/pipeline/postprocessing/definition-list-postprocessor.js +4 -2
- package/src/pipeline/references/collect-external-references.js +65 -8
- package/src/pipeline/references/external-references-service.js +55 -10
- package/src/pipeline/references/fetch-terms-from-index.js +62 -1
- package/src/pipeline/references/process-xtrefs-data.js +34 -0
- package/src/pipeline/references/xtref-utils.js +22 -3
- package/src/pipeline/rendering/render-spec-document.js +0 -1
- package/src/run-healthcheck.js +177 -0
- package/src/utils/logger.js +15 -1
- package/src/utils/message-collector.js +144 -0
- package/templates/template.html +6 -6
- package/test/message-collector.test.js +286 -0
- package/src/markdown-it/link-enhancement.js +0 -98
package/src/health-check.js
CHANGED
|
@@ -1,651 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* @fileoverview Spec-Up-T Health Check Tool
|
|
4
|
+
* @fileoverview Spec-Up-T Health Check Tool - Wrapper for spec-up-t-healthcheck
|
|
5
5
|
*
|
|
6
|
-
* This script
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* This script is called from consuming projects via:
|
|
7
|
+
* require('spec-up-t/src/health-check.js')
|
|
8
|
+
*
|
|
9
|
+
* It delegates to the new spec-up-t-healthcheck npm package while maintaining
|
|
10
|
+
* backward compatibility with existing consuming projects.
|
|
9
11
|
*
|
|
10
12
|
* @author Spec-Up-T Team
|
|
11
|
-
* @version
|
|
12
|
-
* @since 2025-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @typedef {Object} HealthCheckResult
|
|
17
|
-
* @property {string} name - Name of the specific check
|
|
18
|
-
* @property {boolean|string} success - Success status (true, false, or 'partial')
|
|
19
|
-
* @property {string} [status] - Status override ('warning', 'pass', 'fail')
|
|
20
|
-
* @property {string} [details] - Additional details about the check result
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @typedef {Object} HealthCheckSection
|
|
25
|
-
* @property {string} title - Title of the check section
|
|
26
|
-
* @property {HealthCheckResult[]} results - Array of individual check results
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @typedef {Object} RepositoryInfo
|
|
31
|
-
* @property {string} host - Git hosting service (e.g., 'github')
|
|
32
|
-
* @property {string} account - Account/organization name
|
|
33
|
-
* @property {string} repo - Repository name
|
|
34
|
-
* @property {string} [branch] - Branch name
|
|
35
|
-
* @property {boolean} [verified] - Whether repository existence was verified
|
|
36
|
-
*/
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* @typedef {Object} StatusDisplay
|
|
40
|
-
* @property {string} statusClass - CSS class for styling
|
|
41
|
-
* @property {string} statusIcon - HTML icon element
|
|
42
|
-
* @property {string} statusText - Display text for status
|
|
13
|
+
* @version 2.0.0
|
|
14
|
+
* @since 2025-10-11
|
|
15
|
+
* @deprecated The old implementation is replaced by spec-up-t-healthcheck
|
|
43
16
|
*/
|
|
44
17
|
|
|
45
|
-
const fs = require('fs');
|
|
46
18
|
const path = require('path');
|
|
47
|
-
const
|
|
48
|
-
const https = require('https');
|
|
49
|
-
const fileOpener = require('./utils/file-opener');
|
|
50
|
-
|
|
51
|
-
// Import modules from the health-check directory
|
|
52
|
-
const externalSpecsChecker = require('./health-check/external-specs-checker');
|
|
53
|
-
const termReferencesChecker = require('./health-check/term-references-checker');
|
|
54
|
-
const specsConfigurationChecker = require('./health-check/specs-configuration-checker');
|
|
55
|
-
const termsIntroChecker = require('./health-check/terms-intro-checker');
|
|
56
|
-
const destinationGitignoreChecker = require('./health-check/destination-gitignore-checker');
|
|
57
|
-
const trefTermChecker = require('./health-check/tref-term-checker');
|
|
19
|
+
const {spawn} = require('child_process');
|
|
58
20
|
|
|
59
21
|
/**
|
|
60
|
-
*
|
|
61
|
-
* @constant {string}
|
|
62
|
-
*/
|
|
63
|
-
const OUTPUT_DIR = path.join(process.cwd(), '.cache');
|
|
64
|
-
|
|
65
|
-
// Create output directory if it doesn't exist
|
|
66
|
-
if (!fs.existsSync(OUTPUT_DIR)) {
|
|
67
|
-
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Retrieves repository information from specs.json file
|
|
72
|
-
*
|
|
73
|
-
* Reads the specs.json file to extract repository configuration including
|
|
74
|
-
* host, account, repo name, and branch. Falls back to default values if
|
|
75
|
-
* the file doesn't exist or is missing required fields.
|
|
22
|
+
* Main function that runs the health check on the consuming project
|
|
76
23
|
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @returns {Promise<RepositoryInfo>} Repository information object
|
|
24
|
+
* This function is called when a consuming project executes:
|
|
25
|
+
* npm run healthCheck
|
|
80
26
|
*
|
|
81
|
-
*
|
|
82
|
-
* const repoInfo = await getRepoInfo();
|
|
83
|
-
* console.log(repoInfo.account); // 'blockchain-bird'
|
|
27
|
+
* Which triggers: require('spec-up-t/src/health-check.js')
|
|
84
28
|
*/
|
|
85
|
-
async function
|
|
86
|
-
|
|
87
|
-
// Path to the default boilerplate specs.json
|
|
88
|
-
const defaultSpecsPath = path.join(
|
|
89
|
-
__dirname,
|
|
90
|
-
'install-from-boilerplate',
|
|
91
|
-
'boilerplate',
|
|
92
|
-
'specs.json'
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
let defaultValues = {
|
|
96
|
-
host: 'github',
|
|
97
|
-
account: 'blockchain-bird',
|
|
98
|
-
repo: 'spec-up-t'
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
// Try to load default values from boilerplate specs.json
|
|
102
|
-
if (fs.existsSync(defaultSpecsPath)) {
|
|
103
|
-
try {
|
|
104
|
-
const defaultSpecsContent = fs.readFileSync(defaultSpecsPath, 'utf8');
|
|
105
|
-
const defaultSpecs = JSON.parse(defaultSpecsContent);
|
|
106
|
-
|
|
107
|
-
if (defaultSpecs?.specs?.[0]?.source) {
|
|
108
|
-
const source = defaultSpecs.specs[0].source;
|
|
109
|
-
defaultValues = {
|
|
110
|
-
host: source.host || defaultValues.host,
|
|
111
|
-
account: source.account || defaultValues.account,
|
|
112
|
-
repo: source.repo || defaultValues.repo
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
Logger.error('Error reading boilerplate specs.json:', error);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Look for specs.json in the current working directory (where the command is run from)
|
|
121
|
-
const specsPath = path.join(process.cwd(), 'specs.json');
|
|
122
|
-
|
|
123
|
-
if (fs.existsSync(specsPath)) {
|
|
124
|
-
const specsContent = fs.readFileSync(specsPath, 'utf8');
|
|
125
|
-
const specs = JSON.parse(specsContent);
|
|
126
|
-
|
|
127
|
-
// Check if source field exists and has required properties
|
|
128
|
-
if (specs?.specs?.[0]?.source?.host &&
|
|
129
|
-
specs?.specs?.[0]?.source?.account &&
|
|
130
|
-
specs?.specs?.[0]?.source?.repo) {
|
|
131
|
-
|
|
132
|
-
const sourceInfo = specs.specs[0].source;
|
|
133
|
-
|
|
134
|
-
// Check if values have been changed from defaults
|
|
135
|
-
const valuesChanged =
|
|
136
|
-
sourceInfo.host !== defaultValues.host ||
|
|
137
|
-
sourceInfo.account !== defaultValues.account ||
|
|
138
|
-
sourceInfo.repo !== defaultValues.repo;
|
|
139
|
-
|
|
140
|
-
// If values haven't changed, just return the default values
|
|
141
|
-
// This allows the user to manually change these later
|
|
142
|
-
if (!valuesChanged) {
|
|
143
|
-
return {
|
|
144
|
-
host: sourceInfo.host,
|
|
145
|
-
account: sourceInfo.account,
|
|
146
|
-
repo: sourceInfo.repo,
|
|
147
|
-
branch: sourceInfo.branch || defaultValues.branch
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// If values have changed, verify the repository exists
|
|
152
|
-
const repoExists = await checkRepositoryExists(
|
|
153
|
-
sourceInfo.host,
|
|
154
|
-
sourceInfo.account,
|
|
155
|
-
sourceInfo.repo
|
|
156
|
-
);
|
|
157
|
-
|
|
158
|
-
if (repoExists) {
|
|
159
|
-
// Repository exists, return the verified info
|
|
160
|
-
return {
|
|
161
|
-
host: sourceInfo.host,
|
|
162
|
-
account: sourceInfo.account,
|
|
163
|
-
repo: sourceInfo.repo,
|
|
164
|
-
branch: sourceInfo.branch || defaultValues.branch,
|
|
165
|
-
verified: true
|
|
166
|
-
};
|
|
167
|
-
} else {
|
|
168
|
-
// Repository doesn't exist, but values have been changed
|
|
169
|
-
// Return the values with a verification failed flag
|
|
170
|
-
return {
|
|
171
|
-
host: sourceInfo.host,
|
|
172
|
-
account: sourceInfo.account,
|
|
173
|
-
repo: sourceInfo.repo,
|
|
174
|
-
branch: sourceInfo.branch || defaultValues.branch,
|
|
175
|
-
verified: false
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
Logger.info('specs.json not found');
|
|
181
|
-
}
|
|
182
|
-
} catch (error) {
|
|
183
|
-
Logger.error('Error reading specs.json:', error);
|
|
184
|
-
}
|
|
29
|
+
async function runHealthCheck() {
|
|
30
|
+
console.log('🏥 Initializing Spec-Up-T Health Check...\n');
|
|
185
31
|
|
|
186
|
-
//
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
*
|
|
198
|
-
* Makes an HTTP HEAD request to verify repository existence without
|
|
199
|
-
* downloading the full repository content. Handles timeouts and errors gracefully.
|
|
200
|
-
*
|
|
201
|
-
* @function checkRepositoryExists
|
|
202
|
-
* @param {string} host - Git hosting service (e.g., 'github')
|
|
203
|
-
* @param {string} account - Account or organization name
|
|
204
|
-
* @param {string} repo - Repository name
|
|
205
|
-
* @returns {Promise<boolean>} True if repository exists and is accessible
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* const exists = await checkRepositoryExists('github', 'blockchain-bird', 'spec-up-t');
|
|
209
|
-
* if (exists) {
|
|
210
|
-
* console.log('Repository is accessible');
|
|
211
|
-
* }
|
|
212
|
-
*/
|
|
213
|
-
function checkRepositoryExists(host, account, repo) {
|
|
214
|
-
return new Promise((resolve) => {
|
|
215
|
-
const url = `https://${host}.com/${account}/${repo}`;
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
const request = https.request(url, { method: 'HEAD', timeout: 10000 }, (response) => {
|
|
219
|
-
// 200, 301, 302 status codes indicate the repo exists
|
|
220
|
-
const exists = [200, 301, 302].includes(response.statusCode);
|
|
221
|
-
resolve(exists);
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
request.on('error', (error) => {
|
|
225
|
-
Logger.error('Error checking repository existence:', error.message);
|
|
226
|
-
resolve(false);
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
request.on('timeout', () => {
|
|
230
|
-
Logger.error('Timeout checking repository existence');
|
|
231
|
-
request.destroy();
|
|
232
|
-
resolve(false);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
request.end();
|
|
236
|
-
} catch (error) {
|
|
237
|
-
Logger.error('Error checking repository existence:', error.message);
|
|
238
|
-
resolve(false);
|
|
239
|
-
}
|
|
32
|
+
// Get the path to the run-healthcheck.js script in spec-up-t
|
|
33
|
+
const healthCheckScript = path.join(__dirname, 'run-healthcheck.js');
|
|
34
|
+
|
|
35
|
+
// Pass through all command line arguments (excluding node and script name)
|
|
36
|
+
const args = process.argv.slice(2);
|
|
37
|
+
|
|
38
|
+
// Run the health check script with Node, passing through all arguments
|
|
39
|
+
// The script will automatically check process.cwd() (the consuming project)
|
|
40
|
+
const child = spawn('node', [healthCheckScript, ...args], {
|
|
41
|
+
stdio: 'inherit', // Pass through all I/O
|
|
42
|
+
cwd: process.cwd() // Ensure we're in the consuming project directory
|
|
240
43
|
});
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Formats current timestamp for use in filenames
|
|
245
|
-
*
|
|
246
|
-
* Generates a timestamp string that is safe to use in filenames by
|
|
247
|
-
* replacing special characters with hyphens. Format: YYYY-MM-DD-HH-mm-ssZ
|
|
248
|
-
*
|
|
249
|
-
* @function getFormattedTimestamp
|
|
250
|
-
* @returns {string} Formatted timestamp string suitable for filenames
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* const timestamp = getFormattedTimestamp();
|
|
254
|
-
* console.log(timestamp); // "2025-06-06-14-30-25Z"
|
|
255
|
-
*/
|
|
256
|
-
function getFormattedTimestamp() {
|
|
257
|
-
const now = new Date();
|
|
258
|
-
return now.toISOString()
|
|
259
|
-
.replace(/[T:]/g, '-')
|
|
260
|
-
.replace(/\..+/, '')
|
|
261
|
-
.replace(/Z/g, 'Z');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* Generates a human-readable timestamp for display in reports
|
|
266
|
-
*
|
|
267
|
-
* Creates a localized timestamp string for display purposes,
|
|
268
|
-
* using the system's default locale and timezone.
|
|
269
|
-
*
|
|
270
|
-
* @function getHumanReadableTimestamp
|
|
271
|
-
* @returns {string} Human-readable timestamp string
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* const readable = getHumanReadableTimestamp();
|
|
275
|
-
* console.log(readable); // "6/6/2025, 2:30:25 PM"
|
|
276
|
-
*/
|
|
277
|
-
function getHumanReadableTimestamp() {
|
|
278
|
-
return new Date().toLocaleString();
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Determines status display parameters based on check result
|
|
283
|
-
*
|
|
284
|
-
* Analyzes check results to determine appropriate CSS classes,
|
|
285
|
-
* icons, and text for visual status representation in the HTML report.
|
|
286
|
-
*
|
|
287
|
-
* @function getStatusDisplay
|
|
288
|
-
* @param {HealthCheckResult} result - Check result object
|
|
289
|
-
* @returns {StatusDisplay} Status display configuration
|
|
290
|
-
*
|
|
291
|
-
* @example
|
|
292
|
-
* const display = getStatusDisplay({ success: true });
|
|
293
|
-
* console.log(display.statusText); // "Pass"
|
|
294
|
-
*/
|
|
295
|
-
function getStatusDisplay(result) {
|
|
296
|
-
if (result.status === 'warning' || result.success === 'partial') {
|
|
297
|
-
// Warning status
|
|
298
|
-
return {
|
|
299
|
-
statusClass: 'text-warning',
|
|
300
|
-
statusIcon: '<i class="bi bi-exclamation-triangle-fill"></i>',
|
|
301
|
-
statusText: 'Warning'
|
|
302
|
-
};
|
|
303
|
-
} else if (result.success) {
|
|
304
|
-
// Pass status
|
|
305
|
-
return {
|
|
306
|
-
statusClass: 'text-success',
|
|
307
|
-
statusIcon: '<i class="bi bi-check-circle-fill"></i>',
|
|
308
|
-
statusText: 'Pass'
|
|
309
|
-
};
|
|
310
|
-
} else {
|
|
311
|
-
// Fail status
|
|
312
|
-
return {
|
|
313
|
-
statusClass: 'text-danger',
|
|
314
|
-
statusIcon: '<i class="bi bi-x-circle-fill"></i>',
|
|
315
|
-
statusText: 'Fail'
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Main function to run all health checks and generate the report
|
|
322
|
-
*
|
|
323
|
-
* Orchestrates the execution of all available health check modules,
|
|
324
|
-
* collects results, and generates a comprehensive HTML report.
|
|
325
|
-
* Handles errors gracefully and ensures proper cleanup.
|
|
326
|
-
*
|
|
327
|
-
* @async
|
|
328
|
-
* @function runHealthCheck
|
|
329
|
-
* @throws {Error} When health check execution fails
|
|
330
|
-
*
|
|
331
|
-
* @description
|
|
332
|
-
* The function performs the following checks:
|
|
333
|
-
* - Term reference checks in external specs
|
|
334
|
-
* - External specs URL validation
|
|
335
|
-
* - Term references validation
|
|
336
|
-
* - specs.json configuration validation
|
|
337
|
-
* - Terms introduction file validation
|
|
338
|
-
* - .gitignore destination directory check
|
|
339
|
-
*
|
|
340
|
-
* @example
|
|
341
|
-
* try {
|
|
342
|
-
* await runHealthCheck();
|
|
343
|
-
* console.log('Health check completed successfully');
|
|
344
|
-
* } catch (error) {
|
|
345
|
-
* console.error('Health check failed:', error);
|
|
346
|
-
* }
|
|
347
|
-
*/
|
|
348
|
-
async function runHealthCheck() {
|
|
349
|
-
Logger.info('Running health checks...');
|
|
350
|
-
|
|
351
|
-
// Collection to store all check results
|
|
352
|
-
const results = [];
|
|
353
|
-
|
|
354
|
-
try {
|
|
355
|
-
|
|
356
|
-
// Run term reference tref check
|
|
357
|
-
const trefTermResults = await trefTermChecker.checkTrefTerms(process.cwd());
|
|
358
|
-
results.push({
|
|
359
|
-
title: 'Check Trefs in all external specs',
|
|
360
|
-
results: trefTermResults
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Run external specs URL check
|
|
364
|
-
const externalSpecsResults = await externalSpecsChecker.checkExternalSpecs(process.cwd());
|
|
365
|
-
results.push({
|
|
366
|
-
title: 'Check External Specs URL',
|
|
367
|
-
results: externalSpecsResults
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// Run term references check
|
|
371
|
-
const termReferencesResults = await termReferencesChecker.checkTermReferences(process.cwd());
|
|
372
|
-
results.push({
|
|
373
|
-
title: 'Check Term References',
|
|
374
|
-
results: termReferencesResults
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// Run specs.json configuration check
|
|
378
|
-
const specsConfigResults = await specsConfigurationChecker.checkSpecsJsonConfiguration(process.cwd());
|
|
379
|
-
results.push({
|
|
380
|
-
title: 'Check <code>specs.json</code> configuration',
|
|
381
|
-
results: specsConfigResults
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Run terms-and-definitions-intro.md check
|
|
385
|
-
const termsIntroResults = await termsIntroChecker.checkTermsIntroFile(process.cwd());
|
|
386
|
-
results.push({
|
|
387
|
-
title: 'Check <code>terms-and-definitions-intro.md</code>',
|
|
388
|
-
results: termsIntroResults
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
// Run destination directory gitignore check
|
|
392
|
-
const destinationGitignoreResults = await destinationGitignoreChecker.checkDestinationGitIgnore(process.cwd());
|
|
393
|
-
results.push({
|
|
394
|
-
title: 'Check <code>.gitignore</code>',
|
|
395
|
-
results: destinationGitignoreResults
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
// Add more checks here in the future
|
|
399
|
-
|
|
400
|
-
// Generate and open the report
|
|
401
|
-
await generateReport(results);
|
|
402
|
-
} catch (error) {
|
|
403
|
-
console.error('Error running health checks:', error);
|
|
404
|
-
process.exit(1);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
/**
|
|
409
|
-
* Generates and opens an HTML health check report
|
|
410
|
-
*
|
|
411
|
-
* Creates a comprehensive HTML report with all health check results,
|
|
412
|
-
* saves it to the cache directory, and opens it in the default browser.
|
|
413
|
-
* The report includes interactive features like filtering and status indicators.
|
|
414
|
-
*
|
|
415
|
-
* @async
|
|
416
|
-
* @function generateReport
|
|
417
|
-
* @param {HealthCheckSection[]} checkResults - Array of health check result objects
|
|
418
|
-
* @throws {Error} When report generation or file operations fail
|
|
419
|
-
*
|
|
420
|
-
* @example
|
|
421
|
-
* const results = [
|
|
422
|
-
* {
|
|
423
|
-
* title: 'Configuration Check',
|
|
424
|
-
* results: [{ name: 'specs.json', success: true, details: 'Valid' }]
|
|
425
|
-
* }
|
|
426
|
-
* ];
|
|
427
|
-
* await generateReport(results);
|
|
428
|
-
*/
|
|
429
|
-
async function generateReport(checkResults) {
|
|
430
|
-
const timestamp = getFormattedTimestamp();
|
|
431
|
-
// Get repository information from specs.json
|
|
432
|
-
const repoInfo = await getRepoInfo();
|
|
433
|
-
const reportFileName = `health-check-${timestamp}-${repoInfo.account}-${repoInfo.repo}.html`;
|
|
434
|
-
const reportPath = path.join(OUTPUT_DIR, reportFileName);
|
|
435
|
-
|
|
436
44
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
}
|
|
45
|
+
// Handle process exit
|
|
46
|
+
child.on('exit', (code) => {
|
|
47
|
+
process.exit(code || 0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Handle errors
|
|
51
|
+
child.on('error', (error) => {
|
|
52
|
+
console.error('❌ Failed to run health check:', error.message);
|
|
53
|
+
console.error('\nTroubleshooting:');
|
|
54
|
+
console.error(' - Ensure spec-up-t-healthcheck is installed');
|
|
55
|
+
console.error(' - Try running: npm install in the spec-up-t package');
|
|
56
|
+
console.error(' - Check that Node.js is properly installed');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
451
59
|
}
|
|
452
60
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
* status filtering, and detailed result tables.
|
|
459
|
-
*
|
|
460
|
-
* @function generateHtmlReport
|
|
461
|
-
* @param {HealthCheckSection[]} checkResults - Array of health check result sections
|
|
462
|
-
* @param {string} timestamp - Human-readable timestamp for the report
|
|
463
|
-
* @param {RepositoryInfo} repoInfo - Repository information object
|
|
464
|
-
* @returns {string} Complete HTML document as string
|
|
465
|
-
*
|
|
466
|
-
* @example
|
|
467
|
-
* const html = generateHtmlReport(results, '6/6/2025, 2:30:25 PM', {
|
|
468
|
-
* host: 'github',
|
|
469
|
-
* account: 'blockchain-bird',
|
|
470
|
-
* repo: 'spec-up-t'
|
|
471
|
-
* });
|
|
472
|
-
*/
|
|
473
|
-
function generateHtmlReport(checkResults, timestamp, repoInfo) {
|
|
474
|
-
let resultsHtml = '';
|
|
475
|
-
|
|
476
|
-
// Add repository verification check at the beginning if needed
|
|
477
|
-
if (repoInfo && repoInfo.verified === false) {
|
|
478
|
-
const failStatus = {
|
|
479
|
-
statusClass: 'text-danger',
|
|
480
|
-
statusIcon: '<i class="bi bi-x-circle-fill"></i>',
|
|
481
|
-
statusText: 'Fail'
|
|
482
|
-
};
|
|
483
|
-
|
|
484
|
-
// Create a new section at the top for repository verification
|
|
485
|
-
resultsHtml += `
|
|
486
|
-
<div class="card mb-4 results-card alert-danger" data-section="repository-verification">
|
|
487
|
-
<div class="card-header bg-danger text-white">
|
|
488
|
-
<h5>Repository Verification</h5>
|
|
489
|
-
</div>
|
|
490
|
-
<div class="card-body">
|
|
491
|
-
<table class="table table-striped">
|
|
492
|
-
<thead>
|
|
493
|
-
<tr>
|
|
494
|
-
<th>Status</th>
|
|
495
|
-
<th>Check</th>
|
|
496
|
-
<th>Details</th>
|
|
497
|
-
</tr>
|
|
498
|
-
</thead>
|
|
499
|
-
<tbody>
|
|
500
|
-
<tr data-status="fail" class="check-row">
|
|
501
|
-
<td class="${failStatus.statusClass}" style="white-space: nowrap;">
|
|
502
|
-
${failStatus.statusIcon} <span style="vertical-align: middle;">${failStatus.statusText}</span>
|
|
503
|
-
</td>
|
|
504
|
-
<td>Repository existence check</td>
|
|
505
|
-
<td>The repository at https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo} does not exist or is not accessible. Please verify the repository information in specs.json.</td>
|
|
506
|
-
</tr>
|
|
507
|
-
</tbody>
|
|
508
|
-
</table>
|
|
509
|
-
</div>
|
|
510
|
-
</div>
|
|
511
|
-
`;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
checkResults.forEach(section => {
|
|
515
|
-
resultsHtml += `
|
|
516
|
-
<div class="card mb-4 results-card" data-section="${section.title.replace(/[^a-zA-Z0-9]/g, '-').toLowerCase()}">
|
|
517
|
-
<div class="card-header">
|
|
518
|
-
<h5>${section.title}</h5>
|
|
519
|
-
</div>
|
|
520
|
-
<div class="card-body">
|
|
521
|
-
<table class="table table-striped">
|
|
522
|
-
<thead>
|
|
523
|
-
<tr>
|
|
524
|
-
<th>Status</th>
|
|
525
|
-
<th>Check</th>
|
|
526
|
-
<th>Details</th>
|
|
527
|
-
</tr>
|
|
528
|
-
</thead>
|
|
529
|
-
<tbody>
|
|
530
|
-
`;
|
|
531
|
-
|
|
532
|
-
section.results.forEach(result => {
|
|
533
|
-
const { statusClass, statusIcon, statusText } = getStatusDisplay(result);
|
|
534
|
-
|
|
535
|
-
// Add data-status attribute to identify rows by status and reorder columns to put status first
|
|
536
|
-
resultsHtml += `
|
|
537
|
-
<tr data-status="${statusText.toLowerCase()}" class="check-row">
|
|
538
|
-
<td class="${statusClass}" style="white-space: nowrap;">
|
|
539
|
-
${statusIcon} <span style="vertical-align: middle;">${statusText}</span>
|
|
540
|
-
</td>
|
|
541
|
-
<td>${result.name}</td>
|
|
542
|
-
<td>${result.details || ''}</td>
|
|
543
|
-
</tr>
|
|
544
|
-
`;
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
resultsHtml += `
|
|
548
|
-
</tbody>
|
|
549
|
-
</table>
|
|
550
|
-
</div>
|
|
551
|
-
</div>
|
|
552
|
-
`;
|
|
61
|
+
// Run the health check if this file is executed directly or required
|
|
62
|
+
if (require.main === module || process.env.SPEC_UP_T_HEALTH_CHECK_RUN !== 'false') {
|
|
63
|
+
runHealthCheck().catch(error => {
|
|
64
|
+
console.error('❌ Health check failed:', error);
|
|
65
|
+
process.exit(1);
|
|
553
66
|
});
|
|
554
|
-
|
|
555
|
-
return `
|
|
556
|
-
<!DOCTYPE html>
|
|
557
|
-
<html lang="en">
|
|
558
|
-
<head>
|
|
559
|
-
<meta charset="UTF-8">
|
|
560
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
561
|
-
<title>Spec-Up-T Health Check Report</title>
|
|
562
|
-
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
563
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
|
564
|
-
<style>
|
|
565
|
-
body {
|
|
566
|
-
padding-top: 2rem;
|
|
567
|
-
padding-bottom: 2rem;
|
|
568
|
-
}
|
|
569
|
-
.report-header {
|
|
570
|
-
margin-bottom: 2rem;
|
|
571
|
-
}
|
|
572
|
-
.timestamp {
|
|
573
|
-
color: #6c757d;
|
|
574
|
-
}
|
|
575
|
-
.filter-toggle {
|
|
576
|
-
margin-bottom: 1rem;
|
|
577
|
-
}
|
|
578
|
-
.hidden-item {
|
|
579
|
-
display: none;
|
|
580
|
-
}
|
|
581
|
-
</style>
|
|
582
|
-
</head>
|
|
583
|
-
<body>
|
|
584
|
-
<div class="container">
|
|
585
|
-
<div class="report-header">
|
|
586
|
-
<h1>Spec-Up-T Health Check Report</h1>
|
|
587
|
-
<p class="timestamp">Generated: ${timestamp}</p>
|
|
588
|
-
<p class="repo-info">
|
|
589
|
-
Repository: <a href="https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}" target="_blank">https://${repoInfo.host}.com/${repoInfo.account}/${repoInfo.repo}</a><br>
|
|
590
|
-
Username: ${repoInfo.account}<br>
|
|
591
|
-
Repo name: ${repoInfo.repo}
|
|
592
|
-
</p>
|
|
593
|
-
</div>
|
|
594
|
-
|
|
595
|
-
<div class="filter-toggle form-check form-switch">
|
|
596
|
-
<input class="form-check-input" type="checkbox" id="togglePassingChecks" checked>
|
|
597
|
-
<label class="form-check-label" for="togglePassingChecks">Show / hide passing checks</label>
|
|
598
|
-
</div>
|
|
599
|
-
|
|
600
|
-
<div class="results">
|
|
601
|
-
${resultsHtml}
|
|
602
|
-
</div>
|
|
603
|
-
</div>
|
|
604
|
-
|
|
605
|
-
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
606
|
-
<script>
|
|
607
|
-
// Toggle function for passing checks
|
|
608
|
-
document.getElementById('togglePassingChecks').addEventListener('change', function() {
|
|
609
|
-
const showPassing = this.checked;
|
|
610
|
-
const passingRows = document.querySelectorAll('tr[data-status="pass"]');
|
|
611
|
-
|
|
612
|
-
// Hide/show passing rows
|
|
613
|
-
passingRows.forEach(row => {
|
|
614
|
-
if (showPassing) {
|
|
615
|
-
row.classList.remove('hidden-item');
|
|
616
|
-
} else {
|
|
617
|
-
row.classList.add('hidden-item');
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
// Check each results card to see if it should be hidden
|
|
622
|
-
document.querySelectorAll('.results-card').forEach(card => {
|
|
623
|
-
const visibleRows = card.querySelectorAll('tr.check-row:not(.hidden-item)');
|
|
624
|
-
|
|
625
|
-
if (visibleRows.length === 0) {
|
|
626
|
-
// If no visible rows, hide the entire card
|
|
627
|
-
card.classList.add('hidden-item');
|
|
628
|
-
} else {
|
|
629
|
-
// Otherwise show it
|
|
630
|
-
card.classList.remove('hidden-item');
|
|
631
|
-
}
|
|
632
|
-
});
|
|
633
|
-
});
|
|
634
|
-
</script>
|
|
635
|
-
</body>
|
|
636
|
-
</html>
|
|
637
|
-
`;
|
|
638
67
|
}
|
|
639
68
|
|
|
640
|
-
|
|
641
|
-
* Script execution entry point
|
|
642
|
-
*
|
|
643
|
-
* Immediately executes the health check when this script is run directly.
|
|
644
|
-
* This allows the script to be used as a standalone command-line tool.
|
|
645
|
-
*
|
|
646
|
-
* @example
|
|
647
|
-
* // Run from command line:
|
|
648
|
-
* // node src/health-check.js
|
|
649
|
-
* // npm run healthCheck
|
|
650
|
-
*/
|
|
651
|
-
runHealthCheck();
|
|
69
|
+
module.exports = runHealthCheck;
|