versionary 0.4.0 → 0.6.0
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 +28 -2
- package/dist/app/release/pr.js +128 -22
- package/dist/app/release/state.js +21 -18
- package/dist/config/schema.d.ts +3 -2
- package/dist/config/schema.js +9 -4
- package/dist/domain/release/plan.js +62 -16
- package/dist/domain/strategy/package-context.d.ts +7 -0
- package/dist/domain/strategy/package-context.js +49 -0
- package/dist/domain/strategy/rust.d.ts +3 -0
- package/dist/domain/strategy/rust.js +136 -15
- package/dist/verify/verify-project.js +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,7 @@ For a quick trial, use:
|
|
|
100
100
|
- optional monorepo planning with `monorepo-mode` and `packages`:
|
|
101
101
|
- `independent` computes package bumps per path
|
|
102
102
|
- `fixed` computes one shared bump across configured package paths
|
|
103
|
+
- per-package `package-name` can override release identity (labels + tag base)
|
|
103
104
|
|
|
104
105
|
Rust strategy examples:
|
|
105
106
|
|
|
@@ -136,6 +137,26 @@ Current rust non-goals/limits:
|
|
|
136
137
|
- does not add missing `version = ...` fields to dependency inline tables
|
|
137
138
|
- does not perform Cargo publish/release to crates.io
|
|
138
139
|
|
|
140
|
+
### Monorepo release names and tag naming
|
|
141
|
+
|
|
142
|
+
For independent monorepo targets, Versionary derives release tags as:
|
|
143
|
+
|
|
144
|
+
- root package (`"."`): `v<version>`
|
|
145
|
+
- non-root package: `<release-name>-v<version>`
|
|
146
|
+
|
|
147
|
+
`release-name` precedence is:
|
|
148
|
+
|
|
149
|
+
1. `packages.<path>.package-name` (explicit override)
|
|
150
|
+
2. strategy-native package name from version file:
|
|
151
|
+
- Node: `package.json` `name`
|
|
152
|
+
- Rust: `Cargo.toml` `[package].name`
|
|
153
|
+
- R: `DESCRIPTION` `Package:`
|
|
154
|
+
3. package path fallback
|
|
155
|
+
|
|
156
|
+
When multiple packages resolve to the same `<release-name>` and version, the run
|
|
157
|
+
fails fast with a duplicate-tag error and suggests setting unique
|
|
158
|
+
`package-name` values.
|
|
159
|
+
|
|
139
160
|
## Commit parsing and release analysis
|
|
140
161
|
|
|
141
162
|
Release planning is based on Conventional Commit parsing semantics:
|
|
@@ -231,7 +252,7 @@ steps:
|
|
|
231
252
|
- id: versionary
|
|
232
253
|
uses: jolars/versionary@v1
|
|
233
254
|
with:
|
|
234
|
-
|
|
255
|
+
token: ${{ secrets.RELEASE_TOKEN }}
|
|
235
256
|
```
|
|
236
257
|
|
|
237
258
|
```yaml
|
|
@@ -247,11 +268,16 @@ steps:
|
|
|
247
268
|
- id: versionary
|
|
248
269
|
uses: jolars/versionary@v1
|
|
249
270
|
with:
|
|
250
|
-
|
|
271
|
+
token: ${{ secrets.RELEASE_TOKEN }}
|
|
251
272
|
- if: ${{ steps.versionary.outputs.release_created == 'true' }}
|
|
252
273
|
run: echo "Released ${{ steps.versionary.outputs.tag_name }}"
|
|
253
274
|
```
|
|
254
275
|
|
|
276
|
+
`token` is used for both GitHub API calls and git push authentication in
|
|
277
|
+
the composite action. This means release-branch force-pushes are attributed to
|
|
278
|
+
that token and can trigger downstream workflows when using a PAT/App token.
|
|
279
|
+
(`github-token` remains as a deprecated alias for backward compatibility.)
|
|
280
|
+
|
|
255
281
|
Action outputs:
|
|
256
282
|
|
|
257
283
|
- `action`: `noop`, `pr-prepared`, `release-published`, `release-skipped`
|
package/dist/app/release/pr.js
CHANGED
|
@@ -10,11 +10,15 @@ exports.openOrUpdateSimpleReviewRequest = openOrUpdateSimpleReviewRequest;
|
|
|
10
10
|
exports.pushReleaseBranch = pushReleaseBranch;
|
|
11
11
|
exports.isReleaseCommitMessage = isReleaseCommitMessage;
|
|
12
12
|
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
14
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const toml_1 = __importDefault(require("@iarna/toml"));
|
|
14
16
|
const load_config_js_1 = require("../../config/load-config.js");
|
|
15
17
|
const changelog_js_1 = require("../../domain/release/changelog.js");
|
|
16
18
|
const plan_js_1 = require("../../domain/release/plan.js");
|
|
19
|
+
const package_context_js_1 = require("../../domain/strategy/package-context.js");
|
|
17
20
|
const resolve_js_1 = require("../../domain/strategy/resolve.js");
|
|
21
|
+
const rust_js_1 = require("../../domain/strategy/rust.js");
|
|
18
22
|
const capabilities_js_1 = require("../../plugins/capabilities.js");
|
|
19
23
|
const runtime_js_1 = require("../../plugins/runtime.js");
|
|
20
24
|
const artifact_rules_js_1 = require("./artifact-rules.js");
|
|
@@ -66,6 +70,101 @@ function ensureCleanWorktree(cwd, logger) {
|
|
|
66
70
|
logger?.warn(`Ignoring safe tracked changes before versionary pr:\n${ignored.join("\n")}`);
|
|
67
71
|
}
|
|
68
72
|
}
|
|
73
|
+
function normalizeReleaseNameForTag(releaseName) {
|
|
74
|
+
return releaseName
|
|
75
|
+
.trim()
|
|
76
|
+
.replace(/^@/u, "")
|
|
77
|
+
.replaceAll("/", "-")
|
|
78
|
+
.replace(/\s+/gu, "-");
|
|
79
|
+
}
|
|
80
|
+
function readNodePackageName(versionPath) {
|
|
81
|
+
const raw = JSON.parse(node_fs_1.default.readFileSync(versionPath, "utf8"));
|
|
82
|
+
const name = raw.name;
|
|
83
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return name.trim();
|
|
87
|
+
}
|
|
88
|
+
function readRustPackageName(versionPath) {
|
|
89
|
+
const parsed = toml_1.default.parse(node_fs_1.default.readFileSync(versionPath, "utf8"));
|
|
90
|
+
const name = parsed.package?.name;
|
|
91
|
+
if (typeof name !== "string" || name.trim().length === 0) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return name.trim();
|
|
95
|
+
}
|
|
96
|
+
function readRPackageName(versionPath) {
|
|
97
|
+
const content = node_fs_1.default.readFileSync(versionPath, "utf8");
|
|
98
|
+
const match = content.match(/^Package:\s*(.+)\s*$/mu);
|
|
99
|
+
if (!match?.[1]) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const name = match[1].trim();
|
|
103
|
+
return name.length > 0 ? name : null;
|
|
104
|
+
}
|
|
105
|
+
function resolveReleaseName(cwd, packagePath, packageConfig, strategyName, versionFile) {
|
|
106
|
+
const configuredName = packageConfig["package-name"]?.trim();
|
|
107
|
+
if (configuredName) {
|
|
108
|
+
return configuredName;
|
|
109
|
+
}
|
|
110
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
111
|
+
if (strategyName === "node") {
|
|
112
|
+
return readNodePackageName(versionPath) ?? packagePath;
|
|
113
|
+
}
|
|
114
|
+
if (strategyName === "rust") {
|
|
115
|
+
return readRustPackageName(versionPath) ?? packagePath;
|
|
116
|
+
}
|
|
117
|
+
if (strategyName === "r") {
|
|
118
|
+
return readRPackageName(versionPath) ?? packagePath;
|
|
119
|
+
}
|
|
120
|
+
return packagePath;
|
|
121
|
+
}
|
|
122
|
+
function buildReleaseTargets(cwd, plan, loadedConfig) {
|
|
123
|
+
const releaseTargets = plan.packages
|
|
124
|
+
? plan.packages
|
|
125
|
+
.filter((pkg) => pkg.nextVersion)
|
|
126
|
+
.map((pkg) => {
|
|
127
|
+
if (pkg.path === ".") {
|
|
128
|
+
return {
|
|
129
|
+
path: pkg.path,
|
|
130
|
+
version: pkg.nextVersion ?? "",
|
|
131
|
+
tag: `v${pkg.nextVersion ?? ""}`,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const packageConfig = loadedConfig.packages?.[pkg.path] ?? {};
|
|
135
|
+
const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(loadedConfig, pkg.path, packageConfig);
|
|
136
|
+
const releaseName = resolveReleaseName(cwd, pkg.path, packageConfig, packageContext.strategy.name, packageContext.versionFile);
|
|
137
|
+
const tagPrefix = normalizeReleaseNameForTag(releaseName);
|
|
138
|
+
return {
|
|
139
|
+
path: pkg.path,
|
|
140
|
+
version: pkg.nextVersion ?? "",
|
|
141
|
+
tag: `${tagPrefix}-v${pkg.nextVersion ?? ""}`,
|
|
142
|
+
};
|
|
143
|
+
})
|
|
144
|
+
: [
|
|
145
|
+
{
|
|
146
|
+
path: ".",
|
|
147
|
+
version: plan.nextVersion ?? "",
|
|
148
|
+
tag: `v${plan.nextVersion ?? ""}`,
|
|
149
|
+
},
|
|
150
|
+
];
|
|
151
|
+
const seenTags = new Map();
|
|
152
|
+
for (const target of releaseTargets) {
|
|
153
|
+
const existingPath = seenTags.get(target.tag);
|
|
154
|
+
if (existingPath) {
|
|
155
|
+
throw new Error(`Duplicate release tag "${target.tag}" for packages "${existingPath}" and "${target.path}". Configure unique "package-name" values.`);
|
|
156
|
+
}
|
|
157
|
+
seenTags.set(target.tag, target.path);
|
|
158
|
+
}
|
|
159
|
+
return releaseTargets;
|
|
160
|
+
}
|
|
161
|
+
function formatReleaseCommitTitle(releaseTargets) {
|
|
162
|
+
if (releaseTargets.length === 0) {
|
|
163
|
+
return "chore(release): v0.0.0";
|
|
164
|
+
}
|
|
165
|
+
const tags = releaseTargets.map((target) => target.tag);
|
|
166
|
+
return `chore(release): ${tags.join(", ")}`;
|
|
167
|
+
}
|
|
69
168
|
function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
|
|
70
169
|
const plan = (0, plan_js_1.createSimplePlan)(cwd);
|
|
71
170
|
const loaded = (0, load_config_js_1.loadConfig)(cwd);
|
|
@@ -74,12 +173,33 @@ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
|
|
|
74
173
|
throw new Error("No releasable commits found. Nothing to open a release PR for.");
|
|
75
174
|
}
|
|
76
175
|
ensureCleanWorktree(cwd, options.logger);
|
|
77
|
-
const updatedVersionFiles =
|
|
176
|
+
const updatedVersionFiles = [];
|
|
177
|
+
const rustManifestVersionTargets = {};
|
|
178
|
+
if (plan.packages && plan.packages.length > 0) {
|
|
179
|
+
for (const packagePlan of plan.packages) {
|
|
180
|
+
if (!packagePlan.nextVersion) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const packageConfig = loaded.config.packages?.[packagePlan.path] ?? {};
|
|
184
|
+
const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(loaded.config, packagePlan.path, packageConfig);
|
|
185
|
+
const packageUpdated = packageContext.strategy.writeVersion(cwd, packageContext.config, packagePlan.nextVersion);
|
|
186
|
+
updatedVersionFiles.push(...packageUpdated);
|
|
187
|
+
if (packageContext.strategy.name === rust_js_1.rustVersionStrategy.name) {
|
|
188
|
+
rustManifestVersionTargets[packageContext.versionFile] =
|
|
189
|
+
packagePlan.nextVersion;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
updatedVersionFiles.push(...(0, rust_js_1.applyRustWorkspaceDependencyUpdates)(cwd, rustManifestVersionTargets));
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
updatedVersionFiles.push(...strategy.writeVersion(cwd, loaded.config, plan.nextVersion));
|
|
196
|
+
}
|
|
78
197
|
const updatedArtifactFiles = (0, artifact_rules_js_1.applyConfiguredArtifactRules)(cwd, loaded.config, plan);
|
|
79
198
|
const section = (0, changelog_js_1.renderSimpleChangelog)(plan);
|
|
80
199
|
(0, changelog_js_1.prependChangelog)(cwd, plan.changelogFile, section);
|
|
200
|
+
const releaseTargets = buildReleaseTargets(cwd, plan, loaded.config);
|
|
81
201
|
const branch = plan.releaseBranchPrefix;
|
|
82
|
-
const title =
|
|
202
|
+
const title = formatReleaseCommitTitle(releaseTargets);
|
|
83
203
|
(0, node_child_process_1.execFileSync)("git", ["checkout", "-B", branch], {
|
|
84
204
|
cwd,
|
|
85
205
|
stdio: ["ignore", "pipe", "ignore"],
|
|
@@ -99,23 +219,6 @@ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
|
|
|
99
219
|
cwd,
|
|
100
220
|
stdio: ["ignore", "pipe", "ignore"],
|
|
101
221
|
});
|
|
102
|
-
const releaseTargets = plan.packages
|
|
103
|
-
? plan.packages
|
|
104
|
-
.filter((pkg) => pkg.nextVersion)
|
|
105
|
-
.map((pkg) => ({
|
|
106
|
-
path: pkg.path,
|
|
107
|
-
version: pkg.nextVersion ?? "",
|
|
108
|
-
tag: pkg.path === "."
|
|
109
|
-
? `v${pkg.nextVersion ?? ""}`
|
|
110
|
-
: `${pkg.path.replaceAll("/", "-")}-v${pkg.nextVersion ?? ""}`,
|
|
111
|
-
}))
|
|
112
|
-
: [
|
|
113
|
-
{
|
|
114
|
-
path: ".",
|
|
115
|
-
version: plan.nextVersion,
|
|
116
|
-
tag: `v${plan.nextVersion}`,
|
|
117
|
-
},
|
|
118
|
-
];
|
|
119
222
|
(0, state_js_1.writeBaselineSha)(cwd, undefined, releaseTargets);
|
|
120
223
|
(0, node_child_process_1.execFileSync)("git", ["add", (0, state_js_1.getBaselineStatePath)(cwd)], {
|
|
121
224
|
cwd,
|
|
@@ -135,10 +238,13 @@ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
|
|
|
135
238
|
};
|
|
136
239
|
}
|
|
137
240
|
function renderSimpleReviewRequestBody(version, previousVersion, commits, plan = null, cwd = process.cwd()) {
|
|
241
|
+
const rootPackageLabel = node_path_1.default.basename(cwd);
|
|
242
|
+
const formatPackageLabel = (packagePath) => packagePath === "." ? rootPackageLabel : packagePath;
|
|
138
243
|
if (plan?.packages && plan.packages.length > 1) {
|
|
139
244
|
const sections = plan.packages
|
|
140
245
|
.filter((pkg) => pkg.nextVersion)
|
|
141
246
|
.map((pkg) => {
|
|
247
|
+
const packageLabel = formatPackageLabel(pkg.path);
|
|
142
248
|
const notes = (0, changelog_js_1.renderSimpleReleaseNotes)({
|
|
143
249
|
currentVersion: pkg.currentVersion,
|
|
144
250
|
nextVersion: pkg.nextVersion ?? "",
|
|
@@ -148,12 +254,12 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
|
|
|
148
254
|
const linkedHeader = notes.match(/^##\s+\[([^\]]+)\]\(([^)]+)\)\s+\(([^)]+)\)/u);
|
|
149
255
|
if (linkedHeader) {
|
|
150
256
|
const [, , compareUrl, date] = linkedHeader;
|
|
151
|
-
return notes.replace(/^##\s+\[[^\]]+\]\([^)]+\)\s+\([^)]+\)/u, `## [${
|
|
257
|
+
return notes.replace(/^##\s+\[[^\]]+\]\([^)]+\)\s+\([^)]+\)/u, `## [${packageLabel}: ${pkg.nextVersion ?? ""}](${compareUrl}) (${date})`);
|
|
152
258
|
}
|
|
153
259
|
const plainHeader = notes.match(/^##\s+([^\s]+)\s+\(([^)]+)\)/u);
|
|
154
260
|
if (plainHeader) {
|
|
155
261
|
const [, , date] = plainHeader;
|
|
156
|
-
return notes.replace(/^##\s+[^\s]+\s+\([^)]+\)/u, `## ${
|
|
262
|
+
return notes.replace(/^##\s+[^\s]+\s+\([^)]+\)/u, `## ${packageLabel}: ${pkg.nextVersion ?? ""} (${date})`);
|
|
157
263
|
}
|
|
158
264
|
return notes;
|
|
159
265
|
})
|
|
@@ -201,5 +307,5 @@ function pushReleaseBranch(cwd, branch) {
|
|
|
201
307
|
});
|
|
202
308
|
}
|
|
203
309
|
function isReleaseCommitMessage(subject) {
|
|
204
|
-
return /^chore\(release\):\
|
|
310
|
+
return /^chore\(release\):\s+(?:v\d+\.\d+\.\d+|\S+-v\d+\.\d+\.\d+)(?:,\s+(?:v\d+\.\d+\.\d+|\S+-v\d+\.\d+\.\d+))*$/u.test(subject);
|
|
205
311
|
}
|
|
@@ -11,26 +11,29 @@ const node_child_process_1 = require("node:child_process");
|
|
|
11
11
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
12
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
13
|
const load_config_js_1 = require("../../config/load-config.js");
|
|
14
|
+
const MANIFEST_VERSION_KEY = "manifest-version";
|
|
15
|
+
const BASELINE_SHA_KEY = "baseline-sha";
|
|
16
|
+
const RELEASE_TARGETS_KEY = "release-targets";
|
|
14
17
|
function parseStateFile(raw, filePath) {
|
|
15
18
|
const parsed = JSON.parse(raw);
|
|
16
19
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
17
20
|
throw new Error(`Invalid release manifest at ${filePath}: expected an object.`);
|
|
18
21
|
}
|
|
19
22
|
const manifest = parsed;
|
|
20
|
-
if (manifest
|
|
21
|
-
manifest
|
|
22
|
-
throw new Error(`Unsupported
|
|
23
|
+
if (manifest[MANIFEST_VERSION_KEY] !== undefined &&
|
|
24
|
+
manifest[MANIFEST_VERSION_KEY] !== 1) {
|
|
25
|
+
throw new Error(`Unsupported ${MANIFEST_VERSION_KEY} in ${filePath}: ${String(manifest[MANIFEST_VERSION_KEY])}.`);
|
|
23
26
|
}
|
|
24
|
-
if (manifest
|
|
25
|
-
typeof manifest
|
|
26
|
-
throw new Error(`Invalid release manifest at ${filePath}:
|
|
27
|
+
if (manifest[BASELINE_SHA_KEY] !== undefined &&
|
|
28
|
+
typeof manifest[BASELINE_SHA_KEY] !== "string") {
|
|
29
|
+
throw new Error(`Invalid release manifest at ${filePath}: ${BASELINE_SHA_KEY} must be a string.`);
|
|
27
30
|
}
|
|
28
|
-
if (manifest
|
|
29
|
-
!Array.isArray(manifest
|
|
30
|
-
throw new Error(`Invalid release manifest at ${filePath}:
|
|
31
|
+
if (manifest[RELEASE_TARGETS_KEY] !== undefined &&
|
|
32
|
+
!Array.isArray(manifest[RELEASE_TARGETS_KEY])) {
|
|
33
|
+
throw new Error(`Invalid release manifest at ${filePath}: ${RELEASE_TARGETS_KEY} must be an array.`);
|
|
31
34
|
}
|
|
32
|
-
if (Array.isArray(manifest
|
|
33
|
-
for (const target of manifest
|
|
35
|
+
if (Array.isArray(manifest[RELEASE_TARGETS_KEY])) {
|
|
36
|
+
for (const target of manifest[RELEASE_TARGETS_KEY]) {
|
|
34
37
|
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
35
38
|
throw new Error(`Invalid release manifest at ${filePath}: each release target must be an object.`);
|
|
36
39
|
}
|
|
@@ -38,7 +41,7 @@ function parseStateFile(raw, filePath) {
|
|
|
38
41
|
if (typeof record.path !== "string" ||
|
|
39
42
|
typeof record.version !== "string" ||
|
|
40
43
|
typeof record.tag !== "string") {
|
|
41
|
-
throw new Error(`Invalid release manifest at ${filePath}:
|
|
44
|
+
throw new Error(`Invalid release manifest at ${filePath}: ${RELEASE_TARGETS_KEY} must contain string path, version, and tag.`);
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -66,7 +69,7 @@ function readBaselineSha(cwd = process.cwd()) {
|
|
|
66
69
|
return null;
|
|
67
70
|
}
|
|
68
71
|
const parsed = parseStateFile(node_fs_1.default.readFileSync(filePath, "utf8"), filePath);
|
|
69
|
-
return parsed
|
|
72
|
+
return parsed[BASELINE_SHA_KEY] ?? null;
|
|
70
73
|
}
|
|
71
74
|
function readReleaseTargets(cwd = process.cwd()) {
|
|
72
75
|
const filePath = getBaselineStatePath(cwd);
|
|
@@ -74,10 +77,10 @@ function readReleaseTargets(cwd = process.cwd()) {
|
|
|
74
77
|
return [];
|
|
75
78
|
}
|
|
76
79
|
const parsed = parseStateFile(node_fs_1.default.readFileSync(filePath, "utf8"), filePath);
|
|
77
|
-
return parsed
|
|
80
|
+
return parsed[RELEASE_TARGETS_KEY] ?? [];
|
|
78
81
|
}
|
|
79
82
|
function writeBaselineSha(cwd = process.cwd(), sha, releaseTargets = []) {
|
|
80
|
-
const
|
|
83
|
+
const baselineShaValue = sha ??
|
|
81
84
|
(0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
82
85
|
cwd,
|
|
83
86
|
encoding: "utf8",
|
|
@@ -85,9 +88,9 @@ function writeBaselineSha(cwd = process.cwd(), sha, releaseTargets = []) {
|
|
|
85
88
|
}).trim();
|
|
86
89
|
const filePath = getBaselineStatePath(cwd);
|
|
87
90
|
const next = {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
releaseTargets,
|
|
91
|
+
[MANIFEST_VERSION_KEY]: 1,
|
|
92
|
+
[BASELINE_SHA_KEY]: baselineShaValue,
|
|
93
|
+
[RELEASE_TARGETS_KEY]: releaseTargets,
|
|
91
94
|
};
|
|
92
95
|
node_fs_1.default.writeFileSync(filePath, `${JSON.stringify(next, null, 2)}\n`, "utf8");
|
|
93
96
|
}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
export declare const configSchema: z.ZodObject<{
|
|
3
|
+
$schema: z.ZodOptional<z.ZodString>;
|
|
3
4
|
version: z.ZodLiteral<1>;
|
|
4
5
|
"review-mode": z.ZodOptional<z.ZodEnum<{
|
|
5
6
|
direct: "direct";
|
|
@@ -33,7 +34,7 @@ export declare const configSchema: z.ZodObject<{
|
|
|
33
34
|
jsonpath: z.ZodOptional<z.ZodString>;
|
|
34
35
|
pattern: z.ZodOptional<z.ZodString>;
|
|
35
36
|
}, z.core.$strip>>>;
|
|
36
|
-
}, z.core.$
|
|
37
|
+
}, z.core.$strict>>>;
|
|
37
38
|
plugins: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
38
|
-
}, z.core.$
|
|
39
|
+
}, z.core.$strict>;
|
|
39
40
|
export type ConfigSchema = z.infer<typeof configSchema>;
|
package/dist/config/schema.js
CHANGED
|
@@ -40,13 +40,17 @@ const artifactRuleSchema = zod_1.z
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
|
-
const packageSchema = zod_1.z
|
|
43
|
+
const packageSchema = zod_1.z
|
|
44
|
+
.object({
|
|
44
45
|
"release-type": zod_1.z.string().optional(),
|
|
45
46
|
"package-name": zod_1.z.string().optional(),
|
|
46
47
|
"exclude-paths": zod_1.z.array(zod_1.z.string()).optional(),
|
|
47
48
|
"extra-files": zod_1.z.array(artifactRuleSchema).optional(),
|
|
48
|
-
})
|
|
49
|
-
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
51
|
+
exports.configSchema = zod_1.z
|
|
52
|
+
.object({
|
|
53
|
+
$schema: zod_1.z.string().optional(),
|
|
50
54
|
version: zod_1.z.literal(1),
|
|
51
55
|
"review-mode": zod_1.z.enum(["direct", "review"]).optional(),
|
|
52
56
|
"version-file": zod_1.z.string().optional(),
|
|
@@ -61,4 +65,5 @@ exports.configSchema = zod_1.z.object({
|
|
|
61
65
|
"release-type": zod_1.z.string().optional(),
|
|
62
66
|
packages: zod_1.z.record(zod_1.z.string().min(1), packageSchema).optional(),
|
|
63
67
|
plugins: zod_1.z.array(zod_1.z.string().min(1)).optional(),
|
|
64
|
-
})
|
|
68
|
+
})
|
|
69
|
+
.strict();
|
|
@@ -9,7 +9,9 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const state_js_1 = require("../../app/release/state.js");
|
|
10
10
|
const load_config_js_1 = require("../../config/load-config.js");
|
|
11
11
|
const commits_js_1 = require("../../infra/git/commits.js");
|
|
12
|
+
const package_context_js_1 = require("../strategy/package-context.js");
|
|
12
13
|
const resolve_js_1 = require("../strategy/resolve.js");
|
|
14
|
+
const rust_js_1 = require("../strategy/rust.js");
|
|
13
15
|
const semver_js_1 = require("./semver.js");
|
|
14
16
|
function getMode(configMode) {
|
|
15
17
|
return configMode ?? "independent";
|
|
@@ -21,11 +23,6 @@ function createSimplePlan(cwd = process.cwd()) {
|
|
|
21
23
|
const changelogFile = loaded.config["changelog-file"] ?? "CHANGELOG.md";
|
|
22
24
|
const releaseBranchPrefix = loaded.config["release-branch"] ?? "versionary/release";
|
|
23
25
|
const baselineSha = (0, state_js_1.readBaselineSha)(cwd) ?? loaded.config["bootstrap-sha"] ?? null;
|
|
24
|
-
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
25
|
-
if (!node_fs_1.default.existsSync(versionPath)) {
|
|
26
|
-
throw new Error(`Versionary requires ${versionFile} to exist.`);
|
|
27
|
-
}
|
|
28
|
-
const currentVersion = strategy.readVersion(cwd, loaded.config);
|
|
29
26
|
const allowStableMajor = loaded.config["allow-stable-major"] ?? false;
|
|
30
27
|
const configuredPackages = Object.entries(loaded.config.packages ?? {}).map(([pkgPath, cfg]) => ({
|
|
31
28
|
path: pkgPath,
|
|
@@ -34,6 +31,11 @@ function createSimplePlan(cwd = process.cwd()) {
|
|
|
34
31
|
const monorepoMode = getMode(loaded.config["monorepo-mode"]);
|
|
35
32
|
const hasPackages = configuredPackages.length > 0;
|
|
36
33
|
if (!hasPackages) {
|
|
34
|
+
const versionPath = node_path_1.default.join(cwd, versionFile);
|
|
35
|
+
if (!node_fs_1.default.existsSync(versionPath)) {
|
|
36
|
+
throw new Error(`Versionary requires ${versionFile} to exist.`);
|
|
37
|
+
}
|
|
38
|
+
const currentVersion = strategy.readVersion(cwd, loaded.config);
|
|
37
39
|
const parsedCommits = (0, commits_js_1.getParsedCommitsSinceLastTag)(cwd, baselineSha);
|
|
38
40
|
const effectiveCommits = (0, commits_js_1.applyRevertSuppression)(parsedCommits);
|
|
39
41
|
const commits = effectiveCommits;
|
|
@@ -55,29 +57,70 @@ function createSimplePlan(cwd = process.cwd()) {
|
|
|
55
57
|
}
|
|
56
58
|
const packagePlans = configuredPackages
|
|
57
59
|
.map((pkg) => {
|
|
60
|
+
const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(loaded.config, pkg.path, pkg);
|
|
61
|
+
const packageCurrentVersion = packageContext.strategy.readVersion(cwd, packageContext.config);
|
|
58
62
|
const parsedCommits = (0, commits_js_1.getParsedCommitsForPath)(cwd, baselineSha, pkg.path, pkg["exclude-paths"] ?? []);
|
|
59
63
|
const effectiveCommits = (0, commits_js_1.applyRevertSuppression)(parsedCommits);
|
|
60
64
|
const commits = effectiveCommits;
|
|
61
65
|
const releaseType = (0, commits_js_1.analyzeParsedCommits)(parsedCommits);
|
|
62
66
|
const nextVersion = releaseType
|
|
63
|
-
? (0, semver_js_1.bumpVersion)(
|
|
67
|
+
? (0, semver_js_1.bumpVersion)(packageCurrentVersion, releaseType, { allowStableMajor })
|
|
64
68
|
: null;
|
|
65
69
|
return {
|
|
66
70
|
path: pkg.path,
|
|
67
71
|
releaseType,
|
|
68
|
-
currentVersion,
|
|
72
|
+
currentVersion: packageCurrentVersion,
|
|
69
73
|
nextVersion,
|
|
70
74
|
commits,
|
|
71
75
|
parsedCommits,
|
|
72
76
|
};
|
|
73
77
|
})
|
|
74
78
|
.sort((a, b) => a.path.localeCompare(b.path));
|
|
79
|
+
const rustManifestVersionTargets = {};
|
|
80
|
+
const rustPackageManifestByPath = {};
|
|
81
|
+
const packageCurrentVersionByPath = {};
|
|
82
|
+
for (const packagePlan of packagePlans) {
|
|
83
|
+
const packageConfig = loaded.config.packages?.[packagePlan.path] ?? {};
|
|
84
|
+
const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(loaded.config, packagePlan.path, packageConfig);
|
|
85
|
+
if (packageContext.strategy.name === rust_js_1.rustVersionStrategy.name) {
|
|
86
|
+
rustPackageManifestByPath[packagePlan.path] = (0, rust_js_1.toCargoManifestPath)(packagePlan.path);
|
|
87
|
+
}
|
|
88
|
+
packageCurrentVersionByPath[packagePlan.path] = packagePlan.currentVersion;
|
|
89
|
+
if (packageContext.strategy.name === rust_js_1.rustVersionStrategy.name) {
|
|
90
|
+
const next = packagePlan.nextVersion;
|
|
91
|
+
if (next) {
|
|
92
|
+
rustManifestVersionTargets[packageContext.versionFile] = next;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const impactedRustManifests = (0, rust_js_1.detectRustDependencyImpact)(cwd, rustManifestVersionTargets, Object.values(rustPackageManifestByPath));
|
|
97
|
+
const impactedPaths = new Set();
|
|
98
|
+
for (const [pkgPath, manifest] of Object.entries(rustPackageManifestByPath)) {
|
|
99
|
+
if (impactedRustManifests.includes(manifest)) {
|
|
100
|
+
impactedPaths.add(pkgPath);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const adjustedPackages = packagePlans.map((pkgPlan) => {
|
|
104
|
+
if (pkgPlan.nextVersion || !impactedPaths.has(pkgPlan.path)) {
|
|
105
|
+
return pkgPlan;
|
|
106
|
+
}
|
|
107
|
+
const current = packageCurrentVersionByPath[pkgPlan.path] ?? pkgPlan.currentVersion;
|
|
108
|
+
return {
|
|
109
|
+
...pkgPlan,
|
|
110
|
+
releaseType: "patch",
|
|
111
|
+
nextVersion: (0, semver_js_1.bumpVersion)(current, "patch", { allowStableMajor }),
|
|
112
|
+
};
|
|
113
|
+
});
|
|
75
114
|
if (monorepoMode === "fixed") {
|
|
76
|
-
const fixedType = (0, commits_js_1.analyzeParsedCommits)(
|
|
115
|
+
const fixedType = (0, commits_js_1.analyzeParsedCommits)(adjustedPackages.flatMap((pkgPlan) => pkgPlan.parsedCommits));
|
|
116
|
+
const fixedBaseVersion = adjustedPackages.find((pkgPlan) => pkgPlan.path === ".")
|
|
117
|
+
?.currentVersion ??
|
|
118
|
+
adjustedPackages[0]?.currentVersion ??
|
|
119
|
+
"0.0.0";
|
|
77
120
|
const fixedNextVersion = fixedType
|
|
78
|
-
? (0, semver_js_1.bumpVersion)(
|
|
121
|
+
? (0, semver_js_1.bumpVersion)(fixedBaseVersion, fixedType, { allowStableMajor })
|
|
79
122
|
: null;
|
|
80
|
-
const adjusted =
|
|
123
|
+
const adjusted = adjustedPackages.map((pkgPlan) => ({
|
|
81
124
|
...pkgPlan,
|
|
82
125
|
releaseType: fixedType,
|
|
83
126
|
nextVersion: fixedNextVersion,
|
|
@@ -85,7 +128,7 @@ function createSimplePlan(cwd = process.cwd()) {
|
|
|
85
128
|
return {
|
|
86
129
|
mode: "simple",
|
|
87
130
|
releaseType: fixedType,
|
|
88
|
-
currentVersion,
|
|
131
|
+
currentVersion: fixedBaseVersion,
|
|
89
132
|
nextVersion: fixedNextVersion,
|
|
90
133
|
versionFile,
|
|
91
134
|
changelogFile,
|
|
@@ -95,20 +138,23 @@ function createSimplePlan(cwd = process.cwd()) {
|
|
|
95
138
|
packages: adjusted,
|
|
96
139
|
};
|
|
97
140
|
}
|
|
98
|
-
const overallType = (0, commits_js_1.analyzeParsedCommits)(
|
|
141
|
+
const overallType = (0, commits_js_1.analyzeParsedCommits)(adjustedPackages.flatMap((pkgPlan) => pkgPlan.parsedCommits));
|
|
142
|
+
const overallBaseVersion = adjustedPackages.find((pkgPlan) => pkgPlan.path === ".")?.currentVersion ??
|
|
143
|
+
adjustedPackages[0]?.currentVersion ??
|
|
144
|
+
"0.0.0";
|
|
99
145
|
const overallNextVersion = overallType
|
|
100
|
-
? (0, semver_js_1.bumpVersion)(
|
|
146
|
+
? (0, semver_js_1.bumpVersion)(overallBaseVersion, overallType, { allowStableMajor })
|
|
101
147
|
: null;
|
|
102
148
|
return {
|
|
103
149
|
mode: "simple",
|
|
104
150
|
releaseType: overallType,
|
|
105
|
-
currentVersion,
|
|
151
|
+
currentVersion: overallBaseVersion,
|
|
106
152
|
nextVersion: overallNextVersion,
|
|
107
153
|
versionFile,
|
|
108
154
|
changelogFile,
|
|
109
155
|
releaseBranchPrefix,
|
|
110
156
|
baselineSha,
|
|
111
|
-
commits:
|
|
112
|
-
packages:
|
|
157
|
+
commits: adjustedPackages.flatMap((pkgPlan) => pkgPlan.commits),
|
|
158
|
+
packages: adjustedPackages,
|
|
113
159
|
};
|
|
114
160
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { VersionaryConfig, VersionaryPackage } from "../../types/config.js";
|
|
2
|
+
import type { VersionStrategy } from "./types.js";
|
|
3
|
+
export declare function resolvePackageStrategyContext(rootConfig: VersionaryConfig, packagePath: string, packageConfig: VersionaryPackage): {
|
|
4
|
+
strategy: VersionStrategy;
|
|
5
|
+
config: VersionaryConfig;
|
|
6
|
+
versionFile: string;
|
|
7
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolvePackageStrategyContext = resolvePackageStrategyContext;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const resolve_js_1 = require("./resolve.js");
|
|
9
|
+
function withVersionFile(config, versionFile) {
|
|
10
|
+
return {
|
|
11
|
+
...config,
|
|
12
|
+
"version-file": versionFile,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function resolvePackageStrategyContext(rootConfig, packagePath, packageConfig) {
|
|
16
|
+
const packageReleaseType = packageConfig["release-type"];
|
|
17
|
+
const baseConfig = packageReleaseType
|
|
18
|
+
? {
|
|
19
|
+
...rootConfig,
|
|
20
|
+
"release-type": packageReleaseType,
|
|
21
|
+
}
|
|
22
|
+
: { ...rootConfig };
|
|
23
|
+
const baseStrategy = (0, resolve_js_1.resolveVersionStrategy)(baseConfig);
|
|
24
|
+
if (!packageReleaseType) {
|
|
25
|
+
const versionFile = baseConfig["version-file"] ?? baseStrategy.getVersionFile(baseConfig);
|
|
26
|
+
const config = withVersionFile(baseConfig, versionFile);
|
|
27
|
+
return {
|
|
28
|
+
strategy: baseStrategy,
|
|
29
|
+
config,
|
|
30
|
+
versionFile,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
const packageVersionFile = packagePath === "."
|
|
34
|
+
? (baseConfig["version-file"] ?? baseStrategy.getVersionFile(baseConfig))
|
|
35
|
+
: node_path_1.default.posix.join(packagePath, baseStrategy.getVersionFile({
|
|
36
|
+
...baseConfig,
|
|
37
|
+
"version-file": undefined,
|
|
38
|
+
}));
|
|
39
|
+
const config = withVersionFile({
|
|
40
|
+
...baseConfig,
|
|
41
|
+
packages: undefined,
|
|
42
|
+
}, packageVersionFile);
|
|
43
|
+
const strategy = (0, resolve_js_1.resolveVersionStrategy)(config);
|
|
44
|
+
return {
|
|
45
|
+
strategy,
|
|
46
|
+
config,
|
|
47
|
+
versionFile: packageVersionFile,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { VersionStrategy } from "./types.js";
|
|
2
|
+
export declare function applyRustWorkspaceDependencyUpdates(cwd: string, manifestToVersion: Record<string, string>): string[];
|
|
3
|
+
export declare function detectRustDependencyImpact(cwd: string, manifestToVersion: Record<string, string>, candidateManifests: string[]): string[];
|
|
4
|
+
export declare function toCargoManifestPath(packagePath: string): string;
|
|
2
5
|
export declare const rustVersionStrategy: VersionStrategy;
|
|
@@ -4,6 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.rustVersionStrategy = void 0;
|
|
7
|
+
exports.applyRustWorkspaceDependencyUpdates = applyRustWorkspaceDependencyUpdates;
|
|
8
|
+
exports.detectRustDependencyImpact = detectRustDependencyImpact;
|
|
9
|
+
exports.toCargoManifestPath = toCargoManifestPath;
|
|
7
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
12
|
const toml_1 = __importDefault(require("@iarna/toml"));
|
|
@@ -74,6 +77,25 @@ function findCargoManifestDirectories(rootDir) {
|
|
|
74
77
|
}
|
|
75
78
|
return [...results].sort((a, b) => a.localeCompare(b));
|
|
76
79
|
}
|
|
80
|
+
function collectAllCrateManifests(cwd) {
|
|
81
|
+
const manifests = [];
|
|
82
|
+
const cargoDirs = findCargoManifestDirectories(cwd);
|
|
83
|
+
for (const dir of cargoDirs) {
|
|
84
|
+
const manifest = dir === "."
|
|
85
|
+
? "Cargo.toml"
|
|
86
|
+
: normalizeSlashPath(node_path_1.default.posix.join(dir, "Cargo.toml"));
|
|
87
|
+
const manifestPath = node_path_1.default.join(cwd, manifest);
|
|
88
|
+
if (!node_fs_1.default.existsSync(manifestPath)) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const cargoTomlRaw = node_fs_1.default.readFileSync(manifestPath, "utf8");
|
|
92
|
+
if (!isCrateManifest(manifest, cargoTomlRaw)) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
manifests.push(manifest);
|
|
96
|
+
}
|
|
97
|
+
return manifests.sort((a, b) => a.localeCompare(b));
|
|
98
|
+
}
|
|
77
99
|
function parseCargoManifest(versionFile, cargoTomlRaw) {
|
|
78
100
|
let parsed;
|
|
79
101
|
try {
|
|
@@ -156,7 +178,7 @@ function resolveWorkspaceMemberManifests(rootDir, workspaceTable) {
|
|
|
156
178
|
});
|
|
157
179
|
return filtered.sort((a, b) => a.localeCompare(b));
|
|
158
180
|
}
|
|
159
|
-
function collectRustTargetManifests(cwd, versionFile) {
|
|
181
|
+
function collectRustTargetManifests(cwd, versionFile, includeWorkspaceMembers) {
|
|
160
182
|
if (node_path_1.default.basename(versionFile) !== "Cargo.toml") {
|
|
161
183
|
throw new Error(`Rust strategy requires "version-file" to point to a Cargo.toml manifest (received "${versionFile}").`);
|
|
162
184
|
}
|
|
@@ -168,7 +190,9 @@ function collectRustTargetManifests(cwd, versionFile) {
|
|
|
168
190
|
const parsedRoot = parseCargoManifest(versionFile, rootRaw);
|
|
169
191
|
const rootIsCrate = parsedRoot.packageTable !== null;
|
|
170
192
|
const rootDir = node_path_1.default.dirname(rootManifestPath);
|
|
171
|
-
const workspaceMembers =
|
|
193
|
+
const workspaceMembers = includeWorkspaceMembers
|
|
194
|
+
? resolveWorkspaceMemberManifests(rootDir, parsedRoot.workspaceTable)
|
|
195
|
+
: [];
|
|
172
196
|
if (rootIsCrate) {
|
|
173
197
|
const relRoot = normalizeSlashPath(node_path_1.default.relative(cwd, rootManifestPath));
|
|
174
198
|
return [...new Set([relRoot, ...workspaceMembers])].sort((a, b) => a.localeCompare(b));
|
|
@@ -263,30 +287,54 @@ function parseDependencyName(line) {
|
|
|
263
287
|
const match = line.match(/^\s*(?:"([^"]+)"|'([^']+)'|([A-Za-z0-9_-]+))\s*=/u);
|
|
264
288
|
return match?.[1] ?? match?.[2] ?? match?.[3] ?? null;
|
|
265
289
|
}
|
|
266
|
-
function
|
|
267
|
-
|
|
290
|
+
function rewriteCargoVersionRequirement(currentRequirement, nextVersion) {
|
|
291
|
+
const simpleRequirementMatch = currentRequirement.match(/^(\s*)(\^|~|>=|<=|>|<|=)?(\s*)([0-9A-Za-z.+-]+)(\s*)$/u);
|
|
292
|
+
if (!simpleRequirementMatch) {
|
|
293
|
+
return nextVersion;
|
|
294
|
+
}
|
|
295
|
+
const [, leadingWhitespace = "", operator = "", operatorWhitespace = "", , trailingWhitespace = "",] = simpleRequirementMatch;
|
|
296
|
+
return `${leadingWhitespace}${operator}${operatorWhitespace}${nextVersion}${trailingWhitespace}`;
|
|
297
|
+
}
|
|
298
|
+
function writeInternalDependencyVersionInLine(line, dependencyName, versionByDependency) {
|
|
299
|
+
const nextVersion = versionByDependency.get(dependencyName);
|
|
300
|
+
if (!nextVersion) {
|
|
268
301
|
return line;
|
|
269
302
|
}
|
|
270
303
|
const stringVersionMatch = line.match(/^(\s*(?:"[^"]+"|'[^']+'|[A-Za-z0-9_-]+)\s*=\s*)(["'])([^"']*)(\2)(\s*(?:#.*)?)?$/u);
|
|
271
304
|
if (stringVersionMatch) {
|
|
272
|
-
const [, prefix = "", quote = '"', , , suffix = ""] = stringVersionMatch;
|
|
273
|
-
|
|
305
|
+
const [, prefix = "", quote = '"', current = "", , suffix = ""] = stringVersionMatch;
|
|
306
|
+
const rewrittenRequirement = rewriteCargoVersionRequirement(current, nextVersion);
|
|
307
|
+
return `${prefix}${quote}${rewrittenRequirement}${quote}${suffix}`;
|
|
274
308
|
}
|
|
275
309
|
const inlineTableMatch = line.match(/^(\s*(?:"[^"]+"|'[^']+'|[A-Za-z0-9_-]+)\s*=\s*\{)(.*)(\}\s*(?:#.*)?)$/u);
|
|
276
|
-
if (
|
|
277
|
-
|
|
310
|
+
if (inlineTableMatch) {
|
|
311
|
+
const [, prefix = "", tableBody = "", suffix = ""] = inlineTableMatch;
|
|
312
|
+
const updatedTableBody = tableBody.replace(/(\bversion\s*=\s*)(["'])([^"']*)(\2)/u, (_match, versionPrefix, quote, currentVersion) => `${versionPrefix}${quote}${rewriteCargoVersionRequirement(String(currentVersion), nextVersion)}${quote}`);
|
|
313
|
+
if (updatedTableBody === tableBody) {
|
|
314
|
+
return line;
|
|
315
|
+
}
|
|
316
|
+
return `${prefix}${updatedTableBody}${suffix}`;
|
|
278
317
|
}
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
if (updatedTableBody === tableBody) {
|
|
318
|
+
const updatedTableLine = line.replace(/(\bversion\s*=\s*)(["'])([^"']*)(\2)/u, (_match, versionPrefix, quote, currentVersion) => `${versionPrefix}${quote}${rewriteCargoVersionRequirement(String(currentVersion), nextVersion)}${quote}`);
|
|
319
|
+
if (updatedTableLine === line) {
|
|
282
320
|
return line;
|
|
283
321
|
}
|
|
284
|
-
return
|
|
322
|
+
return updatedTableLine;
|
|
285
323
|
}
|
|
286
324
|
function writeInternalDependencyVersions(cargoTomlRaw, internalCrates, version) {
|
|
287
325
|
if (internalCrates.size === 0) {
|
|
288
326
|
return cargoTomlRaw;
|
|
289
327
|
}
|
|
328
|
+
const versionByDependency = new Map();
|
|
329
|
+
for (const crateName of internalCrates) {
|
|
330
|
+
versionByDependency.set(crateName, version);
|
|
331
|
+
}
|
|
332
|
+
return writeMappedDependencyVersions(cargoTomlRaw, versionByDependency);
|
|
333
|
+
}
|
|
334
|
+
function writeMappedDependencyVersions(cargoTomlRaw, versionByDependency) {
|
|
335
|
+
if (versionByDependency.size === 0) {
|
|
336
|
+
return cargoTomlRaw;
|
|
337
|
+
}
|
|
290
338
|
const lineEnding = cargoTomlRaw.includes("\r\n") ? "\r\n" : "\n";
|
|
291
339
|
const hasFinalLineEnding = cargoTomlRaw.endsWith("\n") || cargoTomlRaw.endsWith("\r\n");
|
|
292
340
|
const lines = cargoTomlRaw.split(/\r?\n/u);
|
|
@@ -305,7 +353,7 @@ function writeInternalDependencyVersions(cargoTomlRaw, internalCrates, version)
|
|
|
305
353
|
if (!dependencyName) {
|
|
306
354
|
continue;
|
|
307
355
|
}
|
|
308
|
-
lines[index] = writeInternalDependencyVersionInLine(line, dependencyName,
|
|
356
|
+
lines[index] = writeInternalDependencyVersionInLine(line, dependencyName, versionByDependency);
|
|
309
357
|
}
|
|
310
358
|
let updated = lines.join(lineEnding);
|
|
311
359
|
if (hasFinalLineEnding && !updated.endsWith(lineEnding)) {
|
|
@@ -316,6 +364,79 @@ function writeInternalDependencyVersions(cargoTomlRaw, internalCrates, version)
|
|
|
316
364
|
}
|
|
317
365
|
return updated;
|
|
318
366
|
}
|
|
367
|
+
function readPackageNameForManifest(cwd, manifest) {
|
|
368
|
+
const manifestPath = node_path_1.default.join(cwd, manifest);
|
|
369
|
+
const cargoTomlRaw = node_fs_1.default.readFileSync(manifestPath, "utf8");
|
|
370
|
+
if (!isCrateManifest(manifest, cargoTomlRaw)) {
|
|
371
|
+
throw new Error(`Configured rust target "${manifest}" is not a Rust crate manifest to update.`);
|
|
372
|
+
}
|
|
373
|
+
return readCargoPackageName(cargoTomlRaw, manifest);
|
|
374
|
+
}
|
|
375
|
+
function applyRustWorkspaceDependencyUpdates(cwd, manifestToVersion) {
|
|
376
|
+
const versionByDependency = new Map();
|
|
377
|
+
for (const [manifest, version] of Object.entries(manifestToVersion)) {
|
|
378
|
+
if (!version) {
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
const crateName = readPackageNameForManifest(cwd, manifest);
|
|
382
|
+
versionByDependency.set(crateName, version);
|
|
383
|
+
}
|
|
384
|
+
if (versionByDependency.size === 0) {
|
|
385
|
+
return [];
|
|
386
|
+
}
|
|
387
|
+
const updatedFiles = [];
|
|
388
|
+
const manifests = collectAllCrateManifests(cwd);
|
|
389
|
+
for (const manifest of manifests) {
|
|
390
|
+
const manifestPath = node_path_1.default.join(cwd, manifest);
|
|
391
|
+
if (!node_fs_1.default.existsSync(manifestPath)) {
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
const cargoTomlRaw = node_fs_1.default.readFileSync(manifestPath, "utf8");
|
|
395
|
+
if (!isCrateManifest(manifest, cargoTomlRaw)) {
|
|
396
|
+
continue;
|
|
397
|
+
}
|
|
398
|
+
const next = writeMappedDependencyVersions(cargoTomlRaw, versionByDependency);
|
|
399
|
+
if (next !== cargoTomlRaw) {
|
|
400
|
+
node_fs_1.default.writeFileSync(manifestPath, next, "utf8");
|
|
401
|
+
updatedFiles.push(manifest);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return updatedFiles;
|
|
405
|
+
}
|
|
406
|
+
function detectRustDependencyImpact(cwd, manifestToVersion, candidateManifests) {
|
|
407
|
+
const versionByDependency = new Map();
|
|
408
|
+
for (const [manifest, version] of Object.entries(manifestToVersion)) {
|
|
409
|
+
if (!version) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const crateName = readPackageNameForManifest(cwd, manifest);
|
|
413
|
+
versionByDependency.set(crateName, version);
|
|
414
|
+
}
|
|
415
|
+
if (versionByDependency.size === 0) {
|
|
416
|
+
return [];
|
|
417
|
+
}
|
|
418
|
+
const impacted = [];
|
|
419
|
+
for (const manifest of [...new Set(candidateManifests)].sort((a, b) => a.localeCompare(b))) {
|
|
420
|
+
const manifestPath = node_path_1.default.join(cwd, manifest);
|
|
421
|
+
if (!node_fs_1.default.existsSync(manifestPath)) {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
const cargoTomlRaw = node_fs_1.default.readFileSync(manifestPath, "utf8");
|
|
425
|
+
if (!isCrateManifest(manifest, cargoTomlRaw)) {
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const next = writeMappedDependencyVersions(cargoTomlRaw, versionByDependency);
|
|
429
|
+
if (next !== cargoTomlRaw) {
|
|
430
|
+
impacted.push(manifest);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return impacted;
|
|
434
|
+
}
|
|
435
|
+
function toCargoManifestPath(packagePath) {
|
|
436
|
+
return packagePath === "."
|
|
437
|
+
? "Cargo.toml"
|
|
438
|
+
: normalizeSlashPath(node_path_1.default.posix.join(packagePath, "Cargo.toml"));
|
|
439
|
+
}
|
|
319
440
|
exports.rustVersionStrategy = {
|
|
320
441
|
name: "rust",
|
|
321
442
|
getVersionFile(config) {
|
|
@@ -323,7 +444,7 @@ exports.rustVersionStrategy = {
|
|
|
323
444
|
},
|
|
324
445
|
readVersion(cwd, config) {
|
|
325
446
|
const versionFile = this.getVersionFile(config);
|
|
326
|
-
const manifests = collectRustTargetManifests(cwd, versionFile);
|
|
447
|
+
const manifests = collectRustTargetManifests(cwd, versionFile, !config.packages);
|
|
327
448
|
const selectedManifest = manifests[0];
|
|
328
449
|
if (!selectedManifest) {
|
|
329
450
|
throw new Error(`Configured rust target "${versionFile}" did not resolve to a Rust crate manifest.`);
|
|
@@ -333,7 +454,7 @@ exports.rustVersionStrategy = {
|
|
|
333
454
|
},
|
|
334
455
|
writeVersion(cwd, config, version) {
|
|
335
456
|
const versionFile = this.getVersionFile(config);
|
|
336
|
-
const manifests = collectRustTargetManifests(cwd, versionFile);
|
|
457
|
+
const manifests = collectRustTargetManifests(cwd, versionFile, !config.packages);
|
|
337
458
|
const updatedFiles = [];
|
|
338
459
|
const internalCrates = new Set();
|
|
339
460
|
for (const manifest of manifests) {
|
|
@@ -7,6 +7,7 @@ exports.verifyProject = verifyProject;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const load_config_js_1 = require("../config/load-config.js");
|
|
10
|
+
const package_context_js_1 = require("../domain/strategy/package-context.js");
|
|
10
11
|
const resolve_js_1 = require("../domain/strategy/resolve.js");
|
|
11
12
|
function verifyProject(cwd = process.cwd()) {
|
|
12
13
|
const checks = [];
|
|
@@ -25,7 +26,7 @@ function verifyProject(cwd = process.cwd()) {
|
|
|
25
26
|
details: exists ? "Version file exists" : `Missing ${versionFile}`,
|
|
26
27
|
});
|
|
27
28
|
if (config.config.packages) {
|
|
28
|
-
for (const pkgPathRaw of Object.
|
|
29
|
+
for (const [pkgPathRaw, packageConfig] of Object.entries(config.config.packages)) {
|
|
29
30
|
const pkgPath = node_path_1.default.join(cwd, pkgPathRaw);
|
|
30
31
|
const exists = node_fs_1.default.existsSync(pkgPath);
|
|
31
32
|
checks.push({
|
|
@@ -33,6 +34,18 @@ function verifyProject(cwd = process.cwd()) {
|
|
|
33
34
|
ok: exists,
|
|
34
35
|
details: exists ? "Path exists" : `Missing path: ${pkgPathRaw}`,
|
|
35
36
|
});
|
|
37
|
+
if (exists) {
|
|
38
|
+
const packageContext = (0, package_context_js_1.resolvePackageStrategyContext)(config.config, pkgPathRaw, packageConfig);
|
|
39
|
+
const packageVersionFile = packageContext.versionFile;
|
|
40
|
+
const packageVersionExists = node_fs_1.default.existsSync(node_path_1.default.join(cwd, packageVersionFile));
|
|
41
|
+
checks.push({
|
|
42
|
+
name: `version-file:${packageVersionFile}`,
|
|
43
|
+
ok: packageVersionExists,
|
|
44
|
+
details: packageVersionExists
|
|
45
|
+
? "Version file exists"
|
|
46
|
+
: `Missing ${packageVersionFile}`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
36
49
|
}
|
|
37
50
|
}
|
|
38
51
|
const ok = checks.every((c) => c.ok);
|