rman 1.0.10 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +116 -85
  2. package/cli.js +225 -14
  3. package/commands/build.command.js +1 -0
  4. package/commands/changed.command.js +2 -2
  5. package/commands/changelog.command.js +13 -15
  6. package/commands/config.command.js +60 -0
  7. package/commands/diff.command.js +9 -4
  8. package/commands/exec.command.js +2 -8
  9. package/commands/github-release.command.js +1 -0
  10. package/commands/info.command.d.ts +9 -0
  11. package/commands/info.command.js +12 -2
  12. package/commands/run.command.js +5 -8
  13. package/commands/test.command.js +1 -0
  14. package/commands/version.command.js +53 -14
  15. package/constants.js +1 -1
  16. package/core/config.d.ts +257 -6
  17. package/core/config.js +409 -17
  18. package/core/custom-command.d.ts +133 -0
  19. package/core/custom-command.js +99 -0
  20. package/core/extends-config.d.ts +27 -0
  21. package/core/extends-config.js +89 -0
  22. package/core/manifest.d.ts +222 -0
  23. package/core/manifest.js +150 -0
  24. package/core/merge-config.d.ts +57 -0
  25. package/core/merge-config.js +146 -0
  26. package/core/package.d.ts +73 -7
  27. package/core/package.js +86 -24
  28. package/core/plugin.d.ts +112 -0
  29. package/core/plugin.js +189 -0
  30. package/core/repository.d.ts +85 -4
  31. package/core/repository.js +258 -81
  32. package/core/resolve-target.d.ts +12 -0
  33. package/core/resolve-target.js +33 -0
  34. package/core/version-scheme.d.ts +134 -0
  35. package/core/version-scheme.js +148 -0
  36. package/core/workspace.d.ts +68 -0
  37. package/core/workspace.js +83 -0
  38. package/index.d.ts +54 -8
  39. package/index.js +42 -7
  40. package/interfaces/rman-config.interface.d.ts +226 -28
  41. package/package.json +15 -7
  42. package/services/change-hash.service.d.ts +88 -0
  43. package/services/change-hash.service.js +112 -0
  44. package/services/changelog.service.d.ts +8 -13
  45. package/services/changelog.service.js +12 -11
  46. package/services/conventional-commits.service.d.ts +73 -0
  47. package/services/conventional-commits.service.js +116 -0
  48. package/services/docker-publish.service.js +1 -1
  49. package/services/exec.service.js +1 -1
  50. package/services/github-release.service.d.ts +2 -2
  51. package/services/github-release.service.js +10 -5
  52. package/services/list.service.js +5 -2
  53. package/services/run.service.d.ts +68 -3
  54. package/services/run.service.js +162 -70
  55. package/services/system-info.d.ts +22 -7
  56. package/services/system-info.js +8 -23
  57. package/services/version-plan.service.d.ts +244 -0
  58. package/services/version-plan.service.js +414 -0
  59. package/services/version.service.d.ts +92 -82
  60. package/services/version.service.js +233 -383
  61. package/services.d.ts +5 -3
  62. package/services.js +5 -3
  63. package/utils/bin-path.d.ts +59 -0
  64. package/utils/bin-path.js +82 -0
  65. package/utils/child-tracker.d.ts +16 -0
  66. package/utils/child-tracker.js +30 -0
  67. package/utils/exec.d.ts +13 -2
  68. package/utils/exec.js +17 -17
  69. package/utils/git.d.ts +9 -3
  70. package/utils/git.js +10 -2
  71. package/utils/package-filter.d.ts +33 -2
  72. package/utils/package-filter.js +47 -7
  73. package/utils/release-version.js +3 -3
  74. package/utils/run-bin.d.ts +46 -0
  75. package/utils/run-bin.js +63 -0
  76. package/utils/version-stamp.d.ts +48 -0
  77. package/utils/version-stamp.js +88 -0
  78. package/commands/ci.command.js +0 -30
  79. package/commands/clean.command.d.ts +0 -3
  80. package/commands/clean.command.js +0 -36
  81. package/commands/publish.command.d.ts +0 -3
  82. package/commands/publish.command.js +0 -225
  83. package/rmanrc.schema.json +0 -375
  84. package/services/ci.service.d.ts +0 -40
  85. package/services/ci.service.js +0 -204
  86. package/services/clean.service.d.ts +0 -42
  87. package/services/clean.service.js +0 -226
  88. package/services/publish.service.d.ts +0 -79
  89. package/services/publish.service.js +0 -207
  90. package/utils/change-hash.d.ts +0 -68
  91. package/utils/change-hash.js +0 -98
  92. package/utils/conventional-commits.d.ts +0 -52
  93. package/utils/conventional-commits.js +0 -90
  94. package/utils/npm-run-path.d.ts +0 -67
  95. package/utils/npm-run-path.js +0 -63
  96. package/utils/workspace-range.d.ts +0 -17
  97. package/utils/workspace-range.js +0 -28
  98. /package/commands/{ci.command.d.ts → config.command.d.ts} +0 -0
package/core/config.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import path from 'path';
2
+ import semver from 'semver';
1
3
  import type { RmanConfig } from '../interfaces/rman-config.interface.js';
2
4
  /**
3
5
  * Identity helper for authoring a `.rmanrc.cjs`/`.mjs`/`.js` config with full type-checking and
@@ -21,10 +23,259 @@ export declare function defineConfig(config: RmanConfig): RmanConfig;
21
23
  */
22
24
  export declare function readDirConfig(dirname: string): Promise<RmanConfig>;
23
25
  /**
24
- * Resolves the effective config for `targetDir` by cascading from `rootDir`
25
- * down to `targetDir` (inclusive), the same way tsconfig's `extends` chain
26
- * works: each directory level overrides the ones above it. This lets a
27
- * package (or any intermediate directory) narrow or override the repository's
28
- * root configuration for itself and everything below it.
26
+ * Resolves the effective config for the package at `targetDir`, cascading from `rootDir` down to
27
+ * it (inclusive) - each directory level overrides the ones above it, the way tsconfig's `extends`
28
+ * chain does.
29
+ *
30
+ * Every level contributes in two ways, and the difference is the whole model:
31
+ *
32
+ * - **Unmarked keys configure the package of the directory that declares them.** The root's own
33
+ * `.rmanrc` therefore configures the *root package* - which is where repo-wide settings
34
+ * (`packageManager`, `allowBranch`, `version.*`, `githubRelease.*`) are read from anyway - and
35
+ * not, silently, every package under it.
36
+ * - **A `"[selector]"` block configures the packages it names** - `"[*]"` for all of them (the root
37
+ * included), `"[ws:*]"` for every one but the root, `"[/]"` for the root alone, `"[*-dialect]"`
38
+ * for a glob over package names. See `parseSelector`. This is the only way a directory speaks
39
+ * about anything but its own package.
40
+ *
41
+ * Splitting the two matters because the same key means different things to the two audiences. The
42
+ * clearest case is `run.<script>.postScript`: on a package it's that package's build hook, run in
43
+ * its own directory; on the root it's a repo-wide bookend run once at the repository root. A
44
+ * cascade that fed one declaration to both ran a package-relative command (`node
45
+ * ../../support/postbuild.cjs`) at the root, where it cannot resolve.
46
+ *
47
+ * `packageName` is what selectors match against; without it, selector blocks contribute nothing at
48
+ * all. The root package passes its own, since `"[/]"` and `"[*]"` speak to it.
49
+ */
50
+ export declare function resolveConfig(rootDir: string, targetDir: string, cache?: Map<string, RmanConfig>, packageName?: string): Promise<RmanConfig>;
51
+ /** A config key naming packages rather than settings: `"[*]"`, `"[/]"`, `"[ws:*]"`, `"[pkg-a]"`. The
52
+ * brackets are what keep this space from colliding with real config keys - no setting starts with
53
+ * one - and in YAML they also mean the key always needs quoting (`"[*]":`), since a bare `[*]`
54
+ * parses as a flow sequence. */
55
+ export declare function isSelectorKey(key: string): boolean;
56
+ /**
57
+ * **Which packages a selector speaks for.** Three audiences, because a repository has three:
58
+ *
59
+ * | | |
60
+ * | --- | --- |
61
+ * | `"[/]"` | the **root package** only |
62
+ * | `"[*]"`, `"[pkg-a]"`, `"[*-dialect]"` | **every** package the glob matches, root included |
63
+ * | `"[ws:*]"`, `"[workspace:pkg-*]"` | every **non-root** package the glob matches |
64
+ *
65
+ * `/` for the root because that is what a repository root is called everywhere else, and it cannot
66
+ * collide with a package name. `ws:` is a qualifier on the glob rather than a separate spelling of
67
+ * `*`, so `"[ws:pkg-*]"` means what it looks like.
68
+ *
69
+ * **`"[*]"` includes the root, and that is a change from how it used to read.** Before, selectors
70
+ * were not applied to the root at all, so `"[*]"` silently meant "the workspace packages" - a
71
+ * catch-all with an exception nothing in the syntax mentioned. The three names above say which
72
+ * audience is meant; `"[ws:*]"` is the old behaviour, now spelled.
73
+ */
74
+ export declare function parseSelector(key: string): {
75
+ scope: 'root' | 'all' | 'workspace';
76
+ test: (name: string) => boolean;
77
+ };
78
+ /** The glob inside a selector key, as a `RegExp` anchored at both ends - so `"[*-dialect]"` matches
79
+ * `mysql-dialect` but not `my-dialect-helper`. Glob rather than regex, to match every other
80
+ * pattern in rman (`allowBranch`, `changelog.tagPattern`, `clean.include`). */
81
+ export declare function selectorToRegExp(key: string): RegExp;
82
+ /** One package, as an expression sees it - the same shape for the package the config belongs to
83
+ * and for the repository itself, so `${{ repository.basename }}` reads the way `${{ pkg.basename }}` does. */
84
+ /**
85
+ * `file` in a `${{ ... }}` expression: what is actually on disk, resolved against **the package
86
+ * the config was resolved for** - so one declaration in a `"[*]"` block asks each package about
87
+ * its own directory.
88
+ *
89
+ * The pair exists because a config has two different questions about a path, and answering both
90
+ * with one function would mean picking a wrong default for the other:
91
+ *
92
+ * ```yaml
93
+ * "[*]":
94
+ * run:
95
+ * build:
96
+ * # first of these that exists, and an error naming the config path if none do
97
+ * exec: 'tsc -b ${{ file.exists("tsconfig-build.json") || file.resolve("tsconfig.json") }}'
98
+ * ```
99
+ */
100
+ export interface FileScope {
101
+ /**
102
+ * The absolute path if it exists, **`''` if it does not** - so `a || b || c` picks the first one
103
+ * present, and so a miss never reaches the "nullish inside a string" guard that `undefined` would
104
+ * trip. Accepts a relative path (against the package directory) or an absolute one.
105
+ *
106
+ * It returns a path rather than a boolean on purpose: the caller almost always wants the path,
107
+ * and a separate `file.path()` to fetch it after a boolean test would read the disk twice and
108
+ * invite the two calls to disagree.
109
+ */
110
+ exists(target: string): string;
111
+ /** The absolute path, or **throws** - for a file whose absence is a mistake rather than a case to
112
+ * handle. The error names the config path holding the expression, like any other. */
113
+ resolve(target: string): string;
114
+ /**
115
+ * The first of several that exists, or **throws** naming every candidate it tried:
116
+ *
117
+ * ```yaml
118
+ * exec: 'tsc -b ${{ file.resolveFirst("tsconfig-build.json", "tsconfig.build.json", "tsconfig.json") }}'
119
+ * ```
120
+ *
121
+ * The same thing an `exists() || exists() || resolve()` chain does, said once - and it cannot be
122
+ * got subtly wrong the way that chain can: ending it in `exists()` leaves `tsc -b ` with no
123
+ * argument when nothing matches, and tsc then silently falls back to the directory's default
124
+ * rather than reporting that the package has no build config.
125
+ */
126
+ resolveFirst(...targets: string[]): string;
127
+ }
128
+ export interface PackageScope {
129
+ /** The package's own name, scope included (`@sqb/builder`). */
130
+ name: string;
131
+ /** Just the scope (`@sqb`), or `undefined` for an unscoped package. */
132
+ scope: string | undefined;
133
+ /** The name with its scope stripped (`builder`). */
134
+ unscopedName: string;
135
+ version: string;
136
+ /** The package directory's last segment (`builder`) - not always the same as `unscopedName`,
137
+ * which is why both exist, and usually what a sibling path (`../../coverage/builder`) is keyed on. */
138
+ basename: string;
139
+ /** Absolute path to the package's own directory - named as rman's own `Package.dirname` is. */
140
+ dirname: string;
141
+ /** That directory relative to the repository root (`packages/builder`), which is what a command
142
+ * addressing another package from the root usually needs. Empty string for the root itself. */
143
+ relativeDir: string;
144
+ /**
145
+ * Which ecosystem this package belongs to - `'node'` for one read by `rman-node`, empty when no
146
+ * plugin claimed it. The same `Package.provider`, so one declaration can address a single
147
+ * ecosystem in a polyglot repository (`if: "${{ pkg.provider === 'node' }}"`).
148
+ */
149
+ provider: string;
150
+ /**
151
+ * The whole manifest, as a copy - so an expression can reach a field rman itself has no opinion
152
+ * about (`${{ pkg.manifest.engines.node }}`).
153
+ *
154
+ * Named `manifest`, not `json`: which file a package's identity lives in is the ecosystem's
155
+ * business now (see `ManifestProvider`), and `json` was that assumption showing through the one
156
+ * remaining user-facing name. A config written against `${{ pkg.json... }}` needs the rename.
157
+ */
158
+ manifest: Record<string, unknown>;
159
+ /**
160
+ * The version this run is about to write - **bound only during `version`**, and only once its
161
+ * plan is computed. Reading it anywhere else throws rather than yielding `undefined`: no other
162
+ * command has a target version, so an expression asking for one has been put in the wrong place,
163
+ * and a config that quietly evaluates to "undefined" is the failure this evaluator exists to
164
+ * prevent.
165
+ */
166
+ targetVersion: string;
167
+ }
168
+ /** Facts about the repository, on top of the root package's own - because the repository root *is*
169
+ * a package (`repository.name` is what its `package.json` says, `repository.basename` the directory
170
+ * it sits in, and the two genuinely differ). Sharing `PackageScope`'s shape is what makes
171
+ * `repository.version` read the way `pkg.version` does. */
172
+ export interface RepositoryScope extends PackageScope {
173
+ monorepo: boolean;
174
+ /** Every package in the repository - the root included only when it *is* the one package. */
175
+ packages: PackageScope[];
176
+ /** One package by name, or `undefined` - for reaching a sibling's directory. */
177
+ package(name: string): PackageScope | undefined;
178
+ /** Read from git only if an expression actually asks for it, then remembered: a repository that
179
+ * never mentions these pays nothing, and every command resolves config. All `undefined` outside
180
+ * a git checkout, which is a legitimate state rather than an error. */
181
+ git: GitScope;
182
+ }
183
+ export interface GitScope {
184
+ branch: string | undefined;
185
+ sha: string | undefined;
186
+ shortSha: string | undefined;
187
+ /** Whether the working tree has uncommitted changes. */
188
+ dirty: boolean | undefined;
189
+ }
190
+ /**
191
+ * What a `${{ ... }}` expression can see - the bindings of the fresh global it is evaluated in.
192
+ * Namespaced rather than a flat bag of loose names: one obvious place per fact, and room to add
193
+ * helpers to `pkg`/`repository` later without crowding the global.
194
+ *
195
+ * Alongside these, **the config's own top-level keys are bound bare** (`${{ publish.directory }}`,
196
+ * `${{ clean.include }}`) - see `interpolateConfig`. They are not listed here because they come
197
+ * from the config being interpolated, not from this object; a name here wins over a config key of
198
+ * the same name.
199
+ *
200
+ * **Trap: bare `${{ version }}` is the `version` *options block*, not the package's version
201
+ * string** - that is `${{ pkg.version }}`. Same word, two different things, and the plain one
202
+ * belongs to the config because every other config key is reachable that way.
203
+ */
204
+ export interface ConfigScope {
205
+ /** The package the config was resolved for - which is what lets one declaration at the root
206
+ * still say something package-specific. */
207
+ pkg: PackageScope;
208
+ repository: RepositoryScope;
209
+ /** Paths, resolved against the package the config was resolved for. */
210
+ file: FileScope;
211
+ env: Record<string, string | undefined>;
212
+ /** rman's own `semver`, for the arithmetic every release config eventually wants
213
+ * (`semver.major(pkg.version)`). */
214
+ semver: typeof semver;
215
+ /**
216
+ * Node's own `node:path` - `${{ path.join(pkg.dirname, 'LICENSE') }}`, rather than gluing
217
+ * strings with `+ "/" +` and getting a double separator or none.
218
+ *
219
+ * The platform's flavour, not `path.posix`, so a joined path is the one the shell on *this*
220
+ * machine understands; `path.posix` and `path.win32` are reachable through it when a config
221
+ * genuinely needs one of them (a Docker image path, say, which is always posix).
222
+ */
223
+ path: typeof path;
224
+ }
225
+ /**
226
+ * Evaluates every `${{ ... }}` expression in **every** string value of a resolved config, against
227
+ * the package it was resolved for:
228
+ *
229
+ * ```yaml
230
+ * "[*]":
231
+ * clean:
232
+ * include: ["build", "../../coverage/${{ pkg.basename }}"]
233
+ * publish:
234
+ * directory: build
235
+ * docker:
236
+ * image: "panates/${{ pkg.basename }}:${{ semver.major(pkg.version) }}"
237
+ * run:
238
+ * build:
239
+ * # the config's own keys are in scope, so this is not a second copy of "build"
240
+ * after: "cp README.md ${{ publish.directory }}/"
241
+ * ```
242
+ *
243
+ * Every string, with no list of "interpolated keys" to memorize - a rule with exceptions is a rule
244
+ * nobody remembers.
245
+ *
246
+ * The contents are **real JavaScript**, not a template mini-language, so there is no growing list
247
+ * of substitutions to keep adding (`{{major}}`, `{{scope}}`, ...) - see `ConfigScope` for what is
248
+ * in scope.
249
+ *
250
+ * **`${{ }}`, deliberately not `{{ }}`.** A config value may legitimately carry `{{...}}` meant for
251
+ * something else entirely (`helm template --set tag={{.Values.tag}}`); with the plainer delimiter
252
+ * rman would try to evaluate it. To emit a literal, let an expression produce it, the way GitHub
253
+ * Actions does: `${{ '${{' }}`.
254
+ *
255
+ * A string that is *nothing but* one expression keeps the value's own type (`"${{ pkg.private }}"`
256
+ * -> a boolean), since otherwise this could only ever produce strings and settings like
257
+ * `run.<script>.skip` would be unreachable. Embedded in surrounding text it is stringified.
258
+ *
259
+ * Evaluation happens in a fresh V8 context holding only the scope's bindings. That is a clean
260
+ * scope, **not a sandbox** - `node:vm` is explicitly not a security mechanism, and no sandbox is
261
+ * called for here anyway: a `.rmanrc` that can say `exec: "..."` already runs arbitrary shell, so
262
+ * the expression evaluator adds no trust boundary that wasn't already wide open.
263
+ *
264
+ * A failing expression throws with the config path that holds it, rather than being left in place:
265
+ * silently passing through a mistake is how a config ends up quietly doing nothing.
266
+ */
267
+ export declare function interpolateConfig<T>(config: T, scope: ConfigScope, options?: {
268
+ skip?: string[];
269
+ }): T;
270
+ /**
271
+ * Config paths left untouched when a repository's config is first resolved, and evaluated only by
272
+ * the command that runs them.
273
+ *
274
+ * `version`'s own hooks are the one place `${{ pkg.targetVersion }}` makes sense, and the version
275
+ * being written is not known until `version` has computed its plan - long after the config was
276
+ * resolved. Evaluating these eagerly would throw while merely *loading* the repository, so any
277
+ * command at all would fail on a config that mentions it.
29
278
  */
30
- export declare function resolveConfig(rootDir: string, targetDir: string, cache?: Map<string, RmanConfig>): Promise<RmanConfig>;
279
+ export declare const DEFERRED_PATHS: string[];
280
+ /** The `file` namespace for one package's directory - see `FileScope`. */
281
+ export declare function createFileScope(dirname: string): FileScope;