rman 1.2.5 → 2.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -19
- package/cli.d.ts +5 -0
- package/cli.js +214 -88
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changed.command.d.ts +80 -3
- package/commands/changed.command.js +19 -12
- package/commands/changelog.command.d.ts +192 -3
- package/commands/changelog.command.js +87 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +30 -19
- package/commands/diff.command.d.ts +37 -3
- package/commands/diff.command.js +25 -16
- package/commands/exec.command.d.ts +193 -3
- package/commands/exec.command.js +60 -58
- package/commands/github-release.command.d.ts +154 -3
- package/commands/github-release.command.js +67 -37
- package/commands/import.command.d.ts +36 -3
- package/commands/import.command.js +28 -20
- package/commands/info.command.d.ts +35 -7
- package/commands/info.command.js +36 -30
- package/commands/list.command.d.ts +163 -3
- package/commands/list.command.js +109 -71
- package/commands/publish.command.d.ts +231 -0
- package/commands/publish.command.js +304 -0
- package/commands/run.command.d.ts +186 -6
- package/commands/run.command.js +26 -72
- package/commands/test.command.d.ts +173 -3
- package/commands/test.command.js +16 -10
- package/commands/version.command.d.ts +317 -3
- package/commands/version.command.js +149 -69
- package/commands.d.ts +32 -0
- package/commands.js +28 -0
- package/constants.js +1 -1
- package/core/application.d.ts +116 -0
- package/core/application.js +143 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +78 -0
- package/core/config.d.ts +180 -50
- package/core/config.js +332 -153
- package/core/core-services.d.ts +14 -0
- package/core/core-services.js +30 -0
- package/core/core-targets.d.ts +14 -0
- package/core/core-targets.js +16 -0
- package/core/custom-command.d.ts +42 -6
- package/core/custom-command.js +44 -17
- package/core/extends-config.d.ts +13 -5
- package/core/extends-config.js +52 -12
- package/core/load-config-module.d.ts +28 -0
- package/core/load-config-module.js +42 -0
- package/core/manifest.d.ts +47 -25
- package/core/manifest.js +51 -69
- package/core/merge-config.d.ts +33 -34
- package/core/merge-config.js +138 -93
- package/core/package.d.ts +145 -17
- package/core/package.js +128 -36
- package/core/plugin-loader.d.ts +65 -0
- package/core/plugin-loader.js +234 -0
- package/core/plugin.d.ts +135 -90
- package/core/plugin.js +70 -173
- package/core/publish-target.d.ts +124 -0
- package/core/publish-target.js +30 -0
- package/core/registry.d.ts +30 -0
- package/core/registry.js +47 -0
- package/core/repository.d.ts +72 -9
- package/core/repository.js +293 -43
- package/core/resolve-target.d.ts +1 -1
- package/core/resolve-target.js +1 -1
- package/core/service.d.ts +49 -0
- package/core/service.js +40 -0
- package/core/version-scheme.d.ts +23 -1
- package/core/version-scheme.js +29 -1
- package/core/workspace.d.ts +84 -33
- package/core/workspace.js +63 -22
- package/index.d.ts +111 -14
- package/index.js +85 -9
- package/interfaces/rman-config.interface.d.ts +739 -212
- package/interfaces/rman-config.interface.js +61 -1
- package/package.json +2 -1
- package/plugins/builtins.d.ts +44 -0
- package/plugins/builtins.js +33 -0
- package/plugins/detect.d.ts +78 -0
- package/plugins/detect.js +70 -0
- package/plugins/node/augmentation/rman.augmentation.d.ts +84 -0
- package/plugins/node/augmentation/rman.augmentation.js +1 -0
- package/plugins/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/plugins/node/augmentation/system-info.augmentation.js +79 -0
- package/plugins/node/commands/ci.command.d.ts +131 -0
- package/plugins/node/commands/ci.command.js +59 -0
- package/plugins/node/commands/clean.command.d.ts +183 -0
- package/plugins/node/commands/clean.command.js +73 -0
- package/plugins/node/index.d.ts +29 -0
- package/plugins/node/index.js +40 -0
- package/plugins/node/node-config.interface.d.ts +77 -0
- package/plugins/node/node-config.interface.js +7 -0
- package/plugins/node/node-manifest.provider.d.ts +68 -0
- package/plugins/node/node-manifest.provider.js +125 -0
- package/plugins/node/node.platform.d.ts +53 -0
- package/plugins/node/node.platform.js +134 -0
- package/plugins/node/npm-publish-target.d.ts +73 -0
- package/plugins/node/npm-publish-target.js +96 -0
- package/plugins/node/services/ci.service.d.ts +47 -0
- package/plugins/node/services/ci.service.js +213 -0
- package/plugins/node/services/clean.service.d.ts +53 -0
- package/plugins/node/services/clean.service.js +237 -0
- package/plugins/node/services/publish.service.d.ts +114 -0
- package/plugins/node/services/publish.service.js +371 -0
- package/plugins/node/services/version-plan.service.d.ts +44 -0
- package/plugins/node/services/version-plan.service.js +58 -0
- package/plugins/node/utils/npm-view.d.ts +48 -0
- package/plugins/node/utils/npm-view.js +71 -0
- package/plugins/node/utils/workspace-range.d.ts +26 -0
- package/plugins/node/utils/workspace-range.js +28 -0
- package/services/change-hash.service.d.ts +2 -2
- package/services/change-hash.service.js +2 -2
- package/services/changelog.service.d.ts +62 -51
- package/services/changelog.service.js +14 -11
- package/services/docker-publish.service.d.ts +50 -29
- package/services/docker-publish.service.js +43 -20
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +44 -33
- package/services/github-release.service.js +13 -10
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +62 -10
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +22 -13
- package/services/run.service.js +262 -223
- package/services/version-plan.service.d.ts +27 -4
- package/services/version-plan.service.js +42 -15
- package/services/version.service.d.ts +31 -11
- package/services/version.service.js +29 -13
- package/targets/docker.target.d.ts +53 -0
- package/targets/docker.target.js +40 -0
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +29 -0
- package/utils/branch-guard.js +31 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/logger.d.ts +1 -1
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +127 -7
- package/utils/package-filter.js +197 -16
- package/utils/printable-config.d.ts +1 -1
- package/utils/printable-config.js +1 -1
- package/utils/run-bin.d.ts +10 -0
- package/utils/run-bin.js +1 -1
- package/utils/run-options.d.ts +97 -0
- package/utils/run-options.js +81 -0
- package/utils/version-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ rman version
|
|
|
66
66
|
# Apply it: bump versions, write CHANGELOG.md, commit, tag
|
|
67
67
|
rman version --changelog
|
|
68
68
|
|
|
69
|
-
# Publish everything that isn't already on the registry
|
|
69
|
+
# Publish everything that isn't already on the registry
|
|
70
70
|
rman publish
|
|
71
71
|
```
|
|
72
72
|
|
|
@@ -90,17 +90,29 @@ worked examples of every single command, see **[docs/cli-rman.md](https://github
|
|
|
90
90
|
| [`diff [package]`](#rman-diff-package) | Shows the git diff since a package's (or the repo's) last release tag. |
|
|
91
91
|
| [`changelog`](#rman-changelog) | Generates a changelog per package from unreleased commits. |
|
|
92
92
|
| [`version [bump]`](#rman-version-bump) | Bumps versions of changed packages (and their dependents). |
|
|
93
|
+
| [`publish`](#rman-publish) | Publishes every package whose version isn't on its registry yet. |
|
|
93
94
|
| [`github-release`](#rman-github-release) | Creates the repository's GitHub Release for its release tag. |
|
|
94
95
|
| [`import <path>`](#rman-import-path) | Imports an external git repository as a new package, with history. |
|
|
95
96
|
|
|
96
|
-
**`
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
**`ci` and `clean` come from the `node` built-in**, which ships inside this package - each is about
|
|
98
|
+
npm or TypeScript rather than about repositories, so nothing it contributes exists until a
|
|
99
|
+
repository asks for it:
|
|
99
100
|
|
|
100
101
|
```yaml
|
|
101
|
-
plugins: ['
|
|
102
|
+
plugins: ['node'] # by name
|
|
103
|
+
platform: node # the same statement at a repository root, plus which technology its packages are
|
|
102
104
|
```
|
|
103
105
|
|
|
106
|
+
Or say nothing: a repository that declares no technology gets the one its own files imply, announced
|
|
107
|
+
on stderr rather than guessed silently. It used to take a second package (`rman-node`) and a
|
|
108
|
+
`.rmanrc` before anything worked at all.
|
|
109
|
+
|
|
110
|
+
**`publish` is here, but *where* a package ships is a plugin's to say.** A **publish target** is
|
|
111
|
+
one answer to "is this version on the registry, and how do I push it" - rman ships `docker`
|
|
112
|
+
(any language's project can push an image), and the `node` built-in contributes `npm` with the flags
|
|
113
|
+
that only mean something there (`--access`, `--tag`, `--otp`, `--registry`, ...). So
|
|
114
|
+
`rman publish --help` lists what this repository's targets actually understand.
|
|
115
|
+
|
|
104
116
|
Options shared across several commands:
|
|
105
117
|
|
|
106
118
|
- **`skip`** (`.rmanrc`, per package): leave this package alone - honoured by every command that
|
|
@@ -108,8 +120,10 @@ Options shared across several commands:
|
|
|
108
120
|
- **Package filtering** (`list`, `run`/`build`/`test`, `exec`, `version`, `changelog`, and a
|
|
109
121
|
plugin's own commands): `--scope <glob>`, `--ignore <glob>`, `--deps`, `--dependents` - see
|
|
110
122
|
[Package filtering](https://github.com/panates/rman/blob/main/docs/rman.md#package-filtering-scopeignoredepsdependents)
|
|
111
|
-
for the full semantics.
|
|
112
|
-
|
|
123
|
+
for the full semantics. **`--scope /` is the repository's own root package** - the same `/`
|
|
124
|
+
`.rmanrc`'s `"[/]"` block uses, and not a glob, so `--scope '*'` means the members and a glob
|
|
125
|
+
never picks up the root by name.
|
|
126
|
+
- **`--from-root`/`-r`** (every command that narrows to the package you are standing in -
|
|
113
127
|
`run`/`build`/`test`, `exec`, `changelog`, `diff`): run against the whole repository instead.
|
|
114
128
|
- **Branch guard** (every command that mutates state or runs scripts - `run`/`build`/`test`,
|
|
115
129
|
`exec`, `version`): `--allow-branch <glob>`, `--ignore-branch <glob>` -
|
|
@@ -134,9 +148,10 @@ rman list --scope '@myorg/*' --ignore '*-internal'
|
|
|
134
148
|
|
|
135
149
|
### `rman info`
|
|
136
150
|
|
|
137
|
-
Prints local environment (OS/CPU/memory, Node, git) and repository information. A
|
|
138
|
-
own ecosystem's part - `
|
|
139
|
-
names, plus the installed `rman` packages
|
|
151
|
+
Prints local environment (OS/CPU/memory, Node, git) and repository information. A platform adds its
|
|
152
|
+
own ecosystem's part - the `node` built-in reports whichever package manager
|
|
153
|
+
`.rmanrc "packageManager"` names, plus the installed `rman` packages, and only in a repository that
|
|
154
|
+
asked for it.
|
|
140
155
|
|
|
141
156
|
```bash
|
|
142
157
|
rman info
|
|
@@ -192,12 +207,12 @@ rman exec --topo=false pwd # every package independently, alphabetical o
|
|
|
192
207
|
### `rman config`
|
|
193
208
|
|
|
194
209
|
Prints the **effective** config for the package of the current directory - after the directory
|
|
195
|
-
cascade, `"[selector]"` blocks, `extends
|
|
210
|
+
cascade, `"[selector]"` blocks, `extends` and `${{ ... }}` expressions have all been
|
|
196
211
|
applied. What rman actually sees there, which no single file shows.
|
|
197
212
|
|
|
198
213
|
```bash
|
|
199
214
|
rman config # the package you are standing in
|
|
200
|
-
rman config --root # the repository root's own config instead
|
|
215
|
+
rman config --from-root # the repository root's own config instead
|
|
201
216
|
rman config --json | jq .run
|
|
202
217
|
```
|
|
203
218
|
|
|
@@ -266,6 +281,29 @@ Release-As: patch
|
|
|
266
281
|
See [docs/rman.md#versionservice](https://github.com/panates/rman/blob/main/docs/rman.md#versionservice) for the full grouping/propagation
|
|
267
282
|
algorithm, prerelease semantics, and `"workspace:"` dependency-range handling.
|
|
268
283
|
|
|
284
|
+
### `rman publish`
|
|
285
|
+
|
|
286
|
+
Publishes every package whose current version isn't on its registry yet. Shows the plan first, then
|
|
287
|
+
asks for confirmation (unless `--yes` or `--dry-run`), then publishes in topological order,
|
|
288
|
+
dependencies before dependents.
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
rman publish # show the plan, then ask for confirmation
|
|
292
|
+
rman publish --yes # publish immediately, no confirmation
|
|
293
|
+
rman publish --dry-run --json # "is there anything to release?", for a CI gate
|
|
294
|
+
rman publish --target docker # only the packages configured for that target
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Where a package ships is a publish target, and a target is a contribution.** rman ships `docker`;
|
|
298
|
+
the `node` built-in contributes `npm`. A package says where it goes with `.rmanrc "publish.target"`, or says
|
|
299
|
+
nothing and goes wherever the installed targets claim it - so a Cargo package is never assumed to be
|
|
300
|
+
an npm one. Each target adds its own flags, so `rman publish --help` is worth reading in your own
|
|
301
|
+
repository. See
|
|
302
|
+
[docs/cli/publish.md](https://github.com/panates/rman/blob/main/docs/cli/publish.md).
|
|
303
|
+
|
|
304
|
+
It never looks at whether `version` ran: it inspects what is on disk and on each registry, so it
|
|
305
|
+
behaves the same right after a bump or days later, and re-running is safe.
|
|
306
|
+
|
|
269
307
|
### `rman github-release`
|
|
270
308
|
|
|
271
309
|
Creates the repository's GitHub Release for the version that just shipped - one per run, named after
|
|
@@ -294,7 +332,7 @@ rman import ../my-old-standalone-repo --dest libs # under libs/ instead of pac
|
|
|
294
332
|
already. After importing, add the new directory to your `workspaces` glob if it isn't already
|
|
295
333
|
covered, then run `rman ci` to install it.
|
|
296
334
|
|
|
297
|
-
## Shared config (`extends`) and
|
|
335
|
+
## Shared config (`extends`) and adding to it (`value`)
|
|
298
336
|
|
|
299
337
|
House rules live in one package, and a repository names it:
|
|
300
338
|
|
|
@@ -305,13 +343,18 @@ extends: '@panates/rman-monorepo'
|
|
|
305
343
|
'[*]':
|
|
306
344
|
run:
|
|
307
345
|
build:
|
|
308
|
-
|
|
346
|
+
# adds to the base's step, rather than replacing it
|
|
347
|
+
before: "${{ [...value, 'rm -rf ./cache'] }}"
|
|
309
348
|
```
|
|
310
349
|
|
|
311
350
|
`extends` merges underneath the file naming it (a package, a path, or a list), and may itself be
|
|
312
|
-
chained.
|
|
313
|
-
|
|
314
|
-
|
|
351
|
+
chained. `value` is what the key already resolved to - from the base, a parent directory, or a
|
|
352
|
+
selector - which is what lets a repository add one step without restating a list it doesn't own. It
|
|
353
|
+
is the list form of whatever is underneath, so the spread needs no guard even when nothing is.
|
|
354
|
+
See [docs/rman.md](https://github.com/panates/rman/blob/main/docs/rman.md#inheriting-a-shared-config-extends).
|
|
355
|
+
|
|
356
|
+
There was a `+key` prefix for this and it is gone; one still in a config is refused, naming what to
|
|
357
|
+
write instead.
|
|
315
358
|
|
|
316
359
|
## Your own commands
|
|
317
360
|
|
|
@@ -408,8 +451,9 @@ import { defineConfig } from 'rman';
|
|
|
408
451
|
export default defineConfig({ allowBranch: ['main'] });
|
|
409
452
|
```
|
|
410
453
|
|
|
411
|
-
|
|
412
|
-
|
|
454
|
+
A plugin's keys arrive by declaration merging, so annotating with `RmanConfig` types them too;
|
|
455
|
+
`RmanNodeConfig` is the alias that says out loud which set a config is using. The
|
|
456
|
+
`.rmanrc`/`.rmanrc.yml` forms get no checking - see
|
|
413
457
|
[docs/rman.md#editor-support-types](https://github.com/panates/rman/blob/main/docs/rman.md#editor-support-types) for why the JSON Schema that
|
|
414
458
|
used to cover them was removed.
|
|
415
459
|
|
package/cli.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/** Every built-in command, registered by importing them - see `commands.ts` for why the list lives
|
|
3
|
+
* there rather than here. */
|
|
4
|
+
import './commands.js';
|
|
5
|
+
import { RmanApplication } from './core/application.js';
|
|
2
6
|
export declare function runCli(options?: {
|
|
3
7
|
argv?: string[];
|
|
4
8
|
cwd?: string;
|
|
9
|
+
app?: RmanApplication;
|
|
5
10
|
}): Promise<void>;
|
package/cli.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/** Every built-in command, registered by importing them - see `commands.ts` for why the list lives
|
|
3
|
+
* there rather than here. */
|
|
4
|
+
import './commands.js';
|
|
2
5
|
import { realpathSync } from 'node:fs';
|
|
3
6
|
import path from 'node:path';
|
|
4
7
|
import { fileURLToPath } from 'node:url';
|
|
@@ -6,24 +9,15 @@ import colors from 'ansi-colors';
|
|
|
6
9
|
import * as yaml from 'js-yaml';
|
|
7
10
|
import yargs, {} from 'yargs';
|
|
8
11
|
import { hideBin } from 'yargs/helpers';
|
|
9
|
-
import * as buildCommand from './commands/build.command.js';
|
|
10
|
-
import * as changedCommand from './commands/changed.command.js';
|
|
11
|
-
import * as changelogCommand from './commands/changelog.command.js';
|
|
12
|
-
import * as configCommand from './commands/config.command.js';
|
|
13
|
-
import * as diffCommand from './commands/diff.command.js';
|
|
14
|
-
import * as execCommand from './commands/exec.command.js';
|
|
15
|
-
import * as githubReleaseCommand from './commands/github-release.command.js';
|
|
16
|
-
import * as importCommand from './commands/import.command.js';
|
|
17
|
-
import * as infoCommand from './commands/info.command.js';
|
|
18
|
-
import * as listCommand from './commands/list.command.js';
|
|
19
|
-
import * as runCommand from './commands/run.command.js';
|
|
20
|
-
import * as testCommand from './commands/test.command.js';
|
|
21
|
-
import * as versionCommand from './commands/version.command.js';
|
|
22
12
|
import { version } from './constants.js';
|
|
23
|
-
import {
|
|
13
|
+
import { RmanApplication } from './core/application.js';
|
|
14
|
+
import { commandName, toYargsCommand } from './core/command-builder.js';
|
|
15
|
+
import { assertNoBuiltinShadowing, defaultCommandGlobs, loadCustomCommands, } from './core/custom-command.js';
|
|
16
|
+
import { checkCustomCommand } from './core/plugin-loader.js';
|
|
24
17
|
import { Repository } from './core/repository.js';
|
|
18
|
+
import { commandRegistry } from './interfaces/rman-config.interface.js';
|
|
25
19
|
import { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
|
|
26
|
-
import { filterPackages,
|
|
20
|
+
import { filterPackages, readFromRootOption, readPackageFilterOptions } from './utils/package-filter.js';
|
|
27
21
|
import { printableConfig } from './utils/printable-config.js';
|
|
28
22
|
import { runBin } from './utils/run-bin.js';
|
|
29
23
|
export async function runCli(options) {
|
|
@@ -41,7 +35,10 @@ export async function runCli(options) {
|
|
|
41
35
|
return;
|
|
42
36
|
}
|
|
43
37
|
try {
|
|
44
|
-
|
|
38
|
+
/** One application per run, made here so `--log-level` reaches its logger, and handed to
|
|
39
|
+
* `Repository.create` rather than found through a global. */
|
|
40
|
+
const app = options?.app ?? new RmanApplication();
|
|
41
|
+
const repository = await Repository.create(options?.cwd, { app });
|
|
45
42
|
const program = yargs(_argv)
|
|
46
43
|
.scriptName('rman')
|
|
47
44
|
.version(version)
|
|
@@ -81,7 +78,7 @@ export async function runCli(options) {
|
|
|
81
78
|
* - but it still has to **throw**, not exit.
|
|
82
79
|
*
|
|
83
80
|
* This branch called `process.exit(1)`, and `runCli` is a library entry point: rman's own
|
|
84
|
-
* bin calls it, so do
|
|
81
|
+
* bin calls it, so do the fixtures and every spec. Exiting from in here took the
|
|
85
82
|
* whole process down before any caller could see the rejection - which in mocha meant the
|
|
86
83
|
* first command that failed killed the run and the suite could not report a single result.
|
|
87
84
|
* The exit belongs to the bin entry alone (see `isMain()` at the bottom), which already does
|
|
@@ -99,60 +96,94 @@ export async function runCli(options) {
|
|
|
99
96
|
* quietly does nothing on whichever command forgot it is worse than no flag.
|
|
100
97
|
*/
|
|
101
98
|
interceptConfigFlag(repository, program);
|
|
102
|
-
infoCommand.initCli(repository, program);
|
|
103
|
-
listCommand.initCli(repository, program);
|
|
104
|
-
runCommand.initCli(repository, program);
|
|
105
|
-
buildCommand.initCli(repository, program);
|
|
106
|
-
changelogCommand.initCli(repository, program);
|
|
107
|
-
testCommand.initCli(repository, program);
|
|
108
|
-
versionCommand.initCli(repository, program);
|
|
109
|
-
githubReleaseCommand.initCli(repository, program);
|
|
110
|
-
execCommand.initCli(repository, program);
|
|
111
|
-
changedCommand.initCli(repository, program);
|
|
112
|
-
diffCommand.initCli(repository, program);
|
|
113
|
-
configCommand.initCli(repository, program);
|
|
114
|
-
importCommand.initCli(repository, program);
|
|
115
99
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
100
|
+
* **Built-ins come from `commandRegistry`, in the order their modules were imported.** Each
|
|
101
|
+
* entry is a register function that has not run yet - it runs here, with the repository, and
|
|
102
|
+
* returns the command's declaration; `toYargsCommand` is the only thing that knows how a
|
|
103
|
+
* declaration becomes a yargs registration.
|
|
118
104
|
*
|
|
119
|
-
* -
|
|
120
|
-
*
|
|
121
|
-
|
|
105
|
+
* The thirteen hand-written `initCli(repository, program)` calls this replaces were the second
|
|
106
|
+
* place a command had to be listed, and the list the shadow check guards with was a third.
|
|
107
|
+
*/
|
|
108
|
+
const builtIns = commandRegistry.map(register => register(app));
|
|
109
|
+
for (const meta of builtIns)
|
|
110
|
+
program.command(toYargsCommand(meta));
|
|
111
|
+
/**
|
|
112
|
+
* Commands that are not built in, all from **one** key: `.rmanrc "commands"`, whose default
|
|
113
|
+
* value is `.rman/*.{js,mjs,cjs}`.
|
|
114
|
+
*
|
|
115
|
+
* An entry is a command or a glob naming modules that export one, so a package contributing
|
|
116
|
+
* commands (a built-in's, a plugin's) and a repository writing its own reach yargs by the same path - there
|
|
117
|
+
* is one source of non-built-in commands and one precedence slot. A plugin used to hand them
|
|
118
|
+
* over separately through `addCommand`, which is the step this replaces.
|
|
119
|
+
*
|
|
120
|
+
* Registered after the built-ins so the shadow check below has the full list to compare
|
|
121
|
+
* against. A command taking a *built-in's* name is refused outright.
|
|
122
|
+
*/
|
|
123
|
+
const { globs, direct } = commandEntries(repository);
|
|
124
|
+
const { commands: loaded, errors } = await loadCustomCommands(globs);
|
|
125
|
+
/**
|
|
126
|
+
* The two authoring forms, resolved the same way - a declarative one's factory runs here,
|
|
127
|
+
* where `app.repository` exists, rather than when the config was read.
|
|
128
|
+
*
|
|
129
|
+
* **The file name is the fallback for `command`**, which is the convention a command loaded
|
|
130
|
+
* from a *file* has and one written straight into the config has not: `checkCustomCommand`
|
|
131
|
+
* refuses nameless metadata, and an inline command has nothing to fall back on, so it must say
|
|
132
|
+
* its own name. Spread *under* the factory's result, so metadata that does declare one -
|
|
133
|
+
* `deploy <stage>`, with its positionals - wins.
|
|
134
|
+
*
|
|
135
|
+
* The name comes from what the factory *returned*, not from the file: those differ exactly
|
|
136
|
+
* when the metadata declared one, and using the file name would leave the clash check
|
|
137
|
+
* comparing something yargs never registered.
|
|
138
|
+
*/
|
|
139
|
+
const localModules = [...direct, ...loaded].map(c => {
|
|
140
|
+
if (!c.register)
|
|
141
|
+
return { name: c.name, file: c.file, module: toCustomModule(c.custom, repository, app) };
|
|
142
|
+
const declared = c.register(app);
|
|
143
|
+
const meta = checkCustomCommand({ ...declared, command: declared.command?.trim() || c.name }, c.file);
|
|
144
|
+
return { name: commandName(meta.command), file: c.file, module: toYargsCommand(meta) };
|
|
145
|
+
});
|
|
146
|
+
/**
|
|
147
|
+
* **One command per name, keeping the last** - which is the precedence that already applied,
|
|
148
|
+
* made visible instead of left to yargs.
|
|
122
149
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
150
|
+
* A repository's own `.rman/check.mjs` overriding a command its config contributed is the
|
|
151
|
+
* intended escape hatch, and it is deliberately not an error: the same precedence a package's
|
|
152
|
+
* own `.rmanrc` has over an `extends` base. But both were being *registered*, and what that
|
|
153
|
+
* cost was the help output - measured, `rman --help` listed `deploy` twice, once with each
|
|
154
|
+
* description, and nothing said which of the two would run.
|
|
155
|
+
*
|
|
156
|
+
* Only the listing was wrong; the override itself was already clean (`rman deploy --help`
|
|
157
|
+
* showed the winner's options alone, and the loser's flag was rejected), which is why this is
|
|
158
|
+
* a registration fix and not a change to how a clash resolves. Registered later wins because
|
|
159
|
+
* `loaded` follows `direct` - a `.rman/` file after a contributed command - and yargs took the
|
|
160
|
+
* last, so keeping the last keeps today's behaviour exactly.
|
|
161
|
+
*
|
|
162
|
+
* The survivor takes the *loser's* position in the list, since that is where the name was
|
|
163
|
+
* first seen. Help ordering only.
|
|
164
|
+
*/
|
|
165
|
+
const byName = new Map();
|
|
166
|
+
for (const command of localModules)
|
|
167
|
+
byName.set(command.name, command);
|
|
168
|
+
const commands = [...byName.values()];
|
|
169
|
+
/**
|
|
170
|
+
* Said out loud, at `verbose`, because deduplicating silently is what would make this the
|
|
171
|
+
* trap it is warned about elsewhere: before, two rows at least hinted that something was
|
|
172
|
+
* doubled - after, the overridden command is simply absent, and "my plugin's command does
|
|
173
|
+
* nothing" has no thread to pull. Not a warning: an override is a correct thing to do, and a
|
|
174
|
+
* repository that does it on purpose should not be nagged on every invocation.
|
|
126
175
|
*/
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
for (const custom of commands) {
|
|
135
|
-
program.command({
|
|
136
|
-
command: custom.command,
|
|
137
|
-
describe: custom.describe,
|
|
138
|
-
/** Forwarded, not dropped: this loop builds a *new* spec object, so anything the command
|
|
139
|
-
* declared and is not copied here is silently lost - `--config` printed the whole config
|
|
140
|
-
* for every plugin command until this line existed (measured, on `clean` and `ci`). */
|
|
141
|
-
configKeys: custom.configKeys,
|
|
142
|
-
builder: custom.builder ?? (y => y),
|
|
143
|
-
handler: args => {
|
|
144
|
-
/** Resolved per invocation, not once at registration: `--log-level` is only known now. */
|
|
145
|
-
const logLevel = args.logLevel ?? resolveRootLogLevel(repository);
|
|
146
|
-
const context = {
|
|
147
|
-
repository,
|
|
148
|
-
package: repository.currentPackage,
|
|
149
|
-
runBin: (bin, argv, opts) => runBin(bin, argv, { cwd: repository.dirname, logLevel, ...opts }),
|
|
150
|
-
logger: new Logger(logLevel),
|
|
151
|
-
};
|
|
152
|
-
return custom.handler(context, args);
|
|
153
|
-
},
|
|
154
|
-
});
|
|
176
|
+
if (commands.length < localModules.length) {
|
|
177
|
+
const logger = new Logger(argvLogLevel(_argv) ?? resolveRootLogLevel(repository));
|
|
178
|
+
const survivors = new Set(commands);
|
|
179
|
+
for (const lost of localModules.filter(c => !survivors.has(c))) {
|
|
180
|
+
const winner = byName.get(lost.name);
|
|
181
|
+
logger.verbose(`"${lost.name}" from ${lost.file} is overridden by ${winner.file}.`);
|
|
182
|
+
}
|
|
155
183
|
}
|
|
184
|
+
assertNoBuiltinShadowing(commands, builtInNames(builtIns));
|
|
185
|
+
for (const { module } of commands)
|
|
186
|
+
program.command(module);
|
|
156
187
|
/** Warned about, not thrown: one unparseable file must not take the other commands with it.
|
|
157
188
|
* Loud enough not to be mistaken for success, and it names the file and the reason - "my
|
|
158
189
|
* command isn't there" is otherwise a long afternoon. */
|
|
@@ -199,6 +230,56 @@ export async function runCli(options) {
|
|
|
199
230
|
throw e;
|
|
200
231
|
}
|
|
201
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Everything `.rmanrc "commands"` declares across the repository, split into the globs to load and
|
|
235
|
+
* the commands written straight into the config.
|
|
236
|
+
*
|
|
237
|
+
* **Collected from the root and from every package**, because `commands` is not a root-level key -
|
|
238
|
+
* a package's own `.rmanrc` may contribute one, and a glob was anchored to that file when it was
|
|
239
|
+
* read. The commands themselves are repository-wide; there is one command list, so a package
|
|
240
|
+
* declaring one is contributing it to the repository. (`plugins` and `publishTargets` cannot work
|
|
241
|
+
* this way: they are read before the packages exist, because a plugin is what finds them.)
|
|
242
|
+
*
|
|
243
|
+
* The cascade means a root-declared entry also appears in each package's resolved config, so the
|
|
244
|
+
* same one arrives many times over. Globs are de-duplicated here and by resolved *file* again in
|
|
245
|
+
* the loader - the second pass is the one that matters, since two different globs can name one
|
|
246
|
+
* file. Inline commands are de-duplicated by identity, which is what the cascade produces.
|
|
247
|
+
*
|
|
248
|
+
* The `.rman/` default is used only when nothing was declared anywhere. Declaring `commands` and
|
|
249
|
+
* still wanting `.rman/` scanned means naming it: the key appends to other layers, not to a
|
|
250
|
+
* built-in fallback, and a default that could never be turned off is not a default.
|
|
251
|
+
*/
|
|
252
|
+
function commandEntries(repository) {
|
|
253
|
+
const globs = new Set();
|
|
254
|
+
const seen = new Set();
|
|
255
|
+
const direct = [];
|
|
256
|
+
for (const config of [repository.rootPackage.config, ...repository.getPackages().map(p => p.config)]) {
|
|
257
|
+
const value = config?.commands;
|
|
258
|
+
for (const entry of Array.isArray(value) ? value : value ? [value] : []) {
|
|
259
|
+
if (typeof entry === 'string') {
|
|
260
|
+
if (entry.trim())
|
|
261
|
+
globs.add(entry);
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (!entry || seen.has(entry))
|
|
265
|
+
continue;
|
|
266
|
+
seen.add(entry);
|
|
267
|
+
/** No file to fall back on, so the metadata has to name itself - `checkCustomCommand` says
|
|
268
|
+
* so when it does not. `"commands"` stands in for the file in that message. */
|
|
269
|
+
if (typeof entry === 'function') {
|
|
270
|
+
direct.push({ name: '', file: '"commands"', register: entry });
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
/** `checkCustomCommand` is what refuses a nameless one, with the message that names the
|
|
274
|
+
* omission - reached here rather than at registration so an inline command is checked
|
|
275
|
+
* the same way a plugin's used to be. */
|
|
276
|
+
const custom = checkCustomCommand(entry, '"commands"');
|
|
277
|
+
direct.push({ name: commandName(custom.command), file: '"commands"', custom });
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return { globs: globs.size ? [...globs] : defaultCommandGlobs(repository.dirname), direct };
|
|
282
|
+
}
|
|
202
283
|
/**
|
|
203
284
|
* Replaces `program.command` with a version that wraps every handler: with `--config` it prints
|
|
204
285
|
* what the command would run with and returns, instead of running it.
|
|
@@ -279,10 +360,10 @@ function readOptions(args) {
|
|
|
279
360
|
}
|
|
280
361
|
/** The packages the command would act on - `filterPackages` with the command's own options, so
|
|
281
362
|
* this is the same set the command will compute, `skip` included. Narrowed to the current package
|
|
282
|
-
* for a command that scopes by directory, unless `--root` says otherwise. */
|
|
363
|
+
* for a command that scopes by directory, unless `--from-root` says otherwise. */
|
|
283
364
|
function commandTargets(repository, args) {
|
|
284
365
|
const current = repository.currentPackage;
|
|
285
|
-
if (current && !
|
|
366
|
+
if (current && !readFromRootOption(args))
|
|
286
367
|
return [current];
|
|
287
368
|
return filterPackages(repository.getPackages(), readPackageFilterOptions(args));
|
|
288
369
|
}
|
|
@@ -312,23 +393,68 @@ function isMain() {
|
|
|
312
393
|
}
|
|
313
394
|
if (isMain())
|
|
314
395
|
runCli().catch(() => process.exit(1));
|
|
315
|
-
/**
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
'
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
396
|
+
/**
|
|
397
|
+
* Every name a repository's own command may not take - **derived from what was actually
|
|
398
|
+
* registered**, not listed.
|
|
399
|
+
*
|
|
400
|
+
* It used to be a hand-maintained array, because yargs exposes no such list, and a spec had to pin
|
|
401
|
+
* it against the command sources so that adding a command could not quietly leave a repository's
|
|
402
|
+
* own able to shadow it. With the built-ins coming out of `commandRegistry` the list and the
|
|
403
|
+
* registrations cannot disagree: they are the same walk.
|
|
404
|
+
*
|
|
405
|
+
* Aliases count - `ls` is `list`, and shadowing it would be the same mistake. `completion` is
|
|
406
|
+
* yargs' own command rather than one of ours, so it is the one name still written here.
|
|
407
|
+
*/
|
|
408
|
+
function builtInNames(metas) {
|
|
409
|
+
const names = metas.flatMap(meta => [commandName(meta.command), ...(meta.aliases ?? [])]);
|
|
410
|
+
return [...names, 'completion'];
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* A `CustomCommand` - a `.rman/*.mjs` command, or a plugin's written the older way - as the yargs
|
|
414
|
+
* registration it describes. `toYargsCommand` is the same function for a *declarative* command;
|
|
415
|
+
* this is the other authoring form, and both end at one `program.command`.
|
|
416
|
+
*
|
|
417
|
+
* **Every field has to be copied deliberately**, because this builds a new object: anything the
|
|
418
|
+
* command declared and this forgets is silently lost. `--config` printed the whole config for every
|
|
419
|
+
* plugin command until `configKeys` was on this list (measured, on `clean` and `ci`).
|
|
420
|
+
*/
|
|
421
|
+
function toCustomModule(custom, repository, app) {
|
|
422
|
+
return {
|
|
423
|
+
command: custom.command,
|
|
424
|
+
describe: custom.describe,
|
|
425
|
+
configKeys: custom.configKeys,
|
|
426
|
+
builder: custom.builder ?? ((y) => y),
|
|
427
|
+
handler: (args) => {
|
|
428
|
+
/** Resolved per invocation, not once at registration: `--log-level` is only known now. */
|
|
429
|
+
const logLevel = args.logLevel ?? resolveRootLogLevel(repository);
|
|
430
|
+
const context = {
|
|
431
|
+
repository,
|
|
432
|
+
package: repository.currentPackage,
|
|
433
|
+
runBin: (bin, argv, opts) => runBin(bin, argv, { cwd: repository.dirname, logLevel, app, ...opts }),
|
|
434
|
+
logger: new Logger(logLevel),
|
|
435
|
+
};
|
|
436
|
+
return custom.handler(context, args);
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* `--log-level` read straight off argv, for a message printed **before** yargs parses anything.
|
|
442
|
+
*
|
|
443
|
+
* Commands are registered before `parseAsync` runs - registering them is what makes parsing
|
|
444
|
+
* possible - so a diagnostic emitted at registration time cannot come from `args.logLevel`.
|
|
445
|
+
* Measured: the override note in `runCli` was silent for `--log-level verbose` and appeared only
|
|
446
|
+
* when `.rmanrc` said so, which is the one spelling a reader would not reach for first.
|
|
447
|
+
*
|
|
448
|
+
* A peek, not a parser. Both spellings, first match wins, and a value that is not a level is left
|
|
449
|
+
* alone for yargs to reject in its own words - this must not become a second place where an
|
|
450
|
+
* invalid `--log-level` is diagnosed.
|
|
451
|
+
*/
|
|
452
|
+
function argvLogLevel(argv) {
|
|
453
|
+
for (let i = 0; i < argv.length; i++) {
|
|
454
|
+
const arg = argv[i];
|
|
455
|
+
const value = arg === '--log-level' ? argv[i + 1] : arg.startsWith('--log-level=') ? arg.slice(12) : undefined;
|
|
456
|
+
if (value && LOG_LEVELS.includes(value))
|
|
457
|
+
return value;
|
|
458
|
+
}
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|