rman 1.3.0 → 2.0.0-beta.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.
Files changed (187) hide show
  1. package/README.md +65 -31
  2. package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
  3. package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
  4. package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
  5. package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
  6. package/builtins/platforms/node/commands/ci.command.js +60 -0
  7. package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
  8. package/builtins/platforms/node/commands/clean.command.js +74 -0
  9. package/builtins/platforms/node/index.d.ts +8 -0
  10. package/builtins/platforms/node/index.js +8 -0
  11. package/builtins/platforms/node/node-config.interface.d.ts +76 -0
  12. package/builtins/platforms/node/node-config.interface.js +7 -0
  13. package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
  14. package/builtins/platforms/node/node-manifest.provider.js +125 -0
  15. package/builtins/platforms/node/node.platform.d.ts +53 -0
  16. package/builtins/platforms/node/node.platform.js +134 -0
  17. package/builtins/platforms/node/services/ci.service.d.ts +47 -0
  18. package/builtins/platforms/node/services/ci.service.js +213 -0
  19. package/builtins/platforms/node/services/clean.service.d.ts +53 -0
  20. package/builtins/platforms/node/services/clean.service.js +237 -0
  21. package/builtins/platforms/node/services/publish.service.d.ts +114 -0
  22. package/builtins/platforms/node/services/publish.service.js +371 -0
  23. package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
  24. package/builtins/platforms/node/services/version-plan.service.js +58 -0
  25. package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
  26. package/builtins/platforms/node/utils/workspace-range.js +28 -0
  27. package/builtins/presets/index.d.ts +15 -0
  28. package/builtins/presets/index.js +57 -0
  29. package/builtins/presets/node.d.ts +16 -0
  30. package/builtins/presets/node.js +57 -0
  31. package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
  32. package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
  33. package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
  34. package/builtins/publish-targets/docker/docker.target.js +40 -0
  35. package/builtins/publish-targets/docker/index.d.ts +2 -0
  36. package/builtins/publish-targets/docker/index.js +2 -0
  37. package/builtins/publish-targets/npm/index.d.ts +2 -0
  38. package/builtins/publish-targets/npm/index.js +2 -0
  39. package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
  40. package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
  41. package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
  42. package/builtins/publish-targets/npm/npm-view.js +71 -0
  43. package/cli.d.ts +8 -0
  44. package/cli.js +228 -93
  45. package/commands/build.command.d.ts +177 -3
  46. package/commands/build.command.js +20 -10
  47. package/commands/changelog.command.d.ts +296 -3
  48. package/commands/changelog.command.js +127 -43
  49. package/commands/config.command.d.ts +44 -3
  50. package/commands/config.command.js +53 -24
  51. package/commands/diff.command.d.ts +37 -3
  52. package/commands/diff.command.js +25 -16
  53. package/commands/exec.command.d.ts +193 -3
  54. package/commands/exec.command.js +60 -58
  55. package/commands/github-release.command.d.ts +154 -3
  56. package/commands/github-release.command.js +67 -37
  57. package/commands/import.command.d.ts +36 -3
  58. package/commands/import.command.js +28 -20
  59. package/commands/info.command.d.ts +35 -7
  60. package/commands/info.command.js +36 -30
  61. package/commands/lint.command.d.ts +176 -0
  62. package/commands/lint.command.js +34 -0
  63. package/commands/list.command.d.ts +163 -3
  64. package/commands/list.command.js +109 -71
  65. package/commands/publish.command.d.ts +231 -0
  66. package/commands/publish.command.js +304 -0
  67. package/commands/run.command.d.ts +186 -6
  68. package/commands/run.command.js +26 -72
  69. package/commands/test.command.d.ts +173 -3
  70. package/commands/test.command.js +16 -10
  71. package/commands/version.command.d.ts +330 -3
  72. package/commands/version.command.js +292 -78
  73. package/commands.d.ts +32 -0
  74. package/commands.js +28 -0
  75. package/constants.js +1 -1
  76. package/core/application.d.ts +116 -0
  77. package/core/application.js +160 -0
  78. package/core/classes/package.d.ts +219 -0
  79. package/core/classes/package.js +214 -0
  80. package/core/classes/registry.d.ts +30 -0
  81. package/core/classes/registry.js +47 -0
  82. package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
  83. package/core/{repository.js → classes/repository.js} +195 -75
  84. package/core/classes/service.d.ts +49 -0
  85. package/core/classes/service.js +40 -0
  86. package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
  87. package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
  88. package/core/classes/workspace.d.ts +275 -0
  89. package/core/classes/workspace.js +537 -0
  90. package/core/command-builder.d.ts +14 -0
  91. package/core/command-builder.js +94 -0
  92. package/core/config/config-file-scope.d.ts +107 -0
  93. package/core/config/config-file-scope.js +226 -0
  94. package/core/config/config-interpolator.d.ts +164 -0
  95. package/core/config/config-interpolator.js +577 -0
  96. package/core/config/config-paths.d.ts +65 -0
  97. package/core/config/config-paths.js +78 -0
  98. package/core/config/config-reader.d.ts +192 -0
  99. package/core/config/config-reader.js +727 -0
  100. package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
  101. package/core/config/merge-config.js +260 -0
  102. package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
  103. package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
  104. package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
  105. package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
  106. package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
  107. package/core/interfaces/manifest.js +132 -0
  108. package/core/interfaces/plugin.d.ts +174 -0
  109. package/core/interfaces/plugin.js +124 -0
  110. package/core/interfaces/publish-target.d.ts +136 -0
  111. package/core/interfaces/publish-target.js +43 -0
  112. package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
  113. package/core/interfaces/run-step.js +1 -0
  114. package/core/plugin-loader.d.ts +25 -0
  115. package/core/plugin-loader.js +53 -0
  116. package/index.d.ts +122 -24
  117. package/index.js +66 -16
  118. package/interfaces/config-scope.interface.d.ts +223 -0
  119. package/interfaces/config-scope.interface.js +1 -0
  120. package/interfaces/rman-config.interface.d.ts +821 -203
  121. package/interfaces/rman-config.interface.js +78 -1
  122. package/package.json +3 -1
  123. package/services/change-hash.service.d.ts +42 -8
  124. package/services/change-hash.service.js +92 -13
  125. package/services/changelog.service.d.ts +91 -52
  126. package/services/changelog.service.js +460 -74
  127. package/services/config-service.d.ts +8 -0
  128. package/services/config-service.js +3 -0
  129. package/services/exec.service.d.ts +23 -12
  130. package/services/exec.service.js +14 -9
  131. package/services/github-release.service.d.ts +45 -34
  132. package/services/github-release.service.js +15 -12
  133. package/services/import.service.d.ts +25 -14
  134. package/services/import.service.js +9 -5
  135. package/services/list.service.d.ts +67 -13
  136. package/services/list.service.js +62 -15
  137. package/services/run.service.d.ts +35 -22
  138. package/services/run.service.js +316 -233
  139. package/services/system-info.d.ts +1 -1
  140. package/services/version-plan.service.d.ts +35 -8
  141. package/services/version-plan.service.js +96 -36
  142. package/services/version.service.d.ts +34 -14
  143. package/services/version.service.js +35 -16
  144. package/services.d.ts +1 -1
  145. package/services.js +1 -1
  146. package/utils/bin-path.d.ts +6 -7
  147. package/utils/bin-path.js +7 -18
  148. package/utils/branch-guard.d.ts +30 -1
  149. package/utils/branch-guard.js +31 -0
  150. package/utils/color-yaml.d.ts +8 -0
  151. package/utils/color-yaml.js +86 -0
  152. package/utils/exec.d.ts +10 -0
  153. package/utils/exec.js +1 -1
  154. package/utils/git.d.ts +26 -0
  155. package/utils/git.js +65 -0
  156. package/utils/logger.d.ts +2 -2
  157. package/utils/logger.js +1 -1
  158. package/utils/package-filter.d.ts +135 -8
  159. package/utils/package-filter.js +204 -16
  160. package/utils/printable-config.d.ts +9 -1
  161. package/utils/printable-config.js +46 -1
  162. package/utils/progress-panel.d.ts +9 -0
  163. package/utils/progress-panel.js +23 -1
  164. package/utils/release-version.d.ts +1 -1
  165. package/utils/run-bin.d.ts +10 -0
  166. package/utils/run-bin.js +1 -1
  167. package/utils/run-options.d.ts +97 -0
  168. package/utils/run-options.js +81 -0
  169. package/utils/version-group.d.ts +31 -0
  170. package/utils/version-group.js +43 -0
  171. package/utils/version-stamp.d.ts +1 -1
  172. package/utils/version-stamp.js +1 -1
  173. package/commands/changed.command.d.ts +0 -3
  174. package/commands/changed.command.js +0 -29
  175. package/core/config.d.ts +0 -409
  176. package/core/config.js +0 -939
  177. package/core/extends-config.d.ts +0 -27
  178. package/core/extends-config.js +0 -89
  179. package/core/manifest.js +0 -150
  180. package/core/merge-config.js +0 -219
  181. package/core/package.d.ts +0 -83
  182. package/core/package.js +0 -106
  183. package/core/plugin.d.ts +0 -112
  184. package/core/plugin.js +0 -189
  185. package/core/workspace.d.ts +0 -68
  186. package/core/workspace.js +0 -83
  187. /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
