also looking at this
fix(install): a lock pin decides what installs while it satisfies its spec
#263
3 files
+118
-1
| @@ -972,6 +972,30 @@ pub fn installCommandWithOptions(allocator: std.mem.Allocator, args: []const []c | ||
| 972 | 972 | // ── Parallel pipeline: resolve tree → download → extract ── |
| 973 | 973 | const pipeline = ; |
| 974 | 974 | |
| 975 | // A committed lock decides what gets installed. Every declared range | |
| 976 | // used to be resolved against the registry, so the newest match won | |
| 977 | // and the pin was rewritten; `--frozen-lockfile` installed that newer | |
| 978 | // release and only then failed (#260). A spec the pin no longer | |
| 979 | // satisfies still resolves afresh, so editing a dependency works. | |
| 980 | // | |
| 981 | // Checked before anything is installed, which is the other half of | |
| 982 | // the report: a frozen run must fail without having written to | |
| 983 | // `pantry/` first. | |
| 984 | if (options.frozen_lockfile) { | |
| 985 | for (deps_to_install) |dep| { | |
| 986 | if (env_only_domains.contains(helpers.resolvePackageAlias(helpers.normalizePackageName(dep.name)))) continue; | |
| 987 | if (helpers.lockedVersionSatisfying(&lock_file, dep.name, dep.version) != null) continue; | |
| 988 | // `latest`-style specs are covered by any pin, so reaching | |
| 989 | // here means the lock does not cover this dependency at all, | |
| 990 | // or its pin falls outside the declared spec. | |
| 991 | style.printWarn("Lockfile does not satisfy {s}@{s} but --frozen-lockfile is set\n", .{ dep.name, dep.version }); | |
| 992 | return .{ | |
| 993 | .exit_code = 1, | |
| 994 | .message = try allocator.dupe(u8, "Error: lockfile is out of date (--frozen-lockfile)"), | |
| 995 | }; | |
| 996 | } | |
| 997 | } | |
| 998 | ||
| 975 | 999 | // Build pipeline dep list |
| 976 | 1000 | var pipeline_deps = try allocator.alloc(pipeline.PipelineDep, deps_to_install.len); |
| 977 | 1001 | defer allocator.free(pipeline_deps); |
| @@ -981,7 +1005,7 @@ pub fn installCommandWithOptions(allocator: std.mem.Allocator, args: []const []c | ||
| 981 | 1005 | const is_domain = std.mem.indexOfScalar(u8, clean_name, '.') != null; |
| 982 | 1006 | pipeline_deps[di] = .{ |
| 983 | 1007 | .name = clean_name, |
| 984 | .version = dep.version, | |
| 1008 | .version = helpers.lockedVersionSatisfying(&lock_file, dep.name, dep.version) orelse dep.version, | |
| 985 | 1009 | .source = if (is_domain) .pantry else switch (dep.source) { |
| 986 | 1010 | .registry => .npm, |
| 987 | 1011 | .pantry => .pantry, |