@moku-labs/ci 1.5.3 → 1.5.5

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/README.md CHANGED
@@ -119,7 +119,7 @@ flowchart LR
119
119
  | [`examples/package/publish.local-publish.yml`](examples/package/publish.local-publish.yml) | Fallback: publish from the project's own job. Use it only if the central publish fails npm auth. |
120
120
  | [`rulesets/main.json`](rulesets/main.json) | Branch ruleset for `main`: PR only, no force-push, the four `ci / …` checks required. |
121
121
  | [`examples/dependabot.yml`](examples/dependabot.yml) | Dependabot config: one grouped PR a day for `@moku-labs/*` only. `setup` writes it to `.github/dependabot.yml`. |
122
- | [`examples/org/fleet.yml`](examples/org/fleet.yml) | For the organization's `.github` repository. Every 15 minutes it runs `moku-release fleet --markdown` and puts the table between `<!-- fleet:start -->` and `<!-- fleet:end -->` of `profile/README.md`. |
122
+ | [`examples/org/fleet.yml`](examples/org/fleet.yml) | For the organization's `.github` repository. Every 15 minutes it runs `moku-release fleet --markdown --deps` and puts the table between `<!-- fleet:start -->` and `<!-- fleet:end -->` of `profile/README.md`. |
123
123
  | [`examples/package/dependabot-automerge.yml`](examples/package/dependabot-automerge.yml) | Turns on auto-merge for a Dependabot PR; the ruleset checks stay the gate. After the merge it dispatches `publish.yml` with `patch`, because a merge made by `GITHUB_TOKEN` starts no push run. `setup` writes it and allows auto-merge on the repo. |
124
124
 
125
125
  > [!TIP]
@@ -209,7 +209,7 @@ Examples: `examples/package/ci-with-demos.yml` (label `demos:full`) and
209
209
  | `bun run release:setup` | once per project | Idempotent wizard: workflows, Dependabot, script contract, first publish, first tag, trusted publisher, branch ruleset, auto-merge, then `doctor`. `--dry-run` prints every action, changes nothing and asks nothing. `--yes` answers every confirmation for an agent or CI; a step that needs your npm OTP is then printed, not run. |
210
210
  | `bun run release:doctor` | any time | Changes nothing in the project; it only runs `git fetch --tags` first. Twelve checks, one line each, and the exact `fix:` command for every red line. `--json` for machines. |
211
211
  | `bun run release wait` | after each merge to `main` | Refuses unless `origin/main` holds `HEAD`. Waits for the first release tag that holds the commit, then for npm to serve that version. Stops on a failed run and prints the log command. |
212
- | `bun run release fleet` | any time | Read-only. One table for every repository of the organization: CI on `main`, the release of `main`, tag against npm, commits nobody released, open PRs, old `@moku-labs/*` pins. Then the list of what waits for a person. `--markdown` prints the public rows for the organization page, `--json` every row. |
212
+ | `bun run release fleet` | any time | Read-only. One table for every repository of the organization: CI on `main`, the release of `main`, tag against npm, commits nobody released, open PRs, old `@moku-labs/*` pins. Then the list of what waits for a person. `--markdown` prints the public rows for the organization page, `--json` every row. `--deps` adds two tables: the shared toolchain with the version most repositories are on, and the runtime dependencies that are behind npm. Only the dependencies a manifest names are read. |
213
213
  | `bun run release <patch\|minor\|major\|prerelease>` | a release a person asks for | Refuses unless the tree is clean and `HEAD == origin/main`. Dispatches `publish.yml`, watches the run, verifies the version and dist-tag on npm. |
214
214
 
215
215
  The scripts are plain aliases of the `moku-release` bin. Internals and the list of checks:
package/dist/release.mjs CHANGED
@@ -2055,7 +2055,7 @@ const PROFILE_REPO = ".github";
2055
2055
  /** Matches a release tag, prereleases included: `v1.2.3`, `v1.2.3-rc.0`. Not the moving `v1`. */
2056
2056
  const RELEASE_TAG = /^v\d+\.\d+\.\d+/;
2057
2057
  /** Matches a dependency pinned to one exact version. A range is not a pin and is not judged. */
2058
- const EXACT_VERSION = /^\d+\.\d+\.\d+/;
2058
+ const EXACT_VERSION$1 = /^\d+\.\d+\.\d+/;
2059
2059
  /** The query `gh api graphql -F org=<org>` runs. One call answers the whole table. */
2060
2060
  const FLEET_QUERY = `query($org: String!) {
2061
2061
  organization(login: $org) {
@@ -2064,6 +2064,7 @@ const FLEET_QUERY = `query($org: String!) {
2064
2064
  name
2065
2065
  isPrivate
2066
2066
  manifest: object(expression: "HEAD:package.json") { ... on Blob { text } }
2067
+ bun: object(expression: "HEAD:.bun-version") { ... on Blob { text } }
2067
2068
  publish: object(expression: "HEAD:.github/workflows/publish.yml") { ... on Blob { byteSize } }
2068
2069
  defaultBranchRef {
2069
2070
  target {
@@ -2173,7 +2174,7 @@ function familyPins(manifest) {
2173
2174
  ...manifest.devDependencies,
2174
2175
  ...manifest.dependencies
2175
2176
  };
2176
- return Object.fromEntries(Object.entries(all).filter(([name, spec]) => name.startsWith(FAMILY_SCOPE) && EXACT_VERSION.test(spec)));
2177
+ return Object.fromEntries(Object.entries(all).filter(([name, spec]) => name.startsWith(FAMILY_SCOPE) && EXACT_VERSION$1.test(spec)));
2177
2178
  }
2178
2179
  /**
2179
2180
  * Whether a check belongs to `publish.yml`.
@@ -2200,6 +2201,17 @@ function repoUrl(org, repo, path = "") {
2200
2201
  return `https://github.com/${org}/${repo.name}${path}`;
2201
2202
  }
2202
2203
  /**
2204
+ * The entries of a dependency map that are not family packages.
2205
+ *
2206
+ * @param dependencies - A dependency map of a manifest, when it has one.
2207
+ * @returns The same map without `@moku-labs/*`.
2208
+ * @example
2209
+ * outsideFamily({ "@moku-labs/core": "1.7.2", preact: "10.27.0" }); // { preact: "10.27.0" }
2210
+ */
2211
+ function outsideFamily(dependencies) {
2212
+ return Object.fromEntries(Object.entries(dependencies ?? {}).filter(([name]) => !name.startsWith(FAMILY_SCOPE)));
2213
+ }
2214
+ /**
2203
2215
  * Turn one repository of the answer into a row. The pins are judged later, once the
2204
2216
  * newest version of every package is known.
2205
2217
  *
@@ -2211,6 +2223,7 @@ function repoUrl(org, repo, path = "") {
2211
2223
  function toRepo(raw) {
2212
2224
  const manifest = parseManifest(raw.manifest?.text ?? void 0);
2213
2225
  const head = raw.defaultBranchRef?.target;
2226
+ const bun = raw.bun?.text?.trim();
2214
2227
  const checks = (head?.statusCheckRollup?.contexts?.nodes ?? []).filter((check) => check?.name !== void 0);
2215
2228
  const publishes = raw.publish?.byteSize !== void 0 && manifest.private !== true && manifest.name !== void 0;
2216
2229
  const newest = publishes ? newestRelease(raw) : void 0;
@@ -2232,7 +2245,14 @@ function toRepo(raw) {
2232
2245
  state: rollupState(pr.commits?.nodes?.[0]?.commit?.statusCheckRollup?.state)
2233
2246
  })),
2234
2247
  pins: [],
2235
- pinned: familyPins(manifest)
2248
+ pinned: familyPins(manifest),
2249
+ external: {
2250
+ runtime: outsideFamily(manifest.dependencies),
2251
+ dev: {
2252
+ ...outsideFamily(manifest.devDependencies),
2253
+ ...bun === void 0 || bun === "" ? {} : { bun }
2254
+ }
2255
+ }
2236
2256
  };
2237
2257
  }
2238
2258
  /**
@@ -2369,6 +2389,160 @@ function renderFleetMarkdown(org, repos) {
2369
2389
  ].join("\n");
2370
2390
  }
2371
2391
  //#endregion
2392
+ //#region src/lib/fleet-deps.ts
2393
+ /** A tool counts as shared once this many repositories use it. */
2394
+ const SHARED_FROM = 2;
2395
+ /** Matches a dependency pinned to one exact version. */
2396
+ const EXACT_VERSION = /^\d+\.\d+\.\d+/;
2397
+ /** Matches a range that names a lowest version: `^1.2.3`, `~1.2.3`, `>=1.2.3`. */
2398
+ const RANGE_PREFIX = /^(?:\^|~|>=)\s*(?=\d+\.\d+\.\d+)/;
2399
+ /**
2400
+ * Whether a written version is older than the newest one. An exact version is compared in
2401
+ * full. A range is behind only when the newest major is above its own: inside the major it
2402
+ * already takes the newest. Anything else (`*`, a tag, a URL) is not judged.
2403
+ *
2404
+ * @param spec - The version as the manifest writes it.
2405
+ * @param latest - The newest version on npm.
2406
+ * @returns `true` when the dependency is behind.
2407
+ * @example
2408
+ * isBehind("^5.1.0", "6.0.0"); // true
2409
+ */
2410
+ function isBehind(spec, latest) {
2411
+ if (EXACT_VERSION.test(spec)) return compareSemver(spec, latest) < 0;
2412
+ if (!RANGE_PREFIX.test(spec)) return false;
2413
+ return (parseSemver(spec.replace(RANGE_PREFIX, "")).core[0] ?? 0) < (parseSemver(latest).core[0] ?? 0);
2414
+ }
2415
+ /**
2416
+ * Order two written versions, newest first. Two exact versions are compared as semver,
2417
+ * anything else as text, so the order is stable for a range too.
2418
+ *
2419
+ * @param left - One written version.
2420
+ * @param right - Another written version.
2421
+ * @returns Negative when `left` is the newer one.
2422
+ * @example
2423
+ * newestFirst("1.4.2", "1.3.14"); // negative
2424
+ */
2425
+ function newestFirst(left, right) {
2426
+ return EXACT_VERSION.test(left) && EXACT_VERSION.test(right) ? compareSemver(right, left) : right.localeCompare(left);
2427
+ }
2428
+ /**
2429
+ * The version most repositories are on. On a tie the newer version wins.
2430
+ *
2431
+ * @param versions - The version each repository writes, one entry per repository.
2432
+ * @returns The accepted version, or an empty string for an empty list.
2433
+ * @example
2434
+ * acceptedVersion(["6.0.3", "6.0.3", "7.0.2"]); // "6.0.3"
2435
+ */
2436
+ function acceptedVersion(versions) {
2437
+ const counts = /* @__PURE__ */ new Map();
2438
+ for (const version of versions) counts.set(version, (counts.get(version) ?? 0) + 1);
2439
+ const [accepted = ""] = [...counts.keys()].toSorted((a, b) => (counts.get(b) ?? 0) - (counts.get(a) ?? 0) || newestFirst(a, b));
2440
+ return accepted;
2441
+ }
2442
+ /**
2443
+ * The toolchain table: every dev dependency that at least two repositories use, with the
2444
+ * version most of them are on. On a tie the newer version is the accepted one.
2445
+ *
2446
+ * @param repos - The rows of the fleet.
2447
+ * @returns One row per shared tool, the ones with a repository on another version first.
2448
+ * @example
2449
+ * const tools = toolchainRows(repos);
2450
+ */
2451
+ function toolchainRows(repos) {
2452
+ const uses = /* @__PURE__ */ new Map();
2453
+ for (const repo of repos) for (const [name, version] of Object.entries(repo.external.dev)) uses.set(name, [...uses.get(name) ?? [], {
2454
+ repo: repo.name,
2455
+ version
2456
+ }]);
2457
+ return [...uses.entries()].filter(([, users]) => users.length >= SHARED_FROM).map(([name, users]) => {
2458
+ const accepted = acceptedVersion(users.map((use) => use.version));
2459
+ return {
2460
+ name,
2461
+ accepted,
2462
+ users: users.length,
2463
+ others: users.filter((use) => use.version !== accepted)
2464
+ };
2465
+ }).toSorted((a, b) => Number(b.others.length > 0) - Number(a.others.length > 0) || a.name.localeCompare(b.name));
2466
+ }
2467
+ /**
2468
+ * Every package name whose newest version the two tables need.
2469
+ *
2470
+ * @param repos - The rows of the fleet.
2471
+ * @returns The names of the shared tools and of every runtime dependency, each once.
2472
+ * @example
2473
+ * const names = externalNames(repos);
2474
+ */
2475
+ function externalNames(repos) {
2476
+ const runtime = repos.flatMap((repo) => Object.keys(repo.external.runtime));
2477
+ return [.../* @__PURE__ */ new Set([...toolchainRows(repos).map((row) => row.name), ...runtime])].toSorted();
2478
+ }
2479
+ /**
2480
+ * The runtime table: every runtime dependency that is behind the newest version on npm.
2481
+ *
2482
+ * @param repos - The rows of the fleet.
2483
+ * @param latest - Package name to its newest version on npm.
2484
+ * @returns One row per dependency and repository, by package name.
2485
+ * @example
2486
+ * const behind = runtimeRows(repos, new Map([["preact", "10.27.0"]]));
2487
+ */
2488
+ function runtimeRows(repos, latest) {
2489
+ return repos.flatMap((repo) => Object.entries(repo.external.runtime).flatMap(([name, version]) => {
2490
+ const newest = latest.get(name);
2491
+ return newest !== void 0 && isBehind(version, newest) ? [{
2492
+ name,
2493
+ repo: repo.name,
2494
+ version,
2495
+ latest: newest
2496
+ }] : [];
2497
+ })).toSorted((a, b) => a.name.localeCompare(b.name) || a.repo.localeCompare(b.repo));
2498
+ }
2499
+ /**
2500
+ * The "not on it" cell of a toolchain row.
2501
+ *
2502
+ * @param row - The row.
2503
+ * @returns E.g. `game 2.4.2, core 7.0.2`, or a dash when every repository is on the accepted version.
2504
+ * @example
2505
+ * othersCell(row); // "game 2.4.2"
2506
+ */
2507
+ function othersCell(row) {
2508
+ if (row.others.length === 0) return "—";
2509
+ return row.others.map((use) => `${use.repo} ${use.version}`).join(", ");
2510
+ }
2511
+ /**
2512
+ * Render both tables as two collapsed Markdown blocks, for the organization page.
2513
+ *
2514
+ * @param tools - The toolchain rows, with `latest` filled.
2515
+ * @param runtime - The runtime rows.
2516
+ * @returns The Markdown, without a trailing newline.
2517
+ * @example
2518
+ * renderDepsMarkdown(tools, runtime);
2519
+ */
2520
+ function renderDepsMarkdown(tools, runtime) {
2521
+ const split = tools.filter((row) => row.others.length > 0).length;
2522
+ const old = tools.filter((row) => row.latest !== void 0 && isBehind(row.accepted, row.latest));
2523
+ return [
2524
+ "<details>",
2525
+ `<summary>Toolchain: ${tools.length} shared tools, ${split} on more than one version, ${old.length} behind npm</summary>`,
2526
+ "",
2527
+ "| Tool | Accepted | npm | Used by | Not on it |",
2528
+ "| --- | --- | --- | --- | --- |",
2529
+ ...tools.map((row) => `| ${row.name} | ${row.accepted} | ${row.latest ?? "—"} | ${row.users} | ${othersCell(row)} |`),
2530
+ "",
2531
+ "</details>",
2532
+ "",
2533
+ "<details>",
2534
+ `<summary>Runtime dependencies: ${runtime.length} behind npm</summary>`,
2535
+ "",
2536
+ ...runtime.length === 0 ? ["Every runtime dependency is on the newest version."] : [
2537
+ "| Package | Repo | Version | npm |",
2538
+ "| --- | --- | --- | --- |",
2539
+ ...runtime.map((row) => `| ${row.name} | ${row.repo} | ${row.version} | ${row.latest} |`)
2540
+ ],
2541
+ "",
2542
+ "</details>"
2543
+ ].join("\n");
2544
+ }
2545
+ //#endregion
2372
2546
  //#region src/commands/fleet.ts
2373
2547
  /**
2374
2548
  * The organization the command reads: the owner of this checkout's remote, or of the
@@ -2461,6 +2635,133 @@ function renderTerminal(ui, repos) {
2461
2635
  if (waiting.length === 0) ui.check(true, "nothing waits");
2462
2636
  for (const line of waiting) ui.check(false, line);
2463
2637
  }
2638
+ /** How many `npm view` calls run at once. The registry answers each in under a second. */
2639
+ const REGISTRY_BATCH = 8;
2640
+ /**
2641
+ * The newest version on npm of every named package, read a few at a time.
2642
+ *
2643
+ * @param ctx - The ports and flags.
2644
+ * @param names - The package names.
2645
+ * @returns Package name to its newest version; a package the registry did not answer for is left out.
2646
+ * @example
2647
+ * const latest = await newestVersions(ctx, ["typescript", "preact"]);
2648
+ */
2649
+ async function newestVersions(ctx, names) {
2650
+ const latest = /* @__PURE__ */ new Map();
2651
+ for (let start = 0; start < names.length; start += REGISTRY_BATCH) {
2652
+ const batch = names.slice(start, start + REGISTRY_BATCH);
2653
+ const views = await Promise.all(batch.map((name) => ctx.exec.capture("npm", [
2654
+ "view",
2655
+ name,
2656
+ "version"
2657
+ ])));
2658
+ for (const [index, view] of views.entries()) {
2659
+ const name = batch[index];
2660
+ if (name !== void 0 && view.code === 0 && view.stdout.trim() !== "") latest.set(name, view.stdout.trim());
2661
+ }
2662
+ }
2663
+ return latest;
2664
+ }
2665
+ /**
2666
+ * Build both dependency tables: the shared toolchain, and the runtime dependencies that
2667
+ * npm has moved past.
2668
+ *
2669
+ * @param ctx - The ports and flags.
2670
+ * @param repos - The rows the tables are built from.
2671
+ * @returns The toolchain rows with `latest` filled, and the runtime rows.
2672
+ * @example
2673
+ * const tables = await dependencyTables(ctx, repos);
2674
+ */
2675
+ async function dependencyTables(ctx, repos) {
2676
+ const latest = await newestVersions(ctx, externalNames(repos));
2677
+ return {
2678
+ toolchain: toolchainRows(repos).map((row) => {
2679
+ const newest = latest.get(row.name);
2680
+ return newest === void 0 ? row : {
2681
+ ...row,
2682
+ latest: newest
2683
+ };
2684
+ }),
2685
+ runtime: runtimeRows(repos, latest)
2686
+ };
2687
+ }
2688
+ /**
2689
+ * Print both dependency tables, for a person at a terminal.
2690
+ *
2691
+ * @param ui - The branded console.
2692
+ * @param toolchain - The toolchain rows.
2693
+ * @param runtime - The runtime rows.
2694
+ * @example
2695
+ * renderDepsTerminal(ui, toolchain, runtime);
2696
+ */
2697
+ function renderDepsTerminal(ui, toolchain, runtime) {
2698
+ ui.heading("Toolchain");
2699
+ ui.line([
2700
+ "tool".padEnd(28),
2701
+ "accepted".padEnd(14),
2702
+ "npm".padEnd(14),
2703
+ "not on it"
2704
+ ].join(" "));
2705
+ for (const row of toolchain) ui.line([
2706
+ row.name.padEnd(28),
2707
+ row.accepted.padEnd(14),
2708
+ (row.latest ?? "—").padEnd(14),
2709
+ othersCell(row)
2710
+ ].join(" "));
2711
+ ui.heading("Runtime dependencies behind npm");
2712
+ if (runtime.length === 0) ui.check(true, "every runtime dependency is on the newest version");
2713
+ for (const row of runtime) ui.line([
2714
+ row.name.padEnd(28),
2715
+ row.repo.padEnd(10),
2716
+ row.version.padEnd(14),
2717
+ row.latest
2718
+ ].join(" "));
2719
+ }
2720
+ /**
2721
+ * Print every row as JSON: the rows alone, or the rows with both dependency tables.
2722
+ *
2723
+ * @param ui - The branded console.
2724
+ * @param repos - The rows.
2725
+ * @param tables - The dependency tables, when asked for.
2726
+ * @example
2727
+ * printJson(ui, repos, tables);
2728
+ */
2729
+ function printJson(ui, repos, tables) {
2730
+ const output = tables === void 0 ? repos : {
2731
+ repos,
2732
+ ...tables
2733
+ };
2734
+ ui.line(JSON.stringify(output, void 0, 2));
2735
+ }
2736
+ /**
2737
+ * Print the public rows as Markdown, then the dependency tables as collapsed blocks.
2738
+ *
2739
+ * @param ui - The branded console.
2740
+ * @param org - The organization login.
2741
+ * @param repos - The rows.
2742
+ * @param tables - The dependency tables, when asked for.
2743
+ * @example
2744
+ * printMarkdown(ui, "moku-labs", repos, tables);
2745
+ */
2746
+ function printMarkdown(ui, org, repos, tables) {
2747
+ ui.line(renderFleetMarkdown(org, repos));
2748
+ if (tables === void 0) return;
2749
+ ui.line("");
2750
+ ui.line(renderDepsMarkdown(tables.toolchain, tables.runtime));
2751
+ }
2752
+ /**
2753
+ * Print the table for a terminal, then the dependency tables.
2754
+ *
2755
+ * @param ui - The branded console.
2756
+ * @param repos - The rows.
2757
+ * @param tables - The dependency tables, when asked for.
2758
+ * @example
2759
+ * printTerminal(ui, repos, tables);
2760
+ */
2761
+ function printTerminal(ui, repos, tables) {
2762
+ renderTerminal(ui, repos);
2763
+ if (tables !== void 0) renderDepsTerminal(ui, tables.toolchain, tables.runtime);
2764
+ }
2464
2765
  /**
2465
2766
  * Read the state of every repository of the organization and print it.
2466
2767
  *
@@ -2470,7 +2771,7 @@ function renderTerminal(ui, repos) {
2470
2771
  * const code = await runFleet({ ctx, ui });
2471
2772
  */
2472
2773
  async function runFleet(options) {
2473
- const { ctx, ui, json = false, markdown = false } = options;
2774
+ const { ctx, ui, json = false, markdown = false, deps = false } = options;
2474
2775
  if (!(json || markdown)) ui.lockup({
2475
2776
  wordmark: "moku release",
2476
2777
  label: "fleet"
@@ -2494,9 +2795,11 @@ async function runFleet(options) {
2494
2795
  return 1;
2495
2796
  }
2496
2797
  const repos = await withRegistry(ctx, parsed);
2497
- if (json) ui.line(JSON.stringify(repos, void 0, 2));
2498
- else if (markdown) ui.line(renderFleetMarkdown(org, repos));
2499
- else renderTerminal(ui, repos);
2798
+ const shown = markdown ? repos.filter((repo) => !repo.isPrivate) : repos;
2799
+ const tables = deps ? await dependencyTables(ctx, shown) : void 0;
2800
+ if (json) printJson(ui, repos, tables);
2801
+ else if (markdown) printMarkdown(ui, org, repos, tables);
2802
+ else printTerminal(ui, repos, tables);
2500
2803
  return 0;
2501
2804
  }
2502
2805
  //#endregion
@@ -3364,7 +3667,7 @@ async function runWait(options) {
3364
3667
  * dispatcher.
3365
3668
  *
3366
3669
  * The grammar is deliberately tiny: one positional (a command name, or a semver bump that
3367
- * implies the `release` command) plus five flags. Anything unrecognized resolves to
3670
+ * implies the `release` command) plus six flags. Anything unrecognized resolves to
3368
3671
  * `help` with an `error` set — the CLI never guesses what an operator meant.
3369
3672
  */
3370
3673
  /** The semver bumps `moku-release <type>` accepts, in menu order. */
@@ -3378,6 +3681,7 @@ const RELEASE_TYPES = [
3378
3681
  const KNOWN_FLAGS = /* @__PURE__ */ new Set([
3379
3682
  "--json",
3380
3683
  "--markdown",
3684
+ "--deps",
3381
3685
  "--dry-run",
3382
3686
  "--yes",
3383
3687
  "-y"
@@ -3452,7 +3756,8 @@ function parseArgv(argv) {
3452
3756
  json,
3453
3757
  dryRun,
3454
3758
  yes,
3455
- markdown: flags.includes("--markdown")
3759
+ markdown: flags.includes("--markdown"),
3760
+ deps: flags.includes("--deps")
3456
3761
  };
3457
3762
  if (first === "help") return {
3458
3763
  command: "help",
@@ -3649,7 +3954,7 @@ const USAGE = [
3649
3954
  " moku-release setup one-time wizard: workflows, contract, first publish",
3650
3955
  " moku-release doctor [--json] read-only diagnosis of the release setup",
3651
3956
  " moku-release wait wait for the auto-release of HEAD, verify it on npm",
3652
- " moku-release fleet [--json|--markdown]",
3957
+ " moku-release fleet [--json|--markdown] [--deps]",
3653
3958
  " every repository of the organization in one table",
3654
3959
  ` moku-release <${RELEASE_TYPES.join("|")}>`,
3655
3960
  "",
@@ -3719,7 +4024,8 @@ async function dispatch(parsed, ctx, ui) {
3719
4024
  ctx,
3720
4025
  ui,
3721
4026
  json: parsed.json,
3722
- markdown: parsed.markdown === true
4027
+ markdown: parsed.markdown === true,
4028
+ deps: parsed.deps === true
3723
4029
  });
3724
4030
  if (parsed.command === "release" && parsed.releaseType !== void 0) return runRelease({
3725
4031
  ctx,
@@ -28,7 +28,7 @@ jobs:
28
28
  - name: Read the fleet
29
29
  env:
30
30
  GH_TOKEN: ${{ github.token }}
31
- run: npx --yes --package @moku-labs/ci@1 moku-release fleet --markdown > fleet.md
31
+ run: npx --yes --package @moku-labs/ci@1 moku-release fleet --markdown --deps > fleet.md
32
32
  - name: Put the table between the markers of profile/README.md
33
33
  run: |
34
34
  set -euo pipefail
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moku-labs/ci",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Central CI and release for the moku family: reusable workflows, caller examples and the moku-release CLI.",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "devDependencies": {
34
34
  "@biomejs/biome": "2.4.16",
35
- "@moku-labs/common": "0.3.4",
35
+ "@moku-labs/common": "0.3.6",
36
36
  "@types/bun": "1.3.14",
37
37
  "@vitest/coverage-istanbul": "4.0.18",
38
38
  "eslint": "9.39.3",