@@ -1,17 +1,120 @@
1
1
  import micromatch from 'micromatch';
2
+ /**
3
+ * **`--scope /` is the repository's own root package**, the one selector that is not a glob.
4
+ *
5
+ * The same `/` `.rmanrc`'s `"[/]"` block uses, and for the same reason stated there: *the root is
6
+ * never selected by name.* A glob matches package names, and a name can be anything - so
7
+ * `--scope rman-repo` happened to work (measured) while being exactly the name-based addressing the
8
+ * config selectors were redesigned to remove. `/` is structural, cannot collide with a package
9
+ * (nothing can be named it), and gives "the root" one spelling across config and CLI.
10
+ *
11
+ * Accepted by `--ignore` too. The asymmetry would be the thing to remember, and `--ignore /` -
12
+ * every package but the root - is a real thing to want of `clean`.
13
+ *
14
+ * **It selects nothing on a command whose candidates exclude the root**, which is most of them:
15
+ * `repository.packages` holds the workspace members only, so `rman list --scope /` and
16
+ * `rman run build --scope /` match nothing and say so. That is the honest answer rather than a
17
+ * special case - the root has no `list` row and contributes only `pre`/`post` bookends to `run`.
18
+ * `clean` and `changelog`, which put the root in their candidate list on purpose, are where it bites.
19
+ */
20
+ export const ROOT_SELECTOR = '/';
21
+ /**
22
+ * `--scope`/`--ignore`/`--deps`/`--dependents` as a **declaration** rather than a builder call -
23
+ * spread into a command's `config` block:
24
+ *
25
+ * ```ts
26
+ * config: { ...packageFilterOptions, ...branchGuardOptions, changelog: { ... } }
27
+ * ```
28
+ *
29
+ * **`satisfies`, never a `: Record<...>` annotation.** An annotation widens `type: 'string'` back to
30
+ * `string`, and every type derived from the declaration - the config contribution, the option's own
31
+ * value type - collapses with it. `satisfies` checks the shape and keeps the literals, and it also
32
+ * catches a misspelled key *here*, at the group's own line, rather than in the ten commands that
33
+ * spread it. The `Argv` chaining this replaces could not: a typo there was simply a new option.
34
+ *
35
+ * **Every option is `target: 'cli'`, including the ones that have a config twin.** A shared group
36
+ * belongs to no command, so declaring one `'both'` would contribute `version.scope` - a key nothing
37
+ * reads. Where a `.rmanrc` equivalent exists it is a core key in its own right (`allowBranch`), and
38
+ * a command that reads it names it in `configKeys`.
39
+ */
40
+ export const packageFilterOptions = {
41
+ scope: {
42
+ target: 'cli',
43
+ describe: 'Only include packages whose name matches this glob, or "/" for the root package (repeatable)',
44
+ // Deliberately 'string', not 'array': an array-typed option greedily swallows every
45
+ // following bare word as its own value, which would eat "exec"'s [command..] positional
46
+ // whole. yargs still collects repeated "--scope a --scope b" into an array either way.
47
+ type: 'string',
48
+ },
49
+ ignore: {
50
+ target: 'cli',
51
+ describe: 'Exclude packages matching this glob (or "/" for the root) - applied after --scope',
52
+ type: 'string',
53
+ },
54
+ platform: {
55
+ target: 'cli',
56
+ describe: 'Only include packages of these platforms, e.g. --platform=node,cargo (repeatable)',
57
+ type: 'string',
58
+ },
59
+ deps: {
60
+ target: 'cli',
61
+ describe: 'Also include every package the matched set depends on',
62
+ type: 'boolean',
63
+ },
64
+ dependents: {
65
+ target: 'cli',
66
+ describe: 'Also include every package that depends on the matched set',
67
+ type: 'boolean',
68
+ },
69
+ };
70
+ /**
71
+ * `--from-root`/`-r` - a group of one, and a function because its text is the command's own word
72
+ * for what it does. Spread it like the others: `...fromRootOption('Build')`.
73
+ *
74
+ * Only where a command scopes by the current directory; see `applyFromRootOption` for why adding it
75
+ * elsewhere is worse than leaving it out.
76
+ *
77
+ * **It was `--root` through 1.x, and the name said the opposite of what it does.** Every reader
78
+ * spells it the same single line - `options.fromRoot ? undefined : repository.currentPackage` - so
79
+ * the flag means *ignore where I am standing*, i.e. the **whole repository**. `--root` reads as "the
80
+ * root alone", which is the narrowest possible set rather than the widest, and the confusion was
81
+ * about to become a contradiction: a `--root-only` beside a `--root` that meant "everything" is
82
+ * unreadable.
83
+ *
84
+ * **`-r` is kept, and a two-letter `-fr` is not possible.** yargs' `short-option-groups` is on by
85
+ * default, so `-fr` parses as `-f -r` and `.strict()` answers `Unknown arguments: f, r` (measured).
86
+ * Turning that off makes `-fr` work and breaks every grouped short - `rman list -sj` works today.
87
+ * So the long name carries the meaning and the incumbent short stays.
88
+ */
89
+ export function fromRootOption(verb) {
90
+ return {
91
+ fromRoot: {
92
+ target: 'cli',
93
+ cliName: 'from-root',
94
+ alias: 'r',
95
+ describe: `${verb} across the whole repository even when the current directory is inside a single ` +
96
+ 'package (which otherwise scopes it to just that package). No effect elsewhere.',
97
+ type: 'boolean',
98
+ },
99
+ };
100
+ }
2
101
  /** `--scope`/`--ignore`/`--deps`/`--dependents`, the same shape and describe text in every command
3
102
  * that supports them - mirrors `run.command.ts`'s own `applyRunOptions`. */
