@mutmutco/cli 3.30.0 → 3.31.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.
Files changed (2) hide show
  1. package/dist/main.cjs +45 -20
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -9277,31 +9277,45 @@ async function activateProductRuleset(repo, rulesetBody, client) {
9277
9277
  }
9278
9278
 
9279
9279
  // src/workflow-context.ts
9280
- function parseWorkflowJobIds(yaml) {
9280
+ function parseWorkflowJobs(yaml) {
9281
9281
  const lines = yaml.split(/\r?\n/);
9282
9282
  let inJobs = false;
9283
9283
  let jobIndent = -1;
9284
- const ids = [];
9284
+ const jobs = [];
9285
+ let current = null;
9285
9286
  for (const line of lines) {
9286
9287
  if (!inJobs) {
9287
9288
  if (/^jobs:\s*$/.test(line)) inJobs = true;
9288
9289
  continue;
9289
9290
  }
9290
9291
  if (/^\S/.test(line) && !line.startsWith("#")) break;
9291
- const trimmed = line.trim();
9292
- if (!trimmed || trimmed.startsWith("#")) continue;
9293
9292
  const match = line.match(/^(\s+)([A-Za-z0-9_-]+):\s*$/);
9294
- if (!match) continue;
9295
- const indent = match[1].length;
9296
- if (jobIndent < 0) {
9297
- jobIndent = indent;
9298
- ids.push(match[2]);
9299
- continue;
9293
+ if (match) {
9294
+ const indent = match[1].length;
9295
+ if (jobIndent < 0) jobIndent = indent;
9296
+ if (indent === jobIndent) {
9297
+ current = { id: match[2], body: line, indent };
9298
+ jobs.push(current);
9299
+ continue;
9300
+ }
9301
+ if (indent < jobIndent) break;
9300
9302
  }
9301
- if (indent === jobIndent) ids.push(match[2]);
9302
- else if (indent < jobIndent) break;
9303
+ if (current) current.body += `
9304
+ ${line}`;
9303
9305
  }
9304
- return ids;
9306
+ return jobs;
9307
+ }
9308
+ function jobReportsPullRequestCheck(job) {
9309
+ const lines = job.body.split(/\r?\n/).slice(1);
9310
+ const propertyIndent = lines.filter((line) => line.trim() && !line.trimStart().startsWith("#")).map((line) => line.match(/^(\s+)/)?.[1].length ?? 0).filter((indent) => indent > job.indent).reduce((minimum, indent) => Math.min(minimum, indent), Number.POSITIVE_INFINITY);
9311
+ if (!Number.isFinite(propertyIndent)) return true;
9312
+ const directIf = lines.find((line) => {
9313
+ const match = line.match(/^(\s*)if:\s*(.+)$/);
9314
+ return match?.[1].length === propertyIndent;
9315
+ });
9316
+ if (!directIf) return true;
9317
+ const expression = directIf.replace(/^\s*if:\s*/, "").trim().replace(/^\$\{\{\s*/, "").replace(/\s*\}\}$/, "").trim();
9318
+ return !/^(?:github\.event_name\s*==\s*['"]push['"]|['"]push['"]\s*==\s*github\.event_name)$/.test(expression);
9305
9319
  }
9306
9320
  function workflowTriggersPullRequest(yaml) {
9307
9321
  const onBlock = extractOnBlock(yaml);
@@ -9340,7 +9354,9 @@ function collectPullRequestWorkflowContexts(workflows) {
9340
9354
  const contexts = /* @__PURE__ */ new Set();
9341
9355
  for (const wf of workflows) {
9342
9356
  if (!workflowTriggersPullRequest(wf.body)) continue;
9343
- for (const id of parseWorkflowJobIds(wf.body)) contexts.add(id);
9357
+ for (const job of parseWorkflowJobs(wf.body)) {
9358
+ if (jobReportsPullRequestCheck(job)) contexts.add(job.id);
9359
+ }
9344
9360
  }
9345
9361
  return [...contexts].sort((a, b) => a.localeCompare(b));
9346
9362
  }
@@ -9926,7 +9942,13 @@ async function auditRepoCi(repo, deps) {
9926
9942
  label: "delete_branch_on_merge enabled",
9927
9943
  detail: info.delete_branch_on_merge === true ? void 0 : "false or unavailable"
9928
9944
  });
9929
- const hasGateWorkflow = repoClass === "hub" ? true : repoClass === "content" ? true : await contentExists(deps, repo, baseBranch, PRODUCT_GATE_PATH);
9945
+ const hasCanonicalGateWorkflow = repoClass === "hub" ? true : repoClass === "content" ? true : await contentExists(deps, repo, baseBranch, PRODUCT_GATE_PATH);
9946
+ let prWorkflowPaths = repoClass === "deployable" && hasCanonicalGateWorkflow ? [PRODUCT_GATE_PATH] : [];
9947
+ if (repoClass === "deployable" && !hasCanonicalGateWorkflow) {
9948
+ const listedWorkflowPaths = await listWorkflowPaths(deps, repo, baseBranch);
9949
+ prWorkflowPaths = listedWorkflowPaths === void 0 ? [] : await filterPullRequestTriggered(deps, repo, baseBranch, listedWorkflowPaths);
9950
+ }
9951
+ const hasGateWorkflow = hasCanonicalGateWorkflow || prWorkflowPaths.length > 0;
9930
9952
  let emittedPrContexts;
9931
9953
  const getEmittedPrContexts = async () => {
9932
9954
  emittedPrContexts ??= await resolveEmittedPrContexts(deps, repo, baseBranch);
@@ -9947,7 +9969,7 @@ async function auditRepoCi(repo, deps) {
9947
9969
  checks.push({
9948
9970
  ok: hasGateWorkflow,
9949
9971
  label: `gate workflow committed on ${baseBranch}`,
9950
- detail: hasGateWorkflow ? `read ${PRODUCT_GATE_PATH} at refs/heads/${baseBranch}` : `missing ${PRODUCT_GATE_PATH} at refs/heads/${baseBranch}`,
9972
+ detail: hasCanonicalGateWorkflow ? `read ${PRODUCT_GATE_PATH} at refs/heads/${baseBranch}` : prWorkflowPaths.length > 0 ? `read PR workflows at refs/heads/${baseBranch}: ${prWorkflowPaths.join(", ")}` : `missing ${PRODUCT_GATE_PATH} and no PR-triggered workflows at refs/heads/${baseBranch}`,
9951
9973
  remediation: `mmi-cli bootstrap apply ${repo} --class deployable --execute (seeds gate.yml)`
9952
9974
  });
9953
9975
  checks.push({
@@ -9989,7 +10011,7 @@ async function auditRepoCi(repo, deps) {
9989
10011
  }
9990
10012
  }
9991
10013
  }
9992
- const workflowPaths = hasGateWorkflow && repoClass === "deployable" ? [PRODUCT_GATE_PATH] : [];
10014
+ const workflowPaths = repoClass === "deployable" ? prWorkflowPaths : [];
9993
10015
  const { policy, reason } = resolveMergeCiPolicy({
9994
10016
  workflowPaths,
9995
10017
  registryCi,
@@ -17810,9 +17832,12 @@ var DOC_PLACEHOLDER_PROMPTS = [
17810
17832
  "install, dev server",
17811
17833
  "lint, typecheck, repo gate script",
17812
17834
  "ports, env from vault not files",
17813
- "Describe the product/service",
17814
- "How to run, build, and test locally",
17815
- "System shape: components, data flow, deploy model",
17835
+ "top-level dir/module: what it is, in one line",
17836
+ "Owner/operator \u2014 who runs this day to day",
17837
+ "one human-readable command/steps to get this running locally",
17838
+ "the product/service/library, in one or two bullets",
17839
+ "(top-level dir)",
17840
+ "Languages, frameworks, datastores, major services",
17816
17841
  "Build/test commands, CI gate, deploy target"
17817
17842
  ];
17818
17843
  function unfilledDocPlaceholders(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "3.30.0",
3
+ "version": "3.31.0",
4
4
  "description": "MMI Future CLI — the org dev toolbox (board, registry, keyless secrets, release train, bootstrap, doctor) and the cross-IDE engine the plugin's session-start hook drives.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",