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,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
|
};
|
|
@@ -52,6 +52,66 @@ export declare namespace RunService {
|
|
|
52
52
|
* run:
|
|
53
53
|
* test: mocha # same as test: { exec: mocha }
|
|
54
54
|
*/
|
|
55
|
+
/** One command of a script, with the label the progress panel and the per-step log show. */
|
|
56
|
+
interface ScriptStep {
|
|
57
|
+
name: string;
|
|
58
|
+
command: string;
|
|
59
|
+
}
|
|
60
|
+
/** The three slots a script is made of, each one command or several run in sequence. The same
|
|
61
|
+
* three names a `.rmanrc "run.<script>"` block uses, because they are the same three things. */
|
|
62
|
+
interface ScriptSlots {
|
|
63
|
+
before?: string[];
|
|
64
|
+
exec?: string[];
|
|
65
|
+
after?: string[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Where a package's steps can come from besides its `.rmanrc`.
|
|
69
|
+
*
|
|
70
|
+
* The core knows one source: the config. **`package.json#scripts` is not a source the core has**,
|
|
71
|
+
* because "a script lives in package.json" is true of a Node repository and of nothing else -
|
|
72
|
+
* `rman-node` contributes that one (with npm's `pre<script>`/`post<script>` convention and its
|
|
73
|
+
* `&&` splitting), and a plugin for another ecosystem would contribute its own.
|
|
74
|
+
*
|
|
75
|
+
* Returns `undefined` for "this package declares nothing", not empty slots - the difference
|
|
76
|
+
* decides whether the config's value applies.
|
|
77
|
+
*/
|
|
78
|
+
type StepSource = (pkg: Package, script: string) => ScriptSlots | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Registers a source. Called by `loadPlugins` for each plugin's `runSteps`, in `plugins`
|
|
81
|
+
* declaration order - never as an import side effect, so what is registered is exactly what the
|
|
82
|
+
* repository's `.rmanrc` asked for.
|
|
83
|
+
*/
|
|
84
|
+
function addStepSource(source: StepSource): void;
|
|
85
|
+
/** For tests, which would otherwise leak a source into every later case in the process. */
|
|
86
|
+
function clearStepSources(): void;
|
|
87
|
+
/**
|
|
88
|
+
* What the *package itself* declares for the lifecycle `script`, from the contributed sources
|
|
89
|
+
* alone - no `.rmanrc` involved. `undefined` when it declares nothing.
|
|
90
|
+
*
|
|
91
|
+
* Exported because `run` is not the only lifecycle rman wraps: `version` runs hooks around the
|
|
92
|
+
* version write, and npm spells those `preversion`/`version`/`postversion` in `package.json` -
|
|
93
|
+
* which is the same `pre<script>`/`<script>`/`post<script>` shape a step source already answers.
|
|
94
|
+
* So `VersionService` asks here for `'version'` instead of reading `manifest.raw.scripts` itself,
|
|
95
|
+
* and npm's version lifecycle keeps working with no second seam and no extra line in any plugin.
|
|
96
|
+
* A plugin for another ecosystem gets its own lifecycle hooks the moment it contributes steps.
|
|
97
|
+
*/
|
|
98
|
+
function contributedSlots(pkg: Package, script: string): ScriptSlots | undefined;
|
|
99
|
+
/**
|
|
100
|
+
* Runs one slot of a lifecycle belonging to some operation other than `run` itself - `version`'s
|
|
101
|
+
* hooks around the version write are the only one so far.
|
|
102
|
+
*
|
|
103
|
+
* **Here rather than in `VersionService`, because the rule it applies is this service's**: the
|
|
104
|
+
* package's own declaration for `script` wins over the caller's `fallback`, slot by slot, exactly
|
|
105
|
+
* as `getScriptSteps` decides it for `run`. Kept in two places that rule would drift, and one of
|
|
106
|
+
* the copies would sit in the file that writes versions - which now runs no command of its own at
|
|
107
|
+
* all.
|
|
108
|
+
*
|
|
109
|
+
* `fallback` is the caller's own configured command, **already evaluated**: `version`'s three
|
|
110
|
+
* paths are in `DEFERRED_PATHS` precisely because only the caller can bind
|
|
111
|
+
* `${{ pkg.targetVersion }}`, so interpolating here would either be too early or need a scope this
|
|
112
|
+
* service has no business holding.
|
|
113
|
+
*/
|
|
114
|
+
function runLifecycleSlot(pkg: Package, script: string, slot: keyof ScriptSlots, fallback?: string): Promise<void>;
|
|
55
115
|
function getConfig(pkg: Package, script: string): Record<string, unknown>;
|
|
56
116
|
/**
|
|
57
117
|
* `.rmanrc` conditional execution, GitHub Actions-`if`-flavored but a small closed grammar
|