@intentius/chant 0.124.38 → 0.124.39

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.
@@ -327,9 +327,11 @@ export interface BuildTriggerEdit {
327
327
  * ref that never cancels a run in progress, so GitHub keeps the newest
328
328
  * push waiting and drops the ones between, and each tag's build runs in a
329
329
  * group of its own.
330
- * - `every`: the triggers stay, and the group is the commit itself on the
331
- * branch, so no push cancels or replaces another's run; elsewhere, such as
332
- * a pull request, a new push cancels the run it supersedes.
330
+ * - `every`: the triggers stay, with `workflow_dispatch` added so the tick
331
+ * can start a run the forge never started on a commit (#4216), and the
332
+ * group is the commit itself on the branch, so no push cancels or replaces
333
+ * another's run; elsewhere, such as a pull request or a `ci/build/<sha>`
334
+ * tag, a new push cancels the run it supersedes.
333
335
  *
334
336
  * With `compose` (#4220, a workspace declaring `ci.workflow`), both builds
335
337
  * also run on pushes to `chant/compose/**`: `newest` adds the filter to the
@@ -337,7 +339,7 @@ export interface BuildTriggerEdit {
337
339
  * and `every`'s group keys a compose branch's runs by commit, as the trunk
338
340
  * branch's.
339
341
  *
340
- * Only the top-level `on:` (for `newest`, or `every` with `compose`) and `concurrency:` blocks change,
342
+ * Only the top-level `on:` and `concurrency:` blocks change,
341
343
  * each under {@link BUILD_BLOCK_MARKER}; the rest of the file is kept as it
342
344
  * is. Throws when one workflow holds a phase of each build, which no one
343
345
  * concurrency group can serve.
@@ -375,12 +377,16 @@ export function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSour
375
377
  text = writeTopLevel(text, "concurrency", { group: `chant-build-${tag}-\${{ github.ref }}`, "cancel-in-progress": false });
376
378
  } else {
377
379
  const main = `refs/heads/${green.branch}`;
380
+ const on = triggersOf(isObject(doc) ? doc.on : undefined);
381
+ let next = on;
378
382
  // A push filter that lists branches gains the compose branches; one with no list already runs on every branch.
379
- const on = isObject(doc) && isObject(doc.on) ? doc.on : undefined;
380
- const pushOn = on && isObject(on.push) ? on.push : undefined;
381
- if (compose && on && pushOn && Array.isArray(pushOn.branches) && !pushOn.branches.includes(COMPOSE_BRANCHES)) {
382
- text = writeTopLevel(text, "on", { ...on, push: { ...pushOn, branches: [...pushOn.branches, COMPOSE_BRANCHES] } });
383
+ const pushOn = isObject(on.push) ? on.push : undefined;
384
+ if (compose && pushOn && Array.isArray(pushOn.branches) && !pushOn.branches.includes(COMPOSE_BRANCHES)) {
385
+ next = { ...next, push: { ...pushOn, branches: [...pushOn.branches, COMPOSE_BRANCHES] } };
383
386
  }
387
+ // #4216: the tick starts the workflow by hand at a ci/build/<sha> tag when the forge ran nothing on a commit.
388
+ if (!("workflow_dispatch" in next)) next = { ...next, workflow_dispatch: {} };
389
+ if (next !== on) text = writeTopLevel(text, "on", next);
384
390
  // #4220: a compose branch's commits each get a verdict, so its runs are keyed by commit, as the trunk branch's are.
385
391
  const keyed = compose ? `(github.ref == '${main}' || startsWith(github.ref, 'refs/heads/${COMPOSE_BRANCH_PREFIX}'))` : `github.ref == '${main}'`;
386
392
  const unkeyed = compose ? `!${keyed}` : `github.ref != '${main}'`;
@@ -396,6 +402,14 @@ export function buildTriggerEdits(green: CiGreen, sources: readonly WorkflowSour
396
402
  return out;
397
403
  }
398
404
 
405
+ /** A workflow's `on:` as a map: `on: push` and `on: [push, pull_request]` name triggers with no settings. */
406
+ function triggersOf(on: unknown): Record<string, unknown> {
407
+ if (isObject(on)) return on;
408
+ if (typeof on === "string") return { [on]: null };
409
+ if (Array.isArray(on)) return Object.fromEntries(on.filter((t): t is string => typeof t === "string").map((t) => [t, null]));
410
+ return {};
411
+ }
412
+
399
413
  /**
400
414
  * Replace the top-level `key:` block of a YAML file with `value` (or with
401
415
  * `value`'s lines, the key's own line first), under