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,6 +1,37 @@
1
1
  import colors from 'ansi-colors';
2
2
  import micromatch from 'micromatch';
3
3
  import { GitHelper } from './git.js';
4
+ /**
5
+ * `--allow-branch`/`--ignore-branch` as a declaration, to spread into a command's `config` block.
6
+ * See `packageFilterOptions` for why this is `satisfies` rather than an annotation.
7
+ *
8
+ * **The key is the camelCase one and `cliName` carries the flag's spelling.** `allowBranch` is what
9
+ * a `.rmanrc` writes and what `assertAllowedBranch` reads off the config; `--allow-branch` is what
10
+ * the shell sees. The `Argv` version relied on yargs converting one into the other silently, which
11
+ * is why `applyBranchGuardOptions` declares `'allow-branch'` and `readBranchGuardOptions` reads
12
+ * `args.allowBranch`.
13
+ *
14
+ * **`target: 'cli'`, even though `allowBranch` *is* a config key.** It is a repo-wide core key read
15
+ * off the root, not any command's - so it belongs on `RmanConfig` directly, and a command that
16
+ * reads it names it in `configKeys`. Declaring it `'both'` here would contribute `version.allowBranch`,
17
+ * which nothing reads.
18
+ */
19
+ export const branchGuardOptions = {
20
+ allowBranch: {
21
+ target: 'cli',
22
+ cliName: 'allow-branch',
23
+ describe: 'Refuse to run unless the current branch matches this glob (repeatable) - default: .rmanrc ' +
24
+ '"allowBranch", or no restriction at all',
25
+ type: 'string',
26
+ },
27
+ ignoreBranch: {
28
+ target: 'cli',
29
+ cliName: 'ignore-branch',
30
+ describe: 'Refuse to run if the current branch matches this glob (repeatable) - default: .rmanrc ' +
31
+ '"ignoreBranch", or no restriction at all',
32
+ type: 'string',
33
+ },
34
+ };
4
35
  /** `--allow-branch`/`--ignore-branch`, the same shape and describe text in every command that
5
36
  * supports them - mirrors `package-filter.ts`'s own `applyPackageFilterOptions`. */
6
37
  export function applyBranchGuardOptions(cmd) {
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A YAML document with syntax colour, for the two commands that print one to be **looked at** -
3
+ * `rman config` and `--config`.
4
+ *
5
+ * **Only call this when `process.stdout.isTTY`.** Colouring a document that is being redirected
6
+ * makes it unloadable, which is the whole reason the callers gate on a terminal.
7
+ */
8
+ export declare function colorYaml(text: string): string;
@@ -0,0 +1,86 @@
1
+ import colors from 'ansi-colors';
2
+ /**
3
+ * A YAML document with syntax colour, for the two commands that print one to be **looked at** -
4
+ * `rman config` and `--config`.
5
+ *
6
+ * **Only call this when `process.stdout.isTTY`.** Colouring a document that is being redirected
7
+ * makes it unloadable, which is the whole reason the callers gate on a terminal.
8
+ */
9
+ /* **Line-based and deliberately shallow.** This is a highlighter, not a parser: it colours what a
10
+ * reader's eye uses to navigate - the key, the list marker, a literal - and leaves anything it is
11
+ * not sure about alone. A real parse would have to round-trip js-yaml's own output to gain nothing
12
+ * a reader would notice, and every extra rule is a new way to mangle a value.
13
+ *
14
+ * **What it colours and why that set**:
15
+ *
16
+ * - the **key**, in cyan, because that is what you scan a config for;
17
+ * - **`${{ ... }}`**, in magenta, because rman's configs are full of them and because a `version`
18
+ * hook is printed *raw* (`DEFERRED_PATHS`) - the one place an unresolved expression is correct
19
+ * rather than a bug, and the colour is what stops it reading as a mistake;
20
+ * - **numbers, booleans and `null`**, in yellow, since `skip: false` and `skip: "false"` mean
21
+ * different things and nothing else in the output tells them apart;
22
+ * - **`[Function: name]`**, in grey, because it is this printer's placeholder rather than anything
23
+ * the config says - the one value in the output that is not a value;
24
+ * - the **`-`** of a list item and a `#` comment, in grey, as structure rather than content.
25
+ *
26
+ * A plain string value is left uncoloured on purpose. It is the *content*, usually the longest
27
+ * thing on the line, and colouring it competes with the key for attention - which is the failure
28
+ * mode of a highlighter that paints everything.
29
+ *
30
+ * **The two places a shallow pass has to be careful, and neither is hypothetical:**
31
+ *
32
+ * - **A `:` inside a value.** js-yaml quotes any scalar holding one, so the line is
33
+ * `key: 'a: b'` - and the key pattern stops at the *first* colon, which is the right one. Nothing
34
+ * to do, but it is why the pattern is written `[^:]*` rather than anything greedier.
35
+ * - **A folded block scalar.** `lineWidth: 100` makes js-yaml emit `describe: >-` and continue on
36
+ * deeper-indented lines, and a continuation line is prose: one holding `(default: ...)` would
37
+ * otherwise have half a sentence coloured as a key. So the block's indent is tracked and its
38
+ * content is left alone but for expressions. A `#` there is prose too, not a comment. */
39
+ export function colorYaml(text) {
40
+ /** The indent of the key that opened a block scalar, while one is open. Anything indented past it
41
+ * is that block's content; the first line that is not ends it. */
42
+ let blockIndent;
43
+ return text
44
+ .split('\n')
45
+ .map(line => {
46
+ const indent = line.length - line.trimStart().length;
47
+ if (blockIndent !== undefined) {
48
+ if (line.trim() === '' || indent > blockIndent)
49
+ return colorExpressions(line);
50
+ blockIndent = undefined;
51
+ }
52
+ if (BLOCK_SCALAR.test(line))
53
+ blockIndent = indent;
54
+ return colorLine(line);
55
+ })
56
+ .join('\n');
57
+ }
58
+ /** `key: >-`, `key: |`, `key: >2-`, and the bare `- >-` of a list item. */
59
+ const BLOCK_SCALAR = /:\s*[|>][0-9]*[-+]?\s*$/;
60
+ const EXPRESSION = /\$\{\{[\s\S]*?\}\}/g;
61
+ const LITERAL = /^(true|false|null|~|-?\d+(?:\.\d+)?)$/;
62
+ const FUNCTION = /^"?\[Function(?::[^\]]*)?\]"?$/;
63
+ function colorLine(line) {
64
+ const comment = /^(\s*)#(.*)$/.exec(line);
65
+ if (comment)
66
+ return comment[1] + colors.gray(`#${comment[2]}`);
67
+ /** `indent`, an optional `- ` for a list item, then either `key:` plus a value or a bare item. */
68
+ const parsed = /^(\s*)(- )?(?:([^\s:][^:]*):(\s|$))?([\s\S]*)$/.exec(line);
69
+ if (!parsed)
70
+ return line;
71
+ const [, indent = '', dash, key, gap = '', rest = ''] = parsed;
72
+ return indent + (dash ? colors.gray(dash) : '') + (key ? `${colors.cyan(key)}:${gap}` : '') + colorValue(rest);
73
+ }
74
+ function colorValue(value) {
75
+ const trimmed = value.trim();
76
+ if (!trimmed)
77
+ return value;
78
+ if (LITERAL.test(trimmed))
79
+ return value.replace(trimmed, colors.yellow(trimmed));
80
+ if (FUNCTION.test(trimmed))
81
+ return value.replace(trimmed, colors.gray(trimmed));
82
+ return colorExpressions(value);
83
+ }
84
+ function colorExpressions(value) {
85
+ return value.replace(EXPRESSION, match => colors.magenta(match));
86
+ }
package/utils/exec.d.ts CHANGED
@@ -1,4 +1,14 @@
1
+ import type { RmanApplication } from '../core/application.js';
1
2
  export interface ExecOptions {
3
+ /**
4
+ * The application whose technologies put a repository's locally installed binaries on PATH -
5
+ * `node_modules/.bin` for a Node repository, whatever another technology uses.
6
+ *
7
+ * Passed rather than looked up, so a command run against one repository can never pick up the
8
+ * binaries of another in the same process. Omitted (a caller outside any repository) leaves the
9
+ * inherited PATH exactly as it was, which is also what a repository naming no plugin gets.
10
+ */
11
+ app?: RmanApplication;
2
12
  /** 'inherit' streams the child directly to our stdio (used for non-TTY/CI passthrough).
3
13
  * 'pipe' (default) captures output so the caller can drive a live view via onLine. */
4
14
  stdio?: 'inherit' | 'pipe';
package/utils/exec.js CHANGED
@@ -17,7 +17,7 @@ export async function exec(command, options) {
17
17
  ...options,
18
18
  };
19
19
  opts.env = {
20
- ...BinPath.env({ cwd: opts.cwd }),
20
+ ...BinPath.env({ cwd: opts.cwd, app: opts.app }),
21
21
  ...opts.env,
22
22
  };
23
23
  opts.cwd = opts.cwd || process.cwd();
package/utils/git.d.ts CHANGED
@@ -46,6 +46,19 @@ export declare class GitHelper {
46
46
  /** Tag names matching the glob `pattern` (e.g. `"v*"`, `"pkg-a@*"`), newest version first. */
47
47
  listTags(pattern: string): Promise<string[]>;
48
48
  /** Whether `tag` exists as an exact git tag name (not a glob - a literal ref check). */
49
+ /**
50
+ * Every tag matching `pattern`, paired with the **commit** it points at - newest version first,
51
+ * the same order `listTags` returns.
52
+ *
53
+ * `%(*objectname)` is the dereferenced target and is empty for a lightweight tag, so the commit
54
+ * is that or `%(objectname)`. rman's own tags are lightweight; a repository's existing ones may
55
+ * not be, and an annotated tag's `objectname` is the *tag object*, which matches no commit and
56
+ * would make the tag silently invisible to anything comparing shas.
57
+ */
58
+ listTagCommits(pattern: string): Promise<{
59
+ tag: string;
60
+ sha: string;
61
+ }[]>;
49
62
  tagExists(tag: string): Promise<boolean>;
50
63
  /** The most recent commit that changed `file` (tracked history only) - `undefined` if it has
51
64
  * never been committed. Answers "since when has this file last been updated", independent of
@@ -61,6 +74,19 @@ export declare class GitHelper {
61
74
  /** The repository's very first commit (the oldest root commit, for a history with several) -
62
75
  * `undefined` for a repository with no commits at all. The "since the beginning" boundary for
63
76
  * a package being released for the first time, with no earlier tag to measure from. */
77
+ /** HEAD's own sha, or `undefined` in a repository with no commits yet (or none at all) - the
78
+ * same "answer nothing rather than throw" shape `rootCommit` and `describeTag` take, because
79
+ * every caller here already has a "nothing to go on" branch. */
80
+ /** The date a ref points at, as `YYYY-MM-DD`. The **committer** date, not the author's: a
81
+ * rebased or cherry-picked release commit was authored earlier than it shipped, and what a
82
+ * changelog heading states is when the release happened. `undefined` for a ref that resolves to
83
+ * nothing. */
84
+ commitDate(ref: string): Promise<string | undefined>;
85
+ /** The commit `ref` names - a sha, a tag, a branch - or `undefined` when git does not recognize
86
+ * it. `^{commit}` so an annotated tag answers with the commit rather than the tag object, and
87
+ * so a tree or blob answers with nothing rather than a sha that matches no commit. */
88
+ resolveCommit(ref: string): Promise<string | undefined>;
89
+ headSha(): Promise<string | undefined>;
64
90
  rootCommit(): Promise<string | undefined>;
65
91
  /**
66
92
  * Stages and commits exactly `files` (relative to `cwd`, or absolute) with `message` - never a
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,10 +1,10 @@
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
  /**
5
5
  * Gates console output by verbosity (`--log-level`/`.rmanrc logLevel`) so a command's routine
6
6
  * narration can be turned down without losing failure output - the same semantics `run`'s own
7
- * classic per-step log already used (see `resolveLogLevel` in `commands/run.ts`), centralized here
7
+ * classic per-step log already used (see `resolveLogLevel` in `commands/run.command.ts`), centralized here
8
8
  * so every command can respect `--log-level` instead of reimplementing the checks, or ignoring it
9
9
  * altogether via plain `console.log`.
10
10
  */
package/utils/logger.js CHANGED
@@ -2,7 +2,7 @@ export const LOG_LEVELS = ['silent', 'error', 'info', 'verbose'];
2
2
  /**
3
3
  * Gates console output by verbosity (`--log-level`/`.rmanrc logLevel`) so a command's routine
4
4
  * narration can be turned down without losing failure output - the same semantics `run`'s own
5
- * classic per-step log already used (see `resolveLogLevel` in `commands/run.ts`), centralized here
5
+ * classic per-step log already used (see `resolveLogLevel` in `commands/run.command.ts`), centralized here
6
6
  * so every command can respect `--log-level` instead of reimplementing the checks, or ignoring it
7
7
  * altogether via plain `console.log`.
8
8
  */
@@ -1,15 +1,41 @@
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
- /** Only include packages whose name matches at least one of these globs (e.g. `@scope/*`). */
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[];
16
+ /** Only include packages whose name matches at least one of these globs (e.g. `@scope/*`), or
17
+ * **`"/"`** for the repository's own root package - see `ROOT_SELECTOR`. */
10
18
  scope?: string | string[];
11
- /** Exclude packages whose name matches at least one of these globs - applied after `scope`. */
19
+ /** Exclude packages whose name matches at least one of these globs (or `"/"`) - applied after
20
+ * `scope`. */
12
21
  ignore?: string | string[];
22
+ /**
23
+ * Only include packages belonging to one of these **platforms** (`Package.provider`) - `['node']`,
24
+ * or `'node,cargo'` as one comma-separated value.
25
+ *
26
+ * **Which technology a package belongs to only became a per-package question when the walk
27
+ * started finding nested packages of another platform**, and this is the filter that question
28
+ * needs: in a polyglot repository `rman run build --platform node` is the whole npm half of it,
29
+ * and there was no way to say that. Where every package is one technology it selects everything
30
+ * or nothing, which is the honest answer rather than a flag that does nothing.
31
+ *
32
+ * **Names, not globs.** A platform's name is a short identifier a plugin chose (`'node'`), so the
33
+ * set of valid values is known and finite - which is also why a name that no package in the
34
+ * repository belongs to is an **error** listing the ones that are there, the same call
35
+ * `publish --target` makes. A glob would put this back to guessing, and a typo back to a silent
36
+ * empty result.
37
+ */
38
+ platform?: string | string[];
13
39
  /** Also include every package the matched set depends on (transitively) - e.g. to build
14
40
  * everything a scoped app actually needs. */
15
41
  deps?: boolean;
@@ -17,6 +43,99 @@ export interface PackageFilterOptions {
17
43
  * everything that could be affected by a scoped library's change. */
18
44
  dependents?: boolean;
19
45
  }
46
+ /**
47
+ * **`--scope /` is the repository's own root package**, the one selector that is not a glob.
48
+ *
49
+ * The same `/` `.rmanrc`'s `"[/]"` block uses, and for the same reason stated there: *the root is
50
+ * never selected by name.* A glob matches package names, and a name can be anything - so
51
+ * `--scope rman-repo` happened to work (measured) while being exactly the name-based addressing the
52
+ * config selectors were redesigned to remove. `/` is structural, cannot collide with a package
53
+ * (nothing can be named it), and gives "the root" one spelling across config and CLI.
54
+ *
55
+ * Accepted by `--ignore` too. The asymmetry would be the thing to remember, and `--ignore /` -
56
+ * every package but the root - is a real thing to want of `clean`.
57
+ *
58
+ * **It selects nothing on a command whose candidates exclude the root**, which is most of them:
59
+ * `repository.packages` holds the workspace members only, so `rman list --scope /` and
60
+ * `rman run build --scope /` match nothing and say so. That is the honest answer rather than a
61
+ * special case - the root has no `list` row and contributes only `pre`/`post` bookends to `run`.
62
+ * `clean` and `changelog`, which put the root in their candidate list on purpose, are where it bites.
63
+ */
64
+ export declare const ROOT_SELECTOR = "/";
65
+ /**
66
+ * `--scope`/`--ignore`/`--deps`/`--dependents` as a **declaration** rather than a builder call -
67
+ * spread into a command's `config` block:
68
+ *
69
+ * ```ts
70
+ * config: { ...packageFilterOptions, ...branchGuardOptions, changelog: { ... } }
71
+ * ```
72
+ *
73
+ * **`satisfies`, never a `: Record<...>` annotation.** An annotation widens `type: 'string'` back to
74
+ * `string`, and every type derived from the declaration - the config contribution, the option's own
75
+ * value type - collapses with it. `satisfies` checks the shape and keeps the literals, and it also
76
+ * catches a misspelled key *here*, at the group's own line, rather than in the ten commands that
77
+ * spread it. The `Argv` chaining this replaces could not: a typo there was simply a new option.
78
+ *
79
+ * **Every option is `target: 'cli'`, including the ones that have a config twin.** A shared group
80
+ * belongs to no command, so declaring one `'both'` would contribute `version.scope` - a key nothing
81
+ * reads. Where a `.rmanrc` equivalent exists it is a core key in its own right (`allowBranch`), and
82
+ * a command that reads it names it in `configKeys`.
83
+ */
84
+ export declare const packageFilterOptions: {
85
+ scope: {
86
+ target: "cli";
87
+ describe: string;
88
+ type: "string";
89
+ };
90
+ ignore: {
91
+ target: "cli";
92
+ describe: string;
93
+ type: "string";
94
+ };
95
+ platform: {
96
+ target: "cli";
97
+ describe: string;
98
+ type: "string";
99
+ };
100
+ deps: {
101
+ target: "cli";
102
+ describe: string;
103
+ type: "boolean";
104
+ };
105
+ dependents: {
106
+ target: "cli";
107
+ describe: string;
108
+ type: "boolean";
109
+ };
110
+ };
111
+ /**
112
+ * `--from-root`/`-r` - a group of one, and a function because its text is the command's own word
113
+ * for what it does. Spread it like the others: `...fromRootOption('Build')`.
114
+ *
115
+ * Only where a command scopes by the current directory; see `applyFromRootOption` for why adding it
116
+ * elsewhere is worse than leaving it out.
117
+ *
118
+ * **It was `--root` through 1.x, and the name said the opposite of what it does.** Every reader
119
+ * spells it the same single line - `options.fromRoot ? undefined : repository.currentPackage` - so
120
+ * the flag means *ignore where I am standing*, i.e. the **whole repository**. `--root` reads as "the
121
+ * root alone", which is the narrowest possible set rather than the widest, and the confusion was
122
+ * about to become a contradiction: a `--root-only` beside a `--root` that meant "everything" is
123
+ * unreadable.
124
+ *
125
+ * **`-r` is kept, and a two-letter `-fr` is not possible.** yargs' `short-option-groups` is on by
126
+ * default, so `-fr` parses as `-f -r` and `.strict()` answers `Unknown arguments: f, r` (measured).
127
+ * Turning that off makes `-fr` work and breaks every grouped short - `rman list -sj` works today.
128
+ * So the long name carries the meaning and the incumbent short stays.
129
+ */
130
+ export declare function fromRootOption(verb: string): {
131
+ fromRoot: {
132
+ target: "cli";
133
+ cliName: string;
134
+ alias: string;
135
+ describe: string;
136
+ type: "boolean";
137
+ };
138
+ };
20
139
  /** `--scope`/`--ignore`/`--deps`/`--dependents`, the same shape and describe text in every command
21
140
  * that supports them - mirrors `run.command.ts`'s own `applyRunOptions`. */
22
141
  export declare function applyPackageFilterOptions<T>(cmd: Argv<T>): Argv<T>;
@@ -24,7 +143,8 @@ export declare function readPackageFilterOptions(args: any): PackageFilterOption
24
143
  /**
25
144
  * Narrows `packages` (the full, already-resolved list - toposort order, if any, is preserved)
26
145
  * down to what `options` selects. `scope`/`ignore` match against each package's bare name (glob
27
- * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...); `ignore` is applied after `scope`, on
146
+ * syntax via `micromatch` - `*`, `**`, `{a,b}`, ...), plus `ROOT_SELECTOR` (`"/"`) for the root
147
+ * package, which is matched structurally rather than by name; `ignore` is applied after `scope`, on
28
148
  * whatever it left. `deps`/`dependents` then each independently expand *that* matched set along
29
149
  * `Package.dependencies` (already the full transitive closure - see
30
150
  * `Repository`'s own `_updateDependencies`) and their results are unioned in - so `--deps
@@ -46,7 +166,7 @@ export declare function filterPackages(packages: Package[], options: PackageFilt
46
166
  */
47
167
  applySkip?: boolean): Package[];
48
168
  /**
49
- * `--root`/`-r`, with one describe text instead of four near-identical ones.
169
+ * `--from-root`/`-r`, with one describe text instead of four near-identical ones.
50
170
  *
51
171
  * **It only means anything where a command scopes by the current directory** - `run`/`build`/`test`,
52
172
  * `exec`, `clean`, `changelog` and `diff` narrow to `Repository.currentPackage` when you stand
@@ -54,10 +174,17 @@ applySkip?: boolean): Package[];
54
174
  * repository (`version`, `publish`, `list`, `changed`) it would be a flag that does nothing, which
55
175
  * is worse than not offering it: a no-op flag reads as a promise.
56
176
  *
177
+ * That same rule is why there is no `--root-only` beside it, however naturally the pair reads: it
178
+ * would be a no-op on `run`/`build`/`test` (the root is not in `repository.packages` at all and
179
+ * contributes only `pre`/`post` bookends), identical to this flag on `diff`, already what this flag
180
+ * does on `config`, and on `clean` actively misleading - the root's own sweep recurses through
181
+ * `packages/*`, so a `--root-only` there deletes *more* than a package-scoped run, not less
182
+ * (measured). Where the root genuinely is a candidate, `--scope /` says so - see `ROOT_SELECTOR`.
183
+ *
57
184
  * `verb` is the command's own word for what it does, so the text stays the sentence each command was
58
185
  * already saying.
59
186
  */
60
- export declare function applyRootOption<T>(cmd: Argv<T>, verb: string): Argv<T>;
61
- /** The `--root` flag as the services read it - beside `readPackageFilterOptions`, so a command
187
+ export declare function applyFromRootOption<T>(cmd: Argv<T>, verb: string): Argv<T>;
188
+ /** The `--from-root` flag as the services read it - beside `readPackageFilterOptions`, so a command
62
189
  * reads both the same way. */
63
- export declare function readRootOption(args: any): boolean | undefined;
190
+ export declare function readFromRootOption(args: any): boolean | undefined;