@dennisrepa/elevate 1.5.0 → 1.7.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 +1 -1
- package/dist/cli.js +104 -24
- package/dist/cli.js.map +1 -1
- package/docs/de.md +1 -0
- package/docs/en.md +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -215,7 +215,7 @@ Detailed parameter references are available in [`docs/en.md`](./docs/en.md) and
|
|
|
215
215
|
* **pnpm specifics.** `catalog:` entries are updated where they are declared, in `pnpm-workspace.yaml`. A plain range on a workspace module (`^1.0.0`) is listed as *not linked* and never rewritten to `workspace:`, because pnpm resolves it from the registry. Elevate does not read pnpm settings such as `minimumReleaseAge` or the approval of build scripts, so pnpm may refuse an offered version; the update is then rolled back with pnpm's message. pnpm 8 and older, Windows and `shared-workspace-lockfile=false` are not covered.
|
|
216
216
|
* **Broken configuration stops Elevate.** An `elevate.config.json` that is not valid JSON, not an object, or whose `internalScopes` is not a list of strings is reported and Elevate exits with code `1`, because ignoring it would switch off the protection of internal packages. Without any configuration file Elevate works with defaults.
|
|
217
217
|
* **Dependency sections.** `dependencies`, `devDependencies` and `optionalDependencies` are covered. `peerDependencies` and `overrides` are never changed.
|
|
218
|
-
* **Cargo.** Requirements are raised keeping their precision, so a loose `"1"` only shows breaking updates; wildcard and compound requirements (`*`, `>=1.2, <2`), git and external path dependencies are left alone. Lock-file-only updates,
|
|
218
|
+
* **Cargo.** Requirements are raised keeping their precision, so a loose `"1"` only shows breaking updates; wildcard and compound requirements (`*`, `>=1.2, <2`), git and external path dependencies are left alone. Lock-file-only updates (`[Lock]`) are offered for direct dependencies when `Cargo.lock` is behind a requirement that already allows a newer release; transitive crates are not, and a crate with a pending manifest update shows only that candidate. Registries that need authentication and `[patch]` sections are not covered; a vendored or local-registry source replacement is refused rather than bypassed.
|
|
219
219
|
* **Maven.** Maven plugin versions and declarations inside `<profiles>` are not offered. Dependencies whose version is declared outside the repository (an external parent or BOM) are listed as *not offered* with the reason.
|
|
220
220
|
|
|
221
221
|
---
|
package/dist/cli.js
CHANGED
|
@@ -1351,7 +1351,7 @@ var CargoConfigReader = class {
|
|
|
1351
1351
|
|
|
1352
1352
|
// src/version.ts
|
|
1353
1353
|
import { createRequire } from "module";
|
|
1354
|
-
var VERSION = true ? "1.
|
|
1354
|
+
var VERSION = true ? "1.7.0" : fromPackage();
|
|
1355
1355
|
|
|
1356
1356
|
// src/adapters/cargo/cargo-index.ts
|
|
1357
1357
|
var RegistryAuthRequiredError = class extends Error {
|
|
@@ -1463,6 +1463,13 @@ import semver6 from "semver";
|
|
|
1463
1463
|
// src/adapters/cargo/cargo-versions.ts
|
|
1464
1464
|
import semver5 from "semver";
|
|
1465
1465
|
var rust = (v) => semver5.coerce(v)?.version;
|
|
1466
|
+
function eligibleVersions(versions, options) {
|
|
1467
|
+
const project = options.rustVersion ? rust(options.rustVersion) : void 0;
|
|
1468
|
+
return versions.filter((v) => !v.yanked && semver5.valid(v.version)).filter((v) => options.channel === "all" || semver5.prerelease(v.version) === null).filter((v) => {
|
|
1469
|
+
const needs = v.rustVersion ? rust(v.rustVersion) : void 0;
|
|
1470
|
+
return !(project && needs && semver5.gt(needs, project));
|
|
1471
|
+
}).map((v) => v.version).sort(semver5.rcompare);
|
|
1472
|
+
}
|
|
1466
1473
|
function selectVersion(versions, options) {
|
|
1467
1474
|
const candidates = versions.filter((v) => !v.yanked && semver5.valid(v.version)).filter((v) => options.channel === "all" || semver5.prerelease(v.version) === null).sort((a, b) => semver5.rcompare(a.version, b.version));
|
|
1468
1475
|
const project = options.rustVersion ? rust(options.rustVersion) : void 0;
|
|
@@ -1549,7 +1556,7 @@ function cargoDiff(from, to) {
|
|
|
1549
1556
|
}
|
|
1550
1557
|
|
|
1551
1558
|
// src/adapters/cargo/cargo-scanner.ts
|
|
1552
|
-
import { readFileSync as
|
|
1559
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
1553
1560
|
import { join as join11, relative as relative4 } from "path";
|
|
1554
1561
|
import semver8 from "semver";
|
|
1555
1562
|
|
|
@@ -1621,6 +1628,20 @@ function locateSection(path) {
|
|
|
1621
1628
|
return { section, inWorkspace, target, rest: path.slice(i + 1) };
|
|
1622
1629
|
}
|
|
1623
1630
|
|
|
1631
|
+
// src/adapters/cargo/cargo-lockfile.ts
|
|
1632
|
+
import { existsSync as existsSync7, readFileSync as readFileSync10 } from "fs";
|
|
1633
|
+
import { getStaticTOMLValue as getStaticTOMLValue2, parseTOML as parseTOML3 } from "toml-eslint-parser";
|
|
1634
|
+
function readLockedVersions(lockFile) {
|
|
1635
|
+
const locked = /* @__PURE__ */ new Map();
|
|
1636
|
+
if (!existsSync7(lockFile)) return locked;
|
|
1637
|
+
try {
|
|
1638
|
+
const data = getStaticTOMLValue2(parseTOML3(readFileSync10(lockFile, "utf8")));
|
|
1639
|
+
for (const pkg of data.package ?? []) locked.set(pkg.name, [...locked.get(pkg.name) ?? [], pkg.version]);
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
return locked;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1624
1645
|
// src/adapters/cargo/cargo-scanner.ts
|
|
1625
1646
|
var CONCURRENCY2 = 6;
|
|
1626
1647
|
var DIFF_ORDER2 = { major: 0, minor: 1, patch: 2 };
|
|
@@ -1634,6 +1655,7 @@ var CargoDependencyAdapter = class {
|
|
|
1634
1655
|
const pkg = metadata.packages.find((p) => samePath(join11(p.manifest_path, ".."), module.path));
|
|
1635
1656
|
if (!pkg) return { candidates: [], skipped: [] };
|
|
1636
1657
|
const own = readDeclarations(pkg.manifest_path);
|
|
1658
|
+
const locks = readLockedVersions(join11(metadata.workspace_root, "Cargo.lock"));
|
|
1637
1659
|
const rootManifest = join11(metadata.workspace_root, "Cargo.toml");
|
|
1638
1660
|
const root = readDeclarations(rootManifest);
|
|
1639
1661
|
const groups = groupDependencies(pkg.dependencies);
|
|
@@ -1650,7 +1672,7 @@ var CargoDependencyAdapter = class {
|
|
|
1650
1672
|
return target ? alignment(primary, target, located, common) : null;
|
|
1651
1673
|
}
|
|
1652
1674
|
if (!primary.source || primary.source.startsWith("git+")) return null;
|
|
1653
|
-
const outcome = await this.registryOutcome(primary, located, pkg, metadata, common, context);
|
|
1675
|
+
const outcome = await this.registryOutcome(primary, located, pkg, metadata, locks, common, context);
|
|
1654
1676
|
const other = group.find((d) => d !== primary && !sameRequirement(d.req, primary.req));
|
|
1655
1677
|
if (outcome && "candidate" in outcome && other) {
|
|
1656
1678
|
outcome.note = {
|
|
@@ -1674,7 +1696,7 @@ var CargoDependencyAdapter = class {
|
|
|
1674
1696
|
candidates.sort((a, b) => (DIFF_ORDER2[a.diff] ?? 3) - (DIFF_ORDER2[b.diff] ?? 3));
|
|
1675
1697
|
return { candidates, skipped };
|
|
1676
1698
|
}
|
|
1677
|
-
async registryOutcome(dep, located, pkg, metadata, common, context) {
|
|
1699
|
+
async registryOutcome(dep, located, pkg, metadata, locks, common, context) {
|
|
1678
1700
|
const literal = located?.version.value ?? dep.req;
|
|
1679
1701
|
if (!isUpgradableRequirement(literal)) return null;
|
|
1680
1702
|
if (!located || !sameRequirement(located.version.value, dep.req)) {
|
|
@@ -1708,10 +1730,10 @@ var CargoDependencyAdapter = class {
|
|
|
1708
1730
|
if (floor && held && semver8.gt(held.version, floor)) {
|
|
1709
1731
|
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "incompatible-rust-version", detail: ` (${held.version} needs Rust ${held.requires})` } };
|
|
1710
1732
|
}
|
|
1711
|
-
return
|
|
1733
|
+
return yankedLock(dep, literal, origin, versions, locks);
|
|
1712
1734
|
}
|
|
1713
1735
|
const newRange = rewriteRequirement(literal, latest);
|
|
1714
|
-
if (newRange === literal) return
|
|
1736
|
+
if (newRange === literal) return lockOutcome(dep, literal, origin, versions, pkg, metadata, locks, common, context);
|
|
1715
1737
|
const tag = semver8.prerelease(latest) ? extractPreReleaseTag(latest) ?? "PRE" : void 0;
|
|
1716
1738
|
const diff = cargoDiff(floor, latest);
|
|
1717
1739
|
const candidate = {
|
|
@@ -1734,6 +1756,39 @@ var CargoDependencyAdapter = class {
|
|
|
1734
1756
|
return { candidate };
|
|
1735
1757
|
}
|
|
1736
1758
|
};
|
|
1759
|
+
function lockOutcome(dep, requirement, origin, versions, pkg, metadata, locks, common, context) {
|
|
1760
|
+
const locked = (locks.get(dep.name) ?? []).filter((v) => satisfiesRequirement(requirement, v)).sort(semver8.rcompare)[0];
|
|
1761
|
+
if (!locked) return null;
|
|
1762
|
+
const options = { channel: context.channel, rustVersion: pkg.rust_version ?? void 0 };
|
|
1763
|
+
const allowed = versions.filter((v) => satisfiesRequirement(requirement, v.version));
|
|
1764
|
+
const newer = eligibleVersions(allowed, options).filter((v) => semver8.gt(v, locked));
|
|
1765
|
+
const latest = newer[0];
|
|
1766
|
+
if (!latest) return yankedLock(dep, requirement, origin, versions, locks);
|
|
1767
|
+
const lockFile = join11(metadata.workspace_root, "Cargo.lock");
|
|
1768
|
+
return {
|
|
1769
|
+
candidate: {
|
|
1770
|
+
coordinate: { identifier: dep.name, artifact: dep.name, ecosystem: "cargo", ...dep.registry ? { registry: dep.registry } : {} },
|
|
1771
|
+
currentRange: locked,
|
|
1772
|
+
currentClean: locked,
|
|
1773
|
+
latest,
|
|
1774
|
+
newRange: latest,
|
|
1775
|
+
diff: cargoDiff(locked, latest),
|
|
1776
|
+
scope: common.scope,
|
|
1777
|
+
// A release the requirement allows is compatible by definition, so it is safe to preselect.
|
|
1778
|
+
selected: true,
|
|
1779
|
+
origin,
|
|
1780
|
+
action: "lock",
|
|
1781
|
+
declaration: { file: lockFile, kind: "lockfile", displayPath: display(context.rootDir, lockFile) },
|
|
1782
|
+
availableVersions: newer,
|
|
1783
|
+
...common.qualifier ? { qualifier: common.qualifier } : {}
|
|
1784
|
+
}
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function yankedLock(dep, requirement, origin, versions, locks) {
|
|
1788
|
+
const locked = (locks.get(dep.name) ?? []).filter((v) => satisfiesRequirement(requirement, v)).sort(semver8.rcompare)[0];
|
|
1789
|
+
if (!locked || !versions.some((v) => v.yanked && v.version === locked)) return null;
|
|
1790
|
+
return { skipped: { identifier: dep.name, origin: origin.kind, reason: "locked-version-yanked", detail: locked } };
|
|
1791
|
+
}
|
|
1737
1792
|
function alignment(dep, target, located, common) {
|
|
1738
1793
|
const local = target.version;
|
|
1739
1794
|
if (!located || !local || !semver8.valid(local)) return null;
|
|
@@ -1772,7 +1827,7 @@ function groupDependencies(deps) {
|
|
|
1772
1827
|
}
|
|
1773
1828
|
function readDeclarations(manifestPath) {
|
|
1774
1829
|
try {
|
|
1775
|
-
return parseCargoManifest(
|
|
1830
|
+
return parseCargoManifest(readFileSync11(manifestPath, "utf8"));
|
|
1776
1831
|
} catch {
|
|
1777
1832
|
return [];
|
|
1778
1833
|
}
|
|
@@ -1964,20 +2019,6 @@ function applyRangeEdits(source, edits) {
|
|
|
1964
2019
|
return result;
|
|
1965
2020
|
}
|
|
1966
2021
|
|
|
1967
|
-
// src/adapters/cargo/cargo-lockfile.ts
|
|
1968
|
-
import { existsSync as existsSync7, readFileSync as readFileSync11 } from "fs";
|
|
1969
|
-
import { getStaticTOMLValue as getStaticTOMLValue2, parseTOML as parseTOML3 } from "toml-eslint-parser";
|
|
1970
|
-
function readLockedVersions(lockFile) {
|
|
1971
|
-
const locked = /* @__PURE__ */ new Map();
|
|
1972
|
-
if (!existsSync7(lockFile)) return locked;
|
|
1973
|
-
try {
|
|
1974
|
-
const data = getStaticTOMLValue2(parseTOML3(readFileSync11(lockFile, "utf8")));
|
|
1975
|
-
for (const pkg of data.package ?? []) locked.set(pkg.name, [...locked.get(pkg.name) ?? [], pkg.version]);
|
|
1976
|
-
} catch {
|
|
1977
|
-
}
|
|
1978
|
-
return locked;
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
2022
|
// src/adapters/cargo/cargo-updater.ts
|
|
1982
2023
|
var UPDATE_TIMEOUT_MS = 10 * 6e4;
|
|
1983
2024
|
var workspaceDir = (module, rootDir) => module.aggregatorDir ?? module.path ?? rootDir;
|
|
@@ -1991,6 +2032,9 @@ var CargoUpdaterAdapter = class {
|
|
|
1991
2032
|
const dir = workspaceDir(module, rootDir);
|
|
1992
2033
|
const lockFile = join12(dir, "Cargo.lock");
|
|
1993
2034
|
const lockedBefore = readLockedVersions(lockFile);
|
|
2035
|
+
const lockUpdates = updates.filter((u) => u.action === "lock");
|
|
2036
|
+
updates = updates.filter((u) => u.action !== "lock");
|
|
2037
|
+
this.checkLockUpdates(lockUpdates, lockFile, lockedBefore);
|
|
1994
2038
|
const byFile = /* @__PURE__ */ new Map();
|
|
1995
2039
|
for (const update of updates) {
|
|
1996
2040
|
const file = resolve6(update.declaration?.file ?? join12(module.path, "Cargo.toml"));
|
|
@@ -2028,13 +2072,37 @@ var CargoUpdaterAdapter = class {
|
|
|
2028
2072
|
if (existsSync8(lockFile) && registryUpdates.length > 0) {
|
|
2029
2073
|
await this.refreshLock(dir, lockFile, lockedBefore, registryUpdates, onProgress);
|
|
2030
2074
|
}
|
|
2075
|
+
for (const update of lockUpdates) {
|
|
2076
|
+
const name = update.coordinate.identifier;
|
|
2077
|
+
onProgress(`Locking ${name} at ${update.latest}\u2026`);
|
|
2078
|
+
await this.cargoUpdate(dir, ["-p", `${name}@${update.currentClean}`, "--precise", update.latest]);
|
|
2079
|
+
}
|
|
2080
|
+
const parts = [];
|
|
2081
|
+
if (updates.length > 0) {
|
|
2082
|
+
parts.push(`Updated ${updates.length} requirement(s) in ${writes.length} Cargo.toml file(s)${existsSync8(lockFile) ? "; Cargo.lock refreshed" : ""}`);
|
|
2083
|
+
}
|
|
2084
|
+
if (lockUpdates.length > 0) parts.push(`locked ${lockUpdates.length} crate(s) at a newer version in Cargo.lock`);
|
|
2085
|
+
const message2 = parts.join("; ");
|
|
2031
2086
|
return {
|
|
2032
|
-
updatedCount: updates.length,
|
|
2033
|
-
auditMessage:
|
|
2087
|
+
updatedCount: updates.length + lockUpdates.length,
|
|
2088
|
+
auditMessage: `${message2.charAt(0).toUpperCase()}${message2.slice(1)}.`,
|
|
2034
2089
|
auditSeverity: "clean",
|
|
2035
2090
|
integrityViolations: []
|
|
2036
2091
|
};
|
|
2037
2092
|
}
|
|
2093
|
+
/** A lock-only update needs a lock file that still holds the version the scan saw. */
|
|
2094
|
+
checkLockUpdates(lockUpdates, lockFile, locked) {
|
|
2095
|
+
if (lockUpdates.length === 0) return;
|
|
2096
|
+
if (!existsSync8(lockFile)) {
|
|
2097
|
+
throw new InstallError(`${lockFile} does not exist any more; rescan the module.`);
|
|
2098
|
+
}
|
|
2099
|
+
for (const update of lockUpdates) {
|
|
2100
|
+
const name = update.coordinate.identifier;
|
|
2101
|
+
if (!(locked.get(name) ?? []).includes(update.currentClean)) {
|
|
2102
|
+
throw new InstallError(`Cargo.lock no longer locks ${name} at ${update.currentClean}; rescan the module.`);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2038
2106
|
async resync() {
|
|
2039
2107
|
}
|
|
2040
2108
|
async refreshLock(dir, lockFile, before, updates, onProgress) {
|
|
@@ -2075,11 +2143,13 @@ var same = (a, b) => relative5(resolve7(a), resolve7(b)) === "";
|
|
|
2075
2143
|
function planCheck(metadata, module, changedFiles, locked) {
|
|
2076
2144
|
const args = ["check", "--all-targets", ...locked ? ["--locked"] : []];
|
|
2077
2145
|
const rootManifest = join13(metadata.workspace_root, "Cargo.toml");
|
|
2146
|
+
const lockFile = join13(metadata.workspace_root, "Cargo.lock");
|
|
2078
2147
|
const memberManifests = new Set(metadata.packages.map((p) => resolve7(p.manifest_path)));
|
|
2079
2148
|
if (changedFiles.some((f) => same(f, rootManifest))) {
|
|
2080
2149
|
const isVirtualRoot = !memberManifests.has(resolve7(rootManifest));
|
|
2081
2150
|
if (isVirtualRoot || metadata.packages.length > 1) return [...args, "--workspace"];
|
|
2082
2151
|
}
|
|
2152
|
+
if (changedFiles.length > 0 && changedFiles.every((f) => same(f, lockFile))) return [...args, "--workspace"];
|
|
2083
2153
|
const changed = new Set(
|
|
2084
2154
|
metadata.packages.filter((p) => changedFiles.some((f) => same(f, p.manifest_path))).map((p) => p.name)
|
|
2085
2155
|
);
|
|
@@ -3480,6 +3550,7 @@ var de = {
|
|
|
3480
3550
|
prod: "(prod/compile)",
|
|
3481
3551
|
align: "[Angleichen]",
|
|
3482
3552
|
internal: "[Intern]",
|
|
3553
|
+
lock: "[Lock]",
|
|
3483
3554
|
shared: (count) => `[+${count} Crates]`
|
|
3484
3555
|
},
|
|
3485
3556
|
status: {
|
|
@@ -3591,6 +3662,7 @@ var en = {
|
|
|
3591
3662
|
prod: "(prod/compile)",
|
|
3592
3663
|
align: "[Align]",
|
|
3593
3664
|
internal: "[Internal]",
|
|
3665
|
+
lock: "[Lock]",
|
|
3594
3666
|
shared: (count) => `[+${count} crates]`
|
|
3595
3667
|
},
|
|
3596
3668
|
status: {
|
|
@@ -4361,6 +4433,10 @@ var PackageRow = ({ item, isFocused, t }) => {
|
|
|
4361
4433
|
t.badges.align,
|
|
4362
4434
|
" "
|
|
4363
4435
|
] }),
|
|
4436
|
+
item.action === "lock" && /* @__PURE__ */ jsxs5(Text5, { bold: true, color: theme.colors.brandLight, children: [
|
|
4437
|
+
t.badges.lock,
|
|
4438
|
+
" "
|
|
4439
|
+
] }),
|
|
4364
4440
|
item.origin.kind === "private" && /* @__PURE__ */ jsxs5(Text5, { color: theme.colors.brandLight, children: [
|
|
4365
4441
|
t.badges.internal,
|
|
4366
4442
|
" "
|
|
@@ -5309,6 +5385,8 @@ function describeSkip(skipped) {
|
|
|
5309
5385
|
return `catalog entry could not be resolved${detail}`;
|
|
5310
5386
|
case "incompatible-rust-version":
|
|
5311
5387
|
return `newer versions need a newer Rust than the project's rust-version${detail}`;
|
|
5388
|
+
case "locked-version-yanked":
|
|
5389
|
+
return `Cargo.lock pins a yanked release and no allowed release can replace it${detail}`;
|
|
5312
5390
|
}
|
|
5313
5391
|
}
|
|
5314
5392
|
|
|
@@ -5397,7 +5475,7 @@ async function handleScanCommand(options, config) {
|
|
|
5397
5475
|
if (result.candidates.length === 0) console.log(" \u2728 All dependencies are up to date.");
|
|
5398
5476
|
for (const u of result.candidates) {
|
|
5399
5477
|
const diffTag = `[${u.diff.toUpperCase()}]`.padEnd(9);
|
|
5400
|
-
const actionTag = u.action === "align" ? "[ALIGN] " : u.origin.kind === "private" ? "[INTERNAL] " : "";
|
|
5478
|
+
const actionTag = u.action === "align" ? "[ALIGN] " : u.action === "lock" ? "[LOCK] " : u.origin.kind === "private" ? "[INTERNAL] " : "";
|
|
5401
5479
|
const preBadge = u.isPreRelease ? `[${u.preReleaseTag || "PRE"}] ` : "";
|
|
5402
5480
|
const where = u.declaration && u.declaration.displayPath !== modulePom ? ` (in ${u.declaration.displayPath}${u.declaration.propertyName ? ` \${${u.declaration.propertyName}}` : ""})` : "";
|
|
5403
5481
|
console.log(
|
|
@@ -5534,6 +5612,8 @@ function retarget(found, target, options) {
|
|
|
5534
5612
|
const isPreRelease = isPreReleaseVersion(target);
|
|
5535
5613
|
return {
|
|
5536
5614
|
...found,
|
|
5615
|
+
// A lock update pins exactly the version that was picked.
|
|
5616
|
+
...found.action === "lock" ? { latest: target } : {},
|
|
5537
5617
|
newRange: policyOf(options).rewrite(found.currentRange, target),
|
|
5538
5618
|
diff,
|
|
5539
5619
|
selected: true,
|