rman 2.0.0-beta.2 → 2.0.0-beta.4

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 (162) hide show
  1. package/README.md +2 -12
  2. package/{plugins/node/augmentation/rman.augmentation.d.ts → builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts} +4 -5
  3. package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.js +1 -1
  4. package/{plugins → builtins/platforms}/node/commands/ci.command.d.ts +5 -3
  5. package/{plugins → builtins/platforms}/node/commands/ci.command.js +6 -5
  6. package/{plugins → builtins/platforms}/node/commands/clean.command.d.ts +6 -4
  7. package/{plugins → builtins/platforms}/node/commands/clean.command.js +6 -5
  8. package/builtins/platforms/node/index.d.ts +8 -0
  9. package/builtins/platforms/node/index.js +8 -0
  10. package/{plugins → builtins/platforms}/node/node-config.interface.d.ts +1 -2
  11. package/{plugins → builtins/platforms}/node/node-manifest.provider.d.ts +3 -3
  12. package/{plugins → builtins/platforms}/node/node-manifest.provider.js +3 -3
  13. package/{plugins → builtins/platforms}/node/node.platform.d.ts +3 -3
  14. package/{plugins → builtins/platforms}/node/services/ci.service.d.ts +3 -3
  15. package/{plugins → builtins/platforms}/node/services/ci.service.js +4 -4
  16. package/{plugins → builtins/platforms}/node/services/clean.service.d.ts +3 -3
  17. package/{plugins → builtins/platforms}/node/services/clean.service.js +3 -3
  18. package/{plugins → builtins/platforms}/node/services/publish.service.d.ts +4 -4
  19. package/{plugins → builtins/platforms}/node/services/publish.service.js +13 -7
  20. package/{plugins → builtins/platforms}/node/services/version-plan.service.d.ts +3 -3
  21. package/{plugins → builtins/platforms}/node/services/version-plan.service.js +2 -2
  22. package/builtins/presets/index.d.ts +15 -0
  23. package/builtins/presets/index.js +57 -0
  24. package/builtins/presets/node.d.ts +16 -0
  25. package/builtins/presets/node.js +57 -0
  26. package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +6 -6
  27. package/{services → builtins/publish-targets/docker}/docker-publish.service.js +9 -9
  28. package/{targets → builtins/publish-targets/docker}/docker.target.d.ts +6 -5
  29. package/{targets → builtins/publish-targets/docker}/docker.target.js +1 -1
  30. package/builtins/publish-targets/docker/index.d.ts +2 -0
  31. package/builtins/publish-targets/docker/index.js +2 -0
  32. package/builtins/publish-targets/npm/index.d.ts +2 -0
  33. package/builtins/publish-targets/npm/index.js +2 -0
  34. package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.d.ts +4 -4
  35. package/{plugins/node → builtins/publish-targets/npm}/npm-publish-target.js +4 -5
  36. package/cli.d.ts +3 -0
  37. package/cli.js +16 -7
  38. package/commands/changelog.command.d.ts +106 -2
  39. package/commands/changelog.command.js +40 -0
  40. package/commands/config.command.js +23 -5
  41. package/commands/list.command.js +1 -1
  42. package/commands/publish.command.d.ts +1 -1
  43. package/commands/publish.command.js +1 -1
  44. package/commands/run.command.js +1 -1
  45. package/commands/version.command.d.ts +16 -3
  46. package/commands/version.command.js +143 -9
  47. package/commands.d.ts +0 -1
  48. package/commands.js +0 -1
  49. package/constants.js +1 -1
  50. package/core/application.d.ts +10 -10
  51. package/core/application.js +28 -11
  52. package/core/{package.d.ts → classes/package.d.ts} +12 -4
  53. package/core/{package.js → classes/package.js} +17 -1
  54. package/core/{repository.d.ts → classes/repository.d.ts} +22 -36
  55. package/core/{repository.js → classes/repository.js} +112 -241
  56. package/core/{service.d.ts → classes/service.d.ts} +2 -2
  57. package/core/classes/workspace.d.ts +277 -0
  58. package/core/classes/workspace.js +585 -0
  59. package/core/command-builder.js +17 -1
  60. package/core/config/config-file-scope.d.ts +107 -0
  61. package/core/config/config-file-scope.js +226 -0
  62. package/core/config/config-interpolator.d.ts +164 -0
  63. package/core/config/config-interpolator.js +577 -0
  64. package/core/config/config-paths.d.ts +65 -0
  65. package/core/config/config-paths.js +78 -0
  66. package/core/config/config-reader.d.ts +215 -0
  67. package/core/config/config-reader.js +851 -0
  68. package/core/{merge-config.js → config/merge-config.js} +4 -8
  69. package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +5 -5
  70. package/core/{manifest.d.ts → interfaces/manifest.d.ts} +3 -3
  71. package/core/{manifest.js → interfaces/manifest.js} +2 -2
  72. package/core/{plugin.d.ts → interfaces/plugin.d.ts} +44 -27
  73. package/core/{plugin.js → interfaces/plugin.js} +48 -10
  74. package/core/{publish-target.d.ts → interfaces/publish-target.d.ts} +24 -12
  75. package/core/{publish-target.js → interfaces/publish-target.js} +16 -3
  76. package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
  77. package/core/plugin-loader.d.ts +11 -51
  78. package/core/plugin-loader.js +21 -202
  79. package/index.d.ts +29 -28
  80. package/index.js +20 -46
  81. package/interfaces/config-scope.interface.d.ts +223 -0
  82. package/interfaces/config-scope.interface.js +1 -0
  83. package/interfaces/rman-config.interface.d.ts +100 -5
  84. package/interfaces/rman-config.interface.js +17 -0
  85. package/package.json +3 -2
  86. package/services/change-hash.service.d.ts +40 -6
  87. package/services/change-hash.service.js +90 -11
  88. package/services/changelog.service.d.ts +31 -3
  89. package/services/changelog.service.js +448 -65
  90. package/services/config-service.d.ts +8 -0
  91. package/services/config-service.js +3 -0
  92. package/services/exec.service.d.ts +2 -2
  93. package/services/exec.service.js +1 -1
  94. package/services/github-release.service.d.ts +3 -3
  95. package/services/github-release.service.js +3 -3
  96. package/services/import.service.d.ts +2 -2
  97. package/services/import.service.js +1 -1
  98. package/services/list.service.d.ts +9 -7
  99. package/services/list.service.js +2 -2
  100. package/services/run.service.d.ts +15 -11
  101. package/services/run.service.js +62 -18
  102. package/services/system-info.d.ts +1 -1
  103. package/services/version-plan.service.d.ts +8 -4
  104. package/services/version-plan.service.js +54 -21
  105. package/services/version.service.d.ts +5 -5
  106. package/services/version.service.js +7 -4
  107. package/services.d.ts +1 -1
  108. package/services.js +1 -1
  109. package/utils/branch-guard.d.ts +1 -1
  110. package/utils/color-yaml.d.ts +8 -0
  111. package/utils/color-yaml.js +86 -0
  112. package/utils/git.d.ts +26 -0
  113. package/utils/git.js +65 -0
  114. package/utils/logger.d.ts +1 -1
  115. package/utils/package-filter.d.ts +8 -1
  116. package/utils/package-filter.js +7 -0
  117. package/utils/printable-config.d.ts +8 -0
  118. package/utils/printable-config.js +45 -0
  119. package/utils/progress-panel.d.ts +9 -0
  120. package/utils/progress-panel.js +23 -1
  121. package/utils/release-version.d.ts +1 -1
  122. package/utils/run-options.js +1 -1
  123. package/utils/version-group.d.ts +31 -0
  124. package/utils/version-group.js +43 -0
  125. package/commands/changed.command.d.ts +0 -80
  126. package/commands/changed.command.js +0 -36
  127. package/core/config.d.ts +0 -530
  128. package/core/config.js +0 -1123
  129. package/core/core-services.d.ts +0 -14
  130. package/core/core-services.js +0 -30
  131. package/core/core-targets.d.ts +0 -14
  132. package/core/core-targets.js +0 -16
  133. package/core/extends-config.d.ts +0 -35
  134. package/core/extends-config.js +0 -129
  135. package/core/load-config-module.d.ts +0 -28
  136. package/core/load-config-module.js +0 -42
  137. package/core/workspace.d.ts +0 -119
  138. package/core/workspace.js +0 -124
  139. package/plugins/builtins.d.ts +0 -44
  140. package/plugins/builtins.js +0 -33
  141. package/plugins/detect.d.ts +0 -78
  142. package/plugins/detect.js +0 -70
  143. package/plugins/node/index.d.ts +0 -29
  144. package/plugins/node/index.js +0 -40
  145. /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
  146. /package/{plugins → builtins/platforms}/node/augmentation/system-info.augmentation.d.ts +0 -0
  147. /package/{plugins → builtins/platforms}/node/node-config.interface.js +0 -0
  148. /package/{plugins → builtins/platforms}/node/node.platform.js +0 -0
  149. /package/{plugins → builtins/platforms}/node/utils/workspace-range.d.ts +0 -0
  150. /package/{plugins → builtins/platforms}/node/utils/workspace-range.js +0 -0
  151. /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.d.ts +0 -0
  152. /package/{plugins/node/utils → builtins/publish-targets/npm}/npm-view.js +0 -0
  153. /package/core/{registry.d.ts → classes/registry.d.ts} +0 -0
  154. /package/core/{registry.js → classes/registry.js} +0 -0
  155. /package/core/{service.js → classes/service.js} +0 -0
  156. /package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +0 -0
  157. /package/core/{version-scheme.js → classes/version-scheme.js} +0 -0
  158. /package/core/{merge-config.d.ts → config/merge-config.d.ts} +0 -0
  159. /package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +0 -0
  160. /package/core/{resolve-target.js → config/resolve-config-target.js} +0 -0
  161. /package/core/{custom-command.js → interfaces/custom-command.js} +0 -0
  162. /package/{plugins/node/augmentation/rman.augmentation.js → core/interfaces/run-step.js} +0 -0