4
103
  export function applyPackageFilterOptions(cmd) {
5
104
  return cmd
6
105
  .option('scope', {
7
- describe: 'Only include packages whose name matches this glob (repeatable)',
106
+ describe: 'Only include packages whose name matches this glob, or "/" for the root package (repeatable)',
8
107
  // Deliberately 'string', not 'array': an array-typed option greedily swallows every
9
108
  // following bare word as its own value, which would eat "exec"'s [command..] positional
10
109
  // whole. yargs still collects repeated "--scope a --scope b" into an array either way.
11
110
  type: 'string',
12
111
  })
13
112
  .option('ignore', {
14
- describe: 'Exclude packages whose name matches this glob (repeatable) - applied after --scope',
113
+ describe: 'Exclude packages matching this glob (or "/" for the root) - applied after --scope',
114
+ type: 'string',
115
+ })
116
+ .option('platform', {
117
+ describe: 'Only include packages of these platforms, e.g. --platform=node,cargo (repeatable)',
15
118
  type: 'string',
16
119
  })
17
120
  .option('deps', {
@@ -27,6 +130,8 @@ export function readPackageFilterOptions(args) {
27
130
  return {
28
131
  scope: args.scope,
29
132
  ignore: args.ignore,
133
+ platform: args.platform,
134
+ commandPlatforms: args.commandPlatforms,
30
135
  deps: args.deps,
31
136
  dependents: args.dependents,
32
137
  };
@@ -34,7 +139,8 @@ export function readPackageFilterOptions(args) {
34
139
  /**
35
140
  * Narrows `packages` (the full, already-resolved list - toposort order, if any, is preserved)
36
141
  * down to what `options` selects. `scope`/`ignore` match against each package's bare name (glob
37
- * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...); `ignore` is applied after `scope`, on
142
+ * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...), plus `ROOT_SELECTOR` (`"/"`) for the root
143
+ * package, which is matched structurally rather than by name; `ignore` is applied after `scope`, on
38
144
  * whatever it left. `deps`/`dependents` then each independently expand *that* matched set along
39
145
  * `Package.dependencies` (already the full transitive closure - see
40
146
  * `Repository`'s own `_updateDependencies`) and their results are unioned in - so `--deps
@@ -57,12 +163,22 @@ export function filterPackages(packages, options,
57
163
  applySkip = true) {
58
164
  let matched = applySkip ? packages.filter(p => p.config?.skip !== true) : packages;
59
165
  if (options.scope) {
60
- const patterns = toArray(options.scope);
61
- matched = matched.filter(p => micromatch.isMatch(p.name, patterns));
166
+ const selects = selector(options.scope);
167
+ matched = matched.filter(p => selects(p));
62
168
  }
63
169
  if (options.ignore) {
64
- const patterns = toArray(options.ignore);
65
- matched = matched.filter(p => !micromatch.isMatch(p.name, patterns));
170
+ const selects = selector(options.ignore);
171
+ matched = matched.filter(p => !selects(p));
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
+ }
179
+ if (options.platform !== undefined) {
180
+ const wanted = platformNames(options.platform, packages);
181
+ matched = matched.filter(p => wanted.has(p.provider.toLowerCase()));
66
182
  }
67
183
  if (!options.deps && !options.dependents)
68
184
  return matched;
@@ -83,11 +199,8 @@ applySkip = true) {
83
199
  }
84
200
  return packages.filter(p => included.has(p));
85
201
  }
86
- function toArray(value) {
87
- return Array.isArray(value) ? value : [value];
88
- }
89
202
  /**
90
- * `--root`/`-r`, with one describe text instead of four near-identical ones.
203
+ * `--from-root`/`-r`, with one describe text instead of four near-identical ones.
91
204
  *
92
205
  * **It only means anything where a command scopes by the current directory** - `run`/`build`/`test`,
93
206
  * `exec`, `clean`, `changelog` and `diff` narrow to `Repository.currentPackage` when you stand
@@ -95,19 +208,94 @@ function toArray(value) {
95
208
  * repository (`version`, `publish`, `list`, `changed`) it would be a flag that does nothing, which
96
209
  * is worse than not offering it: a no-op flag reads as a promise.
97
210
  *
211
+ * That same rule is why there is no `--root-only` beside it, however naturally the pair reads: it
212
+ * would be a no-op on `run`/`build`/`test` (the root is not in `repository.packages` at all and
213
+ * contributes only `pre`/`post` bookends), identical to this flag on `diff`, already what this flag
214
+ * does on `config`, and on `clean` actively misleading - the root's own sweep recurses through
215
+ * `packages/*`, so a `--root-only` there deletes *more* than a package-scoped run, not less
216
+ * (measured). Where the root genuinely is a candidate, `--scope /` says so - see `ROOT_SELECTOR`.
217
+ *
98
218
  * `verb` is the command's own word for what it does, so the text stays the sentence each command was
99
219
  * already saying.
100
220
  */
101
- export function applyRootOption(cmd, verb) {
102
- return cmd.option('root', {
221
+ export function applyFromRootOption(cmd, verb) {
222
+ return cmd.option('from-root', {
103
223
  alias: 'r',
104
224
  describe: `${verb} across the whole repository even when the current directory is inside a single ` +
105
225
  'package (which otherwise scopes it to just that package). No effect elsewhere.',
106
226
  type: 'boolean',
107
227
  });
108
228
  }
109
- /** The `--root` flag as the services read it - beside `readPackageFilterOptions`, so a command
229
+ /** The `--from-root` flag as the services read it - beside `readPackageFilterOptions`, so a command
110
230
  * reads both the same way. */
111
- export function readRootOption(args) {
112
- return args.root;
231
+ export function readFromRootOption(args) {
232
+ return args.fromRoot;
233
+ }
234
+ function toArray(value) {
235
+ return Array.isArray(value) ? value : [value];
236
+ }
237
+ /**
238
+ * One `--scope`/`--ignore` value list as a predicate, with `ROOT_SELECTOR` lifted out of the globs.
239
+ *
240
+ * Split once rather than per package. The root is answered by `wantsRoot` alone and everything else
241
+ * by the globs, so the two never consult each other - and `micromatch` is asked only when a glob is
242
+ * left, since an empty pattern list must match no member rather than all of them, which is what
243
+ * makes a bare `--scope /` select the root **and nothing else**.
244
+ *
245
+ * **A glob is never offered the root**, which is the other half of `ROOT_SELECTOR` and the reason it
246
+ * is not merely a second spelling. `.rmanrc`'s selectors state the same rule - `"[my-*]"` cannot
247
+ * pick up a repository whose root package is called `my-repo`, and `"[*]"` means the members - and
248
+ * the CLI disagreed with it: measured, `rman clean --scope 'rman*'` selected this repository's root.
249
+ * For `clean` that is destructive rather than merely surprising, since the root's own sweep recurses
250
+ * through `packages/*`. Globs are the members, `/` is the root, in both vocabularies.
251
+ */
252
+ /**
253
+ * `--platform` as a set of lower-cased names, refusing one no package in the repository belongs to.
254
+ *
255
+ * **Comma-split, unlike `--scope`, and the asymmetry is the values' own.** A platform's name is a
256
+ * short identifier a plugin chose, so `--platform=node,cargo` cannot be ambiguous; a scope glob is
257
+ * arbitrary text, where splitting would take a character away from the pattern language. Repeating
258
+ * the flag works for both.
259
+ *
260
+ * **An unknown name is an error, where an unmatched `--scope` glob is not**, and the difference is
261
+ * whether rman knows the answer set. It does here: the platforms are the ones its packages belong
262
+ * to, so `--platform crago` is a typo rman can see, and left alone it is the silent empty result
263
+ * this whole codebase keeps ruling out. The message lists what the repository has - the same call
264
+ * `publish --target` makes against its registry.
265
+ *
266
+ * **Compared case-insensitively.** `Platform.name` is the authority on spelling and `--platform Node`
267
+ * is not a different request; refusing it would be pedantry with an empty result attached.
268
+ *
269
+ * Asked of the packages rather than of `app.platforms`, which `filterPackages` does not have and
270
+ * which would be the wrong set anyway: a registered platform that claimed no directory is not an
271
+ * answer to "what is in this repository".
272
+ */
273
+ function platformNames(value, packages) {
274
+ const wanted = toArray(value)
275
+ .flatMap(entry => entry.split(','))
276
+ .map(name => name.trim().toLowerCase())
277
+ .filter(Boolean);
278
+ /** An empty `provider` is a package no technology claimed, and there is no spelling for it - a
279
+ * flag selecting "none" would be `--platform ''`, which no shell makes pleasant. Left out of the
280
+ * known set, so naming it is refused like any other unknown. */
281
+ const present = new Set(packages.map(p => p.provider.toLowerCase()).filter(Boolean));
282
+ for (const name of wanted) {
283
+ if (present.has(name))
284
+ continue;
285
+ throw new Error(`--platform "${name}" matches no package in this repository. ` +
286
+ (present.size
287
+ ? `It holds: ${[...present].sort().join(', ')}.`
288
+ : 'No package here belongs to a platform - its ".rmanrc" names no "plugins".'));
289
+ }
290
+ return new Set(wanted);
291
+ }
292
+ function selector(value) {
293
+ const patterns = toArray(value);
294
+ const wantsRoot = patterns.includes(ROOT_SELECTOR);
295
+ const globs = patterns.filter(p => p !== ROOT_SELECTOR);
296
+ /** Against `pkg.selector`, which is what `.rmanrc`'s `"[glob]"` matches - one vocabulary, as the
297
+ * `/` above already is. It was `pkg.name`, and the two coincide for every Node repository; a
298
+ * package having a name at all is an ecosystem's promise, and a repository can assign a selector
299
+ * where its own does not offer one. */
300
+ return pkg => (pkg.isRoot ? wantsRoot : globs.length > 0 && micromatch.isMatch(pkg.selector, globs));
113
301
  }
@@ -5,7 +5,7 @@
5
5
  * Needed because a config legitimately holds functions now: a `run.<script>` or `version.<slot>`
6
6
  * step written as JavaScript, and an `if` written the same way. It was already needed before that,
7
7
  * though, which is the better argument for doing it here rather than at one call site - a
8
- * `plugins` entry given in its object form carries the plugin's seams, and `rman config --root`
8
+ * `plugins` entry given in its object form carries the plugin's seams, and `rman config --from-root`
9
9
  * died on one with `unacceptable kind of an object to dump [object Function]` (measured, on a
10
10
  * repository whose shared config did nothing more unusual than `extends` a plugin package).
11
11
  *
@@ -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*.
@@ -5,7 +6,7 @@
5
6
  * Needed because a config legitimately holds functions now: a `run.<script>` or `version.<slot>`
6
7
  * step written as JavaScript, and an `if` written the same way. It was already needed before that,
7
8
  * though, which is the better argument for doing it here rather than at one call site - a
8
- * `plugins` entry given in its object form carries the plugin's seams, and `rman config --root`
9
+ * `plugins` entry given in its object form carries the plugin's seams, and `rman config --from-root`
9
10
  * died on one with `unacceptable kind of an object to dump [object Function]` (measured, on a
10
11
  * repository whose shared config did nothing more unusual than `extends` a plugin package).
11
12
  *
@@ -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
@@ -1,5 +1,15 @@
1
+ import type { RmanApplication } from '../core/application.js';
1
2
  import { type LogLevel } from './logger.js';
2
3
  export interface RunBinOptions {
4
+ /**
5
+ * The application whose technologies put a repository's locally installed binaries on PATH -
6
+ * `node_modules/.bin` for a Node repository, whatever another technology uses.
7
+ *
8
+ * Passed rather than looked up, so a command run against one repository can never pick up the
9
+ * binaries of another in the same process. Omitted (a caller outside any repository) leaves the
10
+ * inherited PATH exactly as it was, which is also what a repository naming no plugin gets.
11
+ */
12
+ app?: RmanApplication;
3
13
  /** Where to run it, and the directory `node_modules/.bin` is resolved from. Default `process.cwd()`. */
4
14
  cwd?: string;
5
15
  /** 'inherit' streams the child's output straight to the terminal; 'pipe' captures it and resolves
package/utils/run-bin.js CHANGED
@@ -34,7 +34,7 @@ export async function runBin(bin, argv, options = {}) {
34
34
  const child = spawn(process.platform === 'win32' ? `${bin}.cmd` : bin, argv, {
35
35
  cwd,
36
36
  stdio: stdio === 'inherit' ? 'inherit' : 'pipe',
37
- env: BinPath.env({ cwd, env: options.env }),
37
+ env: BinPath.env({ cwd, env: options.env, app: options.app }),
38
38
  windowsHide: true,
39
39
  });
40
40
  /** So an interrupted rman does not leave this running - `exec` always did this and this did not,
@@ -0,0 +1,97 @@
1
+ import type { RunService } from '../services/run.service.js';
2
+ /**
3
+ * Everything `run`, `build` and `test` accept - the package filter, the branch guard, `--from-root`, and
4
+ * the scheduling flags on top.
5
+ *
6
+ * **In `utils/` rather than in `run.command.ts`, which is where the `Argv` version lived.** `build`
7
+ * and `test` had to import it from a sibling command, which reads as a dependency between commands
8
+ * and is not one: they share a declaration, the way they already share `packageFilterOptions`.
9
+ *
10
+ * Every option is `target: 'cli'`. The `.rmanrc` half of these lives at `run.<script>.concurrency`,
11
+ * `.bail`, `.topo`, `.progress` - keyed by script name, so it is the one config shape that cannot be
12
+ * derived from a flat option map and stays hand-written in `RmanConfig`.
13
+ */
14
+ export declare const runOptions: {
15
+ parallel: {
16
+ target: "cli";
17
+ describe: string;
18
+ /** No `type`: the flag takes a boolean *or* a number, and `coerce` is what says so - which is
19
+ * also where `OptionValue` reads this option's type from. */
20
+ coerce: (v: unknown) => boolean | number | undefined;
21
+ };
22
+ bail: {
23
+ target: "cli";
24
+ describe: string;
25
+ type: "boolean";
26
+ };
27
+ topo: {
28
+ target: "cli";
29
+ describe: string;
30
+ type: "boolean";
31
+ };
32
+ progress: {
33
+ target: "cli";
34
+ describe: string;
35
+ type: "boolean";
36
+ };
37
+ changed: {
38
+ target: "cli";
39
+ alias: string;
40
+ describe: string;
41
+ type: "boolean";
42
+ };
43
+ changedSince: {
44
+ target: "cli";
45
+ cliName: string;
46
+ describe: string;
47
+ type: "string";
48
+ /** Declared on the option rather than as a separate `.conflicts()` call - one place says what
49
+ * this flag is and what it cannot be combined with. */
50
+ conflicts: string;
51
+ };
52
+ fromRoot: {
53
+ target: "cli";
54
+ cliName: string;
55
+ alias: string;
56
+ describe: string;
57
+ type: "boolean";
58
+ };
59
+ allowBranch: {
60
+ target: "cli";
61
+ cliName: string;
62
+ describe: string;
63
+ type: "string";
64
+ };
65
+ ignoreBranch: {
66
+ target: "cli";
67
+ cliName: string;
68
+ describe: string;
69
+ type: "string";
70
+ };
71
+ scope: {
72
+ target: "cli";
73
+ describe: string;
74
+ type: "string";
75
+ };
76
+ ignore: {
77
+ target: "cli";
78
+ describe: string;
79
+ type: "string";
80
+ };
81
+ platform: {
82
+ target: "cli";
83
+ describe: string;
84
+ type: "string";
85
+ };
86
+ deps: {
87
+ target: "cli";
88
+ describe: string;
89
+ type: "boolean";
90
+ };
91
+ dependents: {
92
+ target: "cli";
93
+ describe: string;
94
+ type: "boolean";
95
+ };
96
+ };
97
+ export declare function readRunOptions(args: any): RunService.Options;
@@ -0,0 +1,81 @@
1
+ import { branchGuardOptions } from './branch-guard.js';
2
+ import { fromRootOption, packageFilterOptions, readPackageFilterOptions } from './package-filter.js';
3
+ /**
4
+ * Everything `run`, `build` and `test` accept - the package filter, the branch guard, `--from-root`, and
5
+ * the scheduling flags on top.
6
+ *
7
+ * **In `utils/` rather than in `run.command.ts`, which is where the `Argv` version lived.** `build`
8
+ * and `test` had to import it from a sibling command, which reads as a dependency between commands
9
+ * and is not one: they share a declaration, the way they already share `packageFilterOptions`.
10
+ *
11
+ * Every option is `target: 'cli'`. The `.rmanrc` half of these lives at `run.<script>.concurrency`,
12
+ * `.bail`, `.topo`, `.progress` - keyed by script name, so it is the one config shape that cannot be
13
+ * derived from a flat option map and stays hand-written in `RmanConfig`.
14
+ */
15
+ export const runOptions = {
16
+ ...packageFilterOptions,
17
+ ...branchGuardOptions,
18
+ ...fromRootOption('Run'),
19
+ parallel: {
20
+ target: 'cli',
21
+ describe: 'Max packages to build at once: omit/true for CPU count (or .rmanrc run.<script>.concurrency), ' +
22
+ 'a number for that many, false to run serially. Packages always build in dependency order.',
23
+ /** No `type`: the flag takes a boolean *or* a number, and `coerce` is what says so - which is
24
+ * also where `OptionValue` reads this option's type from. */
25
+ coerce: (v) => {
26
+ if (v === undefined)
27
+ return undefined;
28
+ if (v === 'false' || v === false)
29
+ return false;
30
+ if (v === 'true' || v === true)
31
+ return true;
32
+ const n = Number(v);
33
+ return Number.isFinite(n) ? n : true;
34
+ },
35
+ },
36
+ bail: {
37
+ target: 'cli',
38
+ describe: 'Stop on first failure (default: true, overridable per-package via .rmanrc run.<script>.bail)',
39
+ type: 'boolean',
40
+ },
41
+ topo: {
42
+ target: 'cli',
43
+ describe: 'Respect package dependency order: a package waits for its dependencies and is skipped if one ' +
44
+ 'fails. Set false for independent scripts (e.g. lint/test) - order becomes alphabetical and ' +
45
+ 'failures never skip other packages. Default: true, overridable per-package via .rmanrc run.<script>.topo.',
46
+ type: 'boolean',
47
+ },
48
+ progress: {
49
+ target: 'cli',
50
+ describe: 'Show a live progress panel (default: true; auto-disabled when not a TTY; .rmanrc run.<script>.progress)',
51
+ type: 'boolean',
52
+ },
53
+ changed: {
54
+ target: 'cli',
55
+ alias: 'c',
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
+ type: 'boolean',
58
+ },
59
+ changedSince: {
60
+ target: 'cli',
61
+ cliName: 'changed-since',
62
+ describe: 'Only run in packages that have changed since the given git commit/hash',
63
+ type: 'string',
64
+ /** Declared on the option rather than as a separate `.conflicts()` call - one place says what
65
+ * this flag is and what it cannot be combined with. */
66
+ conflicts: 'changed',
67
+ },
68
+ };
69
+ export function readRunOptions(args) {
70
+ return {
71
+ ...readPackageFilterOptions(args),
72
+ parallel: args.parallel,
73
+ topo: args.topo,
74
+ bail: args.bail,
75
+ progress: args.progress,
76
+ changed: args.changed,
77
+ changedSince: args.changedSince,
78
+ logLevel: args.logLevel,
79
+ fromRoot: args.fromRoot,
80
+ };
81
+ }