versionary 0.22.0 → 0.23.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 +26 -0
- package/dist/config/schema.d.ts +1 -0
- package/dist/config/schema.js +86 -1
- package/dist/release/changelog.d.ts +4 -0
- package/dist/release/changelog.js +3 -0
- package/dist/release/plan.d.ts +1 -1
- package/dist/release/plan.js +44 -1
- package/dist/release/pr.js +9 -0
- package/dist/release/semver.d.ts +1 -0
- package/dist/release/semver.js +13 -0
- package/dist/types/config.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,32 @@ For a quick trial, use:
|
|
|
157
157
|
- per-package `package-name` can override release identity (labels + tag base)
|
|
158
158
|
- per-package `changelog-file` writes package release notes to
|
|
159
159
|
`<package-path>/<changelog-file>`
|
|
160
|
+
- per-package `follows` declares an asymmetric version link to one or more
|
|
161
|
+
source packages: when any source bumps, the follower releases too, with
|
|
162
|
+
bump = `max(own bump, max(source bumps))`. The follower's changelog gets a
|
|
163
|
+
`### Dependencies` section listing the followed sources. Use it when one
|
|
164
|
+
package bundles another's artifact (e.g. an editor extension that ships
|
|
165
|
+
the CLI binary). Cycles, self-references, unknown source paths, and
|
|
166
|
+
combining `follows` with `monorepo-mode: "fixed"` are config errors.
|
|
167
|
+
`follows` is non-transitive: A follows B does not imply A follows what B
|
|
168
|
+
follows.
|
|
169
|
+
|
|
170
|
+
```jsonc
|
|
171
|
+
// Editor extension that bundles the root CLI artifact
|
|
172
|
+
{
|
|
173
|
+
"version": 1,
|
|
174
|
+
"release-type": "rust",
|
|
175
|
+
"monorepo-mode": "independent",
|
|
176
|
+
"packages": {
|
|
177
|
+
".": { "exclude-paths": ["editors"] },
|
|
178
|
+
"editors/code": {
|
|
179
|
+
"release-type": "node",
|
|
180
|
+
"package-name": "panache-code",
|
|
181
|
+
"follows": ["."]
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
160
186
|
|
|
161
187
|
Rust strategy examples:
|
|
162
188
|
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
53
53
|
jsonpath: z.ZodOptional<z.ZodString>;
|
|
54
54
|
pattern: z.ZodOptional<z.ZodString>;
|
|
55
55
|
}, z.core.$strip>>>;
|
|
56
|
+
follows: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
57
|
}, z.core.$strict>>>;
|
|
57
58
|
}, z.core.$strict>;
|
|
58
59
|
export type ConfigSchema = z.infer<typeof configSchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -60,6 +60,7 @@ const packageSchema = zod_1.z
|
|
|
60
60
|
"changelog-format": zod_1.z.enum(["markdown-changelog", "r-news"]).optional(),
|
|
61
61
|
"exclude-paths": zod_1.z.array(zod_1.z.string()).optional(),
|
|
62
62
|
"extra-files": zod_1.z.array(artifactRuleSchema).optional(),
|
|
63
|
+
follows: zod_1.z.array(zod_1.z.string().min(1)).optional(),
|
|
63
64
|
})
|
|
64
65
|
.strict();
|
|
65
66
|
exports.configSchema = zod_1.z
|
|
@@ -85,4 +86,88 @@ exports.configSchema = zod_1.z
|
|
|
85
86
|
"release-type": zod_1.z.string().optional(),
|
|
86
87
|
packages: zod_1.z.record(zod_1.z.string().min(1), packageSchema).optional(),
|
|
87
88
|
})
|
|
88
|
-
.strict()
|
|
89
|
+
.strict()
|
|
90
|
+
.superRefine((value, ctx) => {
|
|
91
|
+
const packages = value.packages;
|
|
92
|
+
if (!packages) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const knownPaths = new Set(Object.keys(packages));
|
|
96
|
+
const followsByPath = new Map();
|
|
97
|
+
for (const [packagePath, packageConfig] of Object.entries(packages)) {
|
|
98
|
+
const follows = packageConfig.follows;
|
|
99
|
+
if (!follows || follows.length === 0) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
followsByPath.set(packagePath, follows);
|
|
103
|
+
for (const sourcePath of follows) {
|
|
104
|
+
if (sourcePath === packagePath) {
|
|
105
|
+
ctx.addIssue({
|
|
106
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
107
|
+
message: `Package "${packagePath}" cannot follow itself.`,
|
|
108
|
+
path: ["packages", packagePath, "follows"],
|
|
109
|
+
});
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!knownPaths.has(sourcePath)) {
|
|
113
|
+
ctx.addIssue({
|
|
114
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
115
|
+
message: `Package "${packagePath}" follows unknown package "${sourcePath}".`,
|
|
116
|
+
path: ["packages", packagePath, "follows"],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (followsByPath.size > 0 && value["monorepo-mode"] === "fixed") {
|
|
122
|
+
for (const followerPath of followsByPath.keys()) {
|
|
123
|
+
ctx.addIssue({
|
|
124
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
125
|
+
message: 'Package "follows" cannot be combined with monorepo-mode "fixed" (fixed mode already pins all package versions together).',
|
|
126
|
+
path: ["packages", followerPath, "follows"],
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const reportedCycles = new Set();
|
|
131
|
+
const findCycleFrom = (start) => {
|
|
132
|
+
const trail = [];
|
|
133
|
+
const visit = (node) => {
|
|
134
|
+
const trailIndex = trail.indexOf(node);
|
|
135
|
+
if (trailIndex !== -1) {
|
|
136
|
+
return [...trail.slice(trailIndex), node];
|
|
137
|
+
}
|
|
138
|
+
const sources = followsByPath.get(node);
|
|
139
|
+
if (!sources || sources.length === 0) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
trail.push(node);
|
|
143
|
+
for (const source of sources) {
|
|
144
|
+
if (!knownPaths.has(source)) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const cycle = visit(source);
|
|
148
|
+
if (cycle) {
|
|
149
|
+
return cycle;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
trail.pop();
|
|
153
|
+
return null;
|
|
154
|
+
};
|
|
155
|
+
return visit(start);
|
|
156
|
+
};
|
|
157
|
+
for (const followerPath of followsByPath.keys()) {
|
|
158
|
+
const cycle = findCycleFrom(followerPath);
|
|
159
|
+
if (!cycle) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const key = [...cycle].sort().join("->");
|
|
163
|
+
if (reportedCycles.has(key)) {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
reportedCycles.add(key);
|
|
167
|
+
ctx.addIssue({
|
|
168
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
169
|
+
message: `Package "follows" cycle detected: ${cycle.join(" -> ")}.`,
|
|
170
|
+
path: ["packages", cycle[0] ?? followerPath, "follows"],
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
});
|
|
@@ -30,6 +30,10 @@ export declare function renderPackageChangelogSection(input: {
|
|
|
30
30
|
commits: ParsedCommit[];
|
|
31
31
|
tagPrefix: string;
|
|
32
32
|
cwd?: string;
|
|
33
|
+
dependencies?: Array<{
|
|
34
|
+
name: string;
|
|
35
|
+
version: string;
|
|
36
|
+
}>;
|
|
33
37
|
}): string;
|
|
34
38
|
export declare function prependChangelog(cwd: string, changelogFile: string, section: string, format?: VersionaryChangelogFormat): void;
|
|
35
39
|
export declare function renderRNewsReleaseNotes(input: {
|
|
@@ -257,6 +257,9 @@ function renderPackageChangelogSection(input) {
|
|
|
257
257
|
if (grouped.reverts.length > 0) {
|
|
258
258
|
sections.push("### Reverts", ...grouped.reverts, "");
|
|
259
259
|
}
|
|
260
|
+
if (input.dependencies && input.dependencies.length > 0) {
|
|
261
|
+
sections.push("### Dependencies", ...input.dependencies.map((dependency) => `- updated ${dependency.name} to v${dependency.version}`), "");
|
|
262
|
+
}
|
|
260
263
|
return [header, "", ...sections].join("\n");
|
|
261
264
|
}
|
|
262
265
|
function prependChangelog(cwd, changelogFile, section, format = "markdown-changelog") {
|
package/dist/release/plan.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface ReleasePlan {
|
|
|
18
18
|
releaseType: ReleaseType;
|
|
19
19
|
currentVersion: string;
|
|
20
20
|
nextVersion: string | null;
|
|
21
|
-
bumpReason?: "direct" | "dependency-propagation";
|
|
21
|
+
bumpReason?: "direct" | "dependency-propagation" | "follows";
|
|
22
22
|
dependencySourcePaths?: string[];
|
|
23
23
|
commits: ParsedCommit[];
|
|
24
24
|
}>;
|
package/dist/release/plan.js
CHANGED
|
@@ -98,6 +98,8 @@ function createReleasePlan(cwd = process.cwd()) {
|
|
|
98
98
|
releaseType: null,
|
|
99
99
|
currentVersion: explicitPackagePlans[0]?.currentVersion ?? "0.0.0",
|
|
100
100
|
nextVersion: null,
|
|
101
|
+
bumpReason: undefined,
|
|
102
|
+
dependencySourcePaths: undefined,
|
|
101
103
|
commits: [],
|
|
102
104
|
parsedCommits: [],
|
|
103
105
|
resolvedVersionFile: versionFile,
|
|
@@ -171,7 +173,7 @@ function createReleasePlan(cwd = process.cwd()) {
|
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
|
-
const
|
|
176
|
+
const propagatedPackages = packagePlans.map((pkgPlan) => {
|
|
175
177
|
const dependencySourcePaths = [
|
|
176
178
|
...(dependencySourcePathsByPackage.get(pkgPlan.path) ??
|
|
177
179
|
new Set()),
|
|
@@ -196,6 +198,47 @@ function createReleasePlan(cwd = process.cwd()) {
|
|
|
196
198
|
dependencySourcePaths,
|
|
197
199
|
};
|
|
198
200
|
});
|
|
201
|
+
const followsByPath = new Map();
|
|
202
|
+
for (const [packagePath, packageConfig] of Object.entries(loaded.config.packages ?? {})) {
|
|
203
|
+
const follows = packageConfig.follows ?? [];
|
|
204
|
+
if (follows.length > 0) {
|
|
205
|
+
followsByPath.set(packagePath, follows);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const adjustedPackages = propagatedPackages.map((pkgPlan) => {
|
|
209
|
+
const followsSources = followsByPath.get(pkgPlan.path) ?? [];
|
|
210
|
+
const bumpingSources = followsSources
|
|
211
|
+
.map((sourcePath) => propagatedPackages.find((pkg) => pkg.path === sourcePath))
|
|
212
|
+
.filter((sourcePlan) => Boolean(sourcePlan?.nextVersion));
|
|
213
|
+
if (bumpingSources.length === 0) {
|
|
214
|
+
return pkgPlan;
|
|
215
|
+
}
|
|
216
|
+
const ownReleaseType = pkgPlan.releaseType;
|
|
217
|
+
const combinedReleaseType = (0, semver_js_1.maxReleaseType)([
|
|
218
|
+
ownReleaseType,
|
|
219
|
+
...bumpingSources.map((sourcePlan) => sourcePlan.releaseType),
|
|
220
|
+
]);
|
|
221
|
+
const mergedDependencySourcePaths = [
|
|
222
|
+
...new Set([
|
|
223
|
+
...(pkgPlan.dependencySourcePaths ?? []),
|
|
224
|
+
...bumpingSources.map((sourcePlan) => sourcePlan.path),
|
|
225
|
+
]),
|
|
226
|
+
].sort((a, b) => a.localeCompare(b));
|
|
227
|
+
const sourceDrove = combinedReleaseType !== ownReleaseType ||
|
|
228
|
+
pkgPlan.bumpReason === "dependency-propagation" ||
|
|
229
|
+
pkgPlan.bumpReason === undefined;
|
|
230
|
+
const baseVersion = packageCurrentVersionByPath[pkgPlan.path] ?? pkgPlan.currentVersion;
|
|
231
|
+
const nextVersion = combinedReleaseType
|
|
232
|
+
? (0, semver_js_1.bumpVersion)(baseVersion, combinedReleaseType, { allowStableMajor })
|
|
233
|
+
: null;
|
|
234
|
+
return {
|
|
235
|
+
...pkgPlan,
|
|
236
|
+
releaseType: combinedReleaseType,
|
|
237
|
+
nextVersion,
|
|
238
|
+
bumpReason: sourceDrove ? "follows" : pkgPlan.bumpReason,
|
|
239
|
+
dependencySourcePaths: mergedDependencySourcePaths,
|
|
240
|
+
};
|
|
241
|
+
});
|
|
199
242
|
const visiblePackages = adjustedPackages.filter((pkgPlan) => !pkgPlan.implicitRoot || hasExplicitRootPackage);
|
|
200
243
|
const rootPackagePlan = adjustedPackages.find((pkgPlan) => pkgPlan.path === ".");
|
|
201
244
|
if (!rootPackagePlan) {
|
package/dist/release/pr.js
CHANGED
|
@@ -315,12 +315,21 @@ function prepareReleasePr(cwd = process.cwd(), options = {}) {
|
|
|
315
315
|
if (!packageMetadata) {
|
|
316
316
|
continue;
|
|
317
317
|
}
|
|
318
|
+
const dependencySourcePaths = packagePlan.dependencySourcePaths ?? [];
|
|
319
|
+
const packageDependencies = dependencySourcePaths
|
|
320
|
+
.map((sourcePath) => plan.packages?.find((pkg) => pkg.path === sourcePath && pkg.nextVersion))
|
|
321
|
+
.filter((sourcePackage) => Boolean(sourcePackage))
|
|
322
|
+
.map((pkg) => ({
|
|
323
|
+
name: pkg.path,
|
|
324
|
+
version: pkg.nextVersion,
|
|
325
|
+
}));
|
|
318
326
|
const packageSection = (0, changelog_js_1.renderPackageChangelogSection)({
|
|
319
327
|
currentVersion: packagePlan.currentVersion,
|
|
320
328
|
nextVersion: packagePlan.nextVersion,
|
|
321
329
|
commits: packagePlan.commits,
|
|
322
330
|
tagPrefix: packageMetadata.tagPrefix,
|
|
323
331
|
cwd,
|
|
332
|
+
dependencies: packageDependencies,
|
|
324
333
|
});
|
|
325
334
|
(0, changelog_js_1.prependChangelog)(cwd, node_path_1.default.posix.join(packagePlan.path, packageChangelogFile), packageSection, "markdown-changelog");
|
|
326
335
|
updatedChangelogFiles.push(node_path_1.default.posix.join(packagePlan.path, packageChangelogFile));
|
package/dist/release/semver.d.ts
CHANGED
package/dist/release/semver.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.maxReleaseType = maxReleaseType;
|
|
3
4
|
exports.parseVersion = parseVersion;
|
|
4
5
|
exports.isValidVersion = isValidVersion;
|
|
5
6
|
exports.compareVersions = compareVersions;
|
|
6
7
|
exports.bumpVersion = bumpVersion;
|
|
8
|
+
function maxReleaseType(types) {
|
|
9
|
+
if (types.includes("major")) {
|
|
10
|
+
return "major";
|
|
11
|
+
}
|
|
12
|
+
if (types.includes("minor")) {
|
|
13
|
+
return "minor";
|
|
14
|
+
}
|
|
15
|
+
if (types.includes("patch")) {
|
|
16
|
+
return "patch";
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
7
20
|
const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/u;
|
|
8
21
|
const COMPAT_DOTTED_PRERELEASE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
|
|
9
22
|
function normalizeVersionInput(version) {
|
package/dist/types/config.d.ts
CHANGED