package/utils/git.js CHANGED
@@ -148,6 +148,32 @@ export class GitHelper {
148
148
  }
149
149
  }
150
150
  /** Whether `tag` exists as an exact git tag name (not a glob - a literal ref check). */
151
+ /**
152
+ * Every tag matching `pattern`, paired with the **commit** it points at - newest version first,
153
+ * the same order `listTags` returns.
154
+ *
155
+ * `%(*objectname)` is the dereferenced target and is empty for a lightweight tag, so the commit
156
+ * is that or `%(objectname)`. rman's own tags are lightweight; a repository's existing ones may
157
+ * not be, and an annotated tag's `objectname` is the *tag object*, which matches no commit and
158
+ * would make the tag silently invisible to anything comparing shas.
159
+ */
160
+ async listTagCommits(pattern) {
161
+ try {
162
+ const { stdout } = await execFileAsync('git', ['tag', '--list', pattern, '--sort=-v:refname', '--format=%(refname:short)%09%(objectname)%09%(*objectname)'], { cwd: this.cwd });
163
+ return stdout
164
+ .trim()
165
+ .split(/\r?\n/)
166
+ .filter(Boolean)
167
+ .map(line => {
168
+ const [tag, objectname, deref] = line.split('\t');
169
+ return { tag, sha: deref || objectname };
170
+ })
171
+ .filter(t => t.tag && t.sha);
172
+ }
173
+ catch {
174
+ return [];
175
+ }
176
+ }
151
177
  async tagExists(tag) {
152
178
  try {
153
179
  await execFileAsync('git', ['rev-parse', '--verify', '--quiet', `refs/tags/${tag}`], { cwd: this.cwd });
@@ -197,6 +223,45 @@ export class GitHelper {
197
223
  /** The repository's very first commit (the oldest root commit, for a history with several) -
198
224
  * `undefined` for a repository with no commits at all. The "since the beginning" boundary for
199
225
  * a package being released for the first time, with no earlier tag to measure from. */
226
+ /** HEAD's own sha, or `undefined` in a repository with no commits yet (or none at all) - the
227
+ * same "answer nothing rather than throw" shape `rootCommit` and `describeTag` take, because
228
+ * every caller here already has a "nothing to go on" branch. */
229
+ /** The date a ref points at, as `YYYY-MM-DD`. The **committer** date, not the author's: a
230
+ * rebased or cherry-picked release commit was authored earlier than it shipped, and what a
231
+ * changelog heading states is when the release happened. `undefined` for a ref that resolves to
232
+ * nothing. */
233
+ async commitDate(ref) {
234
+ try {
235
+ const { stdout } = await execFileAsync('git', ['log', '-1', '--format=%cI', ref], { cwd: this.cwd });
236
+ return stdout.trim().slice(0, 10) || undefined;
237
+ }
238
+ catch {
239
+ return undefined;
240
+ }
241
+ }
242
+ /** The commit `ref` names - a sha, a tag, a branch - or `undefined` when git does not recognize
243
+ * it. `^{commit}` so an annotated tag answers with the commit rather than the tag object, and
244
+ * so a tree or blob answers with nothing rather than a sha that matches no commit. */
245
+ async resolveCommit(ref) {
246
+ try {
247
+ const { stdout } = await execFileAsync('git', ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`], {
248
+ cwd: this.cwd,
249
+ });
250
+ return stdout.trim() || undefined;
251
+ }
252
+ catch {
253
+ return undefined;
254
+ }
255
+ }
256
+ async headSha() {
257
+ try {
258
+ const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd: this.cwd });
259
+ return stdout.trim() || undefined;
260
+ }
261
+ catch {
262
+ return undefined;
263
+ }
264
+ }
200
265
  async rootCommit() {
201
266
  try {
202
267
  const { stdout } = await execFileAsync('git', ['rev-list', '--max-parents=0', 'HEAD'], { cwd: this.cwd });
package/utils/logger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Repository } from '../core/repository.js';
1
+ import type { Repository } from '../core/classes/repository.js';
2
2
  export type LogLevel = 'silent' | 'error' | 'info' | 'verbose';
3
3
  export declare const LOG_LEVELS: LogLevel[];
4
4
  /**
@@ -1,11 +1,18 @@
1
1
  import type { Argv } from 'yargs';
2
- import type { Package } from '../core/package.js';
2
+ import type { Package } from '../core/classes/package.js';
3
3
  /** Shared by every command that iterates packages (`run`/`build`/`test`/`exec`, `list`, `ci`,
4
4
  * `clean`, `version`, `publish`, `changelog`) - narrows *which* packages a command applies to,
5
5
  * independent of what the command actually does to them. The repository's own standing filter,
6
6
  * `.rmanrc "skip"`, is applied by `filterPackages` itself rather than being an option here: it is
7
7
  * the repository's statement, not the caller's. */
8
8
  export interface PackageFilterOptions {
9
+ /**
10
+ * The technologies the **command itself** is for - `clean` and `ci` are npm's alone. Absent means
11
+ * every platform, which is what a command that speaks for no one in particular says.
12
+ *
13
+ * Declared as `CommandMetadata.platforms` and carried here; a repository never writes it.
14
+ */
15
+ commandPlatforms?: string[];
9
16
  /** Only include packages whose name matches at least one of these globs (e.g. `@scope/*`), or
10
17
  * **`"/"`** for the repository's own root package - see `ROOT_SELECTOR`. */
11
18
  scope?: string | string[];
@@ -131,6 +131,7 @@ export function readPackageFilterOptions(args) {
131
131
  scope: args.scope,
132
132
  ignore: args.ignore,
133
133
  platform: args.platform,
134
+ commandPlatforms: args.commandPlatforms,
134
135
  deps: args.deps,
135
136
  dependents: args.dependents,
136
137
  };
@@ -169,6 +170,12 @@ applySkip = true) {
169
170
  const selects = selector(options.ignore);
170
171
  matched = matched.filter(p => !selects(p));
171
172
  }
173
+ /** The command's own technologies, before the user's `--platform`: a declaration is the hard
174
+ * limit and the flag narrows inside it. Silent where `--platform` is loud - see the field. */
175
+ if (options.commandPlatforms?.length) {
176
+ const wanted = new Set(options.commandPlatforms.map(n => n.toLowerCase()));
177
+ matched = matched.filter(p => wanted.has(p.provider.toLowerCase()));
178
+ }
172
179
  if (options.platform !== undefined) {
173
180
  const wanted = platformNames(options.platform, packages);
174
181
  matched = matched.filter(p => wanted.has(p.provider.toLowerCase()));
@@ -13,3 +13,11 @@
13
13
  * reads as `[Function]`, which is itself worth seeing.
14
14
  */
15
15
  export declare function printableConfig<T>(config: T): T;
16
+ /**
17
+ * A resolved config with the **contribution keys** dropped - `plugins`, `platforms`, `commands`,
18
+ * `publishTargets` - for `rman config` and `--config`, which exist to show what a repository is
19
+ * *configured* to do.
20
+ *
21
+ * Returns a copy; the config itself is what every command reads and is never touched.
22
+ */
23
+ export declare function withoutContributions<T extends object>(config: T): T;
@@ -1,3 +1,4 @@
1
+ import { CODE_SUBTREES } from '../core/config/config-paths.js';
1
2
  /**
2
3
  * A copy of a resolved config with every value a serializer cannot represent replaced by a short
3
4
  * description of it - for `rman config` and `--config`, which exist to be *looked at*.
@@ -15,6 +16,50 @@
15
16
  export function printableConfig(config) {
16
17
  return walk(config, new WeakSet());
17
18
  }
19
+ /**
20
+ * A resolved config with the **contribution keys** dropped - `plugins`, `platforms`, `commands`,
21
+ * `publishTargets` - for `rman config` and `--config`, which exist to show what a repository is
22
+ * *configured* to do.
23
+ *
24
+ * Returns a copy; the config itself is what every command reads and is never touched.
25
+ */
26
+ /* **Those four are code, not settings** (`CODE_SUBTREES`), and printing them answers a question
27
+ * nobody asked: a platform dumps its manifest provider and version planner, a publish target its
28
+ * whole option table, and `commands` comes out as a list of `[Function]` - which is the clearest
29
+ * evidence of all that they do not belong in this output. Measured on this repository, where
30
+ * `rman config` printed forty lines of the npm target's flags above the two keys its `.rmanrc`
31
+ * actually sets.
32
+ *
33
+ * **And silently, which was argued the other way first.** The note that announced them read as the
34
+ * sibling of the `version.<slot>` one, and it is not: that note explains a *visible* oddity - an
35
+ * unresolved `${{ }}` sitting among resolved values, which without it reads as interpolation being
36
+ * broken - while this one announced an absence the reader was not looking for. The measurement that
37
+ * settled it: presets go under every root, so every config carries all four and the note was
38
+ * printed **every time**, which makes it a banner rather than a report. At this repository's root
39
+ * it was one of three lines. A reader asking which technologies are loaded is asking `rman info`,
40
+ * and one asking whether their command registered is asking `rman --help`.
41
+ *
42
+ * **`CODE_SUBTREES` itself, not a second copy of those four names.** `config-paths.ts` imports
43
+ * nothing, so there is no cycle to buy by reaching for it - and a printer quietly keeping its own
44
+ * list is how a key added to one side stops being dropped by the other. That exact shape was live
45
+ * in this tree an hour earlier: `presetNames()` held a hardcoded `['node']` beside a resolver that
46
+ * looked somewhere else, and the error message named the preset it had just failed to find.
47
+ *
48
+ * **Top level only, matching the runtime rule** (`ConfigInterpolator._isCodePath` checks
49
+ * `segments[0]`). `Resolved` skips these names at *every* level because the selector index
50
+ * re-enters the config, and the two have disagreed since - this is not the place to pick a side,
51
+ * and stripping them deeper would hide a script that happens to be called `commands`. By the time
52
+ * either printer runs the selector blocks are merged in anyway, so there is no deeper one to find.
53
+ *
54
+ * **Distinct from `printableConfig`, which makes a value serializable rather than deciding what is
55
+ * worth showing.** A step written as a function still prints as `[Function: copyDocs]` - that one
56
+ * *is* a setting. */
57
+ export function withoutContributions(config) {
58
+ const result = { ...config };
59
+ for (const key of CODE_SUBTREES)
60
+ delete result[key];
61
+ return result;
62
+ }
18
63
  function walk(value, seen) {
19
64
  if (typeof value === 'function')
20
65
  return `[Function${value.name ? `: ${value.name}` : ''}]`;
@@ -6,6 +6,15 @@ export interface ProgressItem {
6
6
  /** Label of the step currently running, for work with more than one step (e.g. `run`'s
7
7
  * npm-script steps) - omit `stepsTotal` entirely for single-step work like a plain command. */
8
8
  currentStep?: string;
9
+ /**
10
+ * **What the current step actually runs** - the shell command, or a function step's name - shown
11
+ * beside the label so a silent step says what it is.
12
+ *
13
+ * The label alone answers "which slot", which is what a reader already knows; a step sitting at
14
+ * `before (2/9)` for ten seconds with nothing on stdout is the case this exists for, and it is
15
+ * the common one for a build.
16
+ */
17
+ currentCommand?: string;
9
18
  stepIndex?: number;
10
19
  stepsTotal?: number;
11
20
  /** Last captured output line, shown dimmed under the item while it's running. */
@@ -76,7 +76,15 @@ export class ProgressPanel {
76
76
  const step = item.stepsTotal && item.stepsTotal > 1 && item.stepIndex != null
77
77
  ? `${item.currentStep} (${item.stepIndex + 1}/${item.stepsTotal})`
78
78
  : item.currentStep || '';
79
- const group = [`${spinner} ${colors.bold(item.name)} ${colors.gray(step)} ${colors.yellow(elapsed)}`];
79
+ /** Measured against the *plain* text: every piece below is wrapped in escape sequences, and
80
+ * `String.length` counts those, so budgeting on the rendered string wraps a row that fits. */
81
+ const fixed = ` ${item.name} ${step} ${elapsed}`.length;
82
+ const command = truncate(item.currentCommand ?? '', (process.stdout.columns || 80) - fixed - 2);
83
+ const group = [
84
+ `${spinner} ${colors.bold(item.name)} ${colors.gray(step)}` +
85
+ (command ? ` ${colors.cyan(command)}` : '') +
86
+ ` ${colors.yellow(elapsed)}`,
87
+ ];
80
88
  if (item.lastLine)
81
89
  group.push(` ${colors.dim(item.lastLine)}`);
82
90
  if (used + group.length > budget)
@@ -141,6 +149,20 @@ export class ProgressPanel {
141
149
  return { successCount, failedCount, skippedCount };
142
150
  }
143
151
  }
152
+ /**
153
+ * `text` cut to `width`, with a `…` in place of what was dropped.
154
+ *
155
+ * **Cut from the end, not the middle.** A command's information is front-loaded - `tsc -b
156
+ * packages/rman/tsconfig.json` says what it is in the first four characters, and a middle-ellipsis
157
+ * spends them on a tail nobody is reading at a glance. The one thing it must never do is wrap: a
158
+ * block taller than the terminal breaks the panel's cursor-up arithmetic, which is the same reason
159
+ * the row budget above exists.
160
+ */
161
+ function truncate(text, width) {
162
+ if (!text || width <= 1)
163
+ return '';
164
+ return text.length <= width ? text : `${text.slice(0, width - 1)}…`;
165
+ }
144
166
  /** Classic braille "dots" spinner (cli-spinners' default), one frame per render tick. */
145
167
  const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
146
168
  function renderProgressBar(done, total, width = 24) {
@@ -1,4 +1,4 @@
1
- import type { Package } from '../core/package.js';
1
+ import type { Package } from '../core/classes/package.js';
2
2
  import type { GitHelper } from './git.js';
3
3
  /**
4
4
  * A calendar release version: `YYYY.M.D-HHmm`, every part **unpadded** (`2026.9.5-930`, not
@@ -53,7 +53,7 @@ export const runOptions = {
53
53
  changed: {
54
54
  target: 'cli',
55
55
  alias: 'c',
56
- describe: 'Only run in packages that have changed since the last publish',
56
+ describe: 'Only run in packages you have touched but not pushed - uncommitted, or committed and not yet on the upstream branch. Not a release question; see "version --json" for that',
57
57
  type: 'boolean',
58
58
  },
59
59
  changedSince: {
@@ -0,0 +1,31 @@
1
+ import type { Package } from '../core/classes/package.js';
2
+ /**
3
+ * The identity packages are batched by when `version` plans a release - `.rmanrc group`, cascaded
4
+ * like every unmarked key. `true` (the default) puts a package in the implicit repo-wide group, a
5
+ * non-empty string joins exactly the packages naming the same one, and `false` makes it a group of
6
+ * itself.
7
+ *
8
+ * **It lives here rather than on the planner because two unrelated things need the same answer.**
9
+ * `VersionPlanService.resolveGroupKey` batches the plan with it; `ChangeHashService.tagPattern`
10
+ * asks how many lines the repository releases along, which decides whether a repo-wide tag can
11
+ * name a release at all. Two copies of five lines would be easy to write and impossible to keep in
12
+ * step, and the failure they would produce is silent - a boundary computed under one answer and a
13
+ * tag written under the other.
14
+ *
15
+ * `resolveGroupKey` stays `protected` and delegates here, so a planner can still override the
16
+ * batching; what it cannot do is leave the tag pattern behind, which is the point.
17
+ */
18
+ export declare function groupKeyOf(pkg: Package): string;
19
+ /**
20
+ * How many distinct version lines this package's repository releases along - 1 when every package
21
+ * moves together, more once anything is grouped apart.
22
+ *
23
+ * **Structural, exactly like `usesCalendarVersion`'s group count**, and for the same reason: two
24
+ * lines can hold the same number today and diverge tomorrow, so reading the versions would move
25
+ * the answer under the repository's feet.
26
+ *
27
+ * `1` for a package belonging to no repository yet - a bare `new Package(dir, app)`, which the test
28
+ * fixtures build. One line is the answer that changes nothing, which is what an unknown should
29
+ * resolve to here.
30
+ */
31
+ export declare function versionLineCount(pkg: Package): number;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The identity packages are batched by when `version` plans a release - `.rmanrc group`, cascaded
3
+ * like every unmarked key. `true` (the default) puts a package in the implicit repo-wide group, a
4
+ * non-empty string joins exactly the packages naming the same one, and `false` makes it a group of
5
+ * itself.
6
+ *
7
+ * **It lives here rather than on the planner because two unrelated things need the same answer.**
8
+ * `VersionPlanService.resolveGroupKey` batches the plan with it; `ChangeHashService.tagPattern`
9
+ * asks how many lines the repository releases along, which decides whether a repo-wide tag can
10
+ * name a release at all. Two copies of five lines would be easy to write and impossible to keep in
11
+ * step, and the failure they would produce is silent - a boundary computed under one answer and a
12
+ * tag written under the other.
13
+ *
14
+ * `resolveGroupKey` stays `protected` and delegates here, so a planner can still override the
15
+ * batching; what it cannot do is leave the tag pattern behind, which is the point.
16
+ */
17
+ export function groupKeyOf(pkg) {
18
+ const g = pkg.config?.group;
19
+ if (g === false)
20
+ return `solo:${pkg.name}`;
21
+ if (typeof g === 'string' && g)
22
+ return `named:${g}`;
23
+ return 'default';
24
+ }
25
+ /**
26
+ * How many distinct version lines this package's repository releases along - 1 when every package
27
+ * moves together, more once anything is grouped apart.
28
+ *
29
+ * **Structural, exactly like `usesCalendarVersion`'s group count**, and for the same reason: two
30
+ * lines can hold the same number today and diverge tomorrow, so reading the versions would move
31
+ * the answer under the repository's feet.
32
+ *
33
+ * `1` for a package belonging to no repository yet - a bare `new Package(dir, app)`, which the test
34
+ * fixtures build. One line is the answer that changes nothing, which is what an unknown should
35
+ * resolve to here.
36
+ */
37
+ export function versionLineCount(pkg) {
38
+ const repository = pkg.repository;
39
+ const packages = repository?.packages;
40
+ if (!packages?.length)
41
+ return 1;
42
+ return new Set(packages.map(groupKeyOf)).size;
43
+ }
@@ -1,80 +0,0 @@
1
- import { type RmanConfig } from '../interfaces/rman-config.interface.js';
2
- declare const COMMAND: "changed";
3
- declare const config: {
4
- json: {
5
- target: "cli";
6
- alias: string;
7
- describe: string;
8
- type: "boolean";
9
- };
10
- scope: {
11
- target: "cli";
12
- describe: string;
13
- type: "string";
14
- };
15
- ignore: {
16
- target: "cli";
17
- describe: string;
18
- type: "string";
19
- };
20
- platform: {
21
- target: "cli";
22
- describe: string;
23
- type: "string";
24
- };
25
- deps: {
26
- target: "cli";
27
- describe: string;
28
- type: "boolean";
29
- };
30
- dependents: {
31
- target: "cli";
32
- describe: string;
33
- type: "boolean";
34
- };
35
- };
36
- type Args = RmanConfig.ArgsOf<typeof config, typeof COMMAND>;
37
- /** Declares no `target: 'config'` option, so it contributes nothing to `RmanConfig` and writes no
38
- * augmentation - a command that owns no config key simply has none. */
39
- declare const changedCommand: (app: import("../index.js").RmanApplication) => {
40
- command: "changed";
41
- describe: string;
42
- config: {
43
- json: {
44
- target: "cli";
45
- alias: string;
46
- describe: string;
47
- type: "boolean";
48
- };
49
- scope: {
50
- target: "cli";
51
- describe: string;
52
- type: "string";
53
- };
54
- ignore: {
55
- target: "cli";
56
- describe: string;
57
- type: "string";
58
- };
59
- platform: {
60
- target: "cli";
61
- describe: string;
62
- type: "string";
63
- };
64
- deps: {
65
- target: "cli";
66
- describe: string;
67
- type: "boolean";
68
- };
69
- dependents: {
70
- target: "cli";
71
- describe: string;
72
- type: "boolean";
73
- };
74
- };
75
- examples: {
76
- command: string;
77
- }[];
78
- handler: (args: Args) => Promise<void>;
79
- };
80
- export default changedCommand;
@@ -1,36 +0,0 @@
1
- import colors from 'ansi-colors';
2
- import { registerCommand } from '../interfaces/rman-config.interface.js';
3
- import { VersionPlanService } from '../services/version-plan.service.js';
4
- import { packageFilterOptions, readPackageFilterOptions } from '../utils/package-filter.js';
5
- const COMMAND = 'changed';
6
- const config = {
7
- ...packageFilterOptions,
8
- json: { target: 'cli', alias: 'j', describe: 'Print output as JSON', type: 'boolean' },
9
- };
10
- /** Declares no `target: 'config'` option, so it contributes nothing to `RmanConfig` and writes no
11
- * augmentation - a command that owns no config key simply has none. */
12
- const changedCommand = registerCommand(app => {
13
- const repository = app.repository;
14
- return {
15
- command: COMMAND,
16
- describe: 'Shows which packages the next "version" run would bump, without changing anything',
17
- config,
18
- examples: [{ command: '$0 changed' }, { command: '$0 changed --json' }],
19
- handler: async (args) => {
20
- const plan = await VersionPlanService.getPlanner(app).getPlan(repository, readPackageFilterOptions(args));
21
- const changed = plan.filter(e => e.status === 'bump');
22
- if (args.json) {
23
- console.log(JSON.stringify(changed.map(e => ({ name: e.package.name, group: e.group, from: e.from, to: e.to, reason: e.reason })), undefined, 2));
24
- return;
25
- }
26
- if (!changed.length) {
27
- console.log(colors.gray('Nothing has changed.'));
28
- return;
29
- }
30
- for (const e of changed) {
31
- console.log(colors.green('changed'), colors.cyan(e.package.name), colors.gray(`(${e.group})`), e.from, '->', colors.yellow(e.to));
32
- }
33
- },
34
- };
35
- });
36
- export default changedCommand;