@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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +114 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topogram/cli",
3
- "version": "0.3.60",
3
+ "version": "0.3.61",
4
4
  "description": "Topogram CLI for checking Topogram workspaces and generating app bundles.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
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: diagnostics.filter((diagnostic) => diagnostic.severity === "error").length === 0 &&
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 }>}