tegami 1.1.2 → 1.1.3

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.
@@ -1,4 +1,4 @@
1
- import { f as Tegami, j as Draft, t as Awaitable, v as PublishPlan } from "../types-B50RK1rR.js";
1
+ import { f as Tegami, j as Draft, t as Awaitable, v as PublishPlan } from "../types-DEyZ2r-2.js";
2
2
 
3
3
  //#region src/cli/index.d.ts
4
4
  interface TegamiCLIOptions {
package/dist/cli/index.js CHANGED
@@ -279,11 +279,12 @@ function createTegamiCliRegistry(tegami) {
279
279
  };
280
280
  commands.set(definition.path.join("\0"), definition);
281
281
  const api = {
282
- option(name, { short, description = "", type }) {
282
+ option(name, { short, description = "", type, multiple }) {
283
283
  definition.options.push({
284
284
  name,
285
285
  short,
286
286
  type,
287
+ multiple,
287
288
  description
288
289
  });
289
290
  return api;
@@ -295,6 +296,14 @@ function createTegamiCliRegistry(tegami) {
295
296
  });
296
297
  return api;
297
298
  },
299
+ positionals(name) {
300
+ definition.positionals.push({
301
+ name,
302
+ required: false,
303
+ multiple: true
304
+ });
305
+ return api;
306
+ },
298
307
  action(fn) {
299
308
  definition.action = fn;
300
309
  }
@@ -332,7 +341,12 @@ function createTegamiCliRegistry(tegami) {
332
341
  };
333
342
  }
334
343
  function formatUsage(command) {
335
- return [...command.path, ...command.positionals.map((positional) => positional.required ? `<${positional.name}>` : `[${positional.name}]`)].join(" ");
344
+ const s = [...command.path];
345
+ for (const positional of command.positionals) {
346
+ let c = positional.multiple ? `...${positional.name}` : positional.name;
347
+ s.push(positional.required ? `<${c}>` : `[${c}]`);
348
+ }
349
+ return s.join(" ");
336
350
  }
337
351
  function findCommand(commands, argv) {
338
352
  if (argv.length === 0) return commands.get("");
@@ -343,28 +357,34 @@ function findCommand(commands, argv) {
343
357
  }
344
358
  }
345
359
  function parseCommandArgs(command, args) {
346
- const optionConfig = { help: {
360
+ const optionsConfig = { help: {
347
361
  type: "boolean",
348
362
  short: "h"
349
363
  } };
350
- for (const option of command.options) optionConfig[option.name] = option.short ? {
351
- type: option.type,
352
- short: option.short
353
- } : { type: option.type };
364
+ for (const option of command.options) {
365
+ const config = optionsConfig[option.name] = { type: option.type };
366
+ if (option.short) config.short = option.short;
367
+ if (option.multiple) config.multiple = true;
368
+ }
354
369
  const parsed = parseArgs({
355
370
  args,
356
- options: optionConfig,
371
+ options: optionsConfig,
357
372
  strict: true,
358
373
  allowPositionals: command.positionals.length > 0
359
374
  });
360
375
  const positionals = {};
361
- if (parsed.positionals.length > command.positionals.length) throw new Error("Too many arguments");
362
376
  for (const positional of command.positionals) {
377
+ if (positional.multiple) {
378
+ const value = positionals[positional.name] = [];
379
+ while (parsed.positionals.length > 0) value.push(parsed.positionals.shift());
380
+ continue;
381
+ }
363
382
  const value = parsed.positionals.shift();
364
383
  if (value === void 0 && positional.required) throw new Error(`missing required argument: ${positional.name}`);
365
384
  if (value === void 0) continue;
366
385
  positionals[positional.name] = value;
367
386
  }
387
+ if (parsed.positionals.length > 0) throw new Error("Too many arguments");
368
388
  return {
369
389
  values: parsed.values,
370
390
  positionals
@@ -471,9 +491,10 @@ function registerCoreCommands(cli, tegami, options) {
471
491
  cli.command("publish", { description: "publish packages from the publish lock" }).option("dry-run", {
472
492
  type: "boolean",
473
493
  description: "validate the publish lock without publishing"
474
- }).action(async ({ values }) => {
494
+ }).positionals("packages").action(async ({ values, positionals }) => {
475
495
  await publishPackages(tegami, {
476
496
  dryRun: values["dry-run"],
497
+ packages: positionals.packages?.length ? positionals.packages : void 0,
477
498
  cli: options
478
499
  });
479
500
  });
@@ -538,7 +559,10 @@ async function publishPackages(tegami, options) {
538
559
  intro(dryRun ? "Publish packages (dry run)" : "Publish packages");
539
560
  const s = spinner();
540
561
  s.start(dryRun ? "Validating publish lock" : "Publishing packages");
541
- const plan = customPublish ? await customPublish() : await tegami.publish({ dryRun });
562
+ const plan = customPublish ? await customPublish() : await tegami.publish({
563
+ dryRun,
564
+ packages: options.packages
565
+ });
542
566
  if (plan === "skipped") {
543
567
  s.stop(dryRun ? "No publish lock to validate" : "Nothing to publish");
544
568
  outro(`No publishable packages were found in ${context.lockPath}.`);
@@ -1,4 +1,4 @@
1
- import { r as LogGenerator } from "../types-B50RK1rR.js";
1
+ import { r as LogGenerator } from "../types-DEyZ2r-2.js";
2
2
 
3
3
  //#region src/generators/simple.d.ts
4
4
  declare function simpleGenerator(): LogGenerator;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as WorkspacePackage, M as DraftPolicy, N as PackageDraft, O as PackageGraph, P as BumpType, _ as PublishOptions, a as PublishPreflight, b as TegamiContext, c as TegamiPluginOption, d as GenerateChangelogOptions, f as Tegami, g as PackagePublishResult, h as PackagePublishPlan, i as PackageOptions, j as Draft, k as PackageGroup, m as PublishLock, n as GroupOptions, o as TegamiOptions, p as tegami, r as LogGenerator, s as TegamiPlugin, v as PublishPlan, y as CommitChangelog } from "./types-B50RK1rR.js";
1
+ import { A as WorkspacePackage, M as DraftPolicy, N as PackageDraft, O as PackageGraph, P as BumpType, _ as PublishOptions, a as PublishPreflight, b as TegamiContext, c as TegamiPluginOption, d as GenerateChangelogOptions, f as Tegami, g as PackagePublishResult, h as PackagePublishPlan, i as PackageOptions, j as Draft, k as PackageGroup, m as PublishLock, n as GroupOptions, o as TegamiOptions, p as tegami, r as LogGenerator, s as TegamiPlugin, v as PublishPlan, y as CommitChangelog } from "./types-DEyZ2r-2.js";
2
2
  export { type BumpType, type CommitChangelog, type Draft, type DraftPolicy, GenerateChangelogOptions, type GroupOptions, type LogGenerator, type PackageDraft, PackageGraph, type PackageGroup, type PackageOptions, type PackagePublishPlan, type PackagePublishResult, type PublishLock, type PublishOptions, type PublishPlan, type PublishPreflight, Tegami, type TegamiContext, type TegamiOptions, type TegamiPlugin, type TegamiPluginOption, WorkspacePackage, tegami };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { n as generateFromCommits } from "./generate-Bg86OJP4.js";
2
2
  import { r as handlePluginError } from "./error-BhMYq9iW.js";
3
3
  import { a as parseChangelogFile, o as readChangelogEntries, t as createDraft } from "./draft-CzUiQasJ.js";
4
- import { a as runPreflights, i as publishPlanStatus, o as runPublishPlan, r as initPublishPlan, t as npm } from "./npm-DoPhFKji.js";
4
+ import { a as runPreflights, i as publishPlanStatus, o as runPublishPlan, r as initPublishPlan, t as npm } from "./npm-CaBYeOeV.js";
5
5
  import { n as WorkspacePackage, t as PackageGraph } from "./graph-BmXTJZxx.js";
6
6
  import { mkdir, rm, writeFile } from "node:fs/promises";
7
7
  import path, { join } from "node:path";
@@ -98,9 +98,9 @@ function tegami(options = {}) {
98
98
  }
99
99
  return createDraft(changelogs, context);
100
100
  },
101
- async publishStatus() {
101
+ async publishStatus(publishOptions = {}) {
102
102
  const context = await getContextResolved();
103
- const plan = await initPublishPlan(context, {});
103
+ const plan = await initPublishPlan(context, publishOptions);
104
104
  if (!plan) return "idle";
105
105
  await runPreflights(context, plan);
106
106
  return publishPlanStatus(plan, context);
@@ -115,9 +115,9 @@ function tegami(options = {}) {
115
115
  if (Array.from(plan.packages.values()).every((pkg) => pkg.publishResult.type === "skipped")) return "skipped";
116
116
  return plan;
117
117
  },
118
- async cleanup() {
118
+ async cleanup(publishOptions = {}) {
119
119
  const context = await getContextResolved();
120
- const plan = await initPublishPlan(context, {});
120
+ const plan = await initPublishPlan(context, publishOptions);
121
121
  if (!plan) return {
122
122
  state: "skipped",
123
123
  reason: "no-plan"
@@ -8,7 +8,7 @@ import fs, { readFile, writeFile } from "node:fs/promises";
8
8
  import path, { join } from "node:path";
9
9
  import { x } from "tinyexec";
10
10
  import * as semver$1 from "semver";
11
- import { parse as parse$1, parseDocument, stringify } from "yaml";
11
+ import { parseDocument } from "yaml";
12
12
  import { detect } from "package-manager-detector";
13
13
  import { tmpdir } from "node:os";
14
14
  import { intro, note, outro } from "@clack/prompts";
@@ -59,82 +59,115 @@ async function initPublishPlan(context, options) {
59
59
  return plan;
60
60
  }
61
61
  function resolvePublishTargets(plan) {
62
- const ordered = [];
63
- const scanned = /* @__PURE__ */ new Set();
64
- function scan(id, stack) {
62
+ /** the iteration order = publish order */
63
+ const orderedMap = /* @__PURE__ */ new Map();
64
+ /** package id -> true while scanning hard wait, false while scanning optional wait */
65
+ const stack = /* @__PURE__ */ new Map();
66
+ function scan(id) {
65
67
  const preflight = plan.packages.get(id)?.preflight;
66
68
  if (!preflight || !preflight.shouldPublish) return;
67
- if (stack.has(id)) throw new Error(`circular reference of deps: ${[...stack, id].join(" -> ")}`);
68
- if (scanned.has(id)) return;
69
+ switch (stack.get(id)) {
70
+ case true: throw new Error(`circular reference of deps: ${[...stack.keys(), id].join(" -> ")}`);
71
+ case false: return;
72
+ }
73
+ if (orderedMap.has(id)) return;
74
+ let split = false;
69
75
  if (preflight.wait) {
70
- stack.add(id);
71
- for (const dep of preflight.wait) scan(dep, stack);
72
- stack.delete(id);
76
+ stack.set(id, true);
77
+ split ||= preflight.wait.length > 0;
78
+ for (const dep of preflight.wait) scan(dep);
79
+ }
80
+ if (preflight.optionalWait) {
81
+ stack.set(id, false);
82
+ split ||= preflight.optionalWait.length > 0;
83
+ for (const dep of preflight.optionalWait) scan(dep);
73
84
  }
74
- ordered.push(id);
75
- scanned.add(id);
85
+ stack.delete(id);
86
+ orderedMap.set(id, { split });
76
87
  }
77
- const stack = /* @__PURE__ */ new Set();
78
- for (const id of plan.packages.keys()) scan(id, stack);
79
- return ordered;
88
+ for (const id of plan.packages.keys()) scan(id);
89
+ return orderedMap;
80
90
  }
81
91
  async function runPublishPlan(context, plan) {
82
- const { dryRun = false } = plan.options;
83
- const onPublishResult = async (pkg, publishResult) => {
84
- const packagePlan = plan.packages.get(pkg.id);
85
- if (!packagePlan) return;
86
- packagePlan.publishResult = publishResult;
87
- if (publishResult.type === "skipped") return;
88
- for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
89
- pkg,
90
- plan
91
- }));
92
- };
93
- publishLoop: for (const id of resolvePublishTargets(plan)) {
92
+ const { dryRun = false, unstable_maxChunk = 5 } = plan.options;
93
+ for (const plugin of context.plugins) await handlePluginError(plugin, "beforePublishAll", () => plugin.beforePublishAll?.call(context, { plan }));
94
+ async function publish(pkg) {
95
+ if (dryRun) return { type: "published" };
96
+ try {
97
+ for (const plugin of context.plugins) if (await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg })) === false) return { type: "skipped" };
98
+ for (const plugin of context.plugins) {
99
+ const publishResult = await handlePluginError(plugin, "publish", () => plugin.publish?.call(context, {
100
+ pkg,
101
+ plan
102
+ }));
103
+ if (publishResult) return publishResult;
104
+ }
105
+ } catch (e) {
106
+ return {
107
+ type: "failed",
108
+ error: e instanceof Error ? e.message : String(e)
109
+ };
110
+ }
111
+ return {
112
+ type: "failed",
113
+ error: `There is no plugin to publish package "${pkg.id}", please make sure the package has a supported provider plugin.`
114
+ };
115
+ }
116
+ let promises = [];
117
+ for (const [id, { split }] of resolvePublishTargets(plan)) {
94
118
  const pkg = context.graph.get(id);
95
- if (dryRun) {
96
- await onPublishResult(pkg, { type: "published" });
97
- continue;
119
+ const packagePlan = plan.packages.get(pkg.id);
120
+ if (!pkg || !packagePlan) continue;
121
+ if (split || promises.length >= unstable_maxChunk) {
122
+ await Promise.all(promises);
123
+ promises = [];
98
124
  }
99
- for (const plugin of context.plugins) if (await handlePluginError(plugin, "willPublish", () => plugin.willPublish?.call(context, { pkg })) === false) continue publishLoop;
100
- for (const plugin of context.plugins) {
101
- const publishResult = await handlePluginError(plugin, "publish", () => plugin.publish?.call(context, {
125
+ promises.push(publish(pkg).then(async (result) => {
126
+ packagePlan.publishResult = result;
127
+ if (result.type === "skipped") return;
128
+ for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublish", () => plugin.afterPublish?.call(context, {
102
129
  pkg,
103
130
  plan
104
131
  }));
105
- if (publishResult) {
106
- await onPublishResult(pkg, publishResult);
107
- continue publishLoop;
108
- }
109
- }
110
- await onPublishResult(pkg, {
111
- type: "failed",
112
- error: `There is no plugin to publish package "${pkg.id}", please make sure the package has a supported provider plugin.`
113
- });
132
+ }));
114
133
  }
134
+ await Promise.all(promises);
115
135
  for (const packagePlan of plan.packages.values()) packagePlan.publishResult ??= { type: "skipped" };
116
136
  for (const plugin of context.plugins) await handlePluginError(plugin, "afterPublishAll", () => plugin.afterPublishAll?.call(context, { plan }));
117
137
  }
118
138
  async function runPreflights(context, plan) {
119
- const promises = [];
120
- for (const [id, packagePlan] of plan.packages) {
121
- const pkg = context.graph.get(id);
139
+ const { graph } = context;
140
+ await Promise.all(Array.from(plan.packages, async ([id, packagePlan]) => {
141
+ const pkg = graph.get(id);
122
142
  packagePlan.preflight = { shouldPublish: false };
123
- if (!packagePlan.updated) continue;
124
- promises.push((async () => {
125
- for (const plugin of context.plugins) {
126
- const res = await handlePluginError(plugin, "publishPreflight", () => plugin.publishPreflight?.call(context, {
127
- pkg,
128
- plan
129
- }));
130
- if (res) {
131
- packagePlan.preflight = res;
132
- break;
133
- }
143
+ if (!packagePlan.updated) return;
144
+ for (const plugin of context.plugins) {
145
+ const res = await handlePluginError(plugin, "publishPreflight", () => plugin.publishPreflight?.call(context, {
146
+ pkg,
147
+ plan
148
+ }));
149
+ if (res) {
150
+ packagePlan.preflight = res;
151
+ break;
134
152
  }
135
- })());
153
+ }
154
+ }));
155
+ if (plan.options.packages?.length) {
156
+ const only = /* @__PURE__ */ new Set();
157
+ function addOnly(id) {
158
+ if (only.has(id)) return;
159
+ const preflight = plan.packages.get(id)?.preflight;
160
+ if (!preflight) return;
161
+ only.add(id);
162
+ if (preflight.wait) for (const dep of preflight.wait) addOnly(dep);
163
+ if (preflight.optionalWait) for (const dep of preflight.optionalWait) addOnly(dep);
164
+ }
165
+ for (const name of plan.options.packages) for (const pkg of graph.getByName(name)) addOnly(pkg.id);
166
+ for (const [id, packagePlan] of plan.packages) {
167
+ if (only.has(id)) continue;
168
+ packagePlan.preflight.shouldPublish = false;
169
+ }
136
170
  }
137
- await Promise.all(promises);
138
171
  for (const plugin of context.plugins) await handlePluginError(plugin, "afterPreflight", () => plugin.afterPreflight?.call(context, { plan }));
139
172
  }
140
173
  async function publishPlanStatus(plan, context) {
@@ -752,9 +785,10 @@ async function resolveNpmGraph(cwd, client) {
752
785
  if (isNodeError(error) && error.code === "ENOENT") return void 0;
753
786
  throw error;
754
787
  });
755
- const data = content && assertPnpmWorkspace(parse$1(content));
756
- if (data) {
757
- catalogSources.push(createPnpmCatalogSource(pnpmWorkspacePath, data));
788
+ if (content) {
789
+ const doc = parseDocument(content);
790
+ const data = assertPnpmWorkspace(doc.toJSON());
791
+ catalogSources.push(createPnpmCatalogSource(pnpmWorkspacePath, doc));
758
792
  patterns.push(...data.packages ?? []);
759
793
  }
760
794
  } else if (client === "yarn") {
@@ -846,24 +880,26 @@ async function readManifest(packagePath) {
846
880
  assertPackageManifest(parsed);
847
881
  return parsed;
848
882
  }
849
- function createPnpmCatalogSource(filePath, workspace) {
883
+ function createPnpmCatalogSource(filePath, doc) {
850
884
  return {
851
885
  resolve(name, catalogName) {
852
- if (catalogName === "default") return workspace.catalog?.[name];
853
- return workspace.catalogs?.[catalogName]?.[name];
886
+ const value = doc.getIn(catalogName === "default" ? ["catalog", name] : [
887
+ "catalogs",
888
+ catalogName,
889
+ name
890
+ ]);
891
+ return typeof value === "string" ? value : void 0;
854
892
  },
855
893
  setRange(name, catalogName, range) {
856
- if (catalogName === "default") {
857
- workspace.catalog ??= {};
858
- workspace.catalog[name] = range;
859
- } else {
860
- workspace.catalogs ??= {};
861
- workspace.catalogs[catalogName] ??= {};
862
- workspace.catalogs[catalogName][name] = range;
863
- }
894
+ doc.setIn(catalogName === "default" ? ["catalog", name] : [
895
+ "catalogs",
896
+ catalogName,
897
+ name
898
+ ], range);
864
899
  },
865
900
  async write() {
866
- await writeFile(filePath, `${stringify(workspace, { lineWidth: 0 }).trim()}\n`);
901
+ const output = doc.toString();
902
+ await writeFile(filePath, output.endsWith("\n") ? output : `${output}\n`);
867
903
  }
868
904
  };
869
905
  }
@@ -1035,8 +1071,13 @@ function npm({ client: defaultClient, onBreakPeerDep = "set", updateLockFile = t
1035
1071
  active = true;
1036
1072
  },
1037
1073
  async publishPreflight({ pkg }) {
1038
- if (!(pkg instanceof NpmPackage)) return;
1039
- return { shouldPublish: pkg.version !== void 0 && pkg.manifest.private !== true };
1074
+ if (!(pkg instanceof NpmPackage) || !this.npm?.graph) return;
1075
+ const optionalWait = [];
1076
+ for (const { linked } of pkg.listDependencies(this.npm.graph)) if (linked) optionalWait.push(linked.id);
1077
+ return {
1078
+ shouldPublish: pkg.version !== void 0 && pkg.manifest.private !== true,
1079
+ optionalWait: optionalWait.length > 0 ? optionalWait : void 0
1080
+ };
1040
1081
  },
1041
1082
  resolvePlanStatus({ plan }) {
1042
1083
  if (!active) return;
@@ -1,2 +1,2 @@
1
- import { D as cargo, E as CargoPluginOptions, T as CargoPackage, w as CargoGraph } from "../types-B50RK1rR.js";
1
+ import { D as cargo, E as CargoPluginOptions, T as CargoPackage, w as CargoGraph } from "../types-DEyZ2r-2.js";
2
2
  export { CargoGraph, CargoPackage, CargoPluginOptions, cargo };
@@ -1,4 +1,4 @@
1
- import { s as TegamiPlugin } from "../types-B50RK1rR.js";
1
+ import { s as TegamiPlugin } from "../types-DEyZ2r-2.js";
2
2
 
3
3
  //#region src/plugins/git.d.ts
4
4
  interface GitPluginOptions {
@@ -1,4 +1,4 @@
1
- import { A as WorkspacePackage, b as TegamiContext, j as Draft, s as TegamiPlugin, t as Awaitable, v as PublishPlan } from "../types-B50RK1rR.js";
1
+ import { A as WorkspacePackage, b as TegamiContext, j as Draft, s as TegamiPlugin, t as Awaitable, v as PublishPlan } from "../types-DEyZ2r-2.js";
2
2
  import { GitPluginOptions } from "./git.js";
3
3
 
4
4
  //#region src/plugins/github.d.ts
@@ -24,7 +24,7 @@ interface VersionPullRequestOptions {
24
24
  */
25
25
  forceCreate?: boolean;
26
26
  /**
27
- * Pull request branch.
27
+ * Pull request branch. Publish group PRs are created under `<branch>/`.
28
28
  *
29
29
  * @default "tegami/version-packages"
30
30
  */
@@ -35,9 +35,24 @@ interface VersionPullRequestOptions {
35
35
  * @default "main"
36
36
  */
37
37
  base?: string;
38
- /** Override details for "Version Packages" PR. */
38
+ /**
39
+ * Publish groups to split into separate version PRs, each entry is a package (or a list of
40
+ * packages) to version & publish together. Packages not covered by any group are collected
41
+ * into an extra "unlisted packages" PR.
42
+ *
43
+ * Merging a group PR publishes only its members, the remaining PRs are re-synced on every
44
+ * publish run until all of them are merged & published.
45
+ */
46
+ groups?: (string | string[])[];
47
+ /**
48
+ * Override details for a version PR.
49
+ *
50
+ * Only called at version-time: publish group PRs re-synced at publish-time keep the stored
51
+ * title and use a default body.
52
+ */
39
53
  create?: (this: TegamiContext, opts: {
40
54
  draft: Draft;
55
+ publishGroup?: string[];
41
56
  }) => Awaitable<VersionPullRequest>;
42
57
  }
43
58
  /** Options for creating GitHub releases after a successful publish. */
@@ -3,7 +3,7 @@ import { t as changelogFilename } from "../generate-Bg86OJP4.js";
3
3
  import { a as cached, n as execFailure, o as isCI } from "../error-BhMYq9iW.js";
4
4
  import { o as readChangelogEntries, t as createDraft } from "../draft-CzUiQasJ.js";
5
5
  import { git } from "./git.js";
6
- import { n as createVersionRequestBody, r as hasGitChanges, t as commitVersionBranchChanges } from "../version-request-oxy16TJ1.js";
6
+ import { t as onVersionRequest } from "../version-request-DlLpLLK3.js";
7
7
  import { readFile, writeFile } from "node:fs/promises";
8
8
  import { basename, join, relative, resolve } from "node:path";
9
9
  import { x } from "tinyexec";
@@ -346,7 +346,37 @@ function github(options = {}) {
346
346
  function getRenderer(context) {
347
347
  return renderer ??= createChangelogRenderer(context);
348
348
  }
349
- const cliSnapshots = /* @__PURE__ */ new Map();
349
+ const versionRequests = onVersionRequest({
350
+ name: "github",
351
+ summary: "Merge this PR to publish the versioned packages.",
352
+ options: options.versionPr,
353
+ enabled(context) {
354
+ const { repo, token } = context.github ?? {};
355
+ return Boolean(repo && token);
356
+ },
357
+ find(context, { head }) {
358
+ const { repo, token } = context.github;
359
+ return findOpenPullRequest(repo, head, token);
360
+ },
361
+ create(context, request) {
362
+ const { repo, token } = context.github;
363
+ return createPullRequest(repo, {
364
+ title: request.title,
365
+ body: request.body,
366
+ head: request.head,
367
+ base: request.base,
368
+ token
369
+ });
370
+ },
371
+ update(context, number, request) {
372
+ const { repo, token } = context.github;
373
+ return updatePullRequest(repo, number, {
374
+ title: request.title,
375
+ body: request.body,
376
+ token
377
+ });
378
+ }
379
+ });
350
380
  return [git(options), {
351
381
  name: "github",
352
382
  init() {
@@ -356,6 +386,7 @@ function github(options = {}) {
356
386
  };
357
387
  },
358
388
  async resolvePlanStatus({ plan }) {
389
+ if (versionRequests.resolvePlanStatus() === "pending") return "pending";
359
390
  const { repo, token } = this.github;
360
391
  if (!repo || !token || releaseOptions === false) return;
361
392
  const requiredTags = /* @__PURE__ */ new Set();
@@ -430,37 +461,16 @@ function github(options = {}) {
430
461
  if (result.exitCode !== 0) throw execFailure("Failed to configure git remote for GitHub Actions.", result);
431
462
  },
432
463
  initCliDraft() {
433
- for (const pkg of this.graph.getPackages()) cliSnapshots.set(pkg.id, pkg.version);
464
+ versionRequests.initCliDraft.call(this);
434
465
  },
435
- async applyCliDraft(draft) {
436
- const config = options.versionPr ?? {};
437
- if (config === false || !(config.forceCreate || isCI()) || !await hasGitChanges(this.cwd)) return;
438
- const repo = this.github?.repo;
439
- const { branch = "tegami/version-packages", base = "main" } = config;
440
- const basePR = await config.create?.call(this, { draft });
441
- const pr = {
442
- title: basePR?.title ?? "Version Packages",
443
- body: basePR?.body ?? createVersionRequestBody(draft, this, cliSnapshots, "Merge this PR to publish the versioned packages.")
444
- };
445
- await commitVersionBranchChanges(this.cwd, branch, pr.title);
446
- const token = this.github?.token;
447
- if (!repo) return;
448
- const openPr = await findOpenPullRequest(repo, branch, token);
449
- if (openPr !== void 0) {
450
- await updatePullRequest(repo, openPr, {
451
- title: pr.title,
452
- body: pr.body,
453
- token
454
- });
455
- return;
456
- }
457
- await createPullRequest(repo, {
458
- title: pr.title,
459
- body: pr.body,
460
- head: branch,
461
- base,
462
- token
463
- });
466
+ applyCliDraft(draft) {
467
+ return versionRequests.applyCliDraft.call(this, draft);
468
+ },
469
+ initPublishPlan(opts) {
470
+ versionRequests.initPublishPlan.call(this, opts);
471
+ },
472
+ beforePublishAll(opts) {
473
+ return versionRequests.beforePublishAll.call(this, opts);
464
474
  }
465
475
  }];
466
476
  }
@@ -1,4 +1,4 @@
1
- import { A as WorkspacePackage, b as TegamiContext, j as Draft, s as TegamiPlugin, t as Awaitable, v as PublishPlan } from "../types-B50RK1rR.js";
1
+ import { A as WorkspacePackage, b as TegamiContext, j as Draft, s as TegamiPlugin, t as Awaitable, v as PublishPlan } from "../types-DEyZ2r-2.js";
2
2
  import { GitPluginOptions } from "./git.js";
3
3
 
4
4
  //#region src/plugins/gitlab.d.ts
@@ -22,7 +22,7 @@ interface VersionMergeRequestOptions {
22
22
  */
23
23
  forceCreate?: boolean;
24
24
  /**
25
- * Merge request branch.
25
+ * Merge request branch. Publish group MRs are created under `<branch>/`.
26
26
  *
27
27
  * @default "tegami/version-packages"
28
28
  */
@@ -33,9 +33,24 @@ interface VersionMergeRequestOptions {
33
33
  * @default "main"
34
34
  */
35
35
  base?: string;
36
- /** Override details for "Version Packages" MR. */
36
+ /**
37
+ * Publish groups to split into separate version MRs, each entry is a package (or a list of
38
+ * packages) to version & publish together. Packages not covered by any group are collected
39
+ * into an extra "unlisted packages" MR.
40
+ *
41
+ * Merging a group MR publishes only its members, the remaining MRs are re-synced on every
42
+ * publish run until all of them are merged & published.
43
+ */
44
+ groups?: (string | string[])[];
45
+ /**
46
+ * Override details for a version MR.
47
+ *
48
+ * Only called at version-time: publish group MRs re-synced at publish-time keep the stored
49
+ * title and use a default body.
50
+ */
37
51
  create?: (this: TegamiContext, opts: {
38
52
  draft: Draft;
53
+ publishGroup?: string[];
39
54
  }) => Awaitable<VersionMergeRequest>;
40
55
  }
41
56
  /** Options for creating GitLab releases after a successful publish. */