relacher 0.0.7-rc.0 → 0.0.7-rc.2
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/lib-types/builder/cargo.d.ts +2 -1
- package/lib-types/orchestrator.d.ts +7 -22
- package/lib-types/prepare.d.ts +1 -1
- package/lib-types/types.d.ts +8 -0
- package/lib-types/versioning/bump.d.ts +13 -0
- package/lib-types/versioning/index.d.ts +2 -0
- package/package.json +1 -1
- package/src/builder/cargo.ts +10 -5
- package/src/display/print.ts +7 -0
- package/src/orchestrator.ts +36 -32
- package/src/prepare.ts +30 -14
- package/src/run.ts +7 -0
- package/src/types.ts +10 -0
- package/src/versioning/bump.ts +41 -0
- package/src/versioning/index.ts +2 -0
- package/src/versioning/vcs-version-manager.ts +20 -45
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PackageList } from './shared';
|
|
2
2
|
export interface CargoBuilderOptions {
|
|
3
|
+
includePrivate?: boolean;
|
|
3
4
|
}
|
|
4
|
-
export declare function loadCargoDeps(cwd: string,
|
|
5
|
+
export declare function loadCargoDeps(cwd: string, options?: CargoBuilderOptions): PackageList;
|
|
@@ -1,44 +1,29 @@
|
|
|
1
1
|
import { type PackageList } from './builder';
|
|
2
2
|
import type { PreparedUpdate } from './types';
|
|
3
3
|
import { type VcsProvider } from './vcs';
|
|
4
|
-
import { type CascadeRules, type SizePatterns } from './versioning';
|
|
4
|
+
import { type CascadeRules, type SizePatterns, type BumpSize } from './versioning';
|
|
5
5
|
export interface RelacherConfig {
|
|
6
|
-
/** Project root directory. Defaults to `process.cwd()` */
|
|
7
6
|
cwd?: string;
|
|
8
|
-
/** Discovered packages list */
|
|
9
7
|
packages: PackageList;
|
|
10
|
-
/** VCS Provider: 'git', 'jj', or custom VcsProvider. Defaults to auto-detection */
|
|
11
8
|
vcs?: 'git' | 'jj' | VcsProvider;
|
|
12
|
-
/** Custom RC identifier (default: 'rc') */
|
|
13
9
|
rcIdentifier?: string;
|
|
14
|
-
/** Custom Conventional Commit patterns */
|
|
15
10
|
sizes?: SizePatterns;
|
|
16
|
-
/** Dependency bump cascade rules */
|
|
17
11
|
cascade?: CascadeRules;
|
|
18
|
-
/** Exclude nested watch paths from parent bumps (default: true) */
|
|
19
12
|
excludeNestedWatches?: boolean;
|
|
20
|
-
/**
|
|
13
|
+
/** Force a bump size across all packages (e.g. true for patch, or 'minor' / 'major') */
|
|
14
|
+
force?: boolean | BumpSize;
|
|
15
|
+
/** Selectively force bump sizes for specific packages */
|
|
16
|
+
forcePackages?: Record<string, BumpSize>;
|
|
21
17
|
commitTitle?: (bumps: Record<string, string>, defaultTitle: string) => string;
|
|
22
18
|
}
|
|
23
19
|
export declare function createRelacher(config: RelacherConfig): {
|
|
24
20
|
vcs: VcsProvider;
|
|
25
|
-
/**
|
|
26
|
-
* Initializes `.relacher.lock` and creates the baseline commit.
|
|
27
|
-
*/
|
|
28
21
|
init(): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Previews and calculates pending version bumps without writing changes.
|
|
31
|
-
*/
|
|
32
22
|
prepare(options?: {
|
|
33
23
|
mode?: "release" | "pre-release";
|
|
24
|
+
force?: boolean | BumpSize;
|
|
25
|
+
forcePackages?: Record<string, BumpSize>;
|
|
34
26
|
}): Promise<PreparedUpdate>;
|
|
35
|
-
/**
|
|
36
|
-
* Writes planned updates to disk and commits via VCS.
|
|
37
|
-
*/
|
|
38
27
|
run(prepared: PreparedUpdate): Promise<void>;
|
|
39
|
-
/**
|
|
40
|
-
* Full interactive CLI entry point.
|
|
41
|
-
* Handles arguments, preflight logs, confirmation prompts, dry-runs, and execution.
|
|
42
|
-
*/
|
|
43
28
|
cli(argv?: string[]): Promise<void>;
|
|
44
29
|
};
|
package/lib-types/prepare.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { BumpSize } from './versioning/types';
|
|
|
6
6
|
export declare function isBumpSizeMatch(actual: BumpSize, pattern: BumpSize): boolean;
|
|
7
7
|
export declare function isUpdateActive(bump: BumpSize, onlyOn?: BumpSize[]): boolean;
|
|
8
8
|
export declare function topologicalSort(items: IntermediateReport[]): IntermediateReport[];
|
|
9
|
-
export declare function initReportItems(packages: PackageConfig[], cwd: string,
|
|
9
|
+
export declare function initReportItems(packages: PackageConfig[], cwd: string, options?: PrepareOptions): Effect.Effect<IntermediateReport[], Error, VersionManager>;
|
|
10
10
|
export declare function prepare(packages: PackageConfig[] & {
|
|
11
11
|
errors?: Array<{
|
|
12
12
|
name: string;
|
package/lib-types/types.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface PackageConfig {
|
|
|
27
27
|
coupled?: string[];
|
|
28
28
|
group?: string;
|
|
29
29
|
}
|
|
30
|
+
export interface PrepareOptions {
|
|
31
|
+
cwd?: string;
|
|
32
|
+
excludeNestedWatches?: boolean;
|
|
33
|
+
/** Force a bump size across all packages (e.g. true for patch, or 'minor' / 'major') */
|
|
34
|
+
force?: boolean | BumpSize;
|
|
35
|
+
/** Selectively force bump sizes for specific packages */
|
|
36
|
+
forcePackages?: Record<string, BumpSize>;
|
|
37
|
+
}
|
|
30
38
|
export interface PrepareOptions {
|
|
31
39
|
cwd?: string;
|
|
32
40
|
excludeNestedWatches?: boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { BumpSize, PreRelease } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Type guard for pre-release bump objects.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isPreRelease(val: unknown): val is PreRelease;
|
|
6
|
+
/**
|
|
7
|
+
* Numeric priority for bump sizes: major (4) > minor (3) > patch (2) > pre (1) > skip (0).
|
|
8
|
+
*/
|
|
9
|
+
export declare function getBumpPriority(bump: BumpSize): number;
|
|
10
|
+
export declare function isBumpGreater(a: BumpSize, b: BumpSize): boolean;
|
|
11
|
+
export declare function maxBump(a: BumpSize, b: BumpSize): BumpSize;
|
|
12
|
+
export declare function isBumpEqual(a: BumpSize, b: BumpSize): boolean;
|
|
13
|
+
export declare function getBumpByPriority(priority: number, fallbackPre?: PreRelease): BumpSize;
|
package/package.json
CHANGED
package/src/builder/cargo.ts
CHANGED
|
@@ -9,7 +9,9 @@ import { isPathTrackedSync } from '../vcs/common';
|
|
|
9
9
|
|
|
10
10
|
import { createPackageList, type PackageList } from './shared';
|
|
11
11
|
|
|
12
|
-
export interface CargoBuilderOptions {
|
|
12
|
+
export interface CargoBuilderOptions {
|
|
13
|
+
includePrivate?: boolean;
|
|
14
|
+
}
|
|
13
15
|
|
|
14
16
|
interface PathDep {
|
|
15
17
|
name: string;
|
|
@@ -250,7 +252,7 @@ function buildCrateConfig(
|
|
|
250
252
|
};
|
|
251
253
|
}
|
|
252
254
|
|
|
253
|
-
export function loadCargoDeps(cwd: string,
|
|
255
|
+
export function loadCargoDeps(cwd: string, options?: CargoBuilderOptions): PackageList {
|
|
254
256
|
const workspaceRoot = findCargoWorkspaceRoot(cwd);
|
|
255
257
|
const rootCargo = path.join(workspaceRoot, 'Cargo.toml');
|
|
256
258
|
if (!fs.existsSync(rootCargo)) return createPackageList([]);
|
|
@@ -265,9 +267,12 @@ export function loadCargoDeps(cwd: string, _options?: CargoBuilderOptions): Pack
|
|
|
265
267
|
const hasLock = fs.existsSync(path.join(workspaceRoot, 'Cargo.lock'));
|
|
266
268
|
|
|
267
269
|
const publishableTracked = scanner.crateInfos.filter((info) => {
|
|
268
|
-
|
|
269
|
-
if (
|
|
270
|
-
|
|
270
|
+
// Respect includePrivate flag (defaults to false)
|
|
271
|
+
if (!options?.includePrivate) {
|
|
272
|
+
const publishVal = info.doc?.package?.publish;
|
|
273
|
+
if (publishVal === false) return false;
|
|
274
|
+
if (Array.isArray(publishVal) && publishVal.length === 0) return false;
|
|
275
|
+
}
|
|
271
276
|
|
|
272
277
|
const cargoPath = path.join(info.memberPath, 'Cargo.toml');
|
|
273
278
|
return isPathTrackedSync(cargoPath, workspaceRoot);
|
package/src/display/print.ts
CHANGED
|
@@ -82,6 +82,7 @@ export function prettyPrint(prepared: PreparedUpdate): void {
|
|
|
82
82
|
);
|
|
83
83
|
console.log(` ${files.join(' ')}`);
|
|
84
84
|
|
|
85
|
+
// 1. Show commits
|
|
85
86
|
for (const commit of report.commits) {
|
|
86
87
|
const commitBump = matchBumpSize(commit.message, defaultSizes);
|
|
87
88
|
const marker = commitBump !== 'skip' ? `${c.green('✦')}` : `${c.gray('○')}`;
|
|
@@ -90,6 +91,7 @@ export function prettyPrint(prepared: PreparedUpdate): void {
|
|
|
90
91
|
);
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
// 2. Show Cascades
|
|
93
95
|
const changedDeps = prepared.deps.filter(
|
|
94
96
|
(r) => report.depends?.includes(r.name) && r.currentVersion !== r.newVersion,
|
|
95
97
|
);
|
|
@@ -107,6 +109,11 @@ export function prettyPrint(prepared: PreparedUpdate): void {
|
|
|
107
109
|
);
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
// 3. Fallback for forced releases with no commits and no cascades
|
|
113
|
+
if (report.commits.length === 0 && changedDeps.length === 0) {
|
|
114
|
+
console.log(` ${c.gray('○ (forced release - no new commits)')}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
110
117
|
console.log('');
|
|
111
118
|
}
|
|
112
119
|
}
|
package/src/orchestrator.ts
CHANGED
|
@@ -13,35 +13,31 @@ import { makeGitVcsProvider, makeJjVcsProvider, VcsProviderService, type VcsProv
|
|
|
13
13
|
import { isInsideJjWorkTree } from './vcs/common';
|
|
14
14
|
import {
|
|
15
15
|
makeRCVersionManager,
|
|
16
|
-
makeVcsVersionManager,
|
|
17
16
|
VersionManagerService,
|
|
18
17
|
type CascadeRules,
|
|
19
18
|
type SizePatterns,
|
|
19
|
+
type BumpSize,
|
|
20
20
|
} from './versioning';
|
|
21
21
|
|
|
22
|
+
// Replace lines 20-35 in src/orchestrator.ts:
|
|
22
23
|
export interface RelacherConfig {
|
|
23
|
-
/** Project root directory. Defaults to `process.cwd()` */
|
|
24
24
|
cwd?: string;
|
|
25
|
-
/** Discovered packages list */
|
|
26
25
|
packages: PackageList;
|
|
27
|
-
/** VCS Provider: 'git', 'jj', or custom VcsProvider. Defaults to auto-detection */
|
|
28
26
|
vcs?: 'git' | 'jj' | VcsProvider;
|
|
29
|
-
/** Custom RC identifier (default: 'rc') */
|
|
30
27
|
rcIdentifier?: string;
|
|
31
|
-
/** Custom Conventional Commit patterns */
|
|
32
28
|
sizes?: SizePatterns;
|
|
33
|
-
/** Dependency bump cascade rules */
|
|
34
29
|
cascade?: CascadeRules;
|
|
35
|
-
/** Exclude nested watch paths from parent bumps (default: true) */
|
|
36
30
|
excludeNestedWatches?: boolean;
|
|
37
|
-
/**
|
|
31
|
+
/** Force a bump size across all packages (e.g. true for patch, or 'minor' / 'major') */
|
|
32
|
+
force?: boolean | BumpSize;
|
|
33
|
+
/** Selectively force bump sizes for specific packages */
|
|
34
|
+
forcePackages?: Record<string, BumpSize>;
|
|
38
35
|
commitTitle?: (bumps: Record<string, string>, defaultTitle: string) => string;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
38
|
export function createRelacher(config: RelacherConfig) {
|
|
42
39
|
const cwd = config.cwd || process.cwd();
|
|
43
40
|
|
|
44
|
-
// 1. Resolve VCS Provider
|
|
45
41
|
let vcs: VcsProvider;
|
|
46
42
|
if (config.vcs && typeof config.vcs === 'object') {
|
|
47
43
|
vcs = config.vcs;
|
|
@@ -50,11 +46,9 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
50
46
|
} else if (config.vcs === 'jj') {
|
|
51
47
|
vcs = makeJjVcsProvider(cwd);
|
|
52
48
|
} else {
|
|
53
|
-
// Auto-detect JJ or fallback to Git
|
|
54
49
|
vcs = isInsideJjWorkTree(cwd) ? makeJjVcsProvider(cwd) : makeGitVcsProvider(cwd);
|
|
55
50
|
}
|
|
56
51
|
|
|
57
|
-
// 2. Factory for Version Manager
|
|
58
52
|
const createVM = (mode: 'release' | 'pre-release') => {
|
|
59
53
|
return makeRCVersionManager(vcs, {
|
|
60
54
|
upgradeReady: mode === 'release',
|
|
@@ -67,9 +61,6 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
67
61
|
return {
|
|
68
62
|
vcs,
|
|
69
63
|
|
|
70
|
-
/**
|
|
71
|
-
* Initializes `.relacher.lock` and creates the baseline commit.
|
|
72
|
-
*/
|
|
73
64
|
async init(): Promise<void> {
|
|
74
65
|
const program = init(config.packages, { cwd }).pipe(
|
|
75
66
|
Effect.provideService(VcsProviderService, vcs),
|
|
@@ -77,16 +68,22 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
77
68
|
return Effect.runPromise(program);
|
|
78
69
|
},
|
|
79
70
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
|
|
72
|
+
async prepare(
|
|
73
|
+
options: {
|
|
74
|
+
mode?: 'release' | 'pre-release';
|
|
75
|
+
force?: boolean | BumpSize;
|
|
76
|
+
forcePackages?: Record<string, BumpSize>;
|
|
77
|
+
} = {},
|
|
78
|
+
): Promise<PreparedUpdate> {
|
|
84
79
|
const mode = options.mode || 'release';
|
|
85
80
|
const vm = createVM(mode);
|
|
86
81
|
|
|
87
82
|
const program = prepare(config.packages, {
|
|
88
83
|
cwd,
|
|
89
84
|
excludeNestedWatches: config.excludeNestedWatches ?? true,
|
|
85
|
+
force: options.force ?? config.force,
|
|
86
|
+
forcePackages: options.forcePackages ?? config.forcePackages,
|
|
90
87
|
}).pipe(
|
|
91
88
|
Effect.provideService(VersionManagerService, vm),
|
|
92
89
|
Effect.provideService(VcsProviderService, vcs),
|
|
@@ -94,10 +91,6 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
94
91
|
|
|
95
92
|
return Effect.runPromise(program);
|
|
96
93
|
},
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Writes planned updates to disk and commits via VCS.
|
|
100
|
-
*/
|
|
101
94
|
async run(prepared: PreparedUpdate): Promise<void> {
|
|
102
95
|
const program = run(prepared, {
|
|
103
96
|
cwd,
|
|
@@ -108,14 +101,22 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
108
101
|
return Effect.runPromise(program);
|
|
109
102
|
},
|
|
110
103
|
|
|
111
|
-
/**
|
|
112
|
-
* Full interactive CLI entry point.
|
|
113
|
-
* Handles arguments, preflight logs, confirmation prompts, dry-runs, and execution.
|
|
114
|
-
*/
|
|
115
104
|
async cli(argv: string[] = process.argv.slice(2)): Promise<void> {
|
|
116
105
|
const dryRun = argv.includes('--dry-run');
|
|
117
106
|
const yes = argv.includes('-y') || argv.includes('--yes');
|
|
118
|
-
|
|
107
|
+
|
|
108
|
+
// Parse force / empty flags
|
|
109
|
+
const forceFlagIdx = argv.findIndex((a) => a === '--force');
|
|
110
|
+
const hasEmptyFlag = argv.includes('--empty');
|
|
111
|
+
let forceOption: boolean | BumpSize | undefined = undefined;
|
|
112
|
+
|
|
113
|
+
if (forceFlagIdx !== -1 && argv[forceFlagIdx + 1] && !argv[forceFlagIdx + 1]?.startsWith('-')) {
|
|
114
|
+
forceOption = argv[forceFlagIdx + 1] as BumpSize;
|
|
115
|
+
} else if (forceFlagIdx !== -1 || hasEmptyFlag) {
|
|
116
|
+
forceOption = true;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const positional = argv.filter((a) => !a.startsWith('-') && a !== forceOption);
|
|
119
120
|
const command = positional[0] as 'init' | 'pre-release' | 'release' | undefined;
|
|
120
121
|
|
|
121
122
|
if (!command || !['init', 'pre-release', 'release'].includes(command)) {
|
|
@@ -124,8 +125,8 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
124
125
|
'Invalid or missing release command.',
|
|
125
126
|
'',
|
|
126
127
|
'Usage:',
|
|
127
|
-
' relacher pre-release [--dry-run] [-y]',
|
|
128
|
-
' relacher release [--dry-run] [-y]',
|
|
128
|
+
' relacher pre-release [--force] [--dry-run] [-y]',
|
|
129
|
+
' relacher release [--force [patch|minor|major]] [--dry-run] [-y]',
|
|
129
130
|
' relacher init',
|
|
130
131
|
].join('\n'),
|
|
131
132
|
);
|
|
@@ -143,11 +144,14 @@ export function createRelacher(config: RelacherConfig) {
|
|
|
143
144
|
return;
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
const updates = await this.prepare({ mode: command });
|
|
147
|
+
const updates = await this.prepare({ mode: command, force: forceOption });
|
|
147
148
|
log.step('Proposed Updates');
|
|
148
149
|
prettyPrint(updates);
|
|
149
150
|
|
|
150
|
-
if (updates.isEmpty)
|
|
151
|
+
if (updates.isEmpty) {
|
|
152
|
+
log.ok('Everything is up to date.');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
151
155
|
|
|
152
156
|
if (dryRun) {
|
|
153
157
|
log.step('Dry run active');
|
package/src/prepare.ts
CHANGED
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
import type { UpdateAction, UpdateActionResolved } from './updater';
|
|
17
17
|
import { VcsProviderService, type VcsProvider } from './vcs';
|
|
18
18
|
import { VersionManagerService, type VersionManager } from './versioning';
|
|
19
|
+
import { isBumpEqual, isPreRelease } from './versioning/bump';
|
|
19
20
|
import type { BumpSize } from './versioning/types';
|
|
20
21
|
import { getNestedExcludedPaths } from './workspace/paths';
|
|
21
22
|
|
|
@@ -23,16 +24,9 @@ export function isBumpSizeMatch(actual: BumpSize, pattern: BumpSize): boolean {
|
|
|
23
24
|
if (typeof actual === 'string' && typeof pattern === 'string') {
|
|
24
25
|
return actual === pattern;
|
|
25
26
|
}
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
actual
|
|
29
|
-
typeof pattern === 'object' &&
|
|
30
|
-
pattern !== null
|
|
31
|
-
) {
|
|
32
|
-
if (actual.kind === 'pre' && pattern.kind === 'pre') {
|
|
33
|
-
if (pattern.size === undefined || pattern.size === null) return true;
|
|
34
|
-
return actual.size === pattern.size;
|
|
35
|
-
}
|
|
27
|
+
if (isPreRelease(actual) && isPreRelease(pattern)) {
|
|
28
|
+
if (pattern.size === undefined || pattern.size === null) return true;
|
|
29
|
+
return actual.size === pattern.size;
|
|
36
30
|
}
|
|
37
31
|
return false;
|
|
38
32
|
}
|
|
@@ -67,10 +61,31 @@ export function topologicalSort(items: IntermediateReport[]): IntermediateReport
|
|
|
67
61
|
return sorted;
|
|
68
62
|
}
|
|
69
63
|
|
|
64
|
+
function resolvePackageBump(
|
|
65
|
+
name: string,
|
|
66
|
+
evaluatedBump: BumpSize,
|
|
67
|
+
options: PrepareOptions,
|
|
68
|
+
): BumpSize {
|
|
69
|
+
// 1. Check specific package overrides
|
|
70
|
+
if (options.forcePackages && name in options.forcePackages) {
|
|
71
|
+
return options.forcePackages[name]!;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 2. Check global force flag
|
|
75
|
+
if (options.force) {
|
|
76
|
+
if (typeof options.force === 'boolean') {
|
|
77
|
+
return evaluatedBump === 'skip' ? 'patch' : evaluatedBump;
|
|
78
|
+
}
|
|
79
|
+
return options.force;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return evaluatedBump;
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
export function initReportItems(
|
|
71
86
|
packages: PackageConfig[],
|
|
72
87
|
cwd: string,
|
|
73
|
-
|
|
88
|
+
options: PrepareOptions = {},
|
|
74
89
|
): Effect.Effect<IntermediateReport[], Error, VersionManager> {
|
|
75
90
|
return Effect.gen(function* () {
|
|
76
91
|
const versionManager = yield* VersionManagerService;
|
|
@@ -87,7 +102,7 @@ export function initReportItems(
|
|
|
87
102
|
const isMissingVersion = currentVersion === null;
|
|
88
103
|
const actualVersion = currentVersion || '0.0.0';
|
|
89
104
|
|
|
90
|
-
const exclude = excludeNestedWatches ? getNestedExcludedPaths(dep, packages) : [];
|
|
105
|
+
const exclude = options.excludeNestedWatches ? getNestedExcludedPaths(dep, packages) : [];
|
|
91
106
|
const allCommits = yield* versionManager.getCommits(dep, cachedLockfile, exclude, cwd);
|
|
92
107
|
let commitsSincePreRelease = [...allCommits];
|
|
93
108
|
|
|
@@ -100,7 +115,8 @@ export function initReportItems(
|
|
|
100
115
|
}
|
|
101
116
|
}
|
|
102
117
|
|
|
103
|
-
|
|
118
|
+
let selfBump = versionManager.evaluateCommitsBump(commitsSincePreRelease);
|
|
119
|
+
selfBump = resolvePackageBump(dep.name, selfBump, options);
|
|
104
120
|
|
|
105
121
|
let isErroneous = isMissingVersion;
|
|
106
122
|
for (const u of dep.updates || []) {
|
|
@@ -187,7 +203,7 @@ export function prepare(
|
|
|
187
203
|
}
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
const items = yield* initReportItems(trackedPackages, cwd, options
|
|
206
|
+
const items = yield* initReportItems(trackedPackages, cwd, options);
|
|
191
207
|
const sorted = topologicalSort(items);
|
|
192
208
|
versionManager.propagateBumps(sorted);
|
|
193
209
|
versionManager.propagateCoupledBumps(sorted);
|
package/src/run.ts
CHANGED
|
@@ -33,10 +33,12 @@ export function generateCommitMessage(
|
|
|
33
33
|
const firstReleaseBadge = report.isFirstRelease ? ' (first release)' : '';
|
|
34
34
|
commitMessage += `${report.name} (${report.currentVersion} -> ${report.newVersion}) [${report.bump}]${firstReleaseBadge}\n`;
|
|
35
35
|
|
|
36
|
+
// 1. Commits list
|
|
36
37
|
for (const commit of report.commits) {
|
|
37
38
|
commitMessage += ` - ${commit.shortHash} ${commit.message} (<${commit.author}> ${commit.date})\n`;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
// 2. Cascades from local dependencies
|
|
40
42
|
const changedDeps = reports.filter(
|
|
41
43
|
(r) => report.depends?.includes(r.name) && r.currentVersion !== r.newVersion,
|
|
42
44
|
);
|
|
@@ -46,6 +48,11 @@ export function generateCommitMessage(
|
|
|
46
48
|
commitMessage += ` - Cascaded from deps: ${depsStr}\n`;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
// 3. Fallback note for forced releases with no commits and no cascades
|
|
52
|
+
if (report.commits.length === 0 && changedDeps.length === 0) {
|
|
53
|
+
commitMessage += ` - forced release bump (no new commits)\n`;
|
|
54
|
+
}
|
|
55
|
+
|
|
49
56
|
commitMessage += '\n';
|
|
50
57
|
}
|
|
51
58
|
|
package/src/types.ts
CHANGED
|
@@ -31,6 +31,16 @@ export interface PackageConfig {
|
|
|
31
31
|
group?: string;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// In src/types.ts:
|
|
35
|
+
export interface PrepareOptions {
|
|
36
|
+
cwd?: string;
|
|
37
|
+
excludeNestedWatches?: boolean;
|
|
38
|
+
/** Force a bump size across all packages (e.g. true for patch, or 'minor' / 'major') */
|
|
39
|
+
force?: boolean | BumpSize;
|
|
40
|
+
/** Selectively force bump sizes for specific packages */
|
|
41
|
+
forcePackages?: Record<string, BumpSize>;
|
|
42
|
+
}
|
|
43
|
+
|
|
34
44
|
export interface PrepareOptions {
|
|
35
45
|
cwd?: string;
|
|
36
46
|
excludeNestedWatches?: boolean;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BumpSize, PreRelease } from './types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type guard for pre-release bump objects.
|
|
5
|
+
*/
|
|
6
|
+
export function isPreRelease(val: unknown): val is PreRelease {
|
|
7
|
+
return typeof val === 'object' && val !== null && 'kind' in val && (val as PreRelease).kind === 'pre';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Numeric priority for bump sizes: major (4) > minor (3) > patch (2) > pre (1) > skip (0).
|
|
12
|
+
*/
|
|
13
|
+
export function getBumpPriority(bump: BumpSize): number {
|
|
14
|
+
if (bump === 'major') return 4;
|
|
15
|
+
if (bump === 'minor') return 3;
|
|
16
|
+
if (bump === 'patch') return 2;
|
|
17
|
+
if (isPreRelease(bump)) return 1;
|
|
18
|
+
return 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function isBumpGreater(a: BumpSize, b: BumpSize): boolean {
|
|
22
|
+
return getBumpPriority(a) > getBumpPriority(b);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function maxBump(a: BumpSize, b: BumpSize): BumpSize {
|
|
26
|
+
return getBumpPriority(a) >= getBumpPriority(b) ? a : b;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isBumpEqual(a: BumpSize, b: BumpSize): boolean {
|
|
30
|
+
if (typeof a === 'string' && typeof b === 'string') return a === b;
|
|
31
|
+
if (isPreRelease(a) && isPreRelease(b)) return a.size === b.size;
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getBumpByPriority(priority: number, fallbackPre?: PreRelease): BumpSize {
|
|
36
|
+
if (priority === 4) return 'major';
|
|
37
|
+
if (priority === 3) return 'minor';
|
|
38
|
+
if (priority === 2) return 'patch';
|
|
39
|
+
if (priority === 1) return fallbackPre ?? { kind: 'pre' };
|
|
40
|
+
return 'skip';
|
|
41
|
+
}
|
package/src/versioning/index.ts
CHANGED
|
@@ -4,6 +4,13 @@ import { findLastReleaseCommit, type LockfileData } from '../lockfile';
|
|
|
4
4
|
import type { Commit, IntermediateReport, PackageConfig } from '../types';
|
|
5
5
|
import { VcsProviderService, type VcsProvider } from '../vcs';
|
|
6
6
|
|
|
7
|
+
import {
|
|
8
|
+
getBumpByPriority,
|
|
9
|
+
getBumpPriority,
|
|
10
|
+
isBumpEqual,
|
|
11
|
+
isBumpGreater,
|
|
12
|
+
isPreRelease,
|
|
13
|
+
} from './bump';
|
|
7
14
|
import { defaultCascadeRules, defaultSizes } from './default-data';
|
|
8
15
|
import type { BumpSize, CascadeRules, SizePatterns } from './types';
|
|
9
16
|
import { defaultBumpVersion, isVersionGreater, matchBumpSize } from './utils';
|
|
@@ -31,38 +38,6 @@ export interface VersionManager {
|
|
|
31
38
|
|
|
32
39
|
export const VersionManagerService = Context.Service<VersionManager>('VersionManager');
|
|
33
40
|
|
|
34
|
-
const getBumpPriority = (bump: BumpSize): number => {
|
|
35
|
-
if (typeof bump === 'string') {
|
|
36
|
-
if (bump === 'major') return 4;
|
|
37
|
-
if (bump === 'minor') return 3;
|
|
38
|
-
if (bump === 'patch') return 2;
|
|
39
|
-
return 0;
|
|
40
|
-
}
|
|
41
|
-
if (bump && bump.kind === 'pre') return 1;
|
|
42
|
-
return 0;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const isBumpSizeEqual = (a: BumpSize, b: BumpSize): boolean => {
|
|
46
|
-
if (typeof a === 'string' && typeof b === 'string') return a === b;
|
|
47
|
-
if (typeof a === 'object' && a !== null && typeof b === 'object' && b !== null) {
|
|
48
|
-
if (a.kind === 'pre' && b.kind === 'pre') return a.size === b.size;
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const getBumpSizeByPriority = (priorityValue: number, originalBump: BumpSize): BumpSize => {
|
|
54
|
-
if (priorityValue === 4) return 'major';
|
|
55
|
-
if (priorityValue === 3) return 'minor';
|
|
56
|
-
if (priorityValue === 2) return 'patch';
|
|
57
|
-
if (priorityValue === 1) {
|
|
58
|
-
if (typeof originalBump === 'object' && originalBump !== null && originalBump.kind === 'pre') {
|
|
59
|
-
return originalBump;
|
|
60
|
-
}
|
|
61
|
-
return { kind: 'pre' };
|
|
62
|
-
}
|
|
63
|
-
return 'skip';
|
|
64
|
-
};
|
|
65
|
-
|
|
66
41
|
export const makeVcsVersionManager = (
|
|
67
42
|
vcs: VcsProvider,
|
|
68
43
|
options?: {
|
|
@@ -142,14 +117,14 @@ export const makeVcsVersionManager = (
|
|
|
142
117
|
},
|
|
143
118
|
|
|
144
119
|
evaluateCommitsBump: (commits) => {
|
|
145
|
-
let
|
|
120
|
+
let max: BumpSize = 'skip';
|
|
146
121
|
for (const commit of commits) {
|
|
147
122
|
const commitBump = matchBumpSize(commit.message, sizes);
|
|
148
|
-
if (
|
|
149
|
-
|
|
123
|
+
if (isBumpGreater(commitBump, max)) {
|
|
124
|
+
max = commitBump;
|
|
150
125
|
}
|
|
151
126
|
}
|
|
152
|
-
return
|
|
127
|
+
return max;
|
|
153
128
|
},
|
|
154
129
|
|
|
155
130
|
propagateBumps: (sorted) => {
|
|
@@ -180,23 +155,20 @@ export const makeVcsVersionManager = (
|
|
|
180
155
|
const depBumpKey =
|
|
181
156
|
typeof depItem.bump === 'string'
|
|
182
157
|
? depItem.bump
|
|
183
|
-
: depItem.bump
|
|
158
|
+
: isPreRelease(depItem.bump)
|
|
184
159
|
? depItem.bump.size
|
|
185
160
|
? `pre-${depItem.bump.size}`
|
|
186
161
|
: 'pre'
|
|
187
162
|
: 'patch';
|
|
188
163
|
|
|
189
164
|
const cascadedResult = activeRules[depBumpKey];
|
|
190
|
-
if (
|
|
191
|
-
cascadedResult &&
|
|
192
|
-
getBumpPriority(cascadedResult) > getBumpPriority(maxCascadedBump)
|
|
193
|
-
) {
|
|
165
|
+
if (cascadedResult && isBumpGreater(cascadedResult, maxCascadedBump)) {
|
|
194
166
|
maxCascadedBump = cascadedResult;
|
|
195
167
|
}
|
|
196
168
|
}
|
|
197
169
|
}
|
|
198
170
|
|
|
199
|
-
if (
|
|
171
|
+
if (isBumpGreater(maxCascadedBump, item.bump)) {
|
|
200
172
|
item.bump = maxCascadedBump;
|
|
201
173
|
}
|
|
202
174
|
|
|
@@ -226,14 +198,17 @@ export const makeVcsVersionManager = (
|
|
|
226
198
|
} else if (maxPriority === otherPri) {
|
|
227
199
|
targetBump = other.bump;
|
|
228
200
|
} else {
|
|
229
|
-
targetBump =
|
|
201
|
+
targetBump = getBumpByPriority(
|
|
202
|
+
maxPriority,
|
|
203
|
+
isPreRelease(item.bump) ? item.bump : undefined,
|
|
204
|
+
);
|
|
230
205
|
}
|
|
231
206
|
|
|
232
|
-
if (!
|
|
207
|
+
if (!isBumpEqual(item.bump, targetBump)) {
|
|
233
208
|
item.bump = targetBump;
|
|
234
209
|
changed = true;
|
|
235
210
|
}
|
|
236
|
-
if (!
|
|
211
|
+
if (!isBumpEqual(other.bump, targetBump)) {
|
|
237
212
|
other.bump = targetBump;
|
|
238
213
|
changed = true;
|
|
239
214
|
}
|