rman 1.1.1 → 1.2.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/cli.js +3 -2
- package/commands/config.command.js +3 -2
- package/constants.js +1 -1
- package/core/config.d.ts +125 -6
- package/core/config.js +451 -50
- package/core/merge-config.d.ts +24 -0
- package/core/merge-config.js +43 -0
- package/core/repository.d.ts +15 -1
- package/core/repository.js +40 -8
- package/core/run-step.d.ts +75 -0
- package/core/run-step.js +1 -0
- package/index.d.ts +1 -0
- package/interfaces/rman-config.interface.d.ts +58 -17
- package/package.json +2 -1
- package/services/run.service.d.ts +66 -20
- package/services/run.service.js +171 -39
- package/services/version.service.d.ts +13 -3
- package/services/version.service.js +17 -11
- package/utils/printable-config.d.ts +15 -0
- package/utils/printable-config.js +42 -0
package/core/repository.d.ts
CHANGED
|
@@ -24,6 +24,18 @@ export declare class Repository extends Package {
|
|
|
24
24
|
* An internal cache has no business being walked anyway.
|
|
25
25
|
*/
|
|
26
26
|
private _repoScope?;
|
|
27
|
+
/** Cached `${{ git.* }}` facts - see `_gitScope`. Non-enumerable for the same reason as above,
|
|
28
|
+
* and because reading it is a subprocess: a deep walk of a package must not spawn one. */
|
|
29
|
+
private _git?;
|
|
30
|
+
/**
|
|
31
|
+
* Files `${{ read(...) }}` has parsed, shared by every package's scope and keyed by the identity
|
|
32
|
+
* of the bytes - see `readStructuredFile`.
|
|
33
|
+
*
|
|
34
|
+
* **On the repository rather than per scope, and that is the whole point of it**: `configScope`
|
|
35
|
+
* is built once per package, so a cache living there would re-read a repository-level file once
|
|
36
|
+
* for every package that mentions it.
|
|
37
|
+
*/
|
|
38
|
+
private readonly _readCache;
|
|
27
39
|
protected constructor(dirname: string, monorepo: boolean, packages: Package[],
|
|
28
40
|
/** The directory `Repository.create()` was actually invoked from - unlike `dirname` (the
|
|
29
41
|
* resolved repository root, possibly several levels up), this is where the user's shell
|
|
@@ -64,6 +76,8 @@ export declare class Repository extends Package {
|
|
|
64
76
|
configScope(pkg: Package, options?: {
|
|
65
77
|
targetVersion?: string;
|
|
66
78
|
}): ConfigScope;
|
|
79
|
+
/** `${{ git.* }}`, read at most once per repository per process. */
|
|
80
|
+
protected _gitScope(): GitScope;
|
|
67
81
|
/**
|
|
68
82
|
* Resolves the effective rman config for the repository root and every package, cascading
|
|
69
83
|
* root -> intermediate directories -> package directory, so a `.rmanrc` placed anywhere along
|
|
@@ -104,7 +118,7 @@ export declare class Repository extends Package {
|
|
|
104
118
|
*/
|
|
105
119
|
protected _resolveDeclaredPackage(entry: string): Package | undefined;
|
|
106
120
|
protected _updateDependencies(): void;
|
|
107
|
-
/** `git` facts for a `${{
|
|
121
|
+
/** `git` facts for a `${{ git.* }}` expression. Synchronous on purpose: it backs a lazy
|
|
108
122
|
* getter, and a getter cannot await. Everything is `undefined` outside a git checkout - not an
|
|
109
123
|
* error, just a repository without one. */
|
|
110
124
|
protected _readGitScope(dirname: string): GitScope;
|
package/core/repository.js
CHANGED
|
@@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import semver from 'semver';
|
|
4
4
|
import { GitHelper } from '../utils/git.js';
|
|
5
|
-
import { createFileScope, DEFERRED_PATHS, interpolateConfig, readDirConfig, resolveConfig, } from './config.js';
|
|
5
|
+
import { createFileScope, createReadScope, DEFERRED_PATHS, interpolateConfig, readDirConfig, resolveConfig, } from './config.js';
|
|
6
6
|
import { Manifest } from './manifest.js';
|
|
7
7
|
import { Package } from './package.js';
|
|
8
8
|
import { loadPlugins } from './plugin.js';
|
|
@@ -27,6 +27,18 @@ export class Repository extends Package {
|
|
|
27
27
|
* An internal cache has no business being walked anyway.
|
|
28
28
|
*/
|
|
29
29
|
_repoScope;
|
|
30
|
+
/** Cached `${{ git.* }}` facts - see `_gitScope`. Non-enumerable for the same reason as above,
|
|
31
|
+
* and because reading it is a subprocess: a deep walk of a package must not spawn one. */
|
|
32
|
+
_git;
|
|
33
|
+
/**
|
|
34
|
+
* Files `${{ read(...) }}` has parsed, shared by every package's scope and keyed by the identity
|
|
35
|
+
* of the bytes - see `readStructuredFile`.
|
|
36
|
+
*
|
|
37
|
+
* **On the repository rather than per scope, and that is the whole point of it**: `configScope`
|
|
38
|
+
* is built once per package, so a cache living there would re-read a repository-level file once
|
|
39
|
+
* for every package that mentions it.
|
|
40
|
+
*/
|
|
41
|
+
_readCache = new Map();
|
|
30
42
|
constructor(dirname, monorepo, packages,
|
|
31
43
|
/** The directory `Repository.create()` was actually invoked from - unlike `dirname` (the
|
|
32
44
|
* resolved repository root, possibly several levels up), this is where the user's shell
|
|
@@ -113,17 +125,43 @@ export class Repository extends Package {
|
|
|
113
125
|
* seeing a value that means nothing to them.
|
|
114
126
|
*/
|
|
115
127
|
configScope(pkg, options) {
|
|
128
|
+
const _this = this;
|
|
116
129
|
return {
|
|
117
130
|
pkg: this._packageScope(pkg, options?.targetVersion),
|
|
118
131
|
repository: this._repositoryScope(),
|
|
119
132
|
/** `pkg.dirname`, not the repository root: a `"[*]"` block asking whether
|
|
120
133
|
* `tsconfig-build.json` exists has to be answered per package. */
|
|
121
134
|
file: createFileScope(pkg.dirname),
|
|
135
|
+
/** Same base directory as `file`, so one `"[*]"` declaration reads each package's own copy -
|
|
136
|
+
* and the cache is the repository's, so a file they *share* is parsed once. */
|
|
137
|
+
read: createReadScope(pkg.dirname, this._readCache),
|
|
122
138
|
env: { ...process.env },
|
|
123
139
|
semver,
|
|
124
140
|
path,
|
|
141
|
+
/**
|
|
142
|
+
* A getter, and cached on the **repository** rather than in this closure: `configScope` is
|
|
143
|
+
* called once per package, so a per-scope cache would still shell out once per package in a
|
|
144
|
+
* monorepo that mentions git at all.
|
|
145
|
+
*
|
|
146
|
+
* Enumerable, unlike `pkg.targetVersion` - it has a real answer everywhere, so nothing needs
|
|
147
|
+
* hiding. What keeps it lazy is `interpolateConfig` building its context from property
|
|
148
|
+
* descriptors instead of spreading; see the note there.
|
|
149
|
+
*/
|
|
150
|
+
get git() {
|
|
151
|
+
return _this._gitScope();
|
|
152
|
+
},
|
|
125
153
|
};
|
|
126
154
|
}
|
|
155
|
+
/** `${{ git.* }}`, read at most once per repository per process. */
|
|
156
|
+
_gitScope() {
|
|
157
|
+
if (this._git)
|
|
158
|
+
return this._git;
|
|
159
|
+
const built = this._readGitScope(this.dirname);
|
|
160
|
+
/** Defined rather than assigned, so the cache stays out of every enumeration of the repository
|
|
161
|
+
* - the same reason `_repoScope` is defined this way. */
|
|
162
|
+
Object.defineProperty(this, '_git', { value: built, enumerable: false, writable: true });
|
|
163
|
+
return built;
|
|
164
|
+
}
|
|
127
165
|
/**
|
|
128
166
|
* Resolves the effective rman config for the repository root and every package, cascading
|
|
129
167
|
* root -> intermediate directories -> package directory, so a `.rmanrc` placed anywhere along
|
|
@@ -239,17 +277,11 @@ export class Repository extends Package {
|
|
|
239
277
|
if (this._repoScope)
|
|
240
278
|
return this._repoScope;
|
|
241
279
|
const packageScopes = this.packages.map(p => this._packageScope(p));
|
|
242
|
-
const repoDir = this.dirname;
|
|
243
|
-
let gitScope;
|
|
244
|
-
const _this = this;
|
|
245
280
|
const built = {
|
|
246
281
|
...this._packageScope(this.rootPackage),
|
|
247
282
|
monorepo: this.monorepo,
|
|
248
283
|
packages: packageScopes,
|
|
249
284
|
package: (name) => packageScopes.find(p => p.name === name),
|
|
250
|
-
get git() {
|
|
251
|
-
return (gitScope ??= _this._readGitScope(repoDir));
|
|
252
|
-
},
|
|
253
285
|
};
|
|
254
286
|
/** Defined rather than assigned, so the cache stays out of every enumeration of this object -
|
|
255
287
|
* see the field's own doc for what walked into it. */
|
|
@@ -315,7 +347,7 @@ export class Repository extends Package {
|
|
|
315
347
|
deepFindDependencies(pkg, pkg.dependencies);
|
|
316
348
|
}
|
|
317
349
|
}
|
|
318
|
-
/** `git` facts for a `${{
|
|
350
|
+
/** `git` facts for a `${{ git.* }}` expression. Synchronous on purpose: it backs a lazy
|
|
319
351
|
* getter, and a getter cannot await. Everything is `undefined` outside a git checkout - not an
|
|
320
352
|
* error, just a repository without one. */
|
|
321
353
|
_readGitScope(dirname) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Logger } from '../utils/logger.js';
|
|
2
|
+
import type { RunBinOptions, RunBinResult } from '../utils/run-bin.js';
|
|
3
|
+
import type { Package } from './package.js';
|
|
4
|
+
import type { Repository } from './repository.js';
|
|
5
|
+
/**
|
|
6
|
+
* What a **function step** is handed - a `run.<script>.before`/`.exec`/`.after` or a
|
|
7
|
+
* `version.<slot>` written as JavaScript instead of a shell command, and the `if` deciding whether
|
|
8
|
+
* a script runs at all.
|
|
9
|
+
*
|
|
10
|
+
* An object rather than loose parameters, for the reason `CommandContext` is one: a member added
|
|
11
|
+
* later must not break every step already written against it.
|
|
12
|
+
*
|
|
13
|
+
* **The whole point of the function form is *when* it runs.** A `${{ }}` expression is evaluated
|
|
14
|
+
* while the repository's config resolves - which every command does, `rman list` included - so it
|
|
15
|
+
* can only ever answer questions about the state the config was loaded in, and anything it *did*
|
|
16
|
+
* would happen on every invocation. A function step runs when its turn comes, with the real
|
|
17
|
+
* objects, in the package's own directory. Reach for it exactly when that difference matters;
|
|
18
|
+
* a shell command is still the right shape for a shell command.
|
|
19
|
+
*/
|
|
20
|
+
export interface RunStepContext {
|
|
21
|
+
/**
|
|
22
|
+
* The package this step is running for - `pkg`, matching `${{ pkg }}` in an expression rather
|
|
23
|
+
* than `CommandContext.package`. `package` is a reserved word, so that spelling forces every
|
|
24
|
+
* author to rename it while destructuring (`{ package: current }`), which is friction paid at
|
|
25
|
+
* every call site for no benefit.
|
|
26
|
+
*
|
|
27
|
+
* Always set, unlike `CommandContext.package`: a step belongs to a package by construction, and
|
|
28
|
+
* the repo-wide bookend belongs to the root package.
|
|
29
|
+
*/
|
|
30
|
+
pkg: Package;
|
|
31
|
+
repository: Repository;
|
|
32
|
+
/**
|
|
33
|
+
* The directory this step is *about* - the package's own, or the repository root for a monorepo's
|
|
34
|
+
* bookend. The same directory a shell step in this slot is spawned in.
|
|
35
|
+
*
|
|
36
|
+
* **`process.cwd()` is NOT changed, and cannot be.** A shell step gets a real working directory
|
|
37
|
+
* because it is a child process; a function step runs inside rman's own, and `run` executes
|
|
38
|
+
* packages **concurrently** - one step calling `process.chdir()` would move the ground under
|
|
39
|
+
* every other step running at that moment. So a relative path resolves against wherever rman was
|
|
40
|
+
* invoked, which is almost never what the step meant:
|
|
41
|
+
*
|
|
42
|
+
* ```js
|
|
43
|
+
* fs.writeFileSync('out.txt', data) // the repository root. Measured, and wrong.
|
|
44
|
+
* fs.writeFileSync(path.join(ctx.cwd, 'out.txt'), data) // the package
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* `ctx.runBin` is already bound to this directory, so a binary run through it needs no such care.
|
|
48
|
+
*/
|
|
49
|
+
cwd: string;
|
|
50
|
+
/**
|
|
51
|
+
* The repository's locally installed binaries, already carrying this run's `cwd` and log level -
|
|
52
|
+
* handed over rather than imported, for the reason `CommandContext.runBin` is.
|
|
53
|
+
*/
|
|
54
|
+
runBin: (bin: string, argv: string[], options?: RunBinOptions) => Promise<RunBinResult>;
|
|
55
|
+
/** Logger at this run's resolved level. **Prefer it to `console`**: with the live progress panel
|
|
56
|
+
* on, a direct write lands beside the panel rather than in the step's own log. */
|
|
57
|
+
logger: Logger;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A step written as JavaScript. **Failure is a throw** - the return value means nothing, exactly as
|
|
61
|
+
* a non-zero exit is what fails a shell step and what `runBin` rejects on. A step that can report
|
|
62
|
+
* trouble only by returning something nobody reads is a step that passes while doing nothing.
|
|
63
|
+
*/
|
|
64
|
+
export type RunStepFn = (context: RunStepContext) => void | Promise<void>;
|
|
65
|
+
/** One entry of a `before`/`exec`/`after` slot: a shell command, or a function. A list of them runs
|
|
66
|
+
* in sequence, and the two forms mix freely within one list. */
|
|
67
|
+
export type RunStepValue = string | RunStepFn;
|
|
68
|
+
/**
|
|
69
|
+
* A `run.<script>.if` written as JavaScript, deciding whether the script runs for this package.
|
|
70
|
+
*
|
|
71
|
+
* The string form is a small closed grammar (`changed and not private`) which cannot express an
|
|
72
|
+
* arbitrary condition, and a `${{ }}` one is frozen at config-load time. This is evaluated per
|
|
73
|
+
* package, when the run reaches it - the same context a step gets.
|
|
74
|
+
*/
|
|
75
|
+
export type RunConditionFn = (context: RunStepContext) => boolean | Promise<boolean>;
|
package/core/run-step.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { Package } from './core/package.js';
|
|
|
25
25
|
export type { RmanPlugin } from './core/plugin.js';
|
|
26
26
|
export { definePlugin } from './core/plugin.js';
|
|
27
27
|
export { Repository } from './core/repository.js';
|
|
28
|
+
export type { RunConditionFn, RunStepContext, RunStepFn, RunStepValue } from './core/run-step.js';
|
|
28
29
|
export type { ChangeKind } from './core/version-scheme.js';
|
|
29
30
|
/** The numbering seam. `VersionScheme` is abstract - `highestVersion`/`highestBump`/`smallestBump`
|
|
30
31
|
* are implemented from the members around them, so a scheme states only what it must and still
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RmanPlugin } from '../core/plugin.js';
|
|
2
|
+
import type { RunConditionFn, RunStepValue } from '../core/run-step.js';
|
|
2
3
|
/**
|
|
3
4
|
* Adds a `+key` alongside every key of `T`, which **appends** to whatever that key already resolved
|
|
4
5
|
* to instead of replacing it - see `mergeConfig`.
|
|
@@ -9,6 +10,23 @@ import type { RmanPlugin } from '../core/plugin.js';
|
|
|
9
10
|
export type WithAppend<T> = {
|
|
10
11
|
[K in keyof T as `+${K & string}`]?: T[K];
|
|
11
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* The one key every nested config node may carry: `vars` scoping that node's subtree - a fresh copy
|
|
15
|
+
* per level, merged per key over the level above. See `withScopedVars` in `core/config.ts` for what
|
|
16
|
+
* it does at resolution time, and docs/rman.md#scoped-vars for how it reads.
|
|
17
|
+
*
|
|
18
|
+
* **Every nested options interface extends this**, and a new one has to remember to - which is the
|
|
19
|
+
* cost of the runtime rule being general (any object node scopes) while a type can only say it one
|
|
20
|
+
* interface at a time. TypeScript has no way to state "and every object below this may also carry
|
|
21
|
+
* `vars`" without a recursive remap that would wreck the error messages.
|
|
22
|
+
*
|
|
23
|
+
* Extended by the `XOptions` interface rather than declared on `XOptionsKeys`, so `WithAppend` does
|
|
24
|
+
* not generate a `+vars`: appending to `vars` means nothing, since objects merge either way.
|
|
25
|
+
*/
|
|
26
|
+
export interface ScopedVars {
|
|
27
|
+
/** Values for `${{ vars.* }}` to read, for this node and everything under it. */
|
|
28
|
+
vars?: Record<string, unknown>;
|
|
29
|
+
}
|
|
12
30
|
/**
|
|
13
31
|
* The shape of `.rmanrc`/`.rmanrc.yml`/`.rmanrc.cjs`/`.mjs`/`.js` (and `package.json`'s own
|
|
14
32
|
* `"rman"` key) - see docs/rman.md#configuration-rmanrc-rmanrcyml for the full reference. Every
|
|
@@ -120,8 +138,8 @@ export interface RmanConfigKeys {
|
|
|
120
138
|
githubRelease?: RmanConfig.GithubReleaseOptions;
|
|
121
139
|
/** Keyed by npm script name (e.g. `"build"`, `"lint"`, `"test"`). A bare string (or array of
|
|
122
140
|
* them) is shorthand for `{ exec: ... }` - `test: "mocha"` and `test: { exec: "mocha" }` mean
|
|
123
|
-
* exactly the same thing. */
|
|
124
|
-
run?:
|
|
141
|
+
* exactly the same thing, and a bare function is the same shorthand for a function step. */
|
|
142
|
+
run?: RmanConfig.RunConfig;
|
|
125
143
|
/**
|
|
126
144
|
* In-repo packages this one depends on **beyond what its own manifest declares** - purely for
|
|
127
145
|
* rman's own dependency graph (topo-sort, `--deps`/`--dependents`, `run`'s scheduling, the version
|
|
@@ -190,7 +208,7 @@ export declare namespace RmanConfig {
|
|
|
190
208
|
file: string;
|
|
191
209
|
constant?: string;
|
|
192
210
|
};
|
|
193
|
-
interface VersionOptions extends VersionOptionsKeys, WithAppend<VersionOptionsKeys
|
|
211
|
+
interface VersionOptions extends VersionOptionsKeys, WithAppend<VersionOptionsKeys>, ScopedVars {
|
|
194
212
|
}
|
|
195
213
|
interface VersionOptionsKeys {
|
|
196
214
|
commitMessage?: string;
|
|
@@ -225,14 +243,17 @@ export declare namespace RmanConfig {
|
|
|
225
243
|
* that slot of its own (`version` in a Node repository's `package.json#scripts`, whatever a
|
|
226
244
|
* plugin's step source answers elsewhere - the package's own declaration wins, as in `run`).
|
|
227
245
|
* An array runs them in sequence. `${{ pkg.targetVersion }}` is bound here and in the two
|
|
228
|
-
* below, and nowhere else.
|
|
229
|
-
|
|
246
|
+
* below, and nowhere else.
|
|
247
|
+
*
|
|
248
|
+
* A `RunStepFn` runs in place of a shell command - but note that `${{ pkg.targetVersion }}` is
|
|
249
|
+
* a *string* substitution, so a function reads the written version off `pkg` instead. */
|
|
250
|
+
exec?: RunStepValue | RunStepValue[];
|
|
230
251
|
/** Same, before the write (`preversion` in a Node repository). */
|
|
231
|
-
before?:
|
|
252
|
+
before?: RunStepValue | RunStepValue[];
|
|
232
253
|
/** Same, after it (`postversion` in a Node repository). */
|
|
233
|
-
after?:
|
|
254
|
+
after?: RunStepValue | RunStepValue[];
|
|
234
255
|
}
|
|
235
|
-
interface ChangelogOptions extends ChangelogOptionsKeys, WithAppend<ChangelogOptionsKeys
|
|
256
|
+
interface ChangelogOptions extends ChangelogOptionsKeys, WithAppend<ChangelogOptionsKeys>, ScopedVars {
|
|
236
257
|
}
|
|
237
258
|
interface ChangelogOptionsKeys {
|
|
238
259
|
ignoreTypes?: string[];
|
|
@@ -240,7 +261,23 @@ export declare namespace RmanConfig {
|
|
|
240
261
|
filePath?: string;
|
|
241
262
|
tagPattern?: string;
|
|
242
263
|
}
|
|
243
|
-
|
|
264
|
+
/**
|
|
265
|
+
* The `run` block: scripts by name.
|
|
266
|
+
*
|
|
267
|
+
* **`run.vars` works at runtime but is deliberately not in this type**, and the reason is a
|
|
268
|
+
* measured trade rather than an oversight. `run` is keyed by script name, so any encoding that
|
|
269
|
+
* lets `vars` through has to widen the index signature's value type to something object-shaped -
|
|
270
|
+
* and TypeScript then stops excess-property-checking *every* script's options. Measured on the
|
|
271
|
+
* same file: with the widened index, `run: { build: { exce: 'tsc' } }` compiles clean.
|
|
272
|
+
*
|
|
273
|
+
* Catching that typo across every script is worth more than typing one key, so a typed JS config
|
|
274
|
+
* writing `run.vars` needs a cast (`run: { vars: { x: 2 }, build: ... } as RmanConfig['run']`).
|
|
275
|
+
* YAML and JSON configs are unchecked anyway and simply work. A key-remapped index signature
|
|
276
|
+
* (`{ [K in string as K extends 'vars' ? never : K]: ... }`) was tried and does not help - the
|
|
277
|
+
* remap still produces an index signature that claims `vars`.
|
|
278
|
+
*/
|
|
279
|
+
type RunConfig = Record<string, RunStepValue | RunStepValue[] | RunScriptOptions>;
|
|
280
|
+
interface RunScriptOptions extends RunScriptOptionsKeys, WithAppend<RunScriptOptionsKeys>, ScopedVars {
|
|
244
281
|
}
|
|
245
282
|
interface RunScriptOptionsKeys {
|
|
246
283
|
concurrency?: number;
|
|
@@ -250,17 +287,21 @@ export declare namespace RmanConfig {
|
|
|
250
287
|
logLevel?: 'silent' | 'error' | 'info' | 'verbose';
|
|
251
288
|
changedSince?: string;
|
|
252
289
|
skip?: boolean;
|
|
253
|
-
|
|
290
|
+
/** Whether this script runs for a package at all - the small `changed and not private` grammar,
|
|
291
|
+
* or a `RunConditionFn` for a condition it cannot express. Both are evaluated per package when
|
|
292
|
+
* the run reaches it; a `${{ }}` expression here is not, having been resolved when the config
|
|
293
|
+
* loaded. */
|
|
294
|
+
if?: string | RunConditionFn;
|
|
254
295
|
/** Command(s) to run as this script itself, when the package's `package.json` doesn't define
|
|
255
|
-
* it. An array runs them in sequence. */
|
|
256
|
-
exec?:
|
|
296
|
+
* it. An array runs them in sequence, and may mix shell commands with functions. */
|
|
297
|
+
exec?: RunStepValue | RunStepValue[];
|
|
257
298
|
/** Same, for this script's `pre<script>` hook. */
|
|
258
|
-
before?:
|
|
299
|
+
before?: RunStepValue | RunStepValue[];
|
|
259
300
|
/** Same, for its `post<script>` hook. */
|
|
260
|
-
after?:
|
|
301
|
+
after?: RunStepValue | RunStepValue[];
|
|
261
302
|
override?: boolean;
|
|
262
303
|
}
|
|
263
|
-
interface PublishOptions extends PublishOptionsKeys, WithAppend<PublishOptionsKeys
|
|
304
|
+
interface PublishOptions extends PublishOptionsKeys, WithAppend<PublishOptionsKeys>, ScopedVars {
|
|
264
305
|
}
|
|
265
306
|
interface PublishOptionsKeys {
|
|
266
307
|
/** Which **registry** `publish` ships this package to - default `['npm']` (every existing repo
|
|
@@ -286,7 +327,7 @@ export declare namespace RmanConfig {
|
|
|
286
327
|
type PublishTarget = 'npm' | 'docker';
|
|
287
328
|
/** Required once `"docker"` is one of this package's `publish.target`s - `publish --target
|
|
288
329
|
* docker` errors clearly on a package that opts in here but leaves this out. */
|
|
289
|
-
interface DockerPublishOptions extends DockerPublishOptionsKeys, WithAppend<DockerPublishOptionsKeys
|
|
330
|
+
interface DockerPublishOptions extends DockerPublishOptionsKeys, WithAppend<DockerPublishOptionsKeys>, ScopedVars {
|
|
290
331
|
}
|
|
291
332
|
interface DockerPublishOptionsKeys {
|
|
292
333
|
/** DockerHub image name/repository - bare (e.g. `"my-app"`) to be prefixed with
|
|
@@ -314,7 +355,7 @@ export declare namespace RmanConfig {
|
|
|
314
355
|
* (which tag, which repository, what the notes say) already has a sensible source. Nothing here
|
|
315
356
|
* decides *whether* a release is cut: a release records that the repository shipped, so it is
|
|
316
357
|
* always cut, and these are only details about how. */
|
|
317
|
-
interface GithubReleaseOptions extends GithubReleaseOptionsKeys, WithAppend<GithubReleaseOptionsKeys
|
|
358
|
+
interface GithubReleaseOptions extends GithubReleaseOptionsKeys, WithAppend<GithubReleaseOptionsKeys>, ScopedVars {
|
|
318
359
|
}
|
|
319
360
|
interface GithubReleaseOptionsKeys {
|
|
320
361
|
/** Files to attach to the release, as glob patterns relative to the package's own directory
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rman",
|
|
3
3
|
"description": "Repository manager",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.2.2",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@xmldom/xmldom": "^0.9.12",
|
|
8
9
|
"ansi-colors": "^4.1.3",
|
|
9
10
|
"cross-dirname": "^0.1.0",
|
|
10
11
|
"easy-table": "^1.2.0",
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Package } from '../core/package.js';
|
|
2
2
|
import type { Repository } from '../core/repository.js';
|
|
3
|
+
import type { RunStepContext, RunStepFn, RunStepValue } from '../core/run-step.js';
|
|
3
4
|
import { type LogLevel } from '../utils/logger.js';
|
|
4
5
|
import { type PackageFilterOptions } from '../utils/package-filter.js';
|
|
5
6
|
export declare namespace RunService {
|
|
6
|
-
interface Options extends PackageFilterOptions {
|
|
7
|
+
export interface Options extends PackageFilterOptions {
|
|
7
8
|
/** Max packages built at once: `true`/omitted = CPU count, a number = that many, `false` = serial (1). */
|
|
8
9
|
parallel?: boolean | number;
|
|
9
10
|
/** Respect the package dependency graph: a package waits for its dependencies and is skipped
|
|
@@ -52,17 +53,36 @@ export declare namespace RunService {
|
|
|
52
53
|
* run:
|
|
53
54
|
* test: mocha # same as test: { exec: mocha }
|
|
54
55
|
*/
|
|
55
|
-
/**
|
|
56
|
-
|
|
56
|
+
/**
|
|
57
|
+
* One step of a script - a shell command, or a function ([`RunStepFn`](../core/run-step.ts)).
|
|
58
|
+
*
|
|
59
|
+
* A union rather than one shape with two optional fields, so every consumer is made to say which
|
|
60
|
+
* it is handling: the executor that forgets is the one that silently runs nothing, which is
|
|
61
|
+
* exactly the bug this type replaces (a function in `after` used to be dropped by
|
|
62
|
+
* `normalizeScriptValue` and reported as a step that succeeded).
|
|
63
|
+
*/
|
|
64
|
+
export type ScriptStep = CommandStep | FunctionStep;
|
|
65
|
+
interface StepBase {
|
|
66
|
+
/** The slot it came from - `before`/`exec`/`after`, which is what the log line shows. */
|
|
57
67
|
name: string;
|
|
68
|
+
/** What the progress panel and the per-step log print: the command itself, or the function's
|
|
69
|
+
* own name. */
|
|
70
|
+
label: string;
|
|
71
|
+
}
|
|
72
|
+
export interface CommandStep extends StepBase {
|
|
58
73
|
command: string;
|
|
74
|
+
run?: undefined;
|
|
59
75
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
76
|
+
export interface FunctionStep extends StepBase {
|
|
77
|
+
run: RunStepFn;
|
|
78
|
+
command?: undefined;
|
|
79
|
+
}
|
|
80
|
+
/** The three slots a script is made of, each one step or several run in sequence. The same three
|
|
81
|
+
* names a `.rmanrc "run.<script>"` block uses, because they are the same three things. */
|
|
82
|
+
export interface ScriptSlots {
|
|
83
|
+
before?: RunStepValue[];
|
|
84
|
+
exec?: RunStepValue[];
|
|
85
|
+
after?: RunStepValue[];
|
|
66
86
|
}
|
|
67
87
|
/**
|
|
68
88
|
* Where a package's steps can come from besides its `.rmanrc`.
|
|
@@ -75,15 +95,15 @@ export declare namespace RunService {
|
|
|
75
95
|
* Returns `undefined` for "this package declares nothing", not empty slots - the difference
|
|
76
96
|
* decides whether the config's value applies.
|
|
77
97
|
*/
|
|
78
|
-
type StepSource = (pkg: Package, script: string) => ScriptSlots | undefined;
|
|
98
|
+
export type StepSource = (pkg: Package, script: string) => ScriptSlots | undefined;
|
|
79
99
|
/**
|
|
80
100
|
* Registers a source. Called by `loadPlugins` for each plugin's `runSteps`, in `plugins`
|
|
81
101
|
* declaration order - never as an import side effect, so what is registered is exactly what the
|
|
82
102
|
* repository's `.rmanrc` asked for.
|
|
83
103
|
*/
|
|
84
|
-
function addStepSource(source: StepSource): void;
|
|
104
|
+
export function addStepSource(source: StepSource): void;
|
|
85
105
|
/** For tests, which would otherwise leak a source into every later case in the process. */
|
|
86
|
-
function clearStepSources(): void;
|
|
106
|
+
export function clearStepSources(): void;
|
|
87
107
|
/**
|
|
88
108
|
* What the *package itself* declares for the lifecycle `script`, from the contributed sources
|
|
89
109
|
* alone - no `.rmanrc` involved. `undefined` when it declares nothing.
|
|
@@ -95,7 +115,7 @@ export declare namespace RunService {
|
|
|
95
115
|
* and npm's version lifecycle keeps working with no second seam and no extra line in any plugin.
|
|
96
116
|
* A plugin for another ecosystem gets its own lifecycle hooks the moment it contributes steps.
|
|
97
117
|
*/
|
|
98
|
-
function contributedSlots(pkg: Package, script: string): ScriptSlots | undefined;
|
|
118
|
+
export function contributedSlots(pkg: Package, script: string): ScriptSlots | undefined;
|
|
99
119
|
/**
|
|
100
120
|
* Runs one slot of a lifecycle belonging to some operation other than `run` itself - `version`'s
|
|
101
121
|
* hooks around the version write are the only one so far.
|
|
@@ -106,13 +126,38 @@ export declare namespace RunService {
|
|
|
106
126
|
* the copies would sit in the file that writes versions - which now runs no command of its own at
|
|
107
127
|
* all.
|
|
108
128
|
*
|
|
109
|
-
* `fallback` is the caller's own configured
|
|
129
|
+
* `fallback` is the caller's own configured step(s), **already evaluated**: `version`'s three
|
|
110
130
|
* paths are in `DEFERRED_PATHS` precisely because only the caller can bind
|
|
111
131
|
* `${{ pkg.targetVersion }}`, so interpolating here would either be too early or need a scope this
|
|
112
132
|
* service has no business holding.
|
|
133
|
+
*
|
|
134
|
+
* **A list, not one joined string.** `VersionService` used to `join(' && ')` an array into a
|
|
135
|
+
* single shell line, which a function step cannot be part of - and which quietly changed the
|
|
136
|
+
* semantics of the shell case too, since `cd x && y` in one process is not the same as two.
|
|
137
|
+
*/
|
|
138
|
+
export function runLifecycleSlot(pkg: Package, script: string, slot: keyof ScriptSlots, fallback?: RunStepValue[]): Promise<void>;
|
|
139
|
+
export function getConfig(pkg: Package, script: string): Record<string, unknown>;
|
|
140
|
+
/**
|
|
141
|
+
* The context a function step or `if` is handed - see [`RunStepContext`](../core/run-step.ts).
|
|
142
|
+
*
|
|
143
|
+
* `runBin` and `logger` are bound to *this run* rather than left to be imported, which is the
|
|
144
|
+
* whole reason they are handed over: an imported `runBin` knows neither the cwd nor the resolved
|
|
145
|
+
* log level.
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* A `run.<script>.before`/`.exec`/`.after` (or `version.<slot>`) value: one step, or several to
|
|
149
|
+
* run in sequence. A shell command or a function, and a list may mix them.
|
|
150
|
+
*
|
|
151
|
+
* **Anything else throws, naming the path.** It used to `return []`, which meant a value rman did
|
|
152
|
+
* not recognize was dropped with no trace: writing a function here - the obvious guess, and now
|
|
153
|
+
* the supported form - produced `1 succeeded, 0 failed` with the step never run (measured). A
|
|
154
|
+
* configuration mistake has to be loud; silence here reads as success.
|
|
155
|
+
*
|
|
156
|
+
* Exported, and the only implementation: `VersionService` used to carry a second one that behaved
|
|
157
|
+
* differently, which is how `version.<slot>` came to join its array with `' && '`.
|
|
113
158
|
*/
|
|
114
|
-
function
|
|
115
|
-
function
|
|
159
|
+
export function normalizeScriptValue(value: unknown, at: string): RunStepValue[];
|
|
160
|
+
export function createStepContext(pkg: Package, cwd: string): RunStepContext;
|
|
116
161
|
/**
|
|
117
162
|
* `.rmanrc` conditional execution, GitHub Actions-`if`-flavored but a small closed grammar
|
|
118
163
|
* instead of a full expression language (less to get wrong, still covers what's asked for) -
|
|
@@ -126,7 +171,7 @@ export declare namespace RunService {
|
|
|
126
171
|
* if: changed = {CHANGE_HASH} # {NAME} -> process.env.NAME first
|
|
127
172
|
* if: (changed or dirty) and not committed
|
|
128
173
|
*/
|
|
129
|
-
type IfNode = {
|
|
174
|
+
export type IfNode = {
|
|
130
175
|
kind: 'atom';
|
|
131
176
|
name: string;
|
|
132
177
|
value?: string;
|
|
@@ -144,13 +189,14 @@ export declare namespace RunService {
|
|
|
144
189
|
};
|
|
145
190
|
/** Recursive-descent parser over `tokenizeIf`'s output: expr := or ; or := and ('or' and)* ;
|
|
146
191
|
* and := unary ('and' unary)* ; unary := 'not' unary | GROUP | NAME ['=' VALUE] */
|
|
147
|
-
function parseIfExpr(raw: unknown): IfNode | undefined;
|
|
192
|
+
export function parseIfExpr(raw: unknown): IfNode | undefined;
|
|
148
193
|
/** Evaluates a parsed `if` expression for one package. `statusCache` avoids repeat `git` calls
|
|
149
194
|
* for the same reference hash across packages/scripts in a single run. */
|
|
150
|
-
function evaluateIf(repository: Repository, pkg: Package, node: IfNode, statusCache: Map<string, Record<string, Repository.PackageStatus>>): Promise<boolean>;
|
|
151
|
-
function runScript(repository: Repository, script: string, options?: Options & {
|
|
195
|
+
export function evaluateIf(repository: Repository, pkg: Package, node: IfNode, statusCache: Map<string, Record<string, Repository.PackageStatus>>): Promise<boolean>;
|
|
196
|
+
export function runScript(repository: Repository, script: string, options?: Options & {
|
|
152
197
|
commandName?: string;
|
|
153
198
|
}): Promise<void>;
|
|
199
|
+
export {};
|
|
154
200
|
}
|
|
155
201
|
/** Resolution order: explicit CLI flag > the package's resolved `.rmanrc` > `fallback`. */
|
|
156
202
|
export declare function resolveBool(cliValue: boolean | undefined, pkg: Package, script: string, key: string, fallback: boolean): boolean;
|