rman 1.3.0 → 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 +140 -19
- package/core/config.js +258 -74
- 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 +137 -92
- 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 -44
- 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 +725 -202
- 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
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { RmanApplication } from '../core/application.js';
|
|
2
3
|
import { assertOneScheme, semverScheme, VersionScheme } from '../core/version-scheme.js';
|
|
3
4
|
import { GitHelper } from '../utils/git.js';
|
|
4
5
|
import { filterPackages } from '../utils/package-filter.js';
|
|
@@ -118,7 +119,7 @@ export class VersionPlanService {
|
|
|
118
119
|
changeByPackage.set(pkg.name, { bump: undefined, reason: `explicit version ${explicitVersion}` });
|
|
119
120
|
return;
|
|
120
121
|
}
|
|
121
|
-
const since = await this.detectBoundary(git, pkg, options);
|
|
122
|
+
const since = await this.plannerFor(pkg).detectBoundary(git, pkg, options);
|
|
122
123
|
const commits = since ? await git.listCommits({ hash: since }) : await git.listAllCommits();
|
|
123
124
|
const belongsToPkg = (c) => c.files.some(f => !path.relative(pkg.dirname, f).startsWith('..'));
|
|
124
125
|
const real = commits.filter(c => belongsToPkg(c) && !ConventionalCommitsService.isReleaseCommit(c.subject, commitMessage));
|
|
@@ -152,6 +153,39 @@ export class VersionPlanService {
|
|
|
152
153
|
}
|
|
153
154
|
return result;
|
|
154
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* **Which planner answers for one package: its own technology's.**
|
|
158
|
+
*
|
|
159
|
+
* The two abstract members above are the ecosystem's, and a repository can hold more than one.
|
|
160
|
+
* `app.versionPlanner` is a single slot - last registration wins - so in a polyglot repository
|
|
161
|
+
* both of them used to be answered by whichever plugin happened to register last: a Cargo
|
|
162
|
+
* package's boundary fell back to `npm view`, and its cascade assumed npm's caret ranges. That is
|
|
163
|
+
* the same shape of bug the hardcoded `['npm']` publish default was, and the same fix - ask the
|
|
164
|
+
* technology that read the manifest.
|
|
165
|
+
*
|
|
166
|
+
* Falls back to `this`, which is what a single-technology repository always gets and what a
|
|
167
|
+
* `Plugin` contributing no planner of its own means.
|
|
168
|
+
*/
|
|
169
|
+
plannerFor(pkg) {
|
|
170
|
+
return pkg.platform.versionPlanner ?? this;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* `cascade` for a whole group, which is the unit it is asked about - one answer applies to every
|
|
174
|
+
* member, because the group releases as one version line.
|
|
175
|
+
*
|
|
176
|
+
* **Members disagreeing take the widest answer**, and the direction is deliberate: a cascade that
|
|
177
|
+
* is too narrow produces a plan that silently *releases too little* (the failure `cascade`'s own
|
|
178
|
+
* doc calls invisible), while one that is too wide releases a package that did not strictly need
|
|
179
|
+
* it - visible, and harmless. A mixed-technology group is unusual; a mixed group quietly skipping
|
|
180
|
+
* a dependent that pins exact versions is a broken install.
|
|
181
|
+
*/
|
|
182
|
+
cascadeFor(members, bump) {
|
|
183
|
+
const answers = new Set(members.map(m => this.plannerFor(m).cascade(bump)));
|
|
184
|
+
for (const widest of CASCADE_WIDEST_FIRST)
|
|
185
|
+
if (answers.has(widest))
|
|
186
|
+
return widest;
|
|
187
|
+
return 'changed';
|
|
188
|
+
}
|
|
155
189
|
/**
|
|
156
190
|
* The largest bump `commits` ask for, in `scheme`'s own names.
|
|
157
191
|
*
|
|
@@ -246,7 +280,7 @@ export class VersionPlanService {
|
|
|
246
280
|
to = scheme.next(current, bump, { preid });
|
|
247
281
|
}
|
|
248
282
|
const bumping = new Set(changed);
|
|
249
|
-
const cascade = bump ? this.
|
|
283
|
+
const cascade = bump ? this.cascadeFor(members, bump) : 'changed';
|
|
250
284
|
if (cascade === 'group') {
|
|
251
285
|
for (const m of members)
|
|
252
286
|
bumping.add(m);
|
|
@@ -377,15 +411,6 @@ export class VersionPlanService {
|
|
|
377
411
|
* repository layering its own policy plugin after `rman-node` could never take effect - which is
|
|
378
412
|
* the only reason to name two in the first place.
|
|
379
413
|
*/
|
|
380
|
-
function setPlanner(planner) {
|
|
381
|
-
current = planner;
|
|
382
|
-
}
|
|
383
|
-
VersionPlanService.setPlanner = setPlanner;
|
|
384
|
-
/** For tests, which would otherwise leak a planner into every later case in the process. */
|
|
385
|
-
function clearPlanner() {
|
|
386
|
-
current = undefined;
|
|
387
|
-
}
|
|
388
|
-
VersionPlanService.clearPlanner = clearPlanner;
|
|
389
414
|
/**
|
|
390
415
|
* The registered planner. **Throws** when there is none, rather than falling back to some
|
|
391
416
|
* built-in default: `Manifest` and `Workspace` can degrade honestly (a package named after its
|
|
@@ -393,16 +418,18 @@ export class VersionPlanService {
|
|
|
393
418
|
* merely a diminished one - a wrong cascade or a wrong boundary reports a release that is
|
|
394
419
|
* plausible and untrue.
|
|
395
420
|
*/
|
|
396
|
-
function getPlanner() {
|
|
397
|
-
|
|
421
|
+
function getPlanner(app) {
|
|
422
|
+
const planner = app.versionPlanner;
|
|
423
|
+
if (!planner) {
|
|
398
424
|
throw new Error('No version planner is registered, so no version plan can be computed. Name a plugin that ' +
|
|
399
425
|
'contributes one in .rmanrc "plugins" - "rman-node" for a Node repository.');
|
|
400
426
|
}
|
|
401
|
-
return
|
|
427
|
+
return planner;
|
|
402
428
|
}
|
|
403
429
|
VersionPlanService.getPlanner = getPlanner;
|
|
404
|
-
let current;
|
|
405
430
|
})(VersionPlanService || (VersionPlanService = {}));
|
|
431
|
+
/** Widest reach first - what `cascadeFor` walks when a group's members answer differently. */
|
|
432
|
+
const CASCADE_WIDEST_FIRST = ['group', 'dependents', 'changed'];
|
|
406
433
|
/** What one commit says happened, with no reference to any version format - `VersionScheme.bumpFor`
|
|
407
434
|
* turns it into a number's movement. Everything unrecognized is a `'fix'`: something changed, so
|
|
408
435
|
* the smallest release is still warranted. */
|
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import type { Package } from '../core/package.js';
|
|
2
2
|
import type { Repository } from '../core/repository.js';
|
|
3
3
|
import type { RunStepValue } from '../core/run-step.js';
|
|
4
|
+
import { Service } from '../core/service.js';
|
|
4
5
|
import { VersionPlanService } from './version-plan.service.js';
|
|
5
6
|
/**
|
|
6
7
|
* Applying a version plan: the writes. Every manifest edit, dependency-range refresh, stamp,
|
|
7
8
|
* commit and tag lives here; **what** to write is `VersionPlanService`'s answer.
|
|
8
9
|
*
|
|
9
10
|
* Nothing here knows what a `package.json` is, names an npm script, or runs a command: the version
|
|
10
|
-
* goes through `Manifest`/`
|
|
11
|
+
* goes through `Manifest`/`Plugin`'s manifest members, refreshing a sibling's dependency range goes through
|
|
11
12
|
* the same provider, and the lifecycle hooks around the write go through
|
|
12
13
|
* `RunService.runLifecycleSlot` - this module only supplies the `.rmanrc version.<slot>` fallback,
|
|
13
14
|
* which is its own config. What is left is git, that config, and the version stamps.
|
|
14
15
|
*/
|
|
16
|
+
/**
|
|
17
|
+
* A service class - see `ListService` for the shape and `Service` for the three measured
|
|
18
|
+
* consequences a namespace had.
|
|
19
|
+
*
|
|
20
|
+
* **Only `applyPlan` became a method**, because only it takes a repository. `buildCommitMessage`,
|
|
21
|
+
* `stampDockerfile`, `stampSourceFiles` and `normalizeScriptValue` take a `Package` or nothing and
|
|
22
|
+
* stay functions on the namespace below - the same rule that leaves `ChangeHashService` a namespace
|
|
23
|
+
* entirely.
|
|
24
|
+
*
|
|
25
|
+
* **Declared before the namespace**, which TypeScript requires: the other order is
|
|
26
|
+
* `A namespace declaration cannot be located prior to a class with which it is merged`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class VersionService extends Service {
|
|
29
|
+
/**
|
|
30
|
+
* Writes every `'bump'` entry's new version into its own manifest (and refreshes any other bumped
|
|
31
|
+
* package's dependency range on it), runs that package's own version-lifecycle hooks or its
|
|
32
|
+
* `.rmanrc version.before`/`.exec`/`.after` around the write - see the `hook` closure below -
|
|
33
|
+
* then commits and tags **once per group** - so independently-versioned groups each get their own clean commit/tag rather than one entangled
|
|
34
|
+
* commit spanning unrelated version lines. Pushes only when `options.push` is set - same as a
|
|
35
|
+
* plain `npm version`, this never reaches the network on its own otherwise.
|
|
36
|
+
*/
|
|
37
|
+
applyPlan(plan: VersionPlanService.Entry[], options?: VersionService.ApplyOptions): Promise<VersionService.ApplyResult>;
|
|
38
|
+
}
|
|
15
39
|
export declare namespace VersionService {
|
|
16
40
|
interface ApplyOptions {
|
|
17
41
|
/** Push the resulting commit(s) and tag(s) to the remote once applied. Default false - same
|
|
@@ -61,15 +85,6 @@ export declare namespace VersionService {
|
|
|
61
85
|
/** True for the repository's own release tag, which belongs to no single package. */
|
|
62
86
|
release?: boolean;
|
|
63
87
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Writes every `'bump'` entry's new version into its own manifest (and refreshes any other bumped
|
|
66
|
-
* package's dependency range on it), runs that package's own version-lifecycle hooks or its
|
|
67
|
-
* `.rmanrc version.before`/`.exec`/`.after` around the write - see the `hook` closure below -
|
|
68
|
-
* then commits and tags **once per group** - so independently-versioned groups each get their own clean commit/tag rather than one entangled
|
|
69
|
-
* commit spanning unrelated version lines. Pushes only when `options.push` is set - same as a
|
|
70
|
-
* plain `npm version`, this never reaches the network on its own otherwise.
|
|
71
|
-
*/
|
|
72
|
-
function applyPlan(repository: Repository, plan: VersionPlanService.Entry[], options?: ApplyOptions): Promise<ApplyResult>;
|
|
73
88
|
/** `.rmanrc version.commitMessage` (root-level; `{version}` is replaced when every bumped package
|
|
74
89
|
* in this commit shares one version) - defaults to `"chore(release): v{version}"`, or a plain
|
|
75
90
|
* listing of `name@version` pairs when this particular commit spans different versions (a
|
|
@@ -119,8 +134,13 @@ export declare namespace VersionService {
|
|
|
119
134
|
* pattern missed it) released a tagged commit with a stale constant and said nothing. The error
|
|
120
135
|
* names the file and, when the package's ecosystem declared none, says so.
|
|
121
136
|
*
|
|
122
|
-
* *How* a version is declared is the provider's (`
|
|
137
|
+
* *How* a version is declared is the provider's (`Plugin.stampVersion`); *which* files
|
|
123
138
|
* hold one is the repository's, which is why the list is config and the rewrite is a seam.
|
|
124
139
|
*/
|
|
125
140
|
function stampSourceFiles(pkg: Package, version: string): string[];
|
|
126
141
|
}
|
|
142
|
+
declare module '../core/service.js' {
|
|
143
|
+
interface ServiceMap {
|
|
144
|
+
version: VersionService;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -2,11 +2,11 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { interpolateConfig } from '../core/config.js';
|
|
4
4
|
import { Manifest } from '../core/manifest.js';
|
|
5
|
+
import { Service } from '../core/service.js';
|
|
5
6
|
import { GitHelper } from '../utils/git.js';
|
|
6
7
|
import { expandReleaseTag, isCalendarVersion } from '../utils/release-version.js';
|
|
7
8
|
import { stampVersionLabel } from '../utils/version-stamp.js';
|
|
8
9
|
import { ChangeHashService } from './change-hash.service.js';
|
|
9
|
-
import { ChangelogService } from './changelog.service.js';
|
|
10
10
|
import { RunService } from './run.service.js';
|
|
11
11
|
import { VersionPlanService } from './version-plan.service.js';
|
|
12
12
|
/**
|
|
@@ -14,13 +14,24 @@ import { VersionPlanService } from './version-plan.service.js';
|
|
|
14
14
|
* commit and tag lives here; **what** to write is `VersionPlanService`'s answer.
|
|
15
15
|
*
|
|
16
16
|
* Nothing here knows what a `package.json` is, names an npm script, or runs a command: the version
|
|
17
|
-
* goes through `Manifest`/`
|
|
17
|
+
* goes through `Manifest`/`Plugin`'s manifest members, refreshing a sibling's dependency range goes through
|
|
18
18
|
* the same provider, and the lifecycle hooks around the write go through
|
|
19
19
|
* `RunService.runLifecycleSlot` - this module only supplies the `.rmanrc version.<slot>` fallback,
|
|
20
20
|
* which is its own config. What is left is git, that config, and the version stamps.
|
|
21
21
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* A service class - see `ListService` for the shape and `Service` for the three measured
|
|
24
|
+
* consequences a namespace had.
|
|
25
|
+
*
|
|
26
|
+
* **Only `applyPlan` became a method**, because only it takes a repository. `buildCommitMessage`,
|
|
27
|
+
* `stampDockerfile`, `stampSourceFiles` and `normalizeScriptValue` take a `Package` or nothing and
|
|
28
|
+
* stay functions on the namespace below - the same rule that leaves `ChangeHashService` a namespace
|
|
29
|
+
* entirely.
|
|
30
|
+
*
|
|
31
|
+
* **Declared before the namespace**, which TypeScript requires: the other order is
|
|
32
|
+
* `A namespace declaration cannot be located prior to a class with which it is merged`.
|
|
33
|
+
*/
|
|
34
|
+
export class VersionService extends Service {
|
|
24
35
|
/**
|
|
25
36
|
* Writes every `'bump'` entry's new version into its own manifest (and refreshes any other bumped
|
|
26
37
|
* package's dependency range on it), runs that package's own version-lifecycle hooks or its
|
|
@@ -29,7 +40,8 @@ export var VersionService;
|
|
|
29
40
|
* commit spanning unrelated version lines. Pushes only when `options.push` is set - same as a
|
|
30
41
|
* plain `npm version`, this never reaches the network on its own otherwise.
|
|
31
42
|
*/
|
|
32
|
-
async
|
|
43
|
+
async applyPlan(plan, options = {}) {
|
|
44
|
+
const repository = this.repository;
|
|
33
45
|
const git = new GitHelper({ cwd: repository.dirname });
|
|
34
46
|
// The root's own entry is only ever a real package to write/commit like any other when this
|
|
35
47
|
// *isn't* a monorepo (see `getPlan`) - in a monorepo it's the separate, purely informational
|
|
@@ -71,17 +83,20 @@ export var VersionService;
|
|
|
71
83
|
interpolateConfig(pkg.config?.version?.[slot], scope, { at: ['version', slot] }), `version.${slot}`));
|
|
72
84
|
await hook('before');
|
|
73
85
|
/** Through the manifest, not through a `package.json` field: where a version is written is
|
|
74
|
-
* the provider's business (see `
|
|
86
|
+
* the provider's business (see `Plugin`'s manifest members), and this is the one place rman changes
|
|
75
87
|
* it. */
|
|
76
88
|
pkg.manifest.version = entry.to;
|
|
77
89
|
/** Which fields hold a sibling reference, and what a reference even looks like, is the
|
|
78
90
|
* ecosystem's - npm's four fields and its `"workspace:"` protocol used to be spelled out
|
|
79
|
-
* here. See `
|
|
91
|
+
* here. See `Plugin.updateDependencyVersions`. */
|
|
80
92
|
Manifest.updateDependencyVersions(pkg, bumpedVersions);
|
|
81
93
|
await hook('exec');
|
|
82
94
|
pkg.writeManifest();
|
|
83
95
|
// Before the `after` hook, so a script reacting to the bump sees the whole new state.
|
|
84
|
-
const stamped = [
|
|
96
|
+
const stamped = [
|
|
97
|
+
VersionService.stampDockerfile(pkg, entry.to),
|
|
98
|
+
...VersionService.stampSourceFiles(pkg, entry.to),
|
|
99
|
+
].filter((f) => !!f);
|
|
85
100
|
if (stamped.length) {
|
|
86
101
|
stampedByPackage.set(pkg.name, stamped.map(f => path.relative(repository.dirname, f)));
|
|
87
102
|
}
|
|
@@ -105,9 +120,9 @@ export var VersionService;
|
|
|
105
120
|
for (const entry of bumped) {
|
|
106
121
|
const fromTag = ChangeHashService.expandTag(entry.package, entry.from);
|
|
107
122
|
const from = (await git.tagExists(fromTag)) ? fromTag : undefined;
|
|
108
|
-
const changelogEntries = await
|
|
123
|
+
const changelogEntries = await this.app.getService('changelog').generateToFile({
|
|
109
124
|
scope: entry.package.name,
|
|
110
|
-
|
|
125
|
+
fromRoot: true,
|
|
111
126
|
from,
|
|
112
127
|
// The tag for this release doesn't exist yet (it's created below), so changelog's own
|
|
113
128
|
// tag-derived version would resolve to the *previous* release and label the entry with it.
|
|
@@ -152,7 +167,7 @@ export var VersionService;
|
|
|
152
167
|
files.push(changelogFile);
|
|
153
168
|
files.push(...(stampedByPackage.get(e.package.name) ?? []));
|
|
154
169
|
}
|
|
155
|
-
const message = buildCommitMessage(repository, groupEntries, options.message);
|
|
170
|
+
const message = VersionService.buildCommitMessage(repository, groupEntries, options.message);
|
|
156
171
|
const sha = await git.commit(files, message);
|
|
157
172
|
commits.push({ sha, message, packages: groupEntries.map(e => e.package.name) });
|
|
158
173
|
const tags = new Set(groupEntries.map(e => ChangeHashService.expandTag(e.package, e.to)));
|
|
@@ -182,7 +197,8 @@ export var VersionService;
|
|
|
182
197
|
await git.push();
|
|
183
198
|
return { entries: plan, updated: bumped, commits, tags: tagged, pushed };
|
|
184
199
|
}
|
|
185
|
-
|
|
200
|
+
}
|
|
201
|
+
(function (VersionService) {
|
|
186
202
|
/** `.rmanrc version.commitMessage` (root-level; `{version}` is replaced when every bumped package
|
|
187
203
|
* in this commit shares one version) - defaults to `"chore(release): v{version}"`, or a plain
|
|
188
204
|
* listing of `name@version` pairs when this particular commit spans different versions (a
|
|
@@ -260,7 +276,7 @@ export var VersionService;
|
|
|
260
276
|
* pattern missed it) released a tagged commit with a stale constant and said nothing. The error
|
|
261
277
|
* names the file and, when the package's ecosystem declared none, says so.
|
|
262
278
|
*
|
|
263
|
-
* *How* a version is declared is the provider's (`
|
|
279
|
+
* *How* a version is declared is the provider's (`Plugin.stampVersion`); *which* files
|
|
264
280
|
* hold one is the repository's, which is why the list is config and the rewrite is a seam.
|
|
265
281
|
*/
|
|
266
282
|
function stampSourceFiles(pkg, version) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ConfigValue } from '../core/config.js';
|
|
2
|
+
import type { PublishTarget } from '../core/publish-target.js';
|
|
3
|
+
import type { ScopedVars } from '../interfaces/rman-config.interface.js';
|
|
4
|
+
/**
|
|
5
|
+
* **`publish.docker.*`** - this target's own config block, declared here rather than centrally.
|
|
6
|
+
*
|
|
7
|
+
* Reaches `RmanConfig` through the `PublishTargetConfigs` slot `publish.command.ts` exports, which
|
|
8
|
+
* is the same slot the `node` built-in declares `publish.npm.*` in. Whoever reads a key declares it: the
|
|
9
|
+
* only thing that reads these is `DockerPublishService`, two files away.
|
|
10
|
+
*
|
|
11
|
+
* Required once `"docker"` is one of a package's `publish.target`s - `publish --target docker`
|
|
12
|
+
* errors clearly on a package that opts in and leaves this out.
|
|
13
|
+
*/
|
|
14
|
+
export interface DockerPublishOptions extends DockerPublishOptionsKeys, ScopedVars {
|
|
15
|
+
}
|
|
16
|
+
/** Every key here is a **value**, so every one is a `ConfigValue` - this target declares no step,
|
|
17
|
+
* which is what makes the whole block uniform (see `VersionExtraKeys` for the interface where it
|
|
18
|
+
* is not). */
|
|
19
|
+
export interface DockerPublishOptionsKeys {
|
|
20
|
+
/** DockerHub image name/repository - bare (e.g. `"my-app"`) to be prefixed with
|
|
21
|
+
* `--docker-namespace`/`DOCKERHUB_NAMESPACE`, or already-namespaced (contains a `/`) to use
|
|
22
|
+
* verbatim. */
|
|
23
|
+
image: ConfigValue<string>;
|
|
24
|
+
/** Relative to the package's own directory. Default `"Dockerfile"`. */
|
|
25
|
+
dockerfile?: ConfigValue<string>;
|
|
26
|
+
/** Default `["linux/amd64"]`. */
|
|
27
|
+
platforms?: ConfigValue<string[]>;
|
|
28
|
+
/** Build `cwd` override, relative to the repository root - only needed when the Dockerfile's
|
|
29
|
+
* own `COPY`/`ADD` paths expect something other than the package's own directory (rare). */
|
|
30
|
+
cwd?: ConfigValue<string>;
|
|
31
|
+
/** Named `docker buildx build --build-context <name>=<path>` entries, keyed by name - each
|
|
32
|
+
* path is relative to the package's own directory (or absolute). */
|
|
33
|
+
buildContexts?: ConfigValue<Record<string, string>>;
|
|
34
|
+
/** `docker buildx build --build-arg <name>=<value>` entries - a value of exactly `"$NAME"`
|
|
35
|
+
* expands to `process.env.NAME` at build time (e.g. to pass a CI secret through). */
|
|
36
|
+
buildArgs?: ConfigValue<Record<string, string>>;
|
|
37
|
+
/** A file (relative to the package's own directory) whose contents become the DockerHub repo's
|
|
38
|
+
* full description, if present. Default `"DOCKER_README.md"`. */
|
|
39
|
+
readme?: ConfigValue<string>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* **Docker, as a publish target** - the core's own, and the reason `PublishTarget` is in the core
|
|
43
|
+
* rather than in a plugin.
|
|
44
|
+
*
|
|
45
|
+
* Any language's project can ship an image; nothing about `docker buildx build --push` is npm's, or
|
|
46
|
+
* Cargo's, or Maven's. It was already a core *service*, and could still only be reached through
|
|
47
|
+
* `rman-node`'s `publish` command - so a repository with no JavaScript in it had to install a Node
|
|
48
|
+
* plugin to push a container. This file is the ten lines that fix that.
|
|
49
|
+
*
|
|
50
|
+
* Opt-in, and therefore no `claims`: most packages in a repository are not images, so a package
|
|
51
|
+
* ships here only by naming `"docker"` in its own `publish.target`.
|
|
52
|
+
*/
|
|
53
|
+
export declare const dockerPublishTarget: PublishTarget;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { DOCKER_TARGET } from '../services/docker-publish.service.js';
|
|
2
|
+
/**
|
|
3
|
+
* **Docker, as a publish target** - the core's own, and the reason `PublishTarget` is in the core
|
|
4
|
+
* rather than in a plugin.
|
|
5
|
+
*
|
|
6
|
+
* Any language's project can ship an image; nothing about `docker buildx build --push` is npm's, or
|
|
7
|
+
* Cargo's, or Maven's. It was already a core *service*, and could still only be reached through
|
|
8
|
+
* `rman-node`'s `publish` command - so a repository with no JavaScript in it had to install a Node
|
|
9
|
+
* plugin to push a container. This file is the ten lines that fix that.
|
|
10
|
+
*
|
|
11
|
+
* Opt-in, and therefore no `claims`: most packages in a repository are not images, so a package
|
|
12
|
+
* ships here only by naming `"docker"` in its own `publish.target`.
|
|
13
|
+
*/
|
|
14
|
+
export const dockerPublishTarget = {
|
|
15
|
+
name: DOCKER_TARGET,
|
|
16
|
+
describe: 'Build and push a container image (docker buildx build --push)',
|
|
17
|
+
options: {
|
|
18
|
+
dockerNamespace: {
|
|
19
|
+
target: 'cli',
|
|
20
|
+
cliName: 'docker-namespace',
|
|
21
|
+
describe: 'Prefixed onto a bare (no "/") "publish.docker.image" - default: the DOCKERHUB_NAMESPACE ' +
|
|
22
|
+
'environment variable.',
|
|
23
|
+
type: 'string',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
getPlan(ctx) {
|
|
27
|
+
return ctx.app.getService('dockerPublish').getPlan(optionsOf(ctx));
|
|
28
|
+
},
|
|
29
|
+
applyPlan(ctx, plan) {
|
|
30
|
+
/** The entries are this target's own - `getPlan` produced them - so the cast reads back what
|
|
31
|
+
* was put in rather than claiming anything new. The command only ever passes a plan back to
|
|
32
|
+
* the target that made it. */
|
|
33
|
+
return ctx.app.getService('dockerPublish').applyPlan(plan);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
/** The shared filters plus the one flag this target declared - read here rather than in the service,
|
|
37
|
+
* so the service keeps taking a plain options object and stays callable without a CLI. */
|
|
38
|
+
function optionsOf(ctx) {
|
|
39
|
+
return { ...ctx.options, namespace: ctx.args.dockerNamespace };
|
|
40
|
+
}
|
package/utils/bin-path.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RmanApplication } from '../core/application.js';
|
|
1
2
|
/**
|
|
2
3
|
* Where a repository's **locally installed executables** live, so a command an author wrote
|
|
3
4
|
* (`eslint .`, `cargo build`) is found the way it would be in their own shell.
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
*
|
|
7
8
|
* - **Which directories** is the *ecosystem's* answer, and the core has none - `node_modules/.bin`
|
|
8
9
|
* walked up the directory chain is npm's layout and nothing else's (a Python venv says
|
|
9
|
-
* `.venv/bin`, a Ruby project `bin`). `rman-node` contributes it; see `
|
|
10
|
+
* `.venv/bin`, a Ruby project `bin`). `rman-node` contributes it; see `Plugin.getBinPaths`.
|
|
10
11
|
* - **How a PATH is spelled** is the *operating system's*, and that stays here: the variable is
|
|
11
12
|
* `PATH` everywhere except Windows, where its case is whatever the environment happens to use.
|
|
12
13
|
* That has nothing to do with any ecosystem, and every provider would otherwise get it wrong
|
|
@@ -22,20 +23,18 @@ export declare namespace BinPath {
|
|
|
22
23
|
* Return them most-specific-first; the core concatenates providers without reordering. */
|
|
23
24
|
type Provider = (cwd: string) => string[];
|
|
24
25
|
interface EnvOptions {
|
|
26
|
+
/** The application whose technologies contribute directories. Omitted where a caller genuinely
|
|
27
|
+
* has none - `exec` outside a repository - which leaves the inherited PATH untouched. */
|
|
28
|
+
readonly app?: RmanApplication;
|
|
25
29
|
/** The directory the command will run in. Default `process.cwd()`. */
|
|
26
30
|
readonly cwd?: string;
|
|
27
31
|
/** The environment to derive from, like `process.env`. Default `process.env`. */
|
|
28
32
|
readonly env?: ProcessEnv;
|
|
29
33
|
}
|
|
30
|
-
/** Registers a provider. Called by `loadPlugins` for each plugin's `binPaths`, in `plugins`
|
|
31
|
-
* declaration order - so what is on PATH is a function of the repository's own config. */
|
|
32
|
-
function addProvider(provider: Provider): void;
|
|
33
|
-
/** For tests, which would otherwise leak a provider into every later case in the process. */
|
|
34
|
-
function clearProviders(): void;
|
|
35
34
|
/** Every provider's directories for `cwd`, concatenated in declaration order. Empty for a
|
|
36
35
|
* repository that names no plugin - the inherited PATH then stands on its own, which is the
|
|
37
36
|
* honest answer rather than a guess at some ecosystem's layout. */
|
|
38
|
-
function resolve(cwd: string): string[];
|
|
37
|
+
function resolve(app: RmanApplication, cwd: string): string[];
|
|
39
38
|
/**
|
|
40
39
|
* `env` with the contributed directories prepended to its PATH - what `exec` and `runBin` hand to
|
|
41
40
|
* a child process.
|
package/utils/bin-path.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import process from 'node:process';
|
|
3
|
+
import { RmanApplication } from '../core/application.js';
|
|
3
4
|
/**
|
|
4
5
|
* Where a repository's **locally installed executables** live, so a command an author wrote
|
|
5
6
|
* (`eslint .`, `cargo build`) is found the way it would be in their own shell.
|
|
@@ -8,7 +9,7 @@ import process from 'node:process';
|
|
|
8
9
|
*
|
|
9
10
|
* - **Which directories** is the *ecosystem's* answer, and the core has none - `node_modules/.bin`
|
|
10
11
|
* walked up the directory chain is npm's layout and nothing else's (a Python venv says
|
|
11
|
-
* `.venv/bin`, a Ruby project `bin`). `rman-node` contributes it; see `
|
|
12
|
+
* `.venv/bin`, a Ruby project `bin`). `rman-node` contributes it; see `Plugin.getBinPaths`.
|
|
12
13
|
* - **How a PATH is spelled** is the *operating system's*, and that stays here: the variable is
|
|
13
14
|
* `PATH` everywhere except Windows, where its case is whatever the environment happens to use.
|
|
14
15
|
* That has nothing to do with any ecosystem, and every provider would otherwise get it wrong
|
|
@@ -20,25 +21,12 @@ import process from 'node:process';
|
|
|
20
21
|
*/
|
|
21
22
|
export var BinPath;
|
|
22
23
|
(function (BinPath) {
|
|
23
|
-
/** Registers a provider. Called by `loadPlugins` for each plugin's `binPaths`, in `plugins`
|
|
24
|
-
* declaration order - so what is on PATH is a function of the repository's own config. */
|
|
25
|
-
function addProvider(provider) {
|
|
26
|
-
if (providers.includes(provider))
|
|
27
|
-
return;
|
|
28
|
-
providers.push(provider);
|
|
29
|
-
}
|
|
30
|
-
BinPath.addProvider = addProvider;
|
|
31
|
-
/** For tests, which would otherwise leak a provider into every later case in the process. */
|
|
32
|
-
function clearProviders() {
|
|
33
|
-
providers.length = 0;
|
|
34
|
-
}
|
|
35
|
-
BinPath.clearProviders = clearProviders;
|
|
36
24
|
/** Every provider's directories for `cwd`, concatenated in declaration order. Empty for a
|
|
37
25
|
* repository that names no plugin - the inherited PATH then stands on its own, which is the
|
|
38
26
|
* honest answer rather than a guess at some ecosystem's layout. */
|
|
39
|
-
function resolve(cwd) {
|
|
27
|
+
function resolve(app, cwd) {
|
|
40
28
|
const dir = path.resolve(cwd);
|
|
41
|
-
return
|
|
29
|
+
return [...app.platforms].flatMap(stack => stack.getBinPaths?.(dir) ?? []);
|
|
42
30
|
}
|
|
43
31
|
BinPath.resolve = resolve;
|
|
44
32
|
/**
|
|
@@ -52,7 +40,9 @@ export var BinPath;
|
|
|
52
40
|
const cwd = options.cwd || process.cwd();
|
|
53
41
|
const result = { ...(options.env || process.env) };
|
|
54
42
|
const key = pathKey({ env: result });
|
|
55
|
-
|
|
43
|
+
/** No application means no technologies, so nothing is prepended and the inherited PATH stands
|
|
44
|
+
* on its own - the same answer a repository naming no plugin has always got. */
|
|
45
|
+
const entries = options.app ? resolve(options.app, cwd) : [];
|
|
56
46
|
if (!entries.length)
|
|
57
47
|
return result;
|
|
58
48
|
const inherited = result[key];
|
|
@@ -78,5 +68,4 @@ export var BinPath;
|
|
|
78
68
|
.find(key => key.toUpperCase() === 'PATH') || 'Path');
|
|
79
69
|
}
|
|
80
70
|
BinPath.pathKey = pathKey;
|
|
81
|
-
const providers = [];
|
|
82
71
|
})(BinPath || (BinPath = {}));
|
package/utils/branch-guard.d.ts
CHANGED
|
@@ -6,6 +6,35 @@ export interface BranchGuardOptions {
|
|
|
6
6
|
allowBranch?: string | string[];
|
|
7
7
|
ignoreBranch?: string | string[];
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* `--allow-branch`/`--ignore-branch` as a declaration, to spread into a command's `config` block.
|
|
11
|
+
* See `packageFilterOptions` for why this is `satisfies` rather than an annotation.
|
|
12
|
+
*
|
|
13
|
+
* **The key is the camelCase one and `cliName` carries the flag's spelling.** `allowBranch` is what
|
|
14
|
+
* a `.rmanrc` writes and what `assertAllowedBranch` reads off the config; `--allow-branch` is what
|
|
15
|
+
* the shell sees. The `Argv` version relied on yargs converting one into the other silently, which
|
|
16
|
+
* is why `applyBranchGuardOptions` declares `'allow-branch'` and `readBranchGuardOptions` reads
|
|
17
|
+
* `args.allowBranch`.
|
|
18
|
+
*
|
|
19
|
+
* **`target: 'cli'`, even though `allowBranch` *is* a config key.** It is a repo-wide core key read
|
|
20
|
+
* off the root, not any command's - so it belongs on `RmanConfig` directly, and a command that
|
|
21
|
+
* reads it names it in `configKeys`. Declaring it `'both'` here would contribute `version.allowBranch`,
|
|
22
|
+
* which nothing reads.
|
|
23
|
+
*/
|
|
24
|
+
export declare const branchGuardOptions: {
|
|
25
|
+
allowBranch: {
|
|
26
|
+
target: "cli";
|
|
27
|
+
cliName: string;
|
|
28
|
+
describe: string;
|
|
29
|
+
type: "string";
|
|
30
|
+
};
|
|
31
|
+
ignoreBranch: {
|
|
32
|
+
target: "cli";
|
|
33
|
+
cliName: string;
|
|
34
|
+
describe: string;
|
|
35
|
+
type: "string";
|
|
36
|
+
};
|
|
37
|
+
};
|
|
9
38
|
/** `--allow-branch`/`--ignore-branch`, the same shape and describe text in every command that
|
|
10
39
|
* supports them - mirrors `package-filter.ts`'s own `applyPackageFilterOptions`. */
|
|
11
40
|
export declare function applyBranchGuardOptions<T>(cmd: Argv<T>): Argv<T>;
|
package/utils/branch-guard.js
CHANGED
|
@@ -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) {
|
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
package/utils/logger.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ 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
|
*/
|