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.
- package/README.md +65 -31
- package/builtins/platforms/node/augmentation/rmanrc.augmentation.d.ts +83 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.d.ts +26 -0
- package/builtins/platforms/node/augmentation/system-info.augmentation.js +79 -0
- package/builtins/platforms/node/commands/ci.command.d.ts +133 -0
- package/builtins/platforms/node/commands/ci.command.js +60 -0
- package/builtins/platforms/node/commands/clean.command.d.ts +185 -0
- package/builtins/platforms/node/commands/clean.command.js +74 -0
- package/builtins/platforms/node/index.d.ts +8 -0
- package/builtins/platforms/node/index.js +8 -0
- package/builtins/platforms/node/node-config.interface.d.ts +76 -0
- package/builtins/platforms/node/node-config.interface.js +7 -0
- package/builtins/platforms/node/node-manifest.provider.d.ts +68 -0
- package/builtins/platforms/node/node-manifest.provider.js +125 -0
- package/builtins/platforms/node/node.platform.d.ts +53 -0
- package/builtins/platforms/node/node.platform.js +134 -0
- package/builtins/platforms/node/services/ci.service.d.ts +47 -0
- package/builtins/platforms/node/services/ci.service.js +213 -0
- package/builtins/platforms/node/services/clean.service.d.ts +53 -0
- package/builtins/platforms/node/services/clean.service.js +237 -0
- package/builtins/platforms/node/services/publish.service.d.ts +114 -0
- package/builtins/platforms/node/services/publish.service.js +371 -0
- package/builtins/platforms/node/services/version-plan.service.d.ts +44 -0
- package/builtins/platforms/node/services/version-plan.service.js +58 -0
- package/builtins/platforms/node/utils/workspace-range.d.ts +26 -0
- package/builtins/platforms/node/utils/workspace-range.js +28 -0
- package/builtins/presets/index.d.ts +15 -0
- package/builtins/presets/index.js +57 -0
- package/builtins/presets/node.d.ts +16 -0
- package/builtins/presets/node.js +57 -0
- package/{services → builtins/publish-targets/docker}/docker-publish.service.d.ts +52 -31
- package/{services → builtins/publish-targets/docker}/docker-publish.service.js +49 -26
- package/builtins/publish-targets/docker/docker.target.d.ts +54 -0
- package/builtins/publish-targets/docker/docker.target.js +40 -0
- package/builtins/publish-targets/docker/index.d.ts +2 -0
- package/builtins/publish-targets/docker/index.js +2 -0
- package/builtins/publish-targets/npm/index.d.ts +2 -0
- package/builtins/publish-targets/npm/index.js +2 -0
- package/builtins/publish-targets/npm/npm-publish-target.d.ts +73 -0
- package/builtins/publish-targets/npm/npm-publish-target.js +95 -0
- package/builtins/publish-targets/npm/npm-view.d.ts +48 -0
- package/builtins/publish-targets/npm/npm-view.js +71 -0
- package/cli.d.ts +8 -0
- package/cli.js +228 -93
- package/commands/build.command.d.ts +177 -3
- package/commands/build.command.js +20 -10
- package/commands/changelog.command.d.ts +296 -3
- package/commands/changelog.command.js +127 -43
- package/commands/config.command.d.ts +44 -3
- package/commands/config.command.js +53 -24
- 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/lint.command.d.ts +176 -0
- package/commands/lint.command.js +34 -0
- 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 +330 -3
- package/commands/version.command.js +292 -78
- 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 +160 -0
- package/core/classes/package.d.ts +219 -0
- package/core/classes/package.js +214 -0
- package/core/classes/registry.d.ts +30 -0
- package/core/classes/registry.js +47 -0
- package/core/{repository.d.ts → classes/repository.d.ts} +69 -20
- package/core/{repository.js → classes/repository.js} +195 -75
- package/core/classes/service.d.ts +49 -0
- package/core/classes/service.js +40 -0
- package/core/{version-scheme.d.ts → classes/version-scheme.d.ts} +23 -1
- package/core/{version-scheme.js → classes/version-scheme.js} +29 -1
- package/core/classes/workspace.d.ts +275 -0
- package/core/classes/workspace.js +537 -0
- package/core/command-builder.d.ts +14 -0
- package/core/command-builder.js +94 -0
- package/core/config/config-file-scope.d.ts +107 -0
- package/core/config/config-file-scope.js +226 -0
- package/core/config/config-interpolator.d.ts +164 -0
- package/core/config/config-interpolator.js +577 -0
- package/core/config/config-paths.d.ts +65 -0
- package/core/config/config-paths.js +78 -0
- package/core/config/config-reader.d.ts +192 -0
- package/core/config/config-reader.js +727 -0
- package/core/{merge-config.d.ts → config/merge-config.d.ts} +33 -34
- package/core/config/merge-config.js +260 -0
- package/core/{resolve-target.d.ts → config/resolve-config-target.d.ts} +1 -1
- package/core/{resolve-target.js → config/resolve-config-target.js} +1 -1
- package/core/{custom-command.d.ts → interfaces/custom-command.d.ts} +46 -10
- package/core/{custom-command.js → interfaces/custom-command.js} +44 -17
- package/core/{manifest.d.ts → interfaces/manifest.d.ts} +49 -27
- package/core/interfaces/manifest.js +132 -0
- package/core/interfaces/plugin.d.ts +174 -0
- package/core/interfaces/plugin.js +124 -0
- package/core/interfaces/publish-target.d.ts +136 -0
- package/core/interfaces/publish-target.js +43 -0
- package/core/{run-step.d.ts → interfaces/run-step.d.ts} +4 -4
- package/core/interfaces/run-step.js +1 -0
- package/core/plugin-loader.d.ts +25 -0
- package/core/plugin-loader.js +53 -0
- package/index.d.ts +122 -24
- package/index.js +66 -16
- package/interfaces/config-scope.interface.d.ts +223 -0
- package/interfaces/config-scope.interface.js +1 -0
- package/interfaces/rman-config.interface.d.ts +821 -203
- package/interfaces/rman-config.interface.js +78 -1
- package/package.json +3 -1
- package/services/change-hash.service.d.ts +42 -8
- package/services/change-hash.service.js +92 -13
- package/services/changelog.service.d.ts +91 -52
- package/services/changelog.service.js +460 -74
- package/services/config-service.d.ts +8 -0
- package/services/config-service.js +3 -0
- package/services/exec.service.d.ts +23 -12
- package/services/exec.service.js +14 -9
- package/services/github-release.service.d.ts +45 -34
- package/services/github-release.service.js +15 -12
- package/services/import.service.d.ts +25 -14
- package/services/import.service.js +9 -5
- package/services/list.service.d.ts +67 -13
- package/services/list.service.js +62 -15
- package/services/run.service.d.ts +35 -22
- package/services/run.service.js +316 -233
- package/services/system-info.d.ts +1 -1
- package/services/version-plan.service.d.ts +35 -8
- package/services/version-plan.service.js +96 -36
- package/services/version.service.d.ts +34 -14
- package/services/version.service.js +35 -16
- package/services.d.ts +1 -1
- package/services.js +1 -1
- package/utils/bin-path.d.ts +6 -7
- package/utils/bin-path.js +7 -18
- package/utils/branch-guard.d.ts +30 -1
- package/utils/branch-guard.js +31 -0
- package/utils/color-yaml.d.ts +8 -0
- package/utils/color-yaml.js +86 -0
- package/utils/exec.d.ts +10 -0
- package/utils/exec.js +1 -1
- package/utils/git.d.ts +26 -0
- package/utils/git.js +65 -0
- package/utils/logger.d.ts +2 -2
- package/utils/logger.js +1 -1
- package/utils/package-filter.d.ts +135 -8
- package/utils/package-filter.js +204 -16
- package/utils/printable-config.d.ts +9 -1
- package/utils/printable-config.js +46 -1
- package/utils/progress-panel.d.ts +9 -0
- package/utils/progress-panel.js +23 -1
- package/utils/release-version.d.ts +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-group.d.ts +31 -0
- package/utils/version-group.js +43 -0
- package/utils/version-stamp.d.ts +1 -1
- package/utils/version-stamp.js +1 -1
- package/commands/changed.command.d.ts +0 -3
- package/commands/changed.command.js +0 -29
- package/core/config.d.ts +0 -409
- package/core/config.js +0 -939
- package/core/extends-config.d.ts +0 -27
- package/core/extends-config.js +0 -89
- package/core/manifest.js +0 -150
- package/core/merge-config.js +0 -219
- package/core/package.d.ts +0 -83
- package/core/package.js +0 -106
- package/core/plugin.d.ts +0 -112
- package/core/plugin.js +0 -189
- package/core/workspace.d.ts +0 -68
- package/core/workspace.js +0 -83
- /package/{core/run-step.js → builtins/platforms/node/augmentation/rmanrc.augmentation.js} +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
/**
|
|
4
|
+
* `npm view <name> version versions --json` - `undefined` for anything that is not an answer (never
|
|
5
|
+
* published, no network, private or restricted with no access). Catch-everything on purpose: every
|
|
6
|
+
* caller treats "no answer" as a legitimate state rather than a failure.
|
|
7
|
+
*
|
|
8
|
+
* **Two fields in one call, because the two questions are genuinely different and `publish` needs
|
|
9
|
+
* both.** Whether *this* version is already out there decides `up-to-date` vs `publish`, and only
|
|
10
|
+
* `versions` can answer it; what `latest` points at is what the plan *reports*, so a reader can see
|
|
11
|
+
* where the registry stands. Asking `latest` alone was the same approximation in both roles, and it
|
|
12
|
+
* is wrong in both directions: a prerelease on its own dist-tag never moves `latest`, so `publish`
|
|
13
|
+
* kept proposing an already-published version until npm answered 403; and a package whose local
|
|
14
|
+
* version is *behind* `latest` was proposed just as wrongly.
|
|
15
|
+
*
|
|
16
|
+
* **`versions` is normalized to an array defensively, and the reason is what happens if it is
|
|
17
|
+
* not.** `includes` exists on a string too, so a bare `"1.0.0"` would answer `versions.includes()`
|
|
18
|
+
* without any error at all - and answer it by substring, where `"1.0.10".includes("1.0.1")` is
|
|
19
|
+
* `true` (measured). npm's own `--json` output *is* a real array here on this npm (measured: a
|
|
20
|
+
* single-entry `maintainers` comes back as `[...]`, not unwrapped), so this guards a shape
|
|
21
|
+
* difference across npm versions rather than one I reproduced - but a silent wrong answer about
|
|
22
|
+
* whether a version is published is worth two lines.
|
|
23
|
+
*/
|
|
24
|
+
export async function npmViewPackage(name, cwd, options = {}) {
|
|
25
|
+
const argv = ['view', name, 'version', 'versions', '--json'];
|
|
26
|
+
if (options.registry)
|
|
27
|
+
argv.push('--registry', options.registry);
|
|
28
|
+
if (options.userconfig)
|
|
29
|
+
argv.push('--userconfig', options.userconfig);
|
|
30
|
+
try {
|
|
31
|
+
const { stdout } = await execFileAsync('npm', argv, { cwd });
|
|
32
|
+
const text = stdout.trim();
|
|
33
|
+
if (!text)
|
|
34
|
+
return undefined;
|
|
35
|
+
const raw = JSON.parse(text);
|
|
36
|
+
return { latest: raw.version, versions: toVersionList(raw.versions) };
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `npm view <name> version` - the **`latest` dist-tag's** version, or `undefined` for anything that
|
|
44
|
+
* is not an answer.
|
|
45
|
+
*
|
|
46
|
+
* `packageJsonManifest.publishedVersion` asks this so that `detectChangeHash` can guess a tag name
|
|
47
|
+
* for a package with no release tag at all, and `latest` is the right question for it: it wants the
|
|
48
|
+
* one version the ecosystem considers current, not the set of everything ever published. It passes
|
|
49
|
+
* no options - a bare `npm view` already picks up the repository's own `.npmrc` from `cwd`, which
|
|
50
|
+
* is what that path wants. `publish` asks `npmViewPackage` instead; see there for why.
|
|
51
|
+
*/
|
|
52
|
+
export async function npmViewVersion(name, cwd, options = {}) {
|
|
53
|
+
const argv = ['view', name, 'version'];
|
|
54
|
+
if (options.registry)
|
|
55
|
+
argv.push('--registry', options.registry);
|
|
56
|
+
if (options.userconfig)
|
|
57
|
+
argv.push('--userconfig', options.userconfig);
|
|
58
|
+
try {
|
|
59
|
+
const { stdout } = await execFileAsync('npm', argv, { cwd });
|
|
60
|
+
return stdout.trim() || undefined;
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function toVersionList(versions) {
|
|
67
|
+
if (Array.isArray(versions))
|
|
68
|
+
return versions;
|
|
69
|
+
return versions ? [versions] : [];
|
|
70
|
+
}
|
|
71
|
+
const execFileAsync = promisify(execFile);
|
package/cli.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
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;
|
|
10
|
+
/** Forwarded to {@link Repository.create} - see its `presets`. `[]` is a caller with an
|
|
11
|
+
* ecosystem of its own, which is what the core's own fixture is. */
|
|
12
|
+
presets?: readonly string[];
|
|
5
13
|
}): 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,25 +9,17 @@ 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 {
|
|
24
|
-
import { Repository } from './core/repository.js';
|
|
13
|
+
import { RmanApplication } from './core/application.js';
|
|
14
|
+
import { Repository } from './core/classes/repository.js';
|
|
15
|
+
import { commandName, toYargsCommand } from './core/command-builder.js';
|
|
16
|
+
import { assertNoBuiltinShadowing, defaultCommandGlobs, loadCustomCommands, } from './core/interfaces/custom-command.js';
|
|
17
|
+
import { checkCustomCommand } from './core/plugin-loader.js';
|
|
18
|
+
import { commandRegistry } from './interfaces/rman-config.interface.js';
|
|
19
|
+
import { colorYaml } from './utils/color-yaml.js';
|
|
25
20
|
import { LOG_LEVELS, Logger, resolveRootLogLevel } from './utils/logger.js';
|
|
26
|
-
import { filterPackages,
|
|
27
|
-
import { printableConfig } from './utils/printable-config.js';
|
|
21
|
+
import { filterPackages, readFromRootOption, readPackageFilterOptions } from './utils/package-filter.js';
|
|
22
|
+
import { printableConfig, withoutContributions } from './utils/printable-config.js';
|
|
28
23
|
import { runBin } from './utils/run-bin.js';
|
|
29
24
|
export async function runCli(options) {
|
|
30
25
|
const _argv = options?.argv || hideBin(process.argv);
|
|
@@ -41,7 +36,10 @@ export async function runCli(options) {
|
|
|
41
36
|
return;
|
|
42
37
|
}
|
|
43
38
|
try {
|
|
44
|
-
|
|
39
|
+
/** One application per run, made here so `--log-level` reaches its logger, and handed to
|
|
40
|
+
* `Repository.create` rather than found through a global. */
|
|
41
|
+
const app = options?.app ?? new RmanApplication();
|
|
42
|
+
const repository = await Repository.create(options?.cwd, { app, presets: options?.presets });
|
|
45
43
|
const program = yargs(_argv)
|
|
46
44
|
.scriptName('rman')
|
|
47
45
|
.version(version)
|
|
@@ -81,7 +79,7 @@ export async function runCli(options) {
|
|
|
81
79
|
* - but it still has to **throw**, not exit.
|
|
82
80
|
*
|
|
83
81
|
* This branch called `process.exit(1)`, and `runCli` is a library entry point: rman's own
|
|
84
|
-
* bin calls it, so do
|
|
82
|
+
* bin calls it, so do the fixtures and every spec. Exiting from in here took the
|
|
85
83
|
* whole process down before any caller could see the rejection - which in mocha meant the
|
|
86
84
|
* first command that failed killed the run and the suite could not report a single result.
|
|
87
85
|
* The exit belongs to the bin entry alone (see `isMain()` at the bottom), which already does
|
|
@@ -99,60 +97,94 @@ export async function runCli(options) {
|
|
|
99
97
|
* quietly does nothing on whichever command forgot it is worse than no flag.
|
|
100
98
|
*/
|
|
101
99
|
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
100
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
101
|
+
* **Built-ins come from `commandRegistry`, in the order their modules were imported.** Each
|
|
102
|
+
* entry is a register function that has not run yet - it runs here, with the repository, and
|
|
103
|
+
* returns the command's declaration; `toYargsCommand` is the only thing that knows how a
|
|
104
|
+
* declaration becomes a yargs registration.
|
|
118
105
|
*
|
|
119
|
-
* -
|
|
120
|
-
*
|
|
121
|
-
|
|
106
|
+
* The thirteen hand-written `initCli(repository, program)` calls this replaces were the second
|
|
107
|
+
* place a command had to be listed, and the list the shadow check guards with was a third.
|
|
108
|
+
*/
|
|
109
|
+
const builtIns = commandRegistry.map(register => register(app));
|
|
110
|
+
for (const meta of builtIns)
|
|
111
|
+
program.command(toYargsCommand(meta));
|
|
112
|
+
/**
|
|
113
|
+
* Commands that are not built in, all from **one** key: `.rmanrc "commands"`, whose default
|
|
114
|
+
* value is `.rman/*.{js,mjs,cjs}`.
|
|
115
|
+
*
|
|
116
|
+
* An entry is a command or a glob naming modules that export one, so a package contributing
|
|
117
|
+
* commands (a built-in's, a plugin's) and a repository writing its own reach yargs by the same path - there
|
|
118
|
+
* is one source of non-built-in commands and one precedence slot. A plugin used to hand them
|
|
119
|
+
* over separately through `addCommand`, which is the step this replaces.
|
|
120
|
+
*
|
|
121
|
+
* Registered after the built-ins so the shadow check below has the full list to compare
|
|
122
|
+
* against. A command taking a *built-in's* name is refused outright.
|
|
123
|
+
*/
|
|
124
|
+
const { globs, direct } = commandEntries(repository);
|
|
125
|
+
const { commands: loaded, errors } = await loadCustomCommands(globs);
|
|
126
|
+
/**
|
|
127
|
+
* The two authoring forms, resolved the same way - a declarative one's factory runs here,
|
|
128
|
+
* where `app.repository` exists, rather than when the config was read.
|
|
129
|
+
*
|
|
130
|
+
* **The file name is the fallback for `command`**, which is the convention a command loaded
|
|
131
|
+
* from a *file* has and one written straight into the config has not: `checkCustomCommand`
|
|
132
|
+
* refuses nameless metadata, and an inline command has nothing to fall back on, so it must say
|
|
133
|
+
* its own name. Spread *under* the factory's result, so metadata that does declare one -
|
|
134
|
+
* `deploy <stage>`, with its positionals - wins.
|
|
135
|
+
*
|
|
136
|
+
* The name comes from what the factory *returned*, not from the file: those differ exactly
|
|
137
|
+
* when the metadata declared one, and using the file name would leave the clash check
|
|
138
|
+
* comparing something yargs never registered.
|
|
139
|
+
*/
|
|
140
|
+
const localModules = [...direct, ...loaded].map(c => {
|
|
141
|
+
if (!c.register)
|
|
142
|
+
return { name: c.name, file: c.file, module: toCustomModule(c.custom, repository, app) };
|
|
143
|
+
const declared = c.register(app);
|
|
144
|
+
const meta = checkCustomCommand({ ...declared, command: declared.command?.trim() || c.name }, c.file);
|
|
145
|
+
return { name: commandName(meta.command), file: c.file, module: toYargsCommand(meta) };
|
|
146
|
+
});
|
|
147
|
+
/**
|
|
148
|
+
* **One command per name, keeping the last** - which is the precedence that already applied,
|
|
149
|
+
* made visible instead of left to yargs.
|
|
122
150
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
151
|
+
* A repository's own `.rman/check.mjs` overriding a command its config contributed is the
|
|
152
|
+
* intended escape hatch, and it is deliberately not an error: the same precedence a package's
|
|
153
|
+
* own `.rmanrc` has over an `extends` base. But both were being *registered*, and what that
|
|
154
|
+
* cost was the help output - measured, `rman --help` listed `deploy` twice, once with each
|
|
155
|
+
* description, and nothing said which of the two would run.
|
|
156
|
+
*
|
|
157
|
+
* Only the listing was wrong; the override itself was already clean (`rman deploy --help`
|
|
158
|
+
* showed the winner's options alone, and the loser's flag was rejected), which is why this is
|
|
159
|
+
* a registration fix and not a change to how a clash resolves. Registered later wins because
|
|
160
|
+
* `loaded` follows `direct` - a `.rman/` file after a contributed command - and yargs took the
|
|
161
|
+
* last, so keeping the last keeps today's behaviour exactly.
|
|
162
|
+
*
|
|
163
|
+
* The survivor takes the *loser's* position in the list, since that is where the name was
|
|
164
|
+
* first seen. Help ordering only.
|
|
165
|
+
*/
|
|
166
|
+
const byName = new Map();
|
|
167
|
+
for (const command of localModules)
|
|
168
|
+
byName.set(command.name, command);
|
|
169
|
+
const commands = [...byName.values()];
|
|
170
|
+
/**
|
|
171
|
+
* Said out loud, at `verbose`, because deduplicating silently is what would make this the
|
|
172
|
+
* trap it is warned about elsewhere: before, two rows at least hinted that something was
|
|
173
|
+
* doubled - after, the overridden command is simply absent, and "my plugin's command does
|
|
174
|
+
* nothing" has no thread to pull. Not a warning: an override is a correct thing to do, and a
|
|
175
|
+
* repository that does it on purpose should not be nagged on every invocation.
|
|
126
176
|
*/
|
|
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
|
-
});
|
|
177
|
+
if (commands.length < localModules.length) {
|
|
178
|
+
const logger = new Logger(argvLogLevel(_argv) ?? resolveRootLogLevel(repository));
|
|
179
|
+
const survivors = new Set(commands);
|
|
180
|
+
for (const lost of localModules.filter(c => !survivors.has(c))) {
|
|
181
|
+
const winner = byName.get(lost.name);
|
|
182
|
+
logger.verbose(`"${lost.name}" from ${lost.file} is overridden by ${winner.file}.`);
|
|
183
|
+
}
|
|
155
184
|
}
|
|
185
|
+
assertNoBuiltinShadowing(commands, builtInNames(builtIns));
|
|
186
|
+
for (const { module } of commands)
|
|
187
|
+
program.command(module);
|
|
156
188
|
/** Warned about, not thrown: one unparseable file must not take the other commands with it.
|
|
157
189
|
* Loud enough not to be mistaken for success, and it names the file and the reason - "my
|
|
158
190
|
* command isn't there" is otherwise a long afternoon. */
|
|
@@ -199,6 +231,56 @@ export async function runCli(options) {
|
|
|
199
231
|
throw e;
|
|
200
232
|
}
|
|
201
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Everything `.rmanrc "commands"` declares across the repository, split into the globs to load and
|
|
236
|
+
* the commands written straight into the config.
|
|
237
|
+
*
|
|
238
|
+
* **Collected from the root and from every package**, because `commands` is not a root-level key -
|
|
239
|
+
* a package's own `.rmanrc` may contribute one, and a glob was anchored to that file when it was
|
|
240
|
+
* read. The commands themselves are repository-wide; there is one command list, so a package
|
|
241
|
+
* declaring one is contributing it to the repository. (`plugins` and `publishTargets` cannot work
|
|
242
|
+
* this way: they are read before the packages exist, because a plugin is what finds them.)
|
|
243
|
+
*
|
|
244
|
+
* The cascade means a root-declared entry also appears in each package's resolved config, so the
|
|
245
|
+
* same one arrives many times over. Globs are de-duplicated here and by resolved *file* again in
|
|
246
|
+
* the loader - the second pass is the one that matters, since two different globs can name one
|
|
247
|
+
* file. Inline commands are de-duplicated by identity, which is what the cascade produces.
|
|
248
|
+
*
|
|
249
|
+
* The `.rman/` default is used only when nothing was declared anywhere. Declaring `commands` and
|
|
250
|
+
* still wanting `.rman/` scanned means naming it: the key appends to other layers, not to a
|
|
251
|
+
* built-in fallback, and a default that could never be turned off is not a default.
|
|
252
|
+
*/
|
|
253
|
+
function commandEntries(repository) {
|
|
254
|
+
const globs = new Set();
|
|
255
|
+
const seen = new Set();
|
|
256
|
+
const direct = [];
|
|
257
|
+
for (const config of [repository.rootPackage.config, ...repository.getPackages().map(p => p.config)]) {
|
|
258
|
+
const value = config?.commands;
|
|
259
|
+
for (const entry of Array.isArray(value) ? value : value ? [value] : []) {
|
|
260
|
+
if (typeof entry === 'string') {
|
|
261
|
+
if (entry.trim())
|
|
262
|
+
globs.add(entry);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
if (!entry || seen.has(entry))
|
|
266
|
+
continue;
|
|
267
|
+
seen.add(entry);
|
|
268
|
+
/** No file to fall back on, so the metadata has to name itself - `checkCustomCommand` says
|
|
269
|
+
* so when it does not. `"commands"` stands in for the file in that message. */
|
|
270
|
+
if (typeof entry === 'function') {
|
|
271
|
+
direct.push({ name: '', file: '"commands"', register: entry });
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
/** `checkCustomCommand` is what refuses a nameless one, with the message that names the
|
|
275
|
+
* omission - reached here rather than at registration so an inline command is checked
|
|
276
|
+
* the same way a plugin's used to be. */
|
|
277
|
+
const custom = checkCustomCommand(entry, '"commands"');
|
|
278
|
+
direct.push({ name: commandName(custom.command), file: '"commands"', custom });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return { globs: globs.size ? [...globs] : defaultCommandGlobs(repository.dirname), direct };
|
|
283
|
+
}
|
|
202
284
|
/**
|
|
203
285
|
* Replaces `program.command` with a version that wraps every handler: with `--config` it prints
|
|
204
286
|
* what the command would run with and returns, instead of running it.
|
|
@@ -257,9 +339,17 @@ function printCommandConfig(repository, spec, args) {
|
|
|
257
339
|
if (!shown.some(p => p === repository.rootPackage))
|
|
258
340
|
shown.push(repository.rootPackage);
|
|
259
341
|
const config = {};
|
|
260
|
-
for
|
|
261
|
-
|
|
262
|
-
|
|
342
|
+
/** `withoutContributions` for the same reason `rman config` uses it: `plugins`, `platforms`,
|
|
343
|
+
* `commands` and `publishTargets` are code, and a command's config section is about settings.
|
|
344
|
+
* Only the "in full" branch needs it - `pick` already narrows to the keys the command reads. */
|
|
345
|
+
for (const pkg of shown) {
|
|
346
|
+
config[pkg.name] = keys?.length ? pick(pkg.config, keys) : withoutContributions(pkg.config);
|
|
347
|
+
}
|
|
348
|
+
/** Coloured after indenting, not before: `indent` prefixes each line, and an escape sequence
|
|
349
|
+
* already sitting at the start of one is what it would be measuring. Gated on a terminal for the
|
|
350
|
+
* reason `rman config` documents - the section is YAML and has to survive being redirected. */
|
|
351
|
+
const body = indent(yaml.dump(printableConfig(config), { noRefs: true, lineWidth: 100 }).trimEnd());
|
|
352
|
+
console.log(process.stdout.isTTY ? colorYaml(body) : body);
|
|
263
353
|
if (!targets.includes(repository.rootPackage)) {
|
|
264
354
|
console.log(comment(`# "${repository.rootPackage.name}" is the root - listed because repo-wide keys are read there.`));
|
|
265
355
|
}
|
|
@@ -279,10 +369,10 @@ function readOptions(args) {
|
|
|
279
369
|
}
|
|
280
370
|
/** The packages the command would act on - `filterPackages` with the command's own options, so
|
|
281
371
|
* 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. */
|
|
372
|
+
* for a command that scopes by directory, unless `--from-root` says otherwise. */
|
|
283
373
|
function commandTargets(repository, args) {
|
|
284
374
|
const current = repository.currentPackage;
|
|
285
|
-
if (current && !
|
|
375
|
+
if (current && !readFromRootOption(args))
|
|
286
376
|
return [current];
|
|
287
377
|
return filterPackages(repository.getPackages(), readPackageFilterOptions(args));
|
|
288
378
|
}
|
|
@@ -312,23 +402,68 @@ function isMain() {
|
|
|
312
402
|
}
|
|
313
403
|
if (isMain())
|
|
314
404
|
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
|
-
|
|
405
|
+
/**
|
|
406
|
+
* Every name a repository's own command may not take - **derived from what was actually
|
|
407
|
+
* registered**, not listed.
|
|
408
|
+
*
|
|
409
|
+
* It used to be a hand-maintained array, because yargs exposes no such list, and a spec had to pin
|
|
410
|
+
* it against the command sources so that adding a command could not quietly leave a repository's
|
|
411
|
+
* own able to shadow it. With the built-ins coming out of `commandRegistry` the list and the
|
|
412
|
+
* registrations cannot disagree: they are the same walk.
|
|
413
|
+
*
|
|
414
|
+
* Aliases count - `ls` is `list`, and shadowing it would be the same mistake. `completion` is
|
|
415
|
+
* yargs' own command rather than one of ours, so it is the one name still written here.
|
|
416
|
+
*/
|
|
417
|
+
function builtInNames(metas) {
|
|
418
|
+
const names = metas.flatMap(meta => [commandName(meta.command), ...(meta.aliases ?? [])]);
|
|
419
|
+
return [...names, 'completion'];
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* A `CustomCommand` - a `.rman/*.mjs` command, or a plugin's written the older way - as the yargs
|
|
423
|
+
* registration it describes. `toYargsCommand` is the same function for a *declarative* command;
|
|
424
|
+
* this is the other authoring form, and both end at one `program.command`.
|
|
425
|
+
*
|
|
426
|
+
* **Every field has to be copied deliberately**, because this builds a new object: anything the
|
|
427
|
+
* command declared and this forgets is silently lost. `--config` printed the whole config for every
|
|
428
|
+
* plugin command until `configKeys` was on this list (measured, on `clean` and `ci`).
|
|
429
|
+
*/
|
|
430
|
+
function toCustomModule(custom, repository, app) {
|
|
431
|
+
return {
|
|
432
|
+
command: custom.command,
|
|
433
|
+
describe: custom.describe,
|
|
434
|
+
configKeys: custom.configKeys,
|
|
435
|
+
builder: custom.builder ?? ((y) => y),
|
|
436
|
+
handler: (args) => {
|
|
437
|
+
/** Resolved per invocation, not once at registration: `--log-level` is only known now. */
|
|
438
|
+
const logLevel = args.logLevel ?? resolveRootLogLevel(repository);
|
|
439
|
+
const context = {
|
|
440
|
+
repository,
|
|
441
|
+
package: repository.currentPackage,
|
|
442
|
+
runBin: (bin, argv, opts) => runBin(bin, argv, { cwd: repository.dirname, logLevel, app, ...opts }),
|
|
443
|
+
logger: new Logger(logLevel),
|
|
444
|
+
};
|
|
445
|
+
return custom.handler(context, args);
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* `--log-level` read straight off argv, for a message printed **before** yargs parses anything.
|
|
451
|
+
*
|
|
452
|
+
* Commands are registered before `parseAsync` runs - registering them is what makes parsing
|
|
453
|
+
* possible - so a diagnostic emitted at registration time cannot come from `args.logLevel`.
|
|
454
|
+
* Measured: the override note in `runCli` was silent for `--log-level verbose` and appeared only
|
|
455
|
+
* when `.rmanrc` said so, which is the one spelling a reader would not reach for first.
|
|
456
|
+
*
|
|
457
|
+
* A peek, not a parser. Both spellings, first match wins, and a value that is not a level is left
|
|
458
|
+
* alone for yargs to reject in its own words - this must not become a second place where an
|
|
459
|
+
* invalid `--log-level` is diagnosed.
|
|
460
|
+
*/
|
|
461
|
+
function argvLogLevel(argv) {
|
|
462
|
+
for (let i = 0; i < argv.length; i++) {
|
|
463
|
+
const arg = argv[i];
|
|
464
|
+
const value = arg === '--log-level' ? argv[i + 1] : arg.startsWith('--log-level=') ? arg.slice(12) : undefined;
|
|
465
|
+
if (value && LOG_LEVELS.includes(value))
|
|
466
|
+
return value;
|
|
467
|
+
}
|
|
468
|
+
return undefined;
|
|
469
|
+
}
|
|
@@ -1,3 +1,177 @@
|
|
|
1
|
-
import type
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { type RmanConfig } from '../interfaces/rman-config.interface.js';
|
|
2
|
+
declare const COMMAND: "build";
|
|
3
|
+
declare const config: {
|
|
4
|
+
parallel: {
|
|
5
|
+
target: "cli";
|
|
6
|
+
describe: string;
|
|
7
|
+
coerce: (v: unknown) => boolean | number | undefined;
|
|
8
|
+
};
|
|
9
|
+
bail: {
|
|
10
|
+
target: "cli";
|
|
11
|
+
describe: string;
|
|
12
|
+
type: "boolean";
|
|
13
|
+
};
|
|
14
|
+
topo: {
|
|
15
|
+
target: "cli";
|
|
16
|
+
describe: string;
|
|
17
|
+
type: "boolean";
|
|
18
|
+
};
|
|
19
|
+
progress: {
|
|
20
|
+
target: "cli";
|
|
21
|
+
describe: string;
|
|
22
|
+
type: "boolean";
|
|
23
|
+
};
|
|
24
|
+
changed: {
|
|
25
|
+
target: "cli";
|
|
26
|
+
alias: string;
|
|
27
|
+
describe: string;
|
|
28
|
+
type: "boolean";
|
|
29
|
+
};
|
|
30
|
+
changedSince: {
|
|
31
|
+
target: "cli";
|
|
32
|
+
cliName: string;
|
|
33
|
+
describe: string;
|
|
34
|
+
type: "string";
|
|
35
|
+
conflicts: string;
|
|
36
|
+
};
|
|
37
|
+
fromRoot: {
|
|
38
|
+
target: "cli";
|
|
39
|
+
cliName: string;
|
|
40
|
+
alias: string;
|
|
41
|
+
describe: string;
|
|
42
|
+
type: "boolean";
|
|
43
|
+
};
|
|
44
|
+
allowBranch: {
|
|
45
|
+
target: "cli";
|
|
46
|
+
cliName: string;
|
|
47
|
+
describe: string;
|
|
48
|
+
type: "string";
|
|
49
|
+
};
|
|
50
|
+
ignoreBranch: {
|
|
51
|
+
target: "cli";
|
|
52
|
+
cliName: string;
|
|
53
|
+
describe: string;
|
|
54
|
+
type: "string";
|
|
55
|
+
};
|
|
56
|
+
scope: {
|
|
57
|
+
target: "cli";
|
|
58
|
+
describe: string;
|
|
59
|
+
type: "string";
|
|
60
|
+
};
|
|
61
|
+
ignore: {
|
|
62
|
+
target: "cli";
|
|
63
|
+
describe: string;
|
|
64
|
+
type: "string";
|
|
65
|
+
};
|
|
66
|
+
platform: {
|
|
67
|
+
target: "cli";
|
|
68
|
+
describe: string;
|
|
69
|
+
type: "string";
|
|
70
|
+
};
|
|
71
|
+
deps: {
|
|
72
|
+
target: "cli";
|
|
73
|
+
describe: string;
|
|
74
|
+
type: "boolean";
|
|
75
|
+
};
|
|
76
|
+
dependents: {
|
|
77
|
+
target: "cli";
|
|
78
|
+
describe: string;
|
|
79
|
+
type: "boolean";
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
type Args = RmanConfig.ArgsOf<typeof config, typeof COMMAND>;
|
|
83
|
+
declare const buildCommand: (app: import("../index.js").RmanApplication) => {
|
|
84
|
+
command: "build";
|
|
85
|
+
describe: string;
|
|
86
|
+
/**
|
|
87
|
+
* Read, not owned - and it owns nothing at all. `build` is `run build` under another name, so its
|
|
88
|
+
* settings live in `run.build`, which belongs to `run`. Two commands cannot contribute under one
|
|
89
|
+
* top-level key (interface merging is not a deep merge), and this is why they never needed to.
|
|
90
|
+
*/
|
|
91
|
+
configKeys: string[];
|
|
92
|
+
config: {
|
|
93
|
+
parallel: {
|
|
94
|
+
target: "cli";
|
|
95
|
+
describe: string;
|
|
96
|
+
coerce: (v: unknown) => boolean | number | undefined;
|
|
97
|
+
};
|
|
98
|
+
bail: {
|
|
99
|
+
target: "cli";
|
|
100
|
+
describe: string;
|
|
101
|
+
type: "boolean";
|
|
102
|
+
};
|
|
103
|
+
topo: {
|
|
104
|
+
target: "cli";
|
|
105
|
+
describe: string;
|
|
106
|
+
type: "boolean";
|
|
107
|
+
};
|
|
108
|
+
progress: {
|
|
109
|
+
target: "cli";
|
|
110
|
+
describe: string;
|
|
111
|
+
type: "boolean";
|
|
112
|
+
};
|
|
113
|
+
changed: {
|
|
114
|
+
target: "cli";
|
|
115
|
+
alias: string;
|
|
116
|
+
describe: string;
|
|
117
|
+
type: "boolean";
|
|
118
|
+
};
|
|
119
|
+
changedSince: {
|
|
120
|
+
target: "cli";
|
|
121
|
+
cliName: string;
|
|
122
|
+
describe: string;
|
|
123
|
+
type: "string";
|
|
124
|
+
conflicts: string;
|
|
125
|
+
};
|
|
126
|
+
fromRoot: {
|
|
127
|
+
target: "cli";
|
|
128
|
+
cliName: string;
|
|
129
|
+
alias: string;
|
|
130
|
+
describe: string;
|
|
131
|
+
type: "boolean";
|
|
132
|
+
};
|
|
133
|
+
allowBranch: {
|
|
134
|
+
target: "cli";
|
|
135
|
+
cliName: string;
|
|
136
|
+
describe: string;
|
|
137
|
+
type: "string";
|
|
138
|
+
};
|
|
139
|
+
ignoreBranch: {
|
|
140
|
+
target: "cli";
|
|
141
|
+
cliName: string;
|
|
142
|
+
describe: string;
|
|
143
|
+
type: "string";
|
|
144
|
+
};
|
|
145
|
+
scope: {
|
|
146
|
+
target: "cli";
|
|
147
|
+
describe: string;
|
|
148
|
+
type: "string";
|
|
149
|
+
};
|
|
150
|
+
ignore: {
|
|
151
|
+
target: "cli";
|
|
152
|
+
describe: string;
|
|
153
|
+
type: "string";
|
|
154
|
+
};
|
|
155
|
+
platform: {
|
|
156
|
+
target: "cli";
|
|
157
|
+
describe: string;
|
|
158
|
+
type: "string";
|
|
159
|
+
};
|
|
160
|
+
deps: {
|
|
161
|
+
target: "cli";
|
|
162
|
+
describe: string;
|
|
163
|
+
type: "boolean";
|
|
164
|
+
};
|
|
165
|
+
dependents: {
|
|
166
|
+
target: "cli";
|
|
167
|
+
describe: string;
|
|
168
|
+
type: "boolean";
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
examples: {
|
|
172
|
+
command: string;
|
|
173
|
+
description: string;
|
|
174
|
+
}[];
|
|
175
|
+
handler: (args: Args) => Promise<void>;
|
|
176
|
+
};
|
|
177
|
+
export default buildCommand;
|