@topogram/cli 0.3.60 → 0.3.61
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/package.json +1 -1
- package/src/cli.js +114 -2
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -153,6 +153,17 @@ const KNOWN_CLI_CONSUMER_WORKFLOWS = {
|
|
|
153
153
|
"topogram-hello": "Topogram Package Verification",
|
|
154
154
|
"topograms": "Catalog Verification"
|
|
155
155
|
};
|
|
156
|
+
const KNOWN_CLI_CONSUMER_WORKFLOW_JOBS = {
|
|
157
|
+
"topograms": [
|
|
158
|
+
"Validate catalog",
|
|
159
|
+
"Smoke native starter",
|
|
160
|
+
"Smoke starter alias (hello-web)",
|
|
161
|
+
"Smoke starter alias (hello-api)",
|
|
162
|
+
"Smoke starter alias (hello-db)",
|
|
163
|
+
"Smoke starter alias (web-api)",
|
|
164
|
+
"Smoke starter alias (web-api-db)"
|
|
165
|
+
]
|
|
166
|
+
};
|
|
156
167
|
const PACKAGE_UPDATE_CLI_CHECK_SCRIPTS = [
|
|
157
168
|
"cli:surface",
|
|
158
169
|
"doctor",
|
|
@@ -3211,6 +3222,14 @@ function expectedConsumerWorkflowName(name) {
|
|
|
3211
3222
|
return KNOWN_CLI_CONSUMER_WORKFLOWS[name] || null;
|
|
3212
3223
|
}
|
|
3213
3224
|
|
|
3225
|
+
/**
|
|
3226
|
+
* @param {string} name
|
|
3227
|
+
* @returns {string[]}
|
|
3228
|
+
*/
|
|
3229
|
+
function expectedConsumerWorkflowJobs(name) {
|
|
3230
|
+
return KNOWN_CLI_CONSUMER_WORKFLOW_JOBS[name] || [];
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3214
3233
|
/**
|
|
3215
3234
|
* @param {string[]} args
|
|
3216
3235
|
* @param {string} cwd
|
|
@@ -3351,16 +3370,18 @@ function waitForConsumerCi(consumer, options = {}) {
|
|
|
3351
3370
|
/**
|
|
3352
3371
|
* @param {{ name: string, root?: string|null, workflow?: string|null }} consumer
|
|
3353
3372
|
* @param {{ strict?: boolean }} [options]
|
|
3354
|
-
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string }|null, diagnostics: Array<Record<string, any>> }}
|
|
3373
|
+
* @returns {{ checked: boolean, ok: boolean|null, expectedWorkflow: string|null, expectedJobs: string[], headSha: string|null, run: { databaseId?: number, workflowName?: string, status?: string, conclusion?: string, headSha?: string, url?: string, jobs?: Array<Record<string, any>> }|null, diagnostics: Array<Record<string, any>> }}
|
|
3355
3374
|
*/
|
|
3356
3375
|
function inspectConsumerCi(consumer, options = {}) {
|
|
3357
3376
|
const diagnostics = [];
|
|
3358
3377
|
const expectedWorkflow = consumer.workflow || expectedConsumerWorkflowName(consumer.name);
|
|
3378
|
+
const expectedJobs = expectedConsumerWorkflowJobs(consumer.name);
|
|
3359
3379
|
if (!consumer.root || !fs.existsSync(consumer.root)) {
|
|
3360
3380
|
return {
|
|
3361
3381
|
checked: false,
|
|
3362
3382
|
ok: null,
|
|
3363
3383
|
expectedWorkflow,
|
|
3384
|
+
expectedJobs,
|
|
3364
3385
|
headSha: null,
|
|
3365
3386
|
run: null,
|
|
3366
3387
|
diagnostics: []
|
|
@@ -3388,6 +3409,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3388
3409
|
checked: true,
|
|
3389
3410
|
ok: false,
|
|
3390
3411
|
expectedWorkflow,
|
|
3412
|
+
expectedJobs,
|
|
3391
3413
|
headSha,
|
|
3392
3414
|
run: null,
|
|
3393
3415
|
diagnostics
|
|
@@ -3424,6 +3446,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3424
3446
|
checked: true,
|
|
3425
3447
|
ok: false,
|
|
3426
3448
|
expectedWorkflow,
|
|
3449
|
+
expectedJobs,
|
|
3427
3450
|
headSha,
|
|
3428
3451
|
run: null,
|
|
3429
3452
|
diagnostics
|
|
@@ -3454,6 +3477,7 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3454
3477
|
checked: true,
|
|
3455
3478
|
ok: false,
|
|
3456
3479
|
expectedWorkflow,
|
|
3480
|
+
expectedJobs,
|
|
3457
3481
|
headSha,
|
|
3458
3482
|
run: null,
|
|
3459
3483
|
diagnostics
|
|
@@ -3477,17 +3501,105 @@ function inspectConsumerCi(consumer, options = {}) {
|
|
|
3477
3501
|
suggestedFix: "Wait for or fix the consumer verification workflow, then rerun release status."
|
|
3478
3502
|
});
|
|
3479
3503
|
}
|
|
3504
|
+
if (expectedJobs.length > 0 && run.databaseId) {
|
|
3505
|
+
const jobResult = inspectConsumerWorkflowJobs(consumer, run.databaseId, expectedJobs, options);
|
|
3506
|
+
if (jobResult.jobs) {
|
|
3507
|
+
run.jobs = jobResult.jobs;
|
|
3508
|
+
}
|
|
3509
|
+
diagnostics.push(...jobResult.diagnostics);
|
|
3510
|
+
} else if (expectedJobs.length > 0) {
|
|
3511
|
+
diagnostics.push({
|
|
3512
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3513
|
+
severity: options.strict ? "error" : "warning",
|
|
3514
|
+
message: `${consumer.name} ${expectedWorkflow} run did not include a database id, so expected jobs could not be inspected.`,
|
|
3515
|
+
path: run.url || `attebury/${consumer.name}`,
|
|
3516
|
+
suggestedFix: "Rerun release status after GitHub exposes the workflow run id."
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
3480
3520
|
return {
|
|
3481
3521
|
checked: true,
|
|
3482
|
-
ok:
|
|
3522
|
+
ok: errorCount === 0 &&
|
|
3483
3523
|
(!options.strict || (run.status === "completed" && run.conclusion === "success" && (!headSha || !run.headSha || run.headSha === headSha))),
|
|
3484
3524
|
expectedWorkflow,
|
|
3525
|
+
expectedJobs,
|
|
3485
3526
|
headSha,
|
|
3486
3527
|
run,
|
|
3487
3528
|
diagnostics
|
|
3488
3529
|
};
|
|
3489
3530
|
}
|
|
3490
3531
|
|
|
3532
|
+
/**
|
|
3533
|
+
* @param {{ name: string, root?: string|null }} consumer
|
|
3534
|
+
* @param {number|string} runId
|
|
3535
|
+
* @param {string[]} expectedJobs
|
|
3536
|
+
* @param {{ strict?: boolean }} [options]
|
|
3537
|
+
* @returns {{ jobs: Array<Record<string, any>>|null, diagnostics: Array<Record<string, any>> }}
|
|
3538
|
+
*/
|
|
3539
|
+
function inspectConsumerWorkflowJobs(consumer, runId, expectedJobs, options = {}) {
|
|
3540
|
+
const diagnostics = [];
|
|
3541
|
+
const result = childProcess.spawnSync("gh", [
|
|
3542
|
+
"run",
|
|
3543
|
+
"view",
|
|
3544
|
+
String(runId),
|
|
3545
|
+
"--repo",
|
|
3546
|
+
`attebury/${consumer.name}`,
|
|
3547
|
+
"--json",
|
|
3548
|
+
"jobs"
|
|
3549
|
+
], {
|
|
3550
|
+
cwd: consumer.root || process.cwd(),
|
|
3551
|
+
encoding: "utf8",
|
|
3552
|
+
env: { ...process.env, PATH: process.env.PATH || "" }
|
|
3553
|
+
});
|
|
3554
|
+
if (result.status !== 0) {
|
|
3555
|
+
diagnostics.push(commandDiagnostic({
|
|
3556
|
+
code: "release_consumer_ci_jobs_unavailable",
|
|
3557
|
+
severity: options.strict ? "error" : "warning",
|
|
3558
|
+
message: `Could not inspect expected jobs for ${consumer.name}.`,
|
|
3559
|
+
path: `attebury/${consumer.name}`,
|
|
3560
|
+
suggestedFix: "Check GitHub CLI auth/network access, then rerun release status.",
|
|
3561
|
+
result
|
|
3562
|
+
}));
|
|
3563
|
+
return { jobs: null, diagnostics };
|
|
3564
|
+
}
|
|
3565
|
+
let payload = {};
|
|
3566
|
+
try {
|
|
3567
|
+
payload = JSON.parse(String(result.stdout || "{}"));
|
|
3568
|
+
} catch (error) {
|
|
3569
|
+
diagnostics.push({
|
|
3570
|
+
code: "release_consumer_ci_jobs_unreadable",
|
|
3571
|
+
severity: options.strict ? "error" : "warning",
|
|
3572
|
+
message: `Could not parse ${consumer.name} workflow job status: ${messageFromError(error)}`,
|
|
3573
|
+
path: `attebury/${consumer.name}`,
|
|
3574
|
+
suggestedFix: "Rerun release status after GitHub CLI output is valid JSON."
|
|
3575
|
+
});
|
|
3576
|
+
}
|
|
3577
|
+
const jobs = Array.isArray(payload.jobs) ? payload.jobs : [];
|
|
3578
|
+
for (const expectedJob of expectedJobs) {
|
|
3579
|
+
const job = jobs.find((candidate) => candidate?.name === expectedJob);
|
|
3580
|
+
if (!job) {
|
|
3581
|
+
diagnostics.push({
|
|
3582
|
+
code: "release_consumer_ci_job_missing",
|
|
3583
|
+
severity: options.strict ? "error" : "warning",
|
|
3584
|
+
message: `${consumer.name} workflow is missing expected job '${expectedJob}'.`,
|
|
3585
|
+
path: `attebury/${consumer.name}`,
|
|
3586
|
+
suggestedFix: "Update the consumer workflow or the release-status expected job list, then rerun release status."
|
|
3587
|
+
});
|
|
3588
|
+
continue;
|
|
3589
|
+
}
|
|
3590
|
+
if (job.status !== "completed" || job.conclusion !== "success") {
|
|
3591
|
+
diagnostics.push({
|
|
3592
|
+
code: "release_consumer_ci_job_not_successful",
|
|
3593
|
+
severity: options.strict ? "error" : "warning",
|
|
3594
|
+
message: `${consumer.name} job '${expectedJob}' is ${job.status || "unknown"}/${job.conclusion || "unknown"}.`,
|
|
3595
|
+
path: job.url || `attebury/${consumer.name}`,
|
|
3596
|
+
suggestedFix: "Wait for or fix the expected workflow job, then rerun release status."
|
|
3597
|
+
});
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
return { jobs, diagnostics };
|
|
3601
|
+
}
|
|
3602
|
+
|
|
3491
3603
|
/**
|
|
3492
3604
|
* @param {string} cwd
|
|
3493
3605
|
* @returns {Array<{ name: string, root: string|null, path: string, version: string|null, found: boolean }>}
|