rman 1.0.1 → 1.0.4
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 +32 -14
- package/cli.js +2 -2
- package/commands/version.command.js +12 -0
- package/constants.js +1 -1
- package/core/config.d.ts +77 -3
- package/core/config.js +61 -5
- package/core/package.d.ts +2 -1
- package/core/repository.d.ts +6 -2
- package/core/repository.js +17 -8
- package/index.d.ts +2 -0
- package/index.js +1 -10
- package/package.json +3 -2
- package/rmanrc.schema.json +202 -0
package/README.md
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
[![NPM Version][npm-image]][npm-url]
|
|
4
4
|
[![NPM Downloads][downloads-image]][downloads-url]
|
|
5
|
-
[![
|
|
6
|
-
[![
|
|
5
|
+
[![CI Tests][ci-test-image]][ci-test-url]
|
|
6
|
+
[![Test Coverage][coveralls-image]][coveralls-url]
|
|
7
7
|
|
|
8
8
|
**rman** is a monorepo management CLI: a self-contained alternative to reaching for Lerna,
|
|
9
9
|
Changesets, and a handful of shell scripts glued together. One tool for running scripts across
|
|
10
10
|
packages, computing semantic version bumps from your commit history, publishing, changelogs,
|
|
11
11
|
importing external repos with history intact, and more - all driven by a single, cascading
|
|
12
|
-
`.rmanrc`/`.
|
|
12
|
+
`.rmanrc`/`.rmanrc.yml` config.
|
|
13
13
|
|
|
14
14
|
Every command is also available as a **programmatic API** - see [docs/api.md](docs/api.md) if you
|
|
15
15
|
want to call `rman`'s logic directly from a Node.js script instead of shelling out to the CLI.
|
|
@@ -243,6 +243,7 @@ rman version --changelog # also write/fold in each bumped package's CHAN
|
|
|
243
243
|
rman version patch --push # commit, tag, and push in one go
|
|
244
244
|
rman version patch --message "chore(release): {version}"
|
|
245
245
|
rman version --ignore-dirty # exclude dirty packages instead of aborting the whole run
|
|
246
|
+
rman version patch --show # preview what an explicit patch bump would do, without applying it
|
|
246
247
|
```
|
|
247
248
|
|
|
248
249
|
Severity, when not given explicitly, is auto-detected per package/group from
|
|
@@ -298,12 +299,13 @@ covered, then run `rman ci` to install it.
|
|
|
298
299
|
|
|
299
300
|
`rman` reads config cascaded from the repository root down to each package's own directory (the
|
|
300
301
|
same way a `tsconfig.json` `extends` chain works) - a value set closer to a package overrides the
|
|
301
|
-
same key set further up.
|
|
302
|
-
precedence: `package.json`'s own `"rman"` key, `.
|
|
303
|
-
the dotfile-style name).
|
|
302
|
+
same key set further up. Several file forms are supported per directory, merged in increasing
|
|
303
|
+
precedence: `package.json`'s own `"rman"` key, `.rmanrc.yml` (YAML), `.rmanrc` (**JSON**, despite
|
|
304
|
+
the dotfile-style name), and `.rmanrc.cjs`/`.rmanrc.mjs`/`.rmanrc.js` for config that needs real
|
|
305
|
+
logic (a JS module's default export).
|
|
304
306
|
|
|
305
307
|
```yaml
|
|
306
|
-
# .
|
|
308
|
+
# .rmanrc.yml, at the repository root
|
|
307
309
|
packageManager: pnpm
|
|
308
310
|
logLevel: info
|
|
309
311
|
allowBranch: [main, release/*]
|
|
@@ -337,10 +339,26 @@ run:
|
|
|
337
339
|
{ "group": "plugins" }
|
|
338
340
|
```
|
|
339
341
|
|
|
340
|
-
See [docs/api.md#configuration-rmanrc-
|
|
342
|
+
See [docs/api.md#configuration-rmanrc-rmanrcyml](docs/api.md#configuration-rmanrc-rmanrcyml) for the
|
|
341
343
|
full key reference (every `run.<script>.*` sub-key, `clean.*`, `changelog.*`, precedence rules,
|
|
342
344
|
and which keys are root-level-only today).
|
|
343
345
|
|
|
346
|
+
**Editor autocomplete:** `rman` ships a JSON Schema for `.rmanrc`/`.rmanrc.yml` at
|
|
347
|
+
`rman/rmanrc.schema.json` - add `"$schema": "./node_modules/rman/rmanrc.schema.json"` to your
|
|
348
|
+
`.rmanrc` (or the equivalent `# yaml-language-server: $schema=...` comment in `.rmanrc.yml`) to get
|
|
349
|
+
autocomplete and validation in VS Code/WebStorm. For a `.rmanrc.cjs`/`.mjs`/`.js` config, wrap it in
|
|
350
|
+
the exported `defineConfig()` helper instead for the same autocomplete via the `RmanConfig` type:
|
|
351
|
+
|
|
352
|
+
```js
|
|
353
|
+
// .rmanrc.mjs
|
|
354
|
+
import { defineConfig } from 'rman';
|
|
355
|
+
export default defineConfig({ packageManager: 'pnpm' });
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
See
|
|
359
|
+
[docs/api.md#editor-support-json-schema](docs/api.md#editor-support-json-schema) for details,
|
|
360
|
+
including a WebStorm setup that needs no changes to the config file itself.
|
|
361
|
+
|
|
344
362
|
## Programmatic API
|
|
345
363
|
|
|
346
364
|
Every command above is a thin wrapper around an exported service function - call them directly
|
|
@@ -349,7 +367,7 @@ from your own Node.js scripts without shelling out to the `rman` binary:
|
|
|
349
367
|
```ts
|
|
350
368
|
import { Repository, VersionService } from 'rman';
|
|
351
369
|
|
|
352
|
-
const repository = Repository.create();
|
|
370
|
+
const repository = await Repository.create();
|
|
353
371
|
const plan = await VersionService.getPlan(repository);
|
|
354
372
|
await VersionService.applyPlan(repository, plan, { changelog: true, push: true });
|
|
355
373
|
```
|
|
@@ -366,11 +384,11 @@ Full reference, with detailed examples for every service (`VersionService`, `Pub
|
|
|
366
384
|
|
|
367
385
|
rman is available under the [MIT](LICENSE) license.
|
|
368
386
|
|
|
369
|
-
[npm-image]: https://img.shields.io/npm/v/rman
|
|
387
|
+
[npm-image]: https://img.shields.io/npm/v/rman
|
|
370
388
|
[npm-url]: https://npmjs.org/package/rman
|
|
371
389
|
[downloads-image]: https://img.shields.io/npm/dm/rman.svg
|
|
372
390
|
[downloads-url]: https://npmjs.org/package/rman
|
|
373
|
-
[
|
|
374
|
-
[
|
|
375
|
-
[
|
|
376
|
-
[
|
|
391
|
+
[ci-test-image]: https://github.com/panates/rman/actions/workflows/test.yml/badge.svg
|
|
392
|
+
[ci-test-url]: https://github.com/panates/rman/actions/workflows/test.yml
|
|
393
|
+
[coveralls-image]: https://img.shields.io/coveralls/panates/rman/dev.svg
|
|
394
|
+
[coveralls-url]: https://coveralls.io/r/panates/rman
|
package/cli.js
CHANGED
|
@@ -23,10 +23,10 @@ import { Repository } from './core/repository.js';
|
|
|
23
23
|
import { LOG_LEVELS } from './utils/logger.js';
|
|
24
24
|
export async function runCli(options) {
|
|
25
25
|
try {
|
|
26
|
-
const repository = Repository.create(options?.cwd);
|
|
26
|
+
const repository = await Repository.create(options?.cwd);
|
|
27
27
|
const _argv = options?.argv || hideBin(process.argv);
|
|
28
28
|
const program = yargs(_argv)
|
|
29
|
-
.scriptName('
|
|
29
|
+
.scriptName('rman')
|
|
30
30
|
.version(version)
|
|
31
31
|
.alias('version', 'v')
|
|
32
32
|
.usage('$0 <cmd> [options...]')
|
|
@@ -11,6 +11,7 @@ export function initCli(repository, program) {
|
|
|
11
11
|
.example('$0 version patch', '# Bump patch severity directly, applied immediately')
|
|
12
12
|
.example('$0 version', "# Auto-detect severity from commits, show the plan, don't write anything")
|
|
13
13
|
.example('$0 version --interactive', '# Show the plan either way, then ask for confirmation')
|
|
14
|
+
.example('$0 version patch --show', '# Preview what an explicit patch bump would do, without applying it')
|
|
14
15
|
.positional('bump', {
|
|
15
16
|
describe: 'A release-type keyword ("patch"/"minor"/"major") or an explicit semver version. ' +
|
|
16
17
|
'Omit to auto-detect from commits and only preview the plan.',
|
|
@@ -21,6 +22,13 @@ export function initCli(repository, program) {
|
|
|
21
22
|
describe: 'Show the plan and ask for confirmation before applying (with or without an explicit bump)',
|
|
22
23
|
type: 'boolean',
|
|
23
24
|
})
|
|
25
|
+
.option('show', {
|
|
26
|
+
describe: 'Show the resulting plan for the given bump without applying it - unlike omitting bump ' +
|
|
27
|
+
'entirely, this still uses the given release-type keyword/version to compute the plan, ' +
|
|
28
|
+
'just never writes it.',
|
|
29
|
+
type: 'boolean',
|
|
30
|
+
})
|
|
31
|
+
.conflicts('show', 'interactive')
|
|
24
32
|
.option('ignore-dirty', {
|
|
25
33
|
describe: 'Exclude a package with uncommitted local changes instead of aborting the whole run',
|
|
26
34
|
type: 'boolean',
|
|
@@ -70,6 +78,10 @@ export function initCli(repository, program) {
|
|
|
70
78
|
console.log(colors.gray('Nothing to version.'));
|
|
71
79
|
return;
|
|
72
80
|
}
|
|
81
|
+
if (args.show) {
|
|
82
|
+
console.log(colors.gray('Preview only (--show) - nothing was written.'));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
73
85
|
let apply = !!bump;
|
|
74
86
|
if (args.interactive) {
|
|
75
87
|
apply = await confirm('Apply these changes?');
|
package/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.0.
|
|
1
|
+
export const version = '1.0.4';
|
package/core/config.d.ts
CHANGED
|
@@ -1,8 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of `.rmanrc`/`.rmanrc.yml`/`.rmanrc.cjs`/`.mjs`/`.js` (and `package.json`'s own
|
|
3
|
+
* `"rman"` key) - see docs/api.md#configuration-rmanrc-rmanrcyml for the full reference. Every
|
|
4
|
+
* field is optional and cascades from the repository root down to each package's own directory.
|
|
5
|
+
* Purely a typing aid (used by `defineConfig` below, and importable on its own for a `.rmanrc.ts`/
|
|
6
|
+
* `.mts` authored config, or a plain `: RmanConfig` annotation) - never read by rman itself, which
|
|
7
|
+
* only ever sees the plain JS object a JS config file exports.
|
|
8
|
+
*/
|
|
9
|
+
export interface RmanConfig {
|
|
10
|
+
packageManager?: 'npm' | 'yarn' | 'pnpm' | 'bun';
|
|
11
|
+
logLevel?: 'silent' | 'error' | 'info' | 'verbose';
|
|
12
|
+
allowBranch?: string | string[];
|
|
13
|
+
ignoreBranch?: string | string[];
|
|
14
|
+
group?: boolean | string;
|
|
15
|
+
version?: RmanConfig.VersionOptions;
|
|
16
|
+
changelog?: RmanConfig.ChangelogOptions;
|
|
17
|
+
clean?: RmanConfig.CleanOptions;
|
|
18
|
+
/** Keyed by npm script name (e.g. `"build"`, `"lint"`, `"test"`). */
|
|
19
|
+
run?: Record<string, RmanConfig.RunScriptOptions>;
|
|
20
|
+
/** Keyed by the in-repo package's own name. */
|
|
21
|
+
packages?: Record<string, RmanConfig.PackageOptions>;
|
|
22
|
+
}
|
|
23
|
+
export declare namespace RmanConfig {
|
|
24
|
+
interface VersionOptions {
|
|
25
|
+
commitMessage?: string;
|
|
26
|
+
script?: string | string[];
|
|
27
|
+
preScript?: string | string[];
|
|
28
|
+
postScript?: string | string[];
|
|
29
|
+
}
|
|
30
|
+
interface ChangelogOptions {
|
|
31
|
+
ignoreTypes?: string[];
|
|
32
|
+
template?: string;
|
|
33
|
+
filePath?: string;
|
|
34
|
+
tagPattern?: string;
|
|
35
|
+
}
|
|
36
|
+
interface CleanOptions {
|
|
37
|
+
include?: string | string[];
|
|
38
|
+
exclude?: string | string[];
|
|
39
|
+
skip?: boolean;
|
|
40
|
+
}
|
|
41
|
+
interface RunScriptOptions {
|
|
42
|
+
concurrency?: number;
|
|
43
|
+
topo?: boolean;
|
|
44
|
+
bail?: boolean;
|
|
45
|
+
progress?: boolean;
|
|
46
|
+
logLevel?: 'silent' | 'error' | 'info' | 'verbose';
|
|
47
|
+
changedSince?: string;
|
|
48
|
+
skip?: boolean;
|
|
49
|
+
if?: string;
|
|
50
|
+
script?: string | string[];
|
|
51
|
+
preScript?: string | string[];
|
|
52
|
+
postScript?: string | string[];
|
|
53
|
+
override?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface PackageOptions {
|
|
56
|
+
dependencies?: string[] | Record<string, string>;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Identity helper for authoring a `.rmanrc.cjs`/`.mjs`/`.js` config with full type-checking and
|
|
61
|
+
* autocomplete - the same `defineConfig` pattern Vite/Vitest use. Returns `config` completely
|
|
62
|
+
* unchanged; this exists purely so TypeScript can infer/check against `RmanConfig`, not for any
|
|
63
|
+
* runtime behavior:
|
|
64
|
+
*
|
|
65
|
+
* // .rmanrc.mjs
|
|
66
|
+
* import { defineConfig } from 'rman';
|
|
67
|
+
* export default defineConfig({ packageManager: 'pnpm' });
|
|
68
|
+
*
|
|
69
|
+
* // .rmanrc.cjs
|
|
70
|
+
* const { defineConfig } = require('rman');
|
|
71
|
+
* module.exports = defineConfig({ packageManager: 'pnpm' });
|
|
72
|
+
*/
|
|
73
|
+
export declare function defineConfig(config: RmanConfig): RmanConfig;
|
|
1
74
|
/**
|
|
2
75
|
* Reads the rman configuration defined at a single directory level, merging
|
|
3
|
-
* (in increasing precedence): `package.json#rman`, `.
|
|
76
|
+
* (in increasing precedence): `package.json#rman`, `.rmanrc.yml`, `.rmanrc`,
|
|
77
|
+
* then `.rmanrc.cjs`/`.rmanrc.mjs`/`.rmanrc.js` (whichever exist, in that order).
|
|
4
78
|
*/
|
|
5
|
-
export declare function readDirConfig(dirname: string):
|
|
79
|
+
export declare function readDirConfig(dirname: string): Promise<RmanConfig>;
|
|
6
80
|
/**
|
|
7
81
|
* Resolves the effective config for `targetDir` by cascading from `rootDir`
|
|
8
82
|
* down to `targetDir` (inclusive), the same way tsconfig's `extends` chain
|
|
@@ -10,4 +84,4 @@ export declare function readDirConfig(dirname: string): any;
|
|
|
10
84
|
* package (or any intermediate directory) narrow or override the repository's
|
|
11
85
|
* root configuration for itself and everything below it.
|
|
12
86
|
*/
|
|
13
|
-
export declare function resolveConfig(rootDir: string, targetDir: string, cache?: Map<string,
|
|
87
|
+
export declare function resolveConfig(rootDir: string, targetDir: string, cache?: Map<string, RmanConfig>): Promise<RmanConfig>;
|
package/core/config.js
CHANGED
|
@@ -1,12 +1,60 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import * as yaml from 'js-yaml';
|
|
3
|
+
import { createRequire } from 'module';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
import merge from 'putil-merge';
|
|
6
|
+
import { pathToFileURL } from 'url';
|
|
7
|
+
/**
|
|
8
|
+
* Identity helper for authoring a `.rmanrc.cjs`/`.mjs`/`.js` config with full type-checking and
|
|
9
|
+
* autocomplete - the same `defineConfig` pattern Vite/Vitest use. Returns `config` completely
|
|
10
|
+
* unchanged; this exists purely so TypeScript can infer/check against `RmanConfig`, not for any
|
|
11
|
+
* runtime behavior:
|
|
12
|
+
*
|
|
13
|
+
* // .rmanrc.mjs
|
|
14
|
+
* import { defineConfig } from 'rman';
|
|
15
|
+
* export default defineConfig({ packageManager: 'pnpm' });
|
|
16
|
+
*
|
|
17
|
+
* // .rmanrc.cjs
|
|
18
|
+
* const { defineConfig } = require('rman');
|
|
19
|
+
* module.exports = defineConfig({ packageManager: 'pnpm' });
|
|
20
|
+
*/
|
|
21
|
+
export function defineConfig(config) {
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
/** `.rmanrc.cjs`/`.rmanrc.mjs`/`.rmanrc.js`, checked in this order - a JS module whose default
|
|
25
|
+
* export (or, lacking one, the module's own exports object) is the config. Both CommonJS (`.cjs`,
|
|
26
|
+
* or a `.js` under a `"type": "commonjs"` package.json) and native ESM (`.mjs`, or a `.js` under
|
|
27
|
+
* `"type": "module"`) are supported - the reason `readDirConfig`/`resolveConfig` are async at all. */
|
|
28
|
+
const JS_CONFIG_FILES = ['.rmanrc.cjs', '.rmanrc.mjs', '.rmanrc.js'];
|
|
29
|
+
const requireJsConfig = createRequire(import.meta.url);
|
|
30
|
+
/**
|
|
31
|
+
* Loads `file`'s config object. Tries `require()` first - not just an optimization: a CommonJS
|
|
32
|
+
* module's `module.exports` is more reliably observed this way than through dynamic `import()`'s
|
|
33
|
+
* CJS-interop synthesis, which some ESM loader hooks (e.g. ts-node/swc-node-style transpilers
|
|
34
|
+
* registered via `--import`) can end up short-circuiting into an empty object. `require()` throws
|
|
35
|
+
* `ERR_REQUIRE_ESM` for a genuinely-ESM file (`.mjs`, or `.js` under `"type": "module"`) - only
|
|
36
|
+
* then does this fall back to `import()`, the one case that actually needs it. Either path can
|
|
37
|
+
* hand back an ES module namespace instead of a plain object (Node's `require(esm)` support does
|
|
38
|
+
* this too, not just `import()`), so `.default` is preferred whenever present.
|
|
39
|
+
*/
|
|
40
|
+
async function loadJsConfig(file) {
|
|
41
|
+
let mod;
|
|
42
|
+
try {
|
|
43
|
+
mod = requireJsConfig(file);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
if (e?.code !== 'ERR_REQUIRE_ESM')
|
|
47
|
+
throw e;
|
|
48
|
+
mod = await import(pathToFileURL(file).href);
|
|
49
|
+
}
|
|
50
|
+
return mod?.default ?? mod;
|
|
51
|
+
}
|
|
5
52
|
/**
|
|
6
53
|
* Reads the rman configuration defined at a single directory level, merging
|
|
7
|
-
* (in increasing precedence): `package.json#rman`, `.
|
|
54
|
+
* (in increasing precedence): `package.json#rman`, `.rmanrc.yml`, `.rmanrc`,
|
|
55
|
+
* then `.rmanrc.cjs`/`.rmanrc.mjs`/`.rmanrc.js` (whichever exist, in that order).
|
|
8
56
|
*/
|
|
9
|
-
export function readDirConfig(dirname) {
|
|
57
|
+
export async function readDirConfig(dirname) {
|
|
10
58
|
const result = {};
|
|
11
59
|
const pkgJsonFile = path.join(dirname, 'package.json');
|
|
12
60
|
if (fs.existsSync(pkgJsonFile)) {
|
|
@@ -14,7 +62,7 @@ export function readDirConfig(dirname) {
|
|
|
14
62
|
if (pkgJson && typeof pkgJson.rman === 'object')
|
|
15
63
|
merge(result, pkgJson.rman, { deep: true });
|
|
16
64
|
}
|
|
17
|
-
const ymlFile = path.join(dirname, '.
|
|
65
|
+
const ymlFile = path.join(dirname, '.rmanrc.yml');
|
|
18
66
|
if (fs.existsSync(ymlFile)) {
|
|
19
67
|
const obj = yaml.load(fs.readFileSync(ymlFile, 'utf-8'));
|
|
20
68
|
if (obj && typeof obj === 'object')
|
|
@@ -26,6 +74,14 @@ export function readDirConfig(dirname) {
|
|
|
26
74
|
if (obj && typeof obj === 'object')
|
|
27
75
|
merge(result, obj, { deep: true });
|
|
28
76
|
}
|
|
77
|
+
for (const jsFileName of JS_CONFIG_FILES) {
|
|
78
|
+
const jsFile = path.join(dirname, jsFileName);
|
|
79
|
+
if (fs.existsSync(jsFile)) {
|
|
80
|
+
const obj = await loadJsConfig(jsFile);
|
|
81
|
+
if (obj && typeof obj === 'object')
|
|
82
|
+
merge(result, obj, { deep: true });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
29
85
|
return result;
|
|
30
86
|
}
|
|
31
87
|
/**
|
|
@@ -35,12 +91,12 @@ export function readDirConfig(dirname) {
|
|
|
35
91
|
* package (or any intermediate directory) narrow or override the repository's
|
|
36
92
|
* root configuration for itself and everything below it.
|
|
37
93
|
*/
|
|
38
|
-
export function resolveConfig(rootDir, targetDir, cache = new Map()) {
|
|
94
|
+
export async function resolveConfig(rootDir, targetDir, cache = new Map()) {
|
|
39
95
|
const result = {};
|
|
40
96
|
for (const dir of dirChain(rootDir, targetDir)) {
|
|
41
97
|
let local = cache.get(dir);
|
|
42
98
|
if (!local) {
|
|
43
|
-
local = readDirConfig(dir);
|
|
99
|
+
local = await readDirConfig(dir);
|
|
44
100
|
cache.set(dir, local);
|
|
45
101
|
}
|
|
46
102
|
merge(result, local, { deep: true });
|
package/core/package.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { RmanConfig } from './config.js';
|
|
1
2
|
export declare class Package {
|
|
2
3
|
readonly dirname: string;
|
|
3
4
|
private _json;
|
|
4
5
|
dependencies: string[];
|
|
5
6
|
/** Effective rman config for this package, cascaded from the repository root. */
|
|
6
|
-
config:
|
|
7
|
+
config: RmanConfig;
|
|
7
8
|
constructor(dirname: string);
|
|
8
9
|
get basename(): string;
|
|
9
10
|
get name(): string;
|
package/core/repository.d.ts
CHANGED
|
@@ -41,11 +41,15 @@ export declare class Repository extends Package {
|
|
|
41
41
|
* package, cascading root -> intermediate directories -> package directory,
|
|
42
42
|
* so a `.rmanrc` placed anywhere along that path overrides the levels above it.
|
|
43
43
|
*/
|
|
44
|
-
protected _resolveConfigs(): void
|
|
44
|
+
protected _resolveConfigs(): Promise<void>;
|
|
45
45
|
protected _updateDependencies(): void;
|
|
46
46
|
static create(root?: string, options?: {
|
|
47
47
|
deep?: number;
|
|
48
|
-
}): Repository
|
|
48
|
+
}): Promise<Repository>;
|
|
49
|
+
/** Finishes constructing `repo` with the async work a constructor can't do itself - resolving
|
|
50
|
+
* `.rmanrc`/`.rmanrc.yml`/`.rmanrc.cjs`/`.mjs`/`.js` config (which may need a dynamic `import()`)
|
|
51
|
+
* before the dependency graph is built from it. */
|
|
52
|
+
private static _init;
|
|
49
53
|
protected static _resolvePackages(dirname: string, patterns: string[]): Package[];
|
|
50
54
|
}
|
|
51
55
|
export declare namespace Repository {
|
package/core/repository.js
CHANGED
|
@@ -23,8 +23,9 @@ export class Repository extends Package {
|
|
|
23
23
|
this.rootPackage = new Package(dirname);
|
|
24
24
|
if (!monorepo)
|
|
25
25
|
this.packages = [this.rootPackage];
|
|
26
|
-
|
|
27
|
-
this
|
|
26
|
+
// Config resolution can load a `.rmanrc.cjs`/`.mjs`/`.js` module (dynamic `import()`, always
|
|
27
|
+
// async) - a constructor can't `await`, so `create()` finishes this instance off via `_init()`
|
|
28
|
+
// once construction itself (synchronous) completes.
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
31
|
* The package whose own directory contains `cwd` (the deepest match, so a package nested
|
|
@@ -90,15 +91,15 @@ export class Repository extends Package {
|
|
|
90
91
|
* package, cascading root -> intermediate directories -> package directory,
|
|
91
92
|
* so a `.rmanrc` placed anywhere along that path overrides the levels above it.
|
|
92
93
|
*/
|
|
93
|
-
_resolveConfigs() {
|
|
94
|
+
async _resolveConfigs() {
|
|
94
95
|
const cache = new Map();
|
|
95
|
-
const rootConfig = resolveConfig(this.dirname, this.dirname, cache);
|
|
96
|
+
const rootConfig = await resolveConfig(this.dirname, this.dirname, cache);
|
|
96
97
|
this.config = rootConfig;
|
|
97
98
|
this.rootPackage.config = rootConfig;
|
|
98
99
|
for (const pkg of this.packages) {
|
|
99
100
|
if (pkg === this.rootPackage)
|
|
100
101
|
continue;
|
|
101
|
-
pkg.config = resolveConfig(this.dirname, pkg.dirname, cache);
|
|
102
|
+
pkg.config = await resolveConfig(this.dirname, pkg.dirname, cache);
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
_updateDependencies() {
|
|
@@ -149,7 +150,7 @@ export class Repository extends Package {
|
|
|
149
150
|
deepFindDependencies(pkg, pkg.dependencies);
|
|
150
151
|
}
|
|
151
152
|
}
|
|
152
|
-
static create(root, options) {
|
|
153
|
+
static async create(root, options) {
|
|
153
154
|
const dirname = root || process.cwd();
|
|
154
155
|
let deep = options?.deep ?? 10;
|
|
155
156
|
let pkgDirname = dirname;
|
|
@@ -159,7 +160,7 @@ export class Repository extends Package {
|
|
|
159
160
|
const pkgJson = JSON.parse(fs.readFileSync(f, 'utf-8'));
|
|
160
161
|
if (Array.isArray(pkgJson.workspaces)) {
|
|
161
162
|
const packages = this._resolvePackages(pkgDirname, pkgJson.workspaces);
|
|
162
|
-
return new Repository(pkgDirname, true, packages, dirname);
|
|
163
|
+
return Repository._init(new Repository(pkgDirname, true, packages, dirname));
|
|
163
164
|
}
|
|
164
165
|
/** If we reach to the root of the project */
|
|
165
166
|
if (fs.existsSync(path.join(pkgDirname, '.git')))
|
|
@@ -167,7 +168,15 @@ export class Repository extends Package {
|
|
|
167
168
|
}
|
|
168
169
|
pkgDirname = path.resolve(pkgDirname, '..');
|
|
169
170
|
}
|
|
170
|
-
return new Repository(dirname, false, [], dirname);
|
|
171
|
+
return Repository._init(new Repository(dirname, false, [], dirname));
|
|
172
|
+
}
|
|
173
|
+
/** Finishes constructing `repo` with the async work a constructor can't do itself - resolving
|
|
174
|
+
* `.rmanrc`/`.rmanrc.yml`/`.rmanrc.cjs`/`.mjs`/`.js` config (which may need a dynamic `import()`)
|
|
175
|
+
* before the dependency graph is built from it. */
|
|
176
|
+
static async _init(repo) {
|
|
177
|
+
await repo._resolveConfigs();
|
|
178
|
+
repo._updateDependencies();
|
|
179
|
+
return repo;
|
|
171
180
|
}
|
|
172
181
|
static _resolvePackages(dirname, patterns) {
|
|
173
182
|
const packages = [];
|
package/index.d.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* CLI-only concerns (argv parsing, `--help` text, and all console/file presentation) stay in
|
|
9
9
|
* `cli.ts` and the individual `commands/*.command.ts` modules, which are not exported here.
|
|
10
10
|
*/
|
|
11
|
+
export type { RmanConfig } from './core/config.js';
|
|
12
|
+
export { defineConfig } from './core/config.js';
|
|
11
13
|
export { Package } from './core/package.js';
|
|
12
14
|
export { Repository } from './core/repository.js';
|
|
13
15
|
export * from './services.js';
|
package/index.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Programmatic API - the same logic the CLI commands run, importable directly without going
|
|
3
|
-
* through yargs/argv. Each domain's logic lives in a `<Name>` namespace under `./services/*.ts`
|
|
4
|
-
* (e.g. `ChangelogService`, `CiService`, `CleanService`, `ListService`, `RunService`,
|
|
5
|
-
* `SystemInfo`, `VersionService`), re-exported here via `./services.js`. Purpose-specific functions, not one
|
|
6
|
-
* generic `run`/`get` per domain (see e.g. `SystemInfo.getSystemInfo`/`getRepositoryInfo`, kept
|
|
7
|
-
* separate since they're genuinely independent capabilities, not just steps of one operation) -
|
|
8
|
-
* CLI-only concerns (argv parsing, `--help` text, and all console/file presentation) stay in
|
|
9
|
-
* `cli.ts` and the individual `commands/*.command.ts` modules, which are not exported here.
|
|
10
|
-
*/
|
|
1
|
+
export { defineConfig } from './core/config.js';
|
|
11
2
|
export { Package } from './core/package.js';
|
|
12
3
|
export { Repository } from './core/repository.js';
|
|
13
4
|
export * from './services.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rman",
|
|
3
3
|
"description": "Repository manager",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.4",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"dependencies": {
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"types": "./index.d.ts",
|
|
36
36
|
"default": "./index.js"
|
|
37
37
|
},
|
|
38
|
-
"./package.json": "./package.json"
|
|
38
|
+
"./package.json": "./package.json",
|
|
39
|
+
"./rmanrc.schema.json": "./rmanrc.schema.json"
|
|
39
40
|
},
|
|
40
41
|
"bin": {
|
|
41
42
|
"rman": "cli.js"
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/panates/rman/main/schemas/rmanrc.schema.json",
|
|
4
|
+
"title": "rman configuration",
|
|
5
|
+
"description": "Schema for rman's .rmanrc (JSON) and .rmanrc.yml (YAML) config files, and the \"rman\" key in package.json. See docs/api.md#configuration-rmanrc-rmanrcyml for the full reference.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Path/URL to this schema - not read by rman itself, purely for editor tooling."
|
|
11
|
+
},
|
|
12
|
+
"packageManager": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"enum": ["npm", "yarn", "pnpm", "bun"],
|
|
15
|
+
"default": "npm",
|
|
16
|
+
"description": "Package manager used by \"ci\"/\"publish\". Root-level only - an explicit --package-manager CLI flag wins over this."
|
|
17
|
+
},
|
|
18
|
+
"logLevel": {
|
|
19
|
+
"$ref": "#/definitions/logLevel",
|
|
20
|
+
"default": "info",
|
|
21
|
+
"description": "Default verbosity of the classic per-step log for run/build/test/ci/clean. Root-level only. An explicit --log-level CLI flag wins over this."
|
|
22
|
+
},
|
|
23
|
+
"allowBranch": {
|
|
24
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
25
|
+
"description": "Refuse to run a state-changing command unless the current git branch matches one of these globs. Root-level only. An explicit --allow-branch CLI flag replaces this entirely (never merges)."
|
|
26
|
+
},
|
|
27
|
+
"ignoreBranch": {
|
|
28
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
29
|
+
"description": "Refuse to run a state-changing command if the current git branch matches one of these globs. Root-level only. An explicit --ignore-branch CLI flag replaces this entirely (never merges)."
|
|
30
|
+
},
|
|
31
|
+
"group": {
|
|
32
|
+
"description": "Release-versioning group for \"version\"/\"publish\": true (default) puts the package in the implicit repo-wide group; a string joins exactly the other packages sharing that string; false makes it a solo, fully independent group. Per-package cascaded.",
|
|
33
|
+
"default": true,
|
|
34
|
+
"oneOf": [{ "type": "boolean" }, { "type": "string", "minLength": 1 }]
|
|
35
|
+
},
|
|
36
|
+
"version": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"description": "Options for the \"version\" command/VersionService.",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"properties": {
|
|
41
|
+
"commitMessage": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"default": "chore(release): v{version}",
|
|
44
|
+
"description": "Commit message for every group a \"version\" run commits. \"{version}\" is substituted when every bumped package in that commit shares one version. Root-level only."
|
|
45
|
+
},
|
|
46
|
+
"script": {
|
|
47
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
48
|
+
"description": "Command(s) to run as this package's own \"version\" npm-lifecycle step, when its package.json does not define one itself."
|
|
49
|
+
},
|
|
50
|
+
"preScript": {
|
|
51
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
52
|
+
"description": "Command(s) to run as this package's own \"preversion\" step, when its package.json does not define one itself."
|
|
53
|
+
},
|
|
54
|
+
"postScript": {
|
|
55
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
56
|
+
"description": "Command(s) to run as this package's own \"postversion\" step, when its package.json does not define one itself."
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"changelog": {
|
|
61
|
+
"type": "object",
|
|
62
|
+
"description": "Options for the \"changelog\" command/ChangelogService, and for \"version --changelog\".",
|
|
63
|
+
"additionalProperties": false,
|
|
64
|
+
"properties": {
|
|
65
|
+
"ignoreTypes": {
|
|
66
|
+
"type": "array",
|
|
67
|
+
"items": { "type": "string" },
|
|
68
|
+
"default": [],
|
|
69
|
+
"description": "Conventional Commit types (e.g. \"chore\", \"ci\") dropped entirely from changelog output, instead of being folded into \"Other Changes\". Per-package cascaded."
|
|
70
|
+
},
|
|
71
|
+
"template": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Path (relative to the repository root) to a changelog template file - not the template text itself. Supports {{package}}/{{version}}/{{date}}/{{commits}} and {{features}}/{{fixes}}/{{other}}. Per-package cascaded."
|
|
74
|
+
},
|
|
75
|
+
"filePath": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"default": "CHANGELOG.md",
|
|
78
|
+
"description": "Where \"--write\" prepends this package's entry, relative to that package's own directory. Per-package cascaded."
|
|
79
|
+
},
|
|
80
|
+
"tagPattern": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"default": "v*",
|
|
83
|
+
"description": "Glob for this package's release tags. Include \"{name}\" for independent per-package tags (e.g. \"{name}@*\"); omit it for one shared repo-wide tag scheme. Per-package cascaded."
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"clean": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"description": "Options for the \"clean\" command/CleanService.",
|
|
90
|
+
"additionalProperties": false,
|
|
91
|
+
"properties": {
|
|
92
|
+
"include": {
|
|
93
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
94
|
+
"description": "Extra glob(s) to remove, resolved relative to this package's own directory. Per-package cascaded (a package's own value replaces the root's, it does not merge)."
|
|
95
|
+
},
|
|
96
|
+
"exclude": {
|
|
97
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
98
|
+
"description": "Glob(s) protected from \"include\" (and from the built-in TypeScript-artifact cleanup), relative to this package's own directory. Per-package cascaded."
|
|
99
|
+
},
|
|
100
|
+
"skip": {
|
|
101
|
+
"type": "boolean",
|
|
102
|
+
"default": false,
|
|
103
|
+
"description": "Opts this package out of \"clean\" entirely. Per-package cascaded."
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"run": {
|
|
108
|
+
"type": "object",
|
|
109
|
+
"description": "Per-script options for \"run\"/\"build\"/\"test\"/RunService, keyed by npm script name (e.g. \"build\", \"lint\", \"test\").",
|
|
110
|
+
"additionalProperties": { "$ref": "#/definitions/runScriptConfig" }
|
|
111
|
+
},
|
|
112
|
+
"packages": {
|
|
113
|
+
"type": "object",
|
|
114
|
+
"description": "Per-package overrides, keyed by the in-repo package's own name.",
|
|
115
|
+
"additionalProperties": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"additionalProperties": false,
|
|
118
|
+
"properties": {
|
|
119
|
+
"dependencies": {
|
|
120
|
+
"description": "Extra in-repo \"dependencies\" not present in this package's real package.json, purely for rman's own dependency graph (topo-sort, --deps/--dependents, run's task scheduling). An array defaults each entry's range to \"*\"; an object gives an explicit name -> range map.",
|
|
121
|
+
"oneOf": [
|
|
122
|
+
{ "type": "array", "items": { "type": "string" } },
|
|
123
|
+
{ "type": "object", "additionalProperties": { "type": "string" } }
|
|
124
|
+
]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"definitions": {
|
|
131
|
+
"logLevel": {
|
|
132
|
+
"type": "string",
|
|
133
|
+
"enum": ["silent", "error", "info", "verbose"]
|
|
134
|
+
},
|
|
135
|
+
"stringOrStringArray": {
|
|
136
|
+
"oneOf": [
|
|
137
|
+
{ "type": "string", "minLength": 1 },
|
|
138
|
+
{ "type": "array", "items": { "type": "string", "minLength": 1 } }
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
"runScriptConfig": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"additionalProperties": false,
|
|
144
|
+
"properties": {
|
|
145
|
+
"concurrency": {
|
|
146
|
+
"type": "integer",
|
|
147
|
+
"minimum": 1,
|
|
148
|
+
"description": "Max packages built at once for this script. Default: CPU count."
|
|
149
|
+
},
|
|
150
|
+
"topo": {
|
|
151
|
+
"type": "boolean",
|
|
152
|
+
"default": true,
|
|
153
|
+
"description": "Respect the package dependency graph: a package waits for its dependencies and is skipped if one fails. Set false for independent scripts (lint/test) - alphabetical order, no failure skipping."
|
|
154
|
+
},
|
|
155
|
+
"bail": {
|
|
156
|
+
"type": "boolean",
|
|
157
|
+
"default": true,
|
|
158
|
+
"description": "Stop the whole batch on this package's own failure. Unusual precedence: this package-level value outranks even an explicit CLI --bail/--no-bail flag."
|
|
159
|
+
},
|
|
160
|
+
"progress": {
|
|
161
|
+
"type": "boolean",
|
|
162
|
+
"default": true,
|
|
163
|
+
"description": "Show the live progress panel for this script (auto-disabled off a TTY)."
|
|
164
|
+
},
|
|
165
|
+
"logLevel": {
|
|
166
|
+
"$ref": "#/definitions/logLevel",
|
|
167
|
+
"description": "Verbosity of the classic per-step log for this package/script, when the live panel is off."
|
|
168
|
+
},
|
|
169
|
+
"changedSince": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"description": "Root-level fallback commit/hash for \"--changed-since\", used only when the CLI flag is not given."
|
|
172
|
+
},
|
|
173
|
+
"skip": {
|
|
174
|
+
"type": "boolean",
|
|
175
|
+
"default": false,
|
|
176
|
+
"description": "Excludes this package (or the repository root's own pre/post hooks) from running this script entirely."
|
|
177
|
+
},
|
|
178
|
+
"if": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"description": "Conditional-execution expression: atoms \"changed\"/\"dirty\"/\"committed\" (optionally \"= <hash>\" or \"= {ENV_VAR}\"), combined with and/or/not/(...). E.g. \"changed\", \"(changed or dirty) and not committed\"."
|
|
181
|
+
},
|
|
182
|
+
"script": {
|
|
183
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
184
|
+
"description": "Command(s) to run as this package's own script, when its package.json does not define one for this script name."
|
|
185
|
+
},
|
|
186
|
+
"preScript": {
|
|
187
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
188
|
+
"description": "Command(s) to run as this package's own \"pre<script>\" hook, when its package.json does not define one."
|
|
189
|
+
},
|
|
190
|
+
"postScript": {
|
|
191
|
+
"$ref": "#/definitions/stringOrStringArray",
|
|
192
|
+
"description": "Command(s) to run as this package's own \"post<script>\" hook, when its package.json does not define one."
|
|
193
|
+
},
|
|
194
|
+
"override": {
|
|
195
|
+
"type": "boolean",
|
|
196
|
+
"default": false,
|
|
197
|
+
"description": "When true, \"script\"/\"preScript\"/\"postScript\" above replace the package's own package.json definition even when it already has one."
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|