rman 1.0.12 → 1.2.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 +226 -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 +61 -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 +265 -17
- package/core/config.js +651 -76
- 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 +70 -0
- package/core/merge-config.js +193 -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 +91 -1
- package/core/repository.js +277 -132
- package/core/resolve-target.d.ts +12 -0
- package/core/resolve-target.js +33 -0
- package/core/run-step.d.ts +75 -0
- package/core/run-step.js +1 -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 +55 -8
- package/index.js +42 -7
- package/interfaces/rman-config.interface.d.ts +222 -46
- package/package.json +16 -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 +112 -6
- package/services/run.service.js +265 -89
- 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 +102 -82
- package/services/version.service.js +226 -434
- 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/printable-config.d.ts +15 -0
- package/utils/printable-config.js +42 -0
- 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
|
@@ -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
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { detectChangeHash, extractVersion, findLatestTag, tagPattern } from '../utils/change-hash.js';
|
|
4
|
-
import { isReleaseCommit, parseConventionalCommit } from '../utils/conventional-commits.js';
|
|
5
3
|
import { GitHelper } from '../utils/git.js';
|
|
6
4
|
import { filterPackages } from '../utils/package-filter.js';
|
|
5
|
+
import { ChangeHashService } from './change-hash.service.js';
|
|
6
|
+
import { ConventionalCommitsService } from './conventional-commits.service.js';
|
|
7
7
|
export var ChangelogService;
|
|
8
8
|
(function (ChangelogService) {
|
|
9
9
|
/**
|
|
@@ -13,8 +13,8 @@ export var ChangelogService;
|
|
|
13
13
|
* sake side effect (a "save this" request), not presentation, so it stays here rather than in
|
|
14
14
|
* the CLI command - printing what happened is the command's job.
|
|
15
15
|
*/
|
|
16
|
-
async function generateToFile(repository, options = {}
|
|
17
|
-
const entries = await getEntries(repository, options
|
|
16
|
+
async function generateToFile(repository, options = {}) {
|
|
17
|
+
const entries = await getEntries(repository, options);
|
|
18
18
|
for (const entry of entries)
|
|
19
19
|
prependToChangelogFile(entry.package, entry.filePath, entry.content);
|
|
20
20
|
return entries;
|
|
@@ -59,7 +59,7 @@ export var ChangelogService;
|
|
|
59
59
|
* Run from inside a single package's own directory, it only covers that package unless
|
|
60
60
|
* `options.root` says otherwise (see `Repository.currentPackage`).
|
|
61
61
|
*/
|
|
62
|
-
async function getEntries(repository, options = {}
|
|
62
|
+
async function getEntries(repository, options = {}) {
|
|
63
63
|
const cwdScope = options.root ? undefined : repository.currentPackage;
|
|
64
64
|
const packages = repository.getPackages().filter(p => p !== repository.rootPackage);
|
|
65
65
|
const targets = (cwdScope ? [cwdScope] : filterPackages([repository.rootPackage, ...packages], options)).filter(pkg => options.includeSkipped || !pkg.config.publish?.skip);
|
|
@@ -67,7 +67,7 @@ export var ChangelogService;
|
|
|
67
67
|
const commitMessage = repository.rootPackage.config?.version?.commitMessage;
|
|
68
68
|
// dropped up front, not just while grouping - a package whose only commits are release markers
|
|
69
69
|
// should get no entry at all, rather than a heading with nothing real underneath it.
|
|
70
|
-
const dropVersionBumps = (commits) => commits.filter(c => !isReleaseCommit(c.subject, commitMessage));
|
|
70
|
+
const dropVersionBumps = (commits) => commits.filter(c => !ConventionalCommitsService.isReleaseCommit(c.subject, commitMessage));
|
|
71
71
|
// Several packages often resolve to the identical hash (an explicit --from <hash> applies to
|
|
72
72
|
// all of them the same way; under fixed versioning, npm auto-detection usually does too) - so
|
|
73
73
|
// the git fetch for a given hash is cached, run once no matter how many packages share it.
|
|
@@ -86,9 +86,8 @@ export var ChangelogService;
|
|
|
86
86
|
};
|
|
87
87
|
const commitsByTarget = await Promise.all(targets.map(async (pkg) => {
|
|
88
88
|
const catchUpFile = path.join(pkg.dirname, resolveFilePath(pkg, options.filePath));
|
|
89
|
-
const from = await
|
|
89
|
+
const from = await ChangeHashService.detect(git, pkg, {
|
|
90
90
|
from: options.from,
|
|
91
|
-
npmViewVersion: deps.npmViewVersion,
|
|
92
91
|
catchUpFile: fs.existsSync(catchUpFile) ? catchUpFile : undefined,
|
|
93
92
|
});
|
|
94
93
|
return listCommitsCached(from);
|
|
@@ -132,7 +131,7 @@ function ignoreTypesConfig(pkg) {
|
|
|
132
131
|
function groupCommits(subjects, ignoreTypes = new Set()) {
|
|
133
132
|
const grouped = { features: [], fixes: [], other: [] };
|
|
134
133
|
for (const subject of subjects) {
|
|
135
|
-
const parsed =
|
|
134
|
+
const parsed = ConventionalCommitsService.parseSubject(subject);
|
|
136
135
|
if (!parsed) {
|
|
137
136
|
grouped.other.push(subject);
|
|
138
137
|
continue;
|
|
@@ -198,8 +197,10 @@ function resolveFilePath(pkg, optionsFilePath) {
|
|
|
198
197
|
* if no matching tag exists at all (never tagged, or a fresh package) - see `findLatestTag`.
|
|
199
198
|
*/
|
|
200
199
|
async function resolveVersion(git, pkg) {
|
|
201
|
-
const tag = await findLatestTag(git, pkg);
|
|
202
|
-
return tag
|
|
200
|
+
const tag = await ChangeHashService.findLatestTag(git, pkg);
|
|
201
|
+
return tag
|
|
202
|
+
? ChangeHashService.extractVersion(tag, ChangeHashService.tagPattern(pkg).replace('{name}', pkg.name))
|
|
203
|
+
: pkg.version || '';
|
|
203
204
|
}
|
|
204
205
|
/** The most specific package whose directory contains `file` - the repository root itself as the
|
|
205
206
|
* fallback for anything outside every package (e.g. root-level config files). Mirrors
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conventional Commits, as rman reads them: the subject-line shape, the breaking-change markers,
|
|
3
|
+
* the `Release-As:` escape hatch, and the counter-question of whether a commit is a *release
|
|
4
|
+
* marker* rather than a change at all.
|
|
5
|
+
*
|
|
6
|
+
* A namespace for the reason `ChangeHashService` is one - one subject, several operations, named for
|
|
7
|
+
* what they do rather than repeating the subject (`parseSubject`, not `parseConventionalCommit`).
|
|
8
|
+
*
|
|
9
|
+
* **Nothing here knows about versions.** A commit says what happened; `VersionScheme.bumpFor` turns
|
|
10
|
+
* that into how a number moves. `parseReleaseAs` is the one place the two meet, and it deliberately
|
|
11
|
+
* returns the word as written rather than judging it - see its own doc.
|
|
12
|
+
*/
|
|
13
|
+
export declare namespace ConventionalCommitsService {
|
|
14
|
+
/**
|
|
15
|
+
* `type(scope): description`, optionally with a `!` breaking-change marker - Conventional
|
|
16
|
+
* Commits' subject-line shape. Anything that doesn't match falls into "Other Changes" as-is (for
|
|
17
|
+
* changelog entries) or defaults to a patch-level change (for version bump severity) - see
|
|
18
|
+
* `parseSubject`.
|
|
19
|
+
*/
|
|
20
|
+
const SUBJECT_PATTERN: RegExp;
|
|
21
|
+
/** A bare version-bump commit (`"6.0.1"`, `"v2.3.0-beta.1"`, ...) - many release tools commit the
|
|
22
|
+
* version bump itself with just the new version number as the message. That's a release marker,
|
|
23
|
+
* not a real change worth describing (or worth bumping a version over on its own), so it's
|
|
24
|
+
* dropped everywhere a real change is being looked for. */
|
|
25
|
+
const VERSION_BUMP_PATTERN: RegExp;
|
|
26
|
+
/**
|
|
27
|
+
* Whether `subject` is a release marker rather than a real change - dropped everywhere real
|
|
28
|
+
* changes are looked for (changelog entries, and what counts as "changed" for a version bump).
|
|
29
|
+
*
|
|
30
|
+
* Covers the bare-version form other tools use (`VERSION_BUMP_PATTERN`) plus every message shape
|
|
31
|
+
* `version` itself writes: its commit message (`commitMessageTemplate`, or the built-in
|
|
32
|
+
* `"chore(release): v{version}"` when a repo doesn't override it), the multi-version form that
|
|
33
|
+
* template falls back to when one commit spans several versions (`chore(release): a@1.2.0,
|
|
34
|
+
* b@1.3.0`), and the monorepo root's own version-sync commit. Without this, rman's own release
|
|
35
|
+
* commits show up in the changelogs it generates - visible whenever the boundary reaches back past
|
|
36
|
+
* a previous release (see `ChangeHashService.detect`'s `catchUpFile`).
|
|
37
|
+
*/
|
|
38
|
+
function isReleaseCommit(subject: string, commitMessageTemplate?: string): boolean;
|
|
39
|
+
interface ParsedSubject {
|
|
40
|
+
type: string;
|
|
41
|
+
scope?: string;
|
|
42
|
+
/** A `!` right before the `:` (e.g. `feat!:`) - Conventional Commits' inline breaking-change
|
|
43
|
+
* marker. Doesn't cover a `BREAKING CHANGE:` footer, since only the subject line is available. */
|
|
44
|
+
breaking: boolean;
|
|
45
|
+
description: string;
|
|
46
|
+
}
|
|
47
|
+
/** Parses a commit subject as Conventional Commits, or `undefined` if it doesn't match at all
|
|
48
|
+
* (a non-conventional message - still a real change, just with no `type` to key off of). */
|
|
49
|
+
function parseSubject(subject: string): ParsedSubject | undefined;
|
|
50
|
+
/** Whether a commit `body` carries a Conventional Commits `BREAKING CHANGE:` (or
|
|
51
|
+
* `BREAKING-CHANGE:`) footer - the other, footer-based way to mark a breaking change, alongside
|
|
52
|
+
* the inline `!` the subject line alone can carry (see `parseSubject`, whose own
|
|
53
|
+
* `breaking` only ever reflects that marker, never a footer, since it only sees the subject). */
|
|
54
|
+
function hasBreakingChangeFooter(body: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* A `Release-As: <bump>` footer in a commit `body` - lets that one commit's own contribution to the
|
|
57
|
+
* detected bump be overridden by hand, regardless of what its subject line (or a `BREAKING CHANGE:`
|
|
58
|
+
* footer) would otherwise imply. The motivating case: a `feat:` commit that needs to ship right now
|
|
59
|
+
* as a patch, without waiting for the rest of a minor's worth of work to land - `Release-As: patch`
|
|
60
|
+
* on just that commit ships it alone, at patch size, while a later genuine `feat:` (with no
|
|
61
|
+
* override) still correctly triggers a minor of its own. Case-insensitive; the last match wins if a
|
|
62
|
+
* body somehow has more than one, matching how multiple git trailers of the same key are
|
|
63
|
+
* conventionally read (later overrides earlier).
|
|
64
|
+
*
|
|
65
|
+
* **Returns the word as written, and does not judge it** - which bumps exist is the package's
|
|
66
|
+
* `VersionScheme.bumpNames`, and this module is about commit messages. The caller checks it against
|
|
67
|
+
* those and treats anything else as *no override at all*, falling back to what the commit's own
|
|
68
|
+
* subject said (see `VersionPlanService.detectBump`). That keeps two cases harmless: a typo, and release-please's own
|
|
69
|
+
* `Release-As: 1.2.3`, which uses the same trailer for a literal version and appears in real
|
|
70
|
+
* repositories rman gets adopted onto.
|
|
71
|
+
*/
|
|
72
|
+
function parseReleaseAs(body: string): string | undefined;
|
|
73
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conventional Commits, as rman reads them: the subject-line shape, the breaking-change markers,
|
|
3
|
+
* the `Release-As:` escape hatch, and the counter-question of whether a commit is a *release
|
|
4
|
+
* marker* rather than a change at all.
|
|
5
|
+
*
|
|
6
|
+
* A namespace for the reason `ChangeHashService` is one - one subject, several operations, named for
|
|
7
|
+
* what they do rather than repeating the subject (`parseSubject`, not `parseConventionalCommit`).
|
|
8
|
+
*
|
|
9
|
+
* **Nothing here knows about versions.** A commit says what happened; `VersionScheme.bumpFor` turns
|
|
10
|
+
* that into how a number moves. `parseReleaseAs` is the one place the two meet, and it deliberately
|
|
11
|
+
* returns the word as written rather than judging it - see its own doc.
|
|
12
|
+
*/
|
|
13
|
+
export var ConventionalCommitsService;
|
|
14
|
+
(function (ConventionalCommitsService) {
|
|
15
|
+
/**
|
|
16
|
+
* `type(scope): description`, optionally with a `!` breaking-change marker - Conventional
|
|
17
|
+
* Commits' subject-line shape. Anything that doesn't match falls into "Other Changes" as-is (for
|
|
18
|
+
* changelog entries) or defaults to a patch-level change (for version bump severity) - see
|
|
19
|
+
* `parseSubject`.
|
|
20
|
+
*/
|
|
21
|
+
ConventionalCommitsService.SUBJECT_PATTERN = /^(\w+)(\(([^)]+)\))?(!)?:\s*(.+)$/;
|
|
22
|
+
/** A bare version-bump commit (`"6.0.1"`, `"v2.3.0-beta.1"`, ...) - many release tools commit the
|
|
23
|
+
* version bump itself with just the new version number as the message. That's a release marker,
|
|
24
|
+
* not a real change worth describing (or worth bumping a version over on its own), so it's
|
|
25
|
+
* dropped everywhere a real change is being looked for. */
|
|
26
|
+
ConventionalCommitsService.VERSION_BUMP_PATTERN = /^v?\d+\.\d+\.\d+(?:[-+][\w.]+)?$/;
|
|
27
|
+
/**
|
|
28
|
+
* Whether `subject` is a release marker rather than a real change - dropped everywhere real
|
|
29
|
+
* changes are looked for (changelog entries, and what counts as "changed" for a version bump).
|
|
30
|
+
*
|
|
31
|
+
* Covers the bare-version form other tools use (`VERSION_BUMP_PATTERN`) plus every message shape
|
|
32
|
+
* `version` itself writes: its commit message (`commitMessageTemplate`, or the built-in
|
|
33
|
+
* `"chore(release): v{version}"` when a repo doesn't override it), the multi-version form that
|
|
34
|
+
* template falls back to when one commit spans several versions (`chore(release): a@1.2.0,
|
|
35
|
+
* b@1.3.0`), and the monorepo root's own version-sync commit. Without this, rman's own release
|
|
36
|
+
* commits show up in the changelogs it generates - visible whenever the boundary reaches back past
|
|
37
|
+
* a previous release (see `ChangeHashService.detect`'s `catchUpFile`).
|
|
38
|
+
*/
|
|
39
|
+
function isReleaseCommit(subject, commitMessageTemplate) {
|
|
40
|
+
if (ConventionalCommitsService.VERSION_BUMP_PATTERN.test(subject))
|
|
41
|
+
return true;
|
|
42
|
+
if (ROOT_SYNC_PATTERN.test(subject))
|
|
43
|
+
return true;
|
|
44
|
+
if (MULTI_VERSION_RELEASE_PATTERN.test(subject))
|
|
45
|
+
return true;
|
|
46
|
+
// The built-in message is checked even when a repo overrides it: the override only applies to
|
|
47
|
+
// commits spanning a single version (see `buildCommitMessage`), and a repo that adopted one later
|
|
48
|
+
// still has older releases committed under the default.
|
|
49
|
+
if (templatePattern(DEFAULT_COMMIT_MESSAGE).test(subject))
|
|
50
|
+
return true;
|
|
51
|
+
return !!commitMessageTemplate && templatePattern(commitMessageTemplate).test(subject);
|
|
52
|
+
}
|
|
53
|
+
ConventionalCommitsService.isReleaseCommit = isReleaseCommit;
|
|
54
|
+
/** Parses a commit subject as Conventional Commits, or `undefined` if it doesn't match at all
|
|
55
|
+
* (a non-conventional message - still a real change, just with no `type` to key off of). */
|
|
56
|
+
function parseSubject(subject) {
|
|
57
|
+
const m = ConventionalCommitsService.SUBJECT_PATTERN.exec(subject);
|
|
58
|
+
if (!m)
|
|
59
|
+
return undefined;
|
|
60
|
+
const [, type, , scope, breakingMark, description] = m;
|
|
61
|
+
return { type: type.toLowerCase(), scope, breaking: !!breakingMark, description };
|
|
62
|
+
}
|
|
63
|
+
ConventionalCommitsService.parseSubject = parseSubject;
|
|
64
|
+
/** Whether a commit `body` carries a Conventional Commits `BREAKING CHANGE:` (or
|
|
65
|
+
* `BREAKING-CHANGE:`) footer - the other, footer-based way to mark a breaking change, alongside
|
|
66
|
+
* the inline `!` the subject line alone can carry (see `parseSubject`, whose own
|
|
67
|
+
* `breaking` only ever reflects that marker, never a footer, since it only sees the subject). */
|
|
68
|
+
function hasBreakingChangeFooter(body) {
|
|
69
|
+
return /^BREAKING[ -]CHANGE:/im.test(body);
|
|
70
|
+
}
|
|
71
|
+
ConventionalCommitsService.hasBreakingChangeFooter = hasBreakingChangeFooter;
|
|
72
|
+
/**
|
|
73
|
+
* A `Release-As: <bump>` footer in a commit `body` - lets that one commit's own contribution to the
|
|
74
|
+
* detected bump be overridden by hand, regardless of what its subject line (or a `BREAKING CHANGE:`
|
|
75
|
+
* footer) would otherwise imply. The motivating case: a `feat:` commit that needs to ship right now
|
|
76
|
+
* as a patch, without waiting for the rest of a minor's worth of work to land - `Release-As: patch`
|
|
77
|
+
* on just that commit ships it alone, at patch size, while a later genuine `feat:` (with no
|
|
78
|
+
* override) still correctly triggers a minor of its own. Case-insensitive; the last match wins if a
|
|
79
|
+
* body somehow has more than one, matching how multiple git trailers of the same key are
|
|
80
|
+
* conventionally read (later overrides earlier).
|
|
81
|
+
*
|
|
82
|
+
* **Returns the word as written, and does not judge it** - which bumps exist is the package's
|
|
83
|
+
* `VersionScheme.bumpNames`, and this module is about commit messages. The caller checks it against
|
|
84
|
+
* those and treats anything else as *no override at all*, falling back to what the commit's own
|
|
85
|
+
* subject said (see `VersionPlanService.detectBump`). That keeps two cases harmless: a typo, and release-please's own
|
|
86
|
+
* `Release-As: 1.2.3`, which uses the same trailer for a literal version and appears in real
|
|
87
|
+
* repositories rman gets adopted onto.
|
|
88
|
+
*/
|
|
89
|
+
function parseReleaseAs(body) {
|
|
90
|
+
const matches = [...body.matchAll(/^release-as:\s*([\w.+-]+)\s*$/gim)];
|
|
91
|
+
const last = matches.at(-1);
|
|
92
|
+
return last ? last[1].toLowerCase() : undefined;
|
|
93
|
+
}
|
|
94
|
+
ConventionalCommitsService.parseReleaseAs = parseReleaseAs;
|
|
95
|
+
})(ConventionalCommitsService || (ConventionalCommitsService = {}));
|
|
96
|
+
/** A semver version, as it appears inside a commit subject - the `\d+\.\d+\.\d+` core of
|
|
97
|
+
* `VERSION_BUMP_PATTERN`, reusable inside the larger patterns below. */
|
|
98
|
+
const SEMVER_SOURCE = String.raw `\d+\.\d+\.\d+(?:[-+][\w.]+)?`;
|
|
99
|
+
/** Mirrors `VersionService`'s own default `version.commitMessage` - kept in sync by
|
|
100
|
+
* `version.command.ts`'s documented default, not imported, to keep this module dependency-free. */
|
|
101
|
+
const DEFAULT_COMMIT_MESSAGE = 'chore(release): v{version}';
|
|
102
|
+
/** `VersionService.applyPlan`'s trailing commit for a monorepo root's informational version. */
|
|
103
|
+
const ROOT_SYNC_PATTERN = new RegExp(String.raw `^chore: sync root version to ${SEMVER_SOURCE}$`);
|
|
104
|
+
/** What the commit-message template falls back to when one commit covers several versions at once
|
|
105
|
+
* (a cross-group ripple) - `{version}` has nothing single to substitute, so each bumped package is
|
|
106
|
+
* listed by name instead. */
|
|
107
|
+
const MULTI_VERSION_RELEASE_PATTERN = new RegExp(String.raw `^chore\(release\): \S+@${SEMVER_SOURCE}(?:, \S+@${SEMVER_SOURCE})*$`);
|
|
108
|
+
function escapeRegExp(value) {
|
|
109
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
110
|
+
}
|
|
111
|
+
/** Turns a `version.commitMessage` template into a matcher for the commits it produces: every
|
|
112
|
+
* literal part escaped, each `{version}` placeholder standing in for any semver. */
|
|
113
|
+
function templatePattern(template) {
|
|
114
|
+
const source = template.split('{version}').map(escapeRegExp).join(SEMVER_SOURCE);
|
|
115
|
+
return new RegExp(`^${source}$`);
|
|
116
|
+
}
|
|
@@ -185,7 +185,7 @@ async function updateDescription(entry) {
|
|
|
185
185
|
const res = await fetch(`https://hub.docker.com/v2/repositories/${namespace}/${imageName}/`, {
|
|
186
186
|
method: 'PATCH',
|
|
187
187
|
headers: { Authorization: `JWT ${token}`, 'Content-Type': 'application/json' },
|
|
188
|
-
body: JSON.stringify({ description: pkg.
|
|
188
|
+
body: JSON.stringify({ description: pkg.manifest.raw.description, full_description: readme }),
|
|
189
189
|
});
|
|
190
190
|
if (!res.ok)
|
|
191
191
|
throw new Error(`DockerHub description update failed: ${res.status}`);
|
package/services/exec.service.js
CHANGED
|
@@ -47,7 +47,7 @@ export var ExecService;
|
|
|
47
47
|
const names = new Set(packages.map(p => p.name));
|
|
48
48
|
const children = packages.map(pkg => {
|
|
49
49
|
const ctx = panel.addItem(pkg.name);
|
|
50
|
-
const dependencies = topo ? pkg.dependencies.filter(d => names.has(d)) : [];
|
|
50
|
+
const dependencies = topo ? pkg.dependencies.filter(d => names.has(d.name)).map(d => d.name) : [];
|
|
51
51
|
return new Task(() => execForPackage(ctx, panel.enabled, pkg.dirname, command, logger, bail ? () => rootTask?.abort() : () => { }), { name: pkg.name, dependencies });
|
|
52
52
|
});
|
|
53
53
|
panel.start();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { Package } from '../core/package.js';
|
|
2
|
+
import { Repository } from '../core/repository.js';
|
|
3
3
|
export declare namespace GithubReleaseService {
|
|
4
4
|
/** Injectable "does this release already exist" check - mainly for tests, so they don't depend
|
|
5
5
|
* on network access or a real GitHub token. Same shape as `DockerPublishService.Deps`' own
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import fastGlob from 'fast-glob';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
4
|
+
import { Package } from '../core/package.js';
|
|
5
|
+
import { Repository } from '../core/repository.js';
|
|
6
6
|
import { GitHelper } from '../utils/git.js';
|
|
7
7
|
import { expandReleaseTag, isCalendarVersion, releaseTagPattern } from '../utils/release-version.js';
|
|
8
|
+
import { ChangeHashService } from './change-hash.service.js';
|
|
8
9
|
import { ChangelogService } from './changelog.service.js';
|
|
9
10
|
export var GithubReleaseService;
|
|
10
11
|
(function (GithubReleaseService) {
|
|
@@ -129,18 +130,22 @@ const GITHUB_UPLOADS = 'https://uploads.github.com';
|
|
|
129
130
|
* so the release needs a name of its own (`release-*`); a plain one means every package shares it,
|
|
130
131
|
* and that shared version's tag already *is* the release. */
|
|
131
132
|
function releaseTagFor(root) {
|
|
132
|
-
return isCalendarVersion(root.version)
|
|
133
|
+
return isCalendarVersion(root.version)
|
|
134
|
+
? expandReleaseTag(root, root.version)
|
|
135
|
+
: ChangeHashService.expandTag(root, root.version);
|
|
133
136
|
}
|
|
134
137
|
/** The glob matching the tags `releaseTagFor` produces - for stepping back to the previous one. */
|
|
135
138
|
function releaseTagGlob(root) {
|
|
136
|
-
const pattern = isCalendarVersion(root.version) ? releaseTagPattern(root) : tagPattern(root);
|
|
139
|
+
const pattern = isCalendarVersion(root.version) ? releaseTagPattern(root) : ChangeHashService.tagPattern(root);
|
|
137
140
|
return pattern.replace('{name}', root.name);
|
|
138
141
|
}
|
|
139
142
|
function resolvePrerelease(root, version) {
|
|
140
143
|
const configured = root.config?.githubRelease?.prerelease;
|
|
141
144
|
// A calendar version's time part is a semver prerelease identifier by construction - it says
|
|
142
145
|
// nothing about the release being a preview, so it must not be read as one.
|
|
143
|
-
|
|
146
|
+
/** The root's own scheme decides what a preview looks like - `semver.prerelease` was that
|
|
147
|
+
* question with semver's answer baked in. */
|
|
148
|
+
return configured ?? (!isCalendarVersion(version) && root.versionScheme.isPrerelease(version));
|
|
144
149
|
}
|
|
145
150
|
/** `owner/repo` out of either remote URL form git hands back - `git@github.com:owner/repo.git`
|
|
146
151
|
* (SSH) or `https://github.com/owner/repo.git` (HTTPS, credentials and all). `undefined` for
|
package/services/list.service.js
CHANGED
|
@@ -7,7 +7,10 @@ export var ListService;
|
|
|
7
7
|
* status relative to upstream. Pure data - no console output; `rman list`'s own command
|
|
8
8
|
* decides how to present it (table, JSON, parseable, names only, or a dependency graph). */
|
|
9
9
|
async function getPackages(repository, options = {}) {
|
|
10
|
-
|
|
10
|
+
/** `false`: `list` is the inventory, so a package its own `.rmanrc "skip"` excludes is still
|
|
11
|
+
* *in* the repository and still listed - hiding it would answer a different question. Every
|
|
12
|
+
* other caller honours it, being a command that acts rather than reports. */
|
|
13
|
+
const packages = filterPackages(repository.getPackages({ toposort: options.toposort }), options, false);
|
|
11
14
|
const status = await repository.listStatus({ hash: options.changedSince });
|
|
12
15
|
let items = packages.map(p => {
|
|
13
16
|
const target = p.config.publish?.target;
|
|
@@ -18,7 +21,7 @@ export var ListService;
|
|
|
18
21
|
location: path.relative(repository.dirname, p.dirname) || '.',
|
|
19
22
|
private: p.isPrivate,
|
|
20
23
|
status: status[p.name],
|
|
21
|
-
dependencies:
|
|
24
|
+
dependencies: p.dependencies.map(d => d.name),
|
|
22
25
|
publishTargets: [...publishTargets],
|
|
23
26
|
docker: publishTargets.includes('docker') ? p.config.publish?.docker : undefined,
|
|
24
27
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Package } from '../core/package.js';
|
|
2
2
|
import type { Repository } from '../core/repository.js';
|
|
3
|
+
import type { RunStepContext, RunStepFn, RunStepValue } from '../core/run-step.js';
|
|
3
4
|
import { type LogLevel } from '../utils/logger.js';
|
|
4
5
|
import { type PackageFilterOptions } from '../utils/package-filter.js';
|
|
5
6
|
export declare namespace RunService {
|
|
6
|
-
interface Options extends PackageFilterOptions {
|
|
7
|
+
export interface Options extends PackageFilterOptions {
|
|
7
8
|
/** Max packages built at once: `true`/omitted = CPU count, a number = that many, `false` = serial (1). */
|
|
8
9
|
parallel?: boolean | number;
|
|
9
10
|
/** Respect the package dependency graph: a package waits for its dependencies and is skipped
|
|
@@ -52,7 +53,111 @@ export declare namespace RunService {
|
|
|
52
53
|
* run:
|
|
53
54
|
* test: mocha # same as test: { exec: mocha }
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
+
/**
|
|
57
|
+
* One step of a script - a shell command, or a function ([`RunStepFn`](../core/run-step.ts)).
|
|
58
|
+
*
|
|
59
|
+
* A union rather than one shape with two optional fields, so every consumer is made to say which
|
|
60
|
+
* it is handling: the executor that forgets is the one that silently runs nothing, which is
|
|
61
|
+
* exactly the bug this type replaces (a function in `after` used to be dropped by
|
|
62
|
+
* `normalizeScriptValue` and reported as a step that succeeded).
|
|
63
|
+
*/
|
|
64
|
+
export type ScriptStep = CommandStep | FunctionStep;
|
|
65
|
+
interface StepBase {
|
|
66
|
+
/** The slot it came from - `before`/`exec`/`after`, which is what the log line shows. */
|
|
67
|
+
name: string;
|
|
68
|
+
/** What the progress panel and the per-step log print: the command itself, or the function's
|
|
69
|
+
* own name. */
|
|
70
|
+
label: string;
|
|
71
|
+
}
|
|
72
|
+
export interface CommandStep extends StepBase {
|
|
73
|
+
command: string;
|
|
74
|
+
run?: undefined;
|
|
75
|
+
}
|
|
76
|
+
export interface FunctionStep extends StepBase {
|
|
77
|
+
run: RunStepFn;
|
|
78
|
+
command?: undefined;
|
|
79
|
+
}
|
|
80
|
+
/** The three slots a script is made of, each one step or several run in sequence. The same three
|
|
81
|
+
* names a `.rmanrc "run.<script>"` block uses, because they are the same three things. */
|
|
82
|
+
export interface ScriptSlots {
|
|
83
|
+
before?: RunStepValue[];
|
|
84
|
+
exec?: RunStepValue[];
|
|
85
|
+
after?: RunStepValue[];
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Where a package's steps can come from besides its `.rmanrc`.
|
|
89
|
+
*
|
|
90
|
+
* The core knows one source: the config. **`package.json#scripts` is not a source the core has**,
|
|
91
|
+
* because "a script lives in package.json" is true of a Node repository and of nothing else -
|
|
92
|
+
* `rman-node` contributes that one (with npm's `pre<script>`/`post<script>` convention and its
|
|
93
|
+
* `&&` splitting), and a plugin for another ecosystem would contribute its own.
|
|
94
|
+
*
|
|
95
|
+
* Returns `undefined` for "this package declares nothing", not empty slots - the difference
|
|
96
|
+
* decides whether the config's value applies.
|
|
97
|
+
*/
|
|
98
|
+
export type StepSource = (pkg: Package, script: string) => ScriptSlots | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Registers a source. Called by `loadPlugins` for each plugin's `runSteps`, in `plugins`
|
|
101
|
+
* declaration order - never as an import side effect, so what is registered is exactly what the
|
|
102
|
+
* repository's `.rmanrc` asked for.
|
|
103
|
+
*/
|
|
104
|
+
export function addStepSource(source: StepSource): void;
|
|
105
|
+
/** For tests, which would otherwise leak a source into every later case in the process. */
|
|
106
|
+
export function clearStepSources(): void;
|
|
107
|
+
/**
|
|
108
|
+
* What the *package itself* declares for the lifecycle `script`, from the contributed sources
|
|
109
|
+
* alone - no `.rmanrc` involved. `undefined` when it declares nothing.
|
|
110
|
+
*
|
|
111
|
+
* Exported because `run` is not the only lifecycle rman wraps: `version` runs hooks around the
|
|
112
|
+
* version write, and npm spells those `preversion`/`version`/`postversion` in `package.json` -
|
|
113
|
+
* which is the same `pre<script>`/`<script>`/`post<script>` shape a step source already answers.
|
|
114
|
+
* So `VersionService` asks here for `'version'` instead of reading `manifest.raw.scripts` itself,
|
|
115
|
+
* and npm's version lifecycle keeps working with no second seam and no extra line in any plugin.
|
|
116
|
+
* A plugin for another ecosystem gets its own lifecycle hooks the moment it contributes steps.
|
|
117
|
+
*/
|
|
118
|
+
export function contributedSlots(pkg: Package, script: string): ScriptSlots | undefined;
|
|
119
|
+
/**
|
|
120
|
+
* Runs one slot of a lifecycle belonging to some operation other than `run` itself - `version`'s
|
|
121
|
+
* hooks around the version write are the only one so far.
|
|
122
|
+
*
|
|
123
|
+
* **Here rather than in `VersionService`, because the rule it applies is this service's**: the
|
|
124
|
+
* package's own declaration for `script` wins over the caller's `fallback`, slot by slot, exactly
|
|
125
|
+
* as `getScriptSteps` decides it for `run`. Kept in two places that rule would drift, and one of
|
|
126
|
+
* the copies would sit in the file that writes versions - which now runs no command of its own at
|
|
127
|
+
* all.
|
|
128
|
+
*
|
|
129
|
+
* `fallback` is the caller's own configured step(s), **already evaluated**: `version`'s three
|
|
130
|
+
* paths are in `DEFERRED_PATHS` precisely because only the caller can bind
|
|
131
|
+
* `${{ pkg.targetVersion }}`, so interpolating here would either be too early or need a scope this
|
|
132
|
+
* service has no business holding.
|
|
133
|
+
*
|
|
134
|
+
* **A list, not one joined string.** `VersionService` used to `join(' && ')` an array into a
|
|
135
|
+
* single shell line, which a function step cannot be part of - and which quietly changed the
|
|
136
|
+
* semantics of the shell case too, since `cd x && y` in one process is not the same as two.
|
|
137
|
+
*/
|
|
138
|
+
export function runLifecycleSlot(pkg: Package, script: string, slot: keyof ScriptSlots, fallback?: RunStepValue[]): Promise<void>;
|
|
139
|
+
export function getConfig(pkg: Package, script: string): Record<string, unknown>;
|
|
140
|
+
/**
|
|
141
|
+
* The context a function step or `if` is handed - see [`RunStepContext`](../core/run-step.ts).
|
|
142
|
+
*
|
|
143
|
+
* `runBin` and `logger` are bound to *this run* rather than left to be imported, which is the
|
|
144
|
+
* whole reason they are handed over: an imported `runBin` knows neither the cwd nor the resolved
|
|
145
|
+
* log level.
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* A `run.<script>.before`/`.exec`/`.after` (or `version.<slot>`) value: one step, or several to
|
|
149
|
+
* run in sequence. A shell command or a function, and a list may mix them.
|
|
150
|
+
*
|
|
151
|
+
* **Anything else throws, naming the path.** It used to `return []`, which meant a value rman did
|
|
152
|
+
* not recognize was dropped with no trace: writing a function here - the obvious guess, and now
|
|
153
|
+
* the supported form - produced `1 succeeded, 0 failed` with the step never run (measured). A
|
|
154
|
+
* configuration mistake has to be loud; silence here reads as success.
|
|
155
|
+
*
|
|
156
|
+
* Exported, and the only implementation: `VersionService` used to carry a second one that behaved
|
|
157
|
+
* differently, which is how `version.<slot>` came to join its array with `' && '`.
|
|
158
|
+
*/
|
|
159
|
+
export function normalizeScriptValue(value: unknown, at: string): RunStepValue[];
|
|
160
|
+
export function createStepContext(pkg: Package, cwd: string): RunStepContext;
|
|
56
161
|
/**
|
|
57
162
|
* `.rmanrc` conditional execution, GitHub Actions-`if`-flavored but a small closed grammar
|
|
58
163
|
* instead of a full expression language (less to get wrong, still covers what's asked for) -
|
|
@@ -66,7 +171,7 @@ export declare namespace RunService {
|
|
|
66
171
|
* if: changed = {CHANGE_HASH} # {NAME} -> process.env.NAME first
|
|
67
172
|
* if: (changed or dirty) and not committed
|
|
68
173
|
*/
|
|
69
|
-
type IfNode = {
|
|
174
|
+
export type IfNode = {
|
|
70
175
|
kind: 'atom';
|
|
71
176
|
name: string;
|
|
72
177
|
value?: string;
|
|
@@ -84,13 +189,14 @@ export declare namespace RunService {
|
|
|
84
189
|
};
|
|
85
190
|
/** Recursive-descent parser over `tokenizeIf`'s output: expr := or ; or := and ('or' and)* ;
|
|
86
191
|
* and := unary ('and' unary)* ; unary := 'not' unary | GROUP | NAME ['=' VALUE] */
|
|
87
|
-
function parseIfExpr(raw: unknown): IfNode | undefined;
|
|
192
|
+
export function parseIfExpr(raw: unknown): IfNode | undefined;
|
|
88
193
|
/** Evaluates a parsed `if` expression for one package. `statusCache` avoids repeat `git` calls
|
|
89
194
|
* for the same reference hash across packages/scripts in a single run. */
|
|
90
|
-
function evaluateIf(repository: Repository, pkg: Package, node: IfNode, statusCache: Map<string, Record<string, Repository.PackageStatus>>): Promise<boolean>;
|
|
91
|
-
function runScript(repository: Repository, script: string, options?: Options & {
|
|
195
|
+
export function evaluateIf(repository: Repository, pkg: Package, node: IfNode, statusCache: Map<string, Record<string, Repository.PackageStatus>>): Promise<boolean>;
|
|
196
|
+
export function runScript(repository: Repository, script: string, options?: Options & {
|
|
92
197
|
commandName?: string;
|
|
93
198
|
}): Promise<void>;
|
|
199
|
+
export {};
|
|
94
200
|
}
|
|
95
201
|
/** Resolution order: explicit CLI flag > the package's resolved `.rmanrc` > `fallback`. */
|
|
96
202
|
export declare function resolveBool(cliValue: boolean | undefined, pkg: Package, script: string, key: string, fallback: boolean): boolean;
|