rman 1.0.12 → 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.
- package/README.md +90 -70
- package/cli.js +225 -14
- package/commands/build.command.js +1 -0
- package/commands/changed.command.js +2 -2
- package/commands/changelog.command.js +13 -15
- package/commands/config.command.js +60 -0
- package/commands/diff.command.js +9 -4
- package/commands/exec.command.js +2 -8
- package/commands/github-release.command.js +1 -0
- package/commands/info.command.d.ts +9 -0
- package/commands/info.command.js +12 -2
- package/commands/run.command.js +5 -8
- package/commands/test.command.js +1 -0
- package/commands/version.command.js +53 -14
- package/constants.js +1 -1
- package/core/config.d.ts +142 -13
- package/core/config.js +266 -43
- package/core/custom-command.d.ts +133 -0
- package/core/custom-command.js +99 -0
- package/core/extends-config.d.ts +27 -0
- package/core/extends-config.js +89 -0
- package/core/manifest.d.ts +222 -0
- package/core/manifest.js +150 -0
- package/core/merge-config.d.ts +57 -0
- package/core/merge-config.js +146 -0
- package/core/package.d.ts +73 -7
- package/core/package.js +86 -24
- package/core/plugin.d.ts +112 -0
- package/core/plugin.js +189 -0
- package/core/repository.d.ts +77 -1
- package/core/repository.js +242 -129
- package/core/resolve-target.d.ts +12 -0
- package/core/resolve-target.js +33 -0
- package/core/version-scheme.d.ts +134 -0
- package/core/version-scheme.js +148 -0
- package/core/workspace.d.ts +68 -0
- package/core/workspace.js +83 -0
- package/index.d.ts +54 -8
- package/index.js +42 -7
- package/interfaces/rman-config.interface.d.ts +171 -36
- package/package.json +15 -7
- package/services/change-hash.service.d.ts +88 -0
- package/services/change-hash.service.js +112 -0
- package/services/changelog.service.d.ts +8 -13
- package/services/changelog.service.js +12 -11
- package/services/conventional-commits.service.d.ts +73 -0
- package/services/conventional-commits.service.js +116 -0
- package/services/docker-publish.service.js +1 -1
- package/services/exec.service.js +1 -1
- package/services/github-release.service.d.ts +2 -2
- package/services/github-release.service.js +10 -5
- package/services/list.service.js +5 -2
- package/services/run.service.d.ts +60 -0
- package/services/run.service.js +109 -65
- package/services/system-info.d.ts +22 -7
- package/services/system-info.js +8 -23
- package/services/version-plan.service.d.ts +244 -0
- package/services/version-plan.service.js +414 -0
- package/services/version.service.d.ts +92 -82
- package/services/version.service.js +219 -433
- package/services.d.ts +5 -3
- package/services.js +5 -3
- package/utils/bin-path.d.ts +59 -0
- package/utils/bin-path.js +82 -0
- package/utils/child-tracker.d.ts +16 -0
- package/utils/child-tracker.js +30 -0
- package/utils/exec.d.ts +13 -2
- package/utils/exec.js +17 -17
- package/utils/git.d.ts +9 -3
- package/utils/git.js +10 -2
- package/utils/package-filter.d.ts +33 -2
- package/utils/package-filter.js +47 -7
- package/utils/release-version.js +3 -3
- package/utils/run-bin.d.ts +46 -0
- package/utils/run-bin.js +63 -0
- package/utils/version-stamp.d.ts +14 -6
- package/utils/version-stamp.js +25 -13
- package/commands/ci.command.js +0 -30
- package/commands/clean.command.d.ts +0 -3
- package/commands/clean.command.js +0 -36
- package/commands/publish.command.d.ts +0 -3
- package/commands/publish.command.js +0 -225
- package/rmanrc.schema.json +0 -392
- package/services/ci.service.d.ts +0 -40
- package/services/ci.service.js +0 -204
- package/services/clean.service.d.ts +0 -42
- package/services/clean.service.js +0 -226
- package/services/publish.service.d.ts +0 -79
- package/services/publish.service.js +0 -273
- package/utils/change-hash.d.ts +0 -68
- package/utils/change-hash.js +0 -98
- package/utils/conventional-commits.d.ts +0 -52
- package/utils/conventional-commits.js +0 -90
- package/utils/npm-run-path.d.ts +0 -67
- package/utils/npm-run-path.js +0 -63
- package/utils/workspace-range.d.ts +0 -17
- package/utils/workspace-range.js +0 -28
- /package/commands/{ci.command.d.ts → config.command.d.ts} +0 -0
|
@@ -1,32 +1,152 @@
|
|
|
1
|
+
import type { RmanPlugin } from '../core/plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Adds a `+key` alongside every key of `T`, which **appends** to whatever that key already resolved
|
|
4
|
+
* to instead of replacing it - see `mergeConfig`.
|
|
5
|
+
*
|
|
6
|
+
* Generated by key remapping rather than written out, so a key added to the interface gets its
|
|
7
|
+
* append form automatically and the two can never drift apart.
|
|
8
|
+
*/
|
|
9
|
+
export type WithAppend<T> = {
|
|
10
|
+
[K in keyof T as `+${K & string}`]?: T[K];
|
|
11
|
+
};
|
|
1
12
|
/**
|
|
2
13
|
* The shape of `.rmanrc`/`.rmanrc.yml`/`.rmanrc.cjs`/`.mjs`/`.js` (and `package.json`'s own
|
|
3
|
-
* `"rman"` key) - see docs/
|
|
14
|
+
* `"rman"` key) - see docs/rman.md#configuration-rmanrc-rmanrcyml for the full reference. Every
|
|
4
15
|
* field is optional and cascades from the repository root down to each package's own directory.
|
|
5
16
|
* Purely a typing aid (used by `defineConfig` below, and importable on its own for a `.rmanrc.ts`/
|
|
6
17
|
* `.mts` authored config, or a plain `: RmanConfig` annotation) - never read by rman itself, which
|
|
7
18
|
* only ever sees the plain JS object a JS config file exports.
|
|
8
19
|
*/
|
|
9
|
-
export interface RmanConfig {
|
|
10
|
-
|
|
20
|
+
export interface RmanConfig extends RmanConfigKeys, WithAppend<RmanConfigKeys> {
|
|
21
|
+
/**
|
|
22
|
+
* Configs to inherit from, merged **underneath** this one - a shared package
|
|
23
|
+
* (`"@panates/rman-monorepo"`), a relative path, or an array applied in declaration order.
|
|
24
|
+
*
|
|
25
|
+
* A bare name resolves through *this* file's own `node_modules`, so a subpath works too
|
|
26
|
+
* (`"@panates/rman-monorepo/strict"`). The target may be YAML, JSON, or a module exporting a
|
|
27
|
+
* config via `defineConfig`, and may itself `extends` another.
|
|
28
|
+
*
|
|
29
|
+
* Top level only: a `"[selector]"` block naming one is an error rather than a no-op, since
|
|
30
|
+
* inheritance is a statement about this config and not about the packages a selector names.
|
|
31
|
+
*/
|
|
32
|
+
extends?: string | string[];
|
|
33
|
+
}
|
|
34
|
+
/** Every setting a config may carry, without the `+key` append forms or `extends` - the shape
|
|
35
|
+
* `RmanConfig` is built from, kept separate only so `WithAppend` has something to map over. */
|
|
36
|
+
export interface RmanConfigKeys {
|
|
37
|
+
/**
|
|
38
|
+
* **A plugin adds its own keys here, by declaration merging** - `rman-node` contributes
|
|
39
|
+
* `clean` and `publish.directory` from its own
|
|
40
|
+
* `interfaces/rman-config.interface.ts`, so `pkg.config.clean` stays typed wherever it is read
|
|
41
|
+
* without the core having to know npm has a `node_modules` or that TypeScript has build output.
|
|
42
|
+
* `WithAppend` is a mapped type evaluated at use, so an augmented key gets its `+key` form too.
|
|
43
|
+
*
|
|
44
|
+
* A config author annotates with the plugin's own name for the union - `RmanNodeConfig` - which
|
|
45
|
+
* is what makes the import that carries the augmentation explicit rather than incidental.
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* Plugins to load, in declaration order - a *package* contributing commands, where `.rman/*.mjs`
|
|
49
|
+
* contributes one repository's own.
|
|
50
|
+
*
|
|
51
|
+
* Each entry is **either a package name (or path) to import, or a plugin object itself**:
|
|
52
|
+
*
|
|
53
|
+
* ```yaml
|
|
54
|
+
* # .rmanrc.yml - imported by name, resolved through the repository's own node_modules
|
|
55
|
+
* plugins: ['rman-node']
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* ```js
|
|
59
|
+
* // .rmanrc.mjs - or handed over directly, which a JS config can do and a YAML one cannot
|
|
60
|
+
* import { defineConfig, definePlugin } from 'rman';
|
|
61
|
+
* export default defineConfig({ plugins: [definePlugin({ name: 'mine', commands: [...] })] });
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* The object form is what lets a **plugin package export a config** rather than a single plugin:
|
|
65
|
+
* `rman-node`'s entry point is `export default defineConfig({ plugins: [ ... ] })`, so it is an
|
|
66
|
+
* `.rmanrc` like any other and is free to grow a second plugin without changing its shape. When an
|
|
67
|
+
* imported module exports a config this way, **only its `plugins` are read** - a config's other
|
|
68
|
+
* keys reach a repository through `extends`, which is the key that means "merge this underneath
|
|
69
|
+
* mine".
|
|
70
|
+
*
|
|
71
|
+
* This is how everything that only means something in a Node repository lives outside rman's
|
|
72
|
+
* core. A plugin that cannot be loaded is an error, not a skip: silently losing `rman publish` is
|
|
73
|
+
* worse than not starting.
|
|
74
|
+
*
|
|
75
|
+
* Root level only - which commands exist is a property of the repository, not of a package.
|
|
76
|
+
*/
|
|
77
|
+
plugins?: string | RmanPlugin | (string | RmanPlugin)[];
|
|
78
|
+
/**
|
|
79
|
+
* Values for `${{ vars.* }}` to read - a name for something the config would otherwise repeat:
|
|
80
|
+
*
|
|
81
|
+
* ```yaml
|
|
82
|
+
* vars:
|
|
83
|
+
* outDir: build
|
|
84
|
+
* image: 'panates/${{ pkg.basename }}'
|
|
85
|
+
* "[*]":
|
|
86
|
+
* publish:
|
|
87
|
+
* directory: '${{ vars.outDir }}'
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* Any shape, and the values may themselves be expressions - they are evaluated for the package
|
|
91
|
+
* reading them, so one `vars.image` gives each package its own.
|
|
92
|
+
*
|
|
93
|
+
* **The one unmarked key that reaches every package**, rather than only the package of the
|
|
94
|
+
* directory declaring it. A package (or a `"[selector]"` block) overrides it **per key**, so
|
|
95
|
+
* redefining one var keeps the rest.
|
|
96
|
+
*/
|
|
97
|
+
vars?: Record<string, unknown>;
|
|
11
98
|
logLevel?: 'silent' | 'error' | 'info' | 'verbose';
|
|
12
99
|
allowBranch?: string | string[];
|
|
13
100
|
ignoreBranch?: string | string[];
|
|
101
|
+
/**
|
|
102
|
+
* Leave this package alone: **every command that acts on packages skips it** - `run`/`build`/
|
|
103
|
+
* `test`, `exec`, `clean`, `publish`, `version`, `changelog`. Per-package cascaded, so a root
|
|
104
|
+
* `"[selector]"` block can say it for several at once.
|
|
105
|
+
*
|
|
106
|
+
* One standing statement rather than a `skip` invented per command, which is what it was: three
|
|
107
|
+
* separate keys carried it and none of them meant quite the same thing. The finer-grained ones
|
|
108
|
+
* remain for when only one command should stop - `run.<script>.skip` for a single script,
|
|
109
|
+
* `publish.skip` for "never distributed, by any target" (which `changelog` reuses on purpose).
|
|
110
|
+
*
|
|
111
|
+
* **The commands that *report* deliberately ignore it** - `list` still shows the package, because
|
|
112
|
+
* it is still in the repository and an inventory hiding part of one is answering a different
|
|
113
|
+
* question. `changed` follows `version`, since its whole job is to say what `version` would do.
|
|
114
|
+
*/
|
|
115
|
+
skip?: boolean;
|
|
14
116
|
group?: boolean | string;
|
|
15
117
|
version?: RmanConfig.VersionOptions;
|
|
16
118
|
changelog?: RmanConfig.ChangelogOptions;
|
|
17
|
-
clean?: RmanConfig.CleanOptions;
|
|
18
119
|
publish?: RmanConfig.PublishOptions;
|
|
19
120
|
githubRelease?: RmanConfig.GithubReleaseOptions;
|
|
20
121
|
/** Keyed by npm script name (e.g. `"build"`, `"lint"`, `"test"`). A bare string (or array of
|
|
21
122
|
* them) is shorthand for `{ exec: ... }` - `test: "mocha"` and `test: { exec: "mocha" }` mean
|
|
22
123
|
* exactly the same thing. */
|
|
23
124
|
run?: Record<string, string | string[] | RmanConfig.RunScriptOptions>;
|
|
24
|
-
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
|
|
125
|
+
/**
|
|
126
|
+
* In-repo packages this one depends on **beyond what its own manifest declares** - purely for
|
|
127
|
+
* rman's own dependency graph (topo-sort, `--deps`/`--dependents`, `run`'s scheduling, the version
|
|
128
|
+
* cascade). Declared from the root via a selector (`"[pkg-a]": { dependencies: [...] }`) or in the
|
|
129
|
+
* package's own `.rmanrc`.
|
|
130
|
+
*
|
|
131
|
+
* Each entry is a package **name, or a repository-relative directory** - tried in that order. The
|
|
132
|
+
* path form is not a convenience: a name identifies a package only where the ecosystem guarantees
|
|
133
|
+
* uniqueness, which npm does and others do not, while a directory is unique by construction. It is
|
|
134
|
+
* the same reason `Workspace.Layout` carries paths and `Package.dependencies` holds references
|
|
135
|
+
* rather than names.
|
|
136
|
+
*
|
|
137
|
+
* **A list, and only a list.** It used to accept a `Record<string, string>` too, documented as "an
|
|
138
|
+
* explicit name -> range map" - and the ranges went nowhere: the one reader took `Object.keys` and
|
|
139
|
+
* dropped the values. Nor could they ever mean anything here, since the cascade works from groups
|
|
140
|
+
* and severities, and `ManifestProvider.updateDependencyVersions` rewrites ranges in the
|
|
141
|
+
* *manifest* - a range declared only in `.rmanrc` has no file to be written to. What this key
|
|
142
|
+
* states is an **edge**, and an edge needs two ends and nothing else.
|
|
143
|
+
*
|
|
144
|
+
* **Core, and it has to be**: it is layered on top of whatever `ManifestProvider.dependencies`
|
|
145
|
+
* read, and it is the *only* way a repository with no provider at all has a graph - a repo whose
|
|
146
|
+
* manifests rman cannot read can still state its edges by hand. Moving it to an ecosystem plugin
|
|
147
|
+
* would take that away from exactly the repositories that need it.
|
|
148
|
+
*/
|
|
149
|
+
dependencies?: string[];
|
|
30
150
|
/**
|
|
31
151
|
* Config for **other** packages, keyed by a `"[selector]"` naming them - `"[*]"` for every
|
|
32
152
|
* package in the repository, `"[*-dialect]"` for a glob over package names, `"[pkg-a]"` for one.
|
|
@@ -57,7 +177,22 @@ export interface RmanConfig {
|
|
|
57
177
|
[selector: `[${string}]`]: RmanConfig;
|
|
58
178
|
}
|
|
59
179
|
export declare namespace RmanConfig {
|
|
60
|
-
|
|
180
|
+
/**
|
|
181
|
+
* One `version.stamp` entry: a path, or a path plus the identifier that holds the version.
|
|
182
|
+
*
|
|
183
|
+
* The object form exists because the identifier was unnameable - only the exact lowercase word
|
|
184
|
+
* `version` was ever matched, so a Go `const Version`, a Python `__version__` and a plain
|
|
185
|
+
* `appVersion` were all silently skipped. What the identifier *means* is the ecosystem's
|
|
186
|
+
* (`ManifestProvider.stampVersion`); which file holds one is the repository's, which is why it is
|
|
187
|
+
* here.
|
|
188
|
+
*/
|
|
189
|
+
type VersionStampEntry = string | {
|
|
190
|
+
file: string;
|
|
191
|
+
constant?: string;
|
|
192
|
+
};
|
|
193
|
+
interface VersionOptions extends VersionOptionsKeys, WithAppend<VersionOptionsKeys> {
|
|
194
|
+
}
|
|
195
|
+
interface VersionOptionsKeys {
|
|
61
196
|
commitMessage?: string;
|
|
62
197
|
/** Default for `version --changelog` when the CLI flag isn't given - a standing "always fold
|
|
63
198
|
* the changelog into the version-bump commit" policy, rather than something that behaves
|
|
@@ -77,7 +212,7 @@ export declare namespace RmanConfig {
|
|
|
77
212
|
* already declares (never inserts one), and reads the same path `publish --target docker`
|
|
78
213
|
* builds from (`publish.docker.dockerfile`), so a package without one is a no-op. */
|
|
79
214
|
stampDockerfile?: boolean;
|
|
80
|
-
/**
|
|
215
|
+
/** Files whose hard-coded version is rewritten to the version being written, in the same
|
|
81
216
|
* commit as the bump - paths relative to the package's own directory (e.g.
|
|
82
217
|
* `["src/constants.ts"]`). Per-package cascaded; a listed file a package doesn't have is a
|
|
83
218
|
* silent no-op, so one `"[*]"` declaration covers a repo where only some packages carry one.
|
|
@@ -85,27 +220,29 @@ export declare namespace RmanConfig {
|
|
|
85
220
|
* Stamping the source, not the build output: a build-time rewrite leaves the checked-in file
|
|
86
221
|
* claiming a placeholder, so anything running from source reports that placeholder, git never
|
|
87
222
|
* records the released version, and the rewrite has to be redone on every build. */
|
|
88
|
-
stamp?:
|
|
89
|
-
/** Command(s) run
|
|
90
|
-
*
|
|
223
|
+
stamp?: VersionStampEntry | VersionStampEntry[];
|
|
224
|
+
/** Command(s) run at the version write itself, when the package does not declare a hook for
|
|
225
|
+
* that slot of its own (`version` in a Node repository's `package.json#scripts`, whatever a
|
|
226
|
+
* plugin's step source answers elsewhere - the package's own declaration wins, as in `run`).
|
|
227
|
+
* An array runs them in sequence. `${{ pkg.targetVersion }}` is bound here and in the two
|
|
228
|
+
* below, and nowhere else. */
|
|
91
229
|
exec?: string | string[];
|
|
92
|
-
/** Same,
|
|
230
|
+
/** Same, before the write (`preversion` in a Node repository). */
|
|
93
231
|
before?: string | string[];
|
|
94
|
-
/** Same,
|
|
232
|
+
/** Same, after it (`postversion` in a Node repository). */
|
|
95
233
|
after?: string | string[];
|
|
96
234
|
}
|
|
97
|
-
interface ChangelogOptions {
|
|
235
|
+
interface ChangelogOptions extends ChangelogOptionsKeys, WithAppend<ChangelogOptionsKeys> {
|
|
236
|
+
}
|
|
237
|
+
interface ChangelogOptionsKeys {
|
|
98
238
|
ignoreTypes?: string[];
|
|
99
239
|
template?: string;
|
|
100
240
|
filePath?: string;
|
|
101
241
|
tagPattern?: string;
|
|
102
242
|
}
|
|
103
|
-
interface
|
|
104
|
-
include?: string | string[];
|
|
105
|
-
exclude?: string | string[];
|
|
106
|
-
skip?: boolean;
|
|
243
|
+
interface RunScriptOptions extends RunScriptOptionsKeys, WithAppend<RunScriptOptionsKeys> {
|
|
107
244
|
}
|
|
108
|
-
interface
|
|
245
|
+
interface RunScriptOptionsKeys {
|
|
109
246
|
concurrency?: number;
|
|
110
247
|
topo?: boolean;
|
|
111
248
|
bail?: boolean;
|
|
@@ -123,7 +260,9 @@ export declare namespace RmanConfig {
|
|
|
123
260
|
after?: string | string[];
|
|
124
261
|
override?: boolean;
|
|
125
262
|
}
|
|
126
|
-
interface PublishOptions {
|
|
263
|
+
interface PublishOptions extends PublishOptionsKeys, WithAppend<PublishOptionsKeys> {
|
|
264
|
+
}
|
|
265
|
+
interface PublishOptionsKeys {
|
|
127
266
|
/** Which **registry** `publish` ships this package to - default `['npm']` (every existing repo
|
|
128
267
|
* keeps working unchanged). A package that only ever wants Docker images (typically also
|
|
129
268
|
* `"private": true`, since it's not meant for npm at all) sets `['docker']`; both works too.
|
|
@@ -135,14 +274,6 @@ export declare namespace RmanConfig {
|
|
|
135
274
|
* a release happened, and it is never opted into: see `githubRelease` and the
|
|
136
275
|
* `github-release` command. */
|
|
137
276
|
target?: PublishTarget | PublishTarget[];
|
|
138
|
-
/** Where this package's publishable output lives, relative to its own directory (e.g.
|
|
139
|
-
* `"build"`). Per-package cascaded, so a root `"[*]"` block can say it once for the whole
|
|
140
|
-
* repository instead of repeating `publishConfig.directory` in every `package.json` - which
|
|
141
|
-
* still wins when a package declares it, being the more specific statement.
|
|
142
|
-
*
|
|
143
|
-
* Publishing from such a directory means the manifest there is **generated by `publish`**,
|
|
144
|
-
* from the package's own - see `PublishService`. There is nothing to configure about it. */
|
|
145
|
-
directory?: string;
|
|
146
277
|
docker?: DockerPublishOptions;
|
|
147
278
|
/** Excludes this package from `publish` entirely (every target), regardless of
|
|
148
279
|
* `target`/`"private"` - a single, explicit "never published" statement, e.g. for a package
|
|
@@ -155,7 +286,9 @@ export declare namespace RmanConfig {
|
|
|
155
286
|
type PublishTarget = 'npm' | 'docker';
|
|
156
287
|
/** Required once `"docker"` is one of this package's `publish.target`s - `publish --target
|
|
157
288
|
* docker` errors clearly on a package that opts in here but leaves this out. */
|
|
158
|
-
interface DockerPublishOptions {
|
|
289
|
+
interface DockerPublishOptions extends DockerPublishOptionsKeys, WithAppend<DockerPublishOptionsKeys> {
|
|
290
|
+
}
|
|
291
|
+
interface DockerPublishOptionsKeys {
|
|
159
292
|
/** DockerHub image name/repository - bare (e.g. `"my-app"`) to be prefixed with
|
|
160
293
|
* `--docker-namespace`/`DOCKERHUB_NAMESPACE`, or already-namespaced (contains a `/`) to use
|
|
161
294
|
* verbatim. */
|
|
@@ -181,7 +314,9 @@ export declare namespace RmanConfig {
|
|
|
181
314
|
* (which tag, which repository, what the notes say) already has a sensible source. Nothing here
|
|
182
315
|
* decides *whether* a release is cut: a release records that the repository shipped, so it is
|
|
183
316
|
* always cut, and these are only details about how. */
|
|
184
|
-
interface GithubReleaseOptions {
|
|
317
|
+
interface GithubReleaseOptions extends GithubReleaseOptionsKeys, WithAppend<GithubReleaseOptionsKeys> {
|
|
318
|
+
}
|
|
319
|
+
interface GithubReleaseOptionsKeys {
|
|
185
320
|
/** Files to attach to the release, as glob patterns relative to the package's own directory
|
|
186
321
|
* (e.g. `["dist/*.tar.gz"]`). Read from **every** package, since one release covers the whole
|
|
187
322
|
* source tree. A release with no assets at all is still perfectly valid - it records that the
|
|
@@ -191,8 +326,8 @@ export declare namespace RmanConfig {
|
|
|
191
326
|
repository?: string;
|
|
192
327
|
/** Create the release as an unpublished draft. Default `false`. Root-level only. */
|
|
193
328
|
draft?: boolean;
|
|
194
|
-
/** Default: whether the version being released is itself a
|
|
195
|
-
* Root-level only. */
|
|
329
|
+
/** Default: whether the version being released is itself a prerelease by the root's own version
|
|
330
|
+
* scheme (`1.3.0-beta.0` under semver) - see `VersionScheme.isPrerelease`. Root-level only. */
|
|
196
331
|
prerelease?: boolean;
|
|
197
332
|
}
|
|
198
333
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rman",
|
|
3
3
|
"description": "Repository manager",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.1",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@netlify/parse-npm-script": "^0.1.2",
|
|
9
8
|
"ansi-colors": "^4.1.3",
|
|
10
9
|
"cross-dirname": "^0.1.0",
|
|
11
10
|
"easy-table": "^1.2.0",
|
|
@@ -35,8 +34,11 @@
|
|
|
35
34
|
"types": "./index.d.ts",
|
|
36
35
|
"default": "./index.js"
|
|
37
36
|
},
|
|
38
|
-
"./
|
|
39
|
-
|
|
37
|
+
"./cli": {
|
|
38
|
+
"types": "./cli.d.ts",
|
|
39
|
+
"default": "./cli.js"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
40
42
|
},
|
|
41
43
|
"bin": {
|
|
42
44
|
"rman": "cli.js"
|
|
@@ -45,11 +47,15 @@
|
|
|
45
47
|
"node": ">=20.0"
|
|
46
48
|
},
|
|
47
49
|
"contributors": [
|
|
48
|
-
"Eray Hanoglu <e.hanoglu@panates.com>"
|
|
50
|
+
"Eray Hanoglu <e.hanoglu@panates.com>",
|
|
51
|
+
"Ilker Gurelli <i.gurelli@panates.com>",
|
|
52
|
+
"Onur Tokel <o.tokel@panates.com>",
|
|
53
|
+
"Bircan Yuruk <b.yuruk@panates.com>"
|
|
49
54
|
],
|
|
50
55
|
"repository": {
|
|
51
56
|
"type": "git",
|
|
52
|
-
"url": "git+https://github.com/panates/rman.git"
|
|
57
|
+
"url": "git+https://github.com/panates/rman.git",
|
|
58
|
+
"directory": "./packages/rman"
|
|
53
59
|
},
|
|
54
60
|
"keywords": [
|
|
55
61
|
"javascript",
|
|
@@ -59,5 +65,7 @@
|
|
|
59
65
|
"build",
|
|
60
66
|
"lerna"
|
|
61
67
|
],
|
|
62
|
-
"
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
}
|
|
63
71
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Package } from '../core/package.js';
|
|
2
|
+
import type { GitHelper } from '../utils/git.js';
|
|
3
|
+
/**
|
|
4
|
+
* Release boundaries and the tag names that mark them - **the single source for both directions**.
|
|
5
|
+
*
|
|
6
|
+
* Every command that asks question A ("what changed since this package's last release") comes
|
|
7
|
+
* through `detect`, and every command that needs to *name* a tag comes through `expandTag` /
|
|
8
|
+
* `findLatestTag`. No command builds a tag name of its own; that is what keeps `version`'s tag,
|
|
9
|
+
* `changelog`'s boundary and `github-release`'s lookup from drifting apart.
|
|
10
|
+
*
|
|
11
|
+
* A namespace for the reason `Manifest` and `Workspace` are ones: it is one subject with several
|
|
12
|
+
* operations, so the operations are named for what they do rather than carrying the subject in each
|
|
13
|
+
* name (`ChangeHashService.detect`, not `detectChangeHash`), and a plugin can augment it.
|
|
14
|
+
*/
|
|
15
|
+
export declare namespace ChangeHashService {
|
|
16
|
+
/** The one keyword `from` accepts instead of a ref: "work it out per package". Omitting `from`
|
|
17
|
+
* means the same thing - this exists so a pipeline can say it explicitly. */
|
|
18
|
+
const AUTO = "auto";
|
|
19
|
+
interface DetectOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Use this commit/hash directly instead of auto-detecting - applies the same way to every
|
|
22
|
+
* package. `AUTO` (or omitting `from` entirely) asks for auto-detection instead of being
|
|
23
|
+
* treated as a literal ref.
|
|
24
|
+
*
|
|
25
|
+
* The keyword used to be `"npm"`, which named a *source* - and the wrong one: auto-detection is
|
|
26
|
+
* mostly git, and the registry it may consult is now the ecosystem's business (see
|
|
27
|
+
* `ManifestProvider.publishedVersion`). What is being chosen here is a *mode*, so it is spelled
|
|
28
|
+
* as one. **A rename, not an alias**: `--from npm` now means a ref literally called `npm`,
|
|
29
|
+
* which is what it should have meant all along.
|
|
30
|
+
*/
|
|
31
|
+
from?: string;
|
|
32
|
+
/** An existing record of what's already been documented (typically a changelog file) - only
|
|
33
|
+
* consulted while auto-detecting (ignored when `from` is an explicit hash). Guards against a
|
|
34
|
+
* gap: if this file's own last-modifying commit is *older* than the tag detected from the
|
|
35
|
+
* registry - e.g. the file was last updated for 1.1.0, but 1.2.0-1.5.0 were released without
|
|
36
|
+
* ever documenting them, and the registry now reports 1.5.0 - starting from the tag alone would
|
|
37
|
+
* silently skip everything the file never recorded. The boundary becomes the merge-base of the
|
|
38
|
+
* two, so the result always covers at least as much as the file is missing. Has no effect when
|
|
39
|
+
* the file doesn't exist. */
|
|
40
|
+
catchUpFile?: string;
|
|
41
|
+
}
|
|
42
|
+
/** `.rmanrc changelog.tagPattern` (cascaded, per-package overridable) - a glob for this package's
|
|
43
|
+
* release tags. `{name}` (if present) is replaced with the package's own name, e.g. `{name}@*`
|
|
44
|
+
* for independent per-package versioning (`@scope/pkg@1.2.3`, the same scheme lerna/changesets
|
|
45
|
+
* use - `@`/`/` are both fine in a git tag name). Without `{name}`, it's a single repo-wide tag
|
|
46
|
+
* shared by every package (e.g. the default `v*`). */
|
|
47
|
+
function tagPattern(pkg: Package): string;
|
|
48
|
+
/** This package's most recent release tag - the `{name}`-bearing pattern looks up that package's
|
|
49
|
+
* *own* tags directly (newest by version sort); a repo-wide pattern instead finds the nearest tag
|
|
50
|
+
* HEAD actually descends from, since no single package "owns" that tag. `undefined` if never
|
|
51
|
+
* tagged at all (a fresh package, or one that's never been released). Shared by `changelog`
|
|
52
|
+
* (reading the last-documented version) and `version` (finding the boundary a bump measures
|
|
53
|
+
* "since"). */
|
|
54
|
+
function findLatestTag(git: GitHelper, pkg: Package): Promise<string | undefined>;
|
|
55
|
+
/** The forward direction of `findLatestTag`: expands `pkg`'s (cascaded) `.rmanrc
|
|
56
|
+
* changelog.tagPattern` into the concrete tag name `version` belongs under - `{name}` becomes the
|
|
57
|
+
* package's own name, `*` becomes `version`. Shared by `version` (creating the tag),
|
|
58
|
+
* `github-release` (finding the release that tag belongs to), and `detect`'s own registry
|
|
59
|
+
* fallback (mapping a published version back onto a tag), so all three name tags identically. */
|
|
60
|
+
function expandTag(pkg: Package, version: string): string;
|
|
61
|
+
/** The pattern expansion `expandTag` performs, on any pattern - `{name}` becomes `name`, `*`
|
|
62
|
+
* becomes `version`. Shared with the repository's own release tag, which uses a different pattern
|
|
63
|
+
* (see `releaseTagPattern`) but names tags the same way. */
|
|
64
|
+
function applyTagPattern(pattern: string, name: string, version: string): string;
|
|
65
|
+
/** Strips the pattern's literal prefix (everything before its first `*`) from `tag` to get just
|
|
66
|
+
* the version part - e.g. tag `@sqb/builder@1.2.3` against pattern `@sqb/builder@*` -> `1.2.3`.
|
|
67
|
+
* A pattern with no `*` is returned as its own "version" verbatim (an exact tag, nothing to
|
|
68
|
+
* strip). */
|
|
69
|
+
function extractVersion(tag: string, expandedPattern: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Resolves the commit/hash a package's changes should be measured "since" - the boundary
|
|
72
|
+
* `changelog --from` uses, but reusable anywhere a command wants to answer "what changed for this
|
|
73
|
+
* package". An explicit `options.from` (anything but `AUTO`) is returned as-is, applying the same
|
|
74
|
+
* way to every package. Otherwise, it's auto-detected in order: (1) this package's own most recent
|
|
75
|
+
* release tag - the same network-free `findLatestTag` lookup `version`/`changed` themselves use,
|
|
76
|
+
* so all three commands agree on "since when" for any repo whose tags are the ones `rman version`
|
|
77
|
+
* actually created; (2) failing that (no tag at all yet - e.g. onboarding `rman` onto a repo with
|
|
78
|
+
* real release history but no `rman`-created tags), whatever this package's **own ecosystem**
|
|
79
|
+
* reports as its published version (`ManifestProvider.publishedVersion`), mapped to a git tag via
|
|
80
|
+
* `.rmanrc changelog.tagPattern` and used only if that tag actually exists. Either way, if
|
|
81
|
+
* `catchUpFile` is given and exists, the result is widened to also cover anything that file
|
|
82
|
+
* hasn't caught up on yet (see its doc comment). Returns `undefined` when nothing can be resolved
|
|
83
|
+
* at all (never tagged *and* never published, no catch-up file - a genuinely first-ever release) -
|
|
84
|
+
* callers should fall back to their own default in that case (e.g. the whole history, since
|
|
85
|
+
* nothing has ever been released).
|
|
86
|
+
*/
|
|
87
|
+
function detect(git: GitHelper, pkg: Package, options?: DetectOptions): Promise<string | undefined>;
|
|
88
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Manifest } from '../core/manifest.js';
|
|
2
|
+
/**
|
|
3
|
+
* Release boundaries and the tag names that mark them - **the single source for both directions**.
|
|
4
|
+
*
|
|
5
|
+
* Every command that asks question A ("what changed since this package's last release") comes
|
|
6
|
+
* through `detect`, and every command that needs to *name* a tag comes through `expandTag` /
|
|
7
|
+
* `findLatestTag`. No command builds a tag name of its own; that is what keeps `version`'s tag,
|
|
8
|
+
* `changelog`'s boundary and `github-release`'s lookup from drifting apart.
|
|
9
|
+
*
|
|
10
|
+
* A namespace for the reason `Manifest` and `Workspace` are ones: it is one subject with several
|
|
11
|
+
* operations, so the operations are named for what they do rather than carrying the subject in each
|
|
12
|
+
* name (`ChangeHashService.detect`, not `detectChangeHash`), and a plugin can augment it.
|
|
13
|
+
*/
|
|
14
|
+
export var ChangeHashService;
|
|
15
|
+
(function (ChangeHashService) {
|
|
16
|
+
/** The one keyword `from` accepts instead of a ref: "work it out per package". Omitting `from`
|
|
17
|
+
* means the same thing - this exists so a pipeline can say it explicitly. */
|
|
18
|
+
ChangeHashService.AUTO = 'auto';
|
|
19
|
+
/** `.rmanrc changelog.tagPattern` (cascaded, per-package overridable) - a glob for this package's
|
|
20
|
+
* release tags. `{name}` (if present) is replaced with the package's own name, e.g. `{name}@*`
|
|
21
|
+
* for independent per-package versioning (`@scope/pkg@1.2.3`, the same scheme lerna/changesets
|
|
22
|
+
* use - `@`/`/` are both fine in a git tag name). Without `{name}`, it's a single repo-wide tag
|
|
23
|
+
* shared by every package (e.g. the default `v*`). */
|
|
24
|
+
function tagPattern(pkg) {
|
|
25
|
+
const cfg = pkg.config?.changelog;
|
|
26
|
+
return typeof cfg?.tagPattern === 'string' && cfg.tagPattern ? cfg.tagPattern : DEFAULT_TAG_PATTERN;
|
|
27
|
+
}
|
|
28
|
+
ChangeHashService.tagPattern = tagPattern;
|
|
29
|
+
/** This package's most recent release tag - the `{name}`-bearing pattern looks up that package's
|
|
30
|
+
* *own* tags directly (newest by version sort); a repo-wide pattern instead finds the nearest tag
|
|
31
|
+
* HEAD actually descends from, since no single package "owns" that tag. `undefined` if never
|
|
32
|
+
* tagged at all (a fresh package, or one that's never been released). Shared by `changelog`
|
|
33
|
+
* (reading the last-documented version) and `version` (finding the boundary a bump measures
|
|
34
|
+
* "since"). */
|
|
35
|
+
async function findLatestTag(git, pkg) {
|
|
36
|
+
const pattern = tagPattern(pkg);
|
|
37
|
+
const expanded = pattern.replace('{name}', pkg.name);
|
|
38
|
+
return pattern.includes('{name}') ? (await git.listTags(expanded))[0] : await git.describeTag(expanded);
|
|
39
|
+
}
|
|
40
|
+
ChangeHashService.findLatestTag = findLatestTag;
|
|
41
|
+
/** The forward direction of `findLatestTag`: expands `pkg`'s (cascaded) `.rmanrc
|
|
42
|
+
* changelog.tagPattern` into the concrete tag name `version` belongs under - `{name}` becomes the
|
|
43
|
+
* package's own name, `*` becomes `version`. Shared by `version` (creating the tag),
|
|
44
|
+
* `github-release` (finding the release that tag belongs to), and `detect`'s own registry
|
|
45
|
+
* fallback (mapping a published version back onto a tag), so all three name tags identically. */
|
|
46
|
+
function expandTag(pkg, version) {
|
|
47
|
+
return applyTagPattern(tagPattern(pkg), pkg.name, version);
|
|
48
|
+
}
|
|
49
|
+
ChangeHashService.expandTag = expandTag;
|
|
50
|
+
/** The pattern expansion `expandTag` performs, on any pattern - `{name}` becomes `name`, `*`
|
|
51
|
+
* becomes `version`. Shared with the repository's own release tag, which uses a different pattern
|
|
52
|
+
* (see `releaseTagPattern`) but names tags the same way. */
|
|
53
|
+
function applyTagPattern(pattern, name, version) {
|
|
54
|
+
const expanded = pattern.replace('{name}', name);
|
|
55
|
+
const starIdx = expanded.indexOf('*');
|
|
56
|
+
return starIdx === -1 ? expanded : expanded.slice(0, starIdx) + version + expanded.slice(starIdx + 1);
|
|
57
|
+
}
|
|
58
|
+
ChangeHashService.applyTagPattern = applyTagPattern;
|
|
59
|
+
/** Strips the pattern's literal prefix (everything before its first `*`) from `tag` to get just
|
|
60
|
+
* the version part - e.g. tag `@sqb/builder@1.2.3` against pattern `@sqb/builder@*` -> `1.2.3`.
|
|
61
|
+
* A pattern with no `*` is returned as its own "version" verbatim (an exact tag, nothing to
|
|
62
|
+
* strip). */
|
|
63
|
+
function extractVersion(tag, expandedPattern) {
|
|
64
|
+
const starIdx = expandedPattern.indexOf('*');
|
|
65
|
+
if (starIdx === -1)
|
|
66
|
+
return tag;
|
|
67
|
+
const prefix = expandedPattern.slice(0, starIdx);
|
|
68
|
+
return tag.startsWith(prefix) ? tag.slice(prefix.length) : tag;
|
|
69
|
+
}
|
|
70
|
+
ChangeHashService.extractVersion = extractVersion;
|
|
71
|
+
/**
|
|
72
|
+
* Resolves the commit/hash a package's changes should be measured "since" - the boundary
|
|
73
|
+
* `changelog --from` uses, but reusable anywhere a command wants to answer "what changed for this
|
|
74
|
+
* package". An explicit `options.from` (anything but `AUTO`) is returned as-is, applying the same
|
|
75
|
+
* way to every package. Otherwise, it's auto-detected in order: (1) this package's own most recent
|
|
76
|
+
* release tag - the same network-free `findLatestTag` lookup `version`/`changed` themselves use,
|
|
77
|
+
* so all three commands agree on "since when" for any repo whose tags are the ones `rman version`
|
|
78
|
+
* actually created; (2) failing that (no tag at all yet - e.g. onboarding `rman` onto a repo with
|
|
79
|
+
* real release history but no `rman`-created tags), whatever this package's **own ecosystem**
|
|
80
|
+
* reports as its published version (`ManifestProvider.publishedVersion`), mapped to a git tag via
|
|
81
|
+
* `.rmanrc changelog.tagPattern` and used only if that tag actually exists. Either way, if
|
|
82
|
+
* `catchUpFile` is given and exists, the result is widened to also cover anything that file
|
|
83
|
+
* hasn't caught up on yet (see its doc comment). Returns `undefined` when nothing can be resolved
|
|
84
|
+
* at all (never tagged *and* never published, no catch-up file - a genuinely first-ever release) -
|
|
85
|
+
* callers should fall back to their own default in that case (e.g. the whole history, since
|
|
86
|
+
* nothing has ever been released).
|
|
87
|
+
*/
|
|
88
|
+
async function detect(git, pkg, options = {}) {
|
|
89
|
+
if (options.from && options.from !== ChangeHashService.AUTO)
|
|
90
|
+
return options.from;
|
|
91
|
+
let tagHash = await findLatestTag(git, pkg);
|
|
92
|
+
if (!tagHash) {
|
|
93
|
+
/** Through the package's own ecosystem, not through npm: which registry (if any) knows about
|
|
94
|
+
* this package is the `ManifestProvider`'s answer, and in a polyglot repository it differs
|
|
95
|
+
* per package. A repository naming no plugin gets `undefined` here and git tags decide
|
|
96
|
+
* alone. */
|
|
97
|
+
const publishedVersion = await Manifest.publishedVersion(pkg);
|
|
98
|
+
if (publishedVersion) {
|
|
99
|
+
const tag = expandTag(pkg, publishedVersion);
|
|
100
|
+
tagHash = (await git.tagExists(tag)) ? tag : undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const fileHash = options.catchUpFile ? await git.lastCommitTouching(options.catchUpFile) : undefined;
|
|
104
|
+
if (!fileHash)
|
|
105
|
+
return tagHash;
|
|
106
|
+
if (!tagHash)
|
|
107
|
+
return fileHash;
|
|
108
|
+
return (await git.mergeBase(tagHash, fileHash)) ?? tagHash;
|
|
109
|
+
}
|
|
110
|
+
ChangeHashService.detect = detect;
|
|
111
|
+
})(ChangeHashService || (ChangeHashService = {}));
|
|
112
|
+
const DEFAULT_TAG_PATTERN = 'v*';
|
|
@@ -2,20 +2,15 @@ import type { Package } from '../core/package.js';
|
|
|
2
2
|
import type { Repository } from '../core/repository.js';
|
|
3
3
|
import { type PackageFilterOptions } from '../utils/package-filter.js';
|
|
4
4
|
export declare namespace ChangelogService {
|
|
5
|
-
/** Injectable dependencies shared by `getEntries`/`generate` - currently just the npm registry lookup
|
|
6
|
-
* `detectChangeHash` otherwise makes for itself; overridable so tests (and advanced callers)
|
|
7
|
-
* aren't forced through a real network call. */
|
|
8
|
-
interface Deps {
|
|
9
|
-
npmViewVersion?: (name: string, cwd: string) => Promise<string | undefined>;
|
|
10
|
-
}
|
|
11
5
|
interface Options extends PackageFilterOptions {
|
|
12
6
|
/** Generate the changelog since this commit/hash - applied the same way to every package.
|
|
13
|
-
* Default (also `"
|
|
7
|
+
* Default (also `"auto"` explicitly): auto-detect it per package instead, from that package's
|
|
14
8
|
* own most recent release tag first - the same lookup `VersionService`/`changed` use, so this
|
|
15
|
-
* never disagrees with them - falling back to its
|
|
16
|
-
* it has no tag yet (
|
|
17
|
-
*
|
|
18
|
-
*
|
|
9
|
+
* never disagrees with them - falling back to whatever its own ecosystem's registry reports
|
|
10
|
+
* only when it has no tag yet (`ManifestProvider.publishedVersion`, and only ever to guess a
|
|
11
|
+
* tag name - see `detectChangeHash`); a package this can't be resolved for either way (never
|
|
12
|
+
* tagged, unpublished, no plugin) has never been released at all, so its whole history counts
|
|
13
|
+
* as unreleased - the same view `version` takes. */
|
|
19
14
|
from?: string;
|
|
20
15
|
/** Generate for the whole repository even when the current directory is inside a single
|
|
21
16
|
* package (which otherwise scopes it to just that package) - see `Repository.currentPackage`. */
|
|
@@ -67,7 +62,7 @@ export declare namespace ChangelogService {
|
|
|
67
62
|
* sake side effect (a "save this" request), not presentation, so it stays here rather than in
|
|
68
63
|
* the CLI command - printing what happened is the command's job.
|
|
69
64
|
*/
|
|
70
|
-
function generateToFile(repository: Repository, options?: Options
|
|
65
|
+
function generateToFile(repository: Repository, options?: Options): Promise<Entry[]>;
|
|
71
66
|
/**
|
|
72
67
|
* Computes a changelog entry per package (root included) from real commits only - either
|
|
73
68
|
* everything since a given `--from <hash>` (applied the same way to every package), or, by
|
|
@@ -107,5 +102,5 @@ export declare namespace ChangelogService {
|
|
|
107
102
|
* Run from inside a single package's own directory, it only covers that package unless
|
|
108
103
|
* `options.root` says otherwise (see `Repository.currentPackage`).
|
|
109
104
|
*/
|
|
110
|
-
function getEntries(repository: Repository, options?: Options
|
|
105
|
+
function getEntries(repository: Repository, options?: Options): Promise<Entry[]>;
|
|
111
106
|
}
|