bun.lock migration: fix packages with long version string (#21753)

### What does this PR do?

cases like `@prisma/engines-version` with version of
`6.14.0-17.fba13060ef3cfbe5e95af3aaba61eabf2b8a8a20` was having issues
with the version and using a "corrupted" string instead

### How did you verify your code works?

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Michael H
2025-08-11 16:26:03 -07:00
committed by GitHub
parent 8e6184707d
commit 020fe12887
2 changed files with 242 additions and 70 deletions

View File

@@ -475,81 +475,79 @@ const DependencyType = enum {
optional,
peer,
};
const processDeps = struct {
fn process(
deps: bun.StringHashMap(string),
dep_type: DependencyType,
yarn_lock_: *YarnLock,
string_buf_: *Semver.String.Buf,
deps_buf: []Dependency,
res_buf: []Install.PackageID,
log: *logger.Log,
manager: *Install.PackageManager,
yarn_entry_to_package_id: []const Install.PackageID,
) ![]Install.PackageID {
var deps_it = deps.iterator();
var count: usize = 0;
var dep_spec_name_stack = std.heap.stackFallback(1024, bun.default_allocator);
const temp_allocator = dep_spec_name_stack.get();
fn processDeps(
deps: bun.StringHashMap(string),
dep_type: DependencyType,
yarn_lock_: *YarnLock,
string_buf_: *Semver.String.Buf,
deps_buf: []Dependency,
res_buf: []Install.PackageID,
log: *logger.Log,
manager: *Install.PackageManager,
yarn_entry_to_package_id: []const Install.PackageID,
) ![]Install.PackageID {
var deps_it = deps.iterator();
var count: usize = 0;
var dep_spec_name_stack = std.heap.stackFallback(1024, bun.default_allocator);
const temp_allocator = dep_spec_name_stack.get();
while (deps_it.next()) |dep| {
const dep_name = dep.key_ptr.*;
const dep_version = dep.value_ptr.*;
const dep_spec = try std.fmt.allocPrint(
temp_allocator,
"{s}@{s}",
.{ dep_name, dep_version },
);
defer temp_allocator.free(dep_spec);
while (deps_it.next()) |dep| {
const dep_name = dep.key_ptr.*;
const dep_version = dep.value_ptr.*;
const dep_spec = try std.fmt.allocPrint(
temp_allocator,
"{s}@{s}",
.{ dep_name, dep_version },
);
defer temp_allocator.free(dep_spec);
if (yarn_lock_.findEntryBySpec(dep_spec)) |dep_entry| {
const dep_name_hash = stringHash(dep_name);
const dep_name_str = try string_buf_.appendWithHash(dep_name, dep_name_hash);
if (yarn_lock_.findEntryBySpec(dep_spec)) |dep_entry| {
const dep_name_hash = stringHash(dep_name);
const dep_name_str = try string_buf_.appendWithHash(dep_name, dep_name_hash);
const parsed_version = if (YarnLock.Entry.isNpmAlias(dep_version)) blk: {
const alias_info = YarnLock.Entry.parseNpmAlias(dep_version);
break :blk alias_info.version;
} else dep_version;
const parsed_version = if (YarnLock.Entry.isNpmAlias(dep_version)) blk: {
const alias_info = YarnLock.Entry.parseNpmAlias(dep_version);
break :blk alias_info.version;
} else dep_version;
deps_buf[count] = Dependency{
.name = dep_name_str,
.name_hash = dep_name_hash,
.version = Dependency.parse(
yarn_lock_.allocator,
dep_name_str,
dep_name_hash,
parsed_version,
&Semver.SlicedString.init(parsed_version, parsed_version),
log,
manager,
) orelse Dependency.Version{},
.behavior = .{
.prod = dep_type == .production,
.optional = dep_type == .optional,
.dev = dep_type == .development,
.peer = dep_type == .peer,
.workspace = dep_entry.workspace,
},
};
var found_package_id: ?Install.PackageID = null;
outer: for (yarn_lock_.entries.items, 0..) |entry_, yarn_idx| {
for (entry_.specs) |entry_spec| {
if (strings.eql(entry_spec, dep_spec)) {
found_package_id = yarn_entry_to_package_id[yarn_idx];
break :outer;
}
deps_buf[count] = Dependency{
.name = dep_name_str,
.name_hash = dep_name_hash,
.version = Dependency.parse(
yarn_lock_.allocator,
dep_name_str,
dep_name_hash,
parsed_version,
&Semver.SlicedString.init(parsed_version, parsed_version),
log,
manager,
) orelse Dependency.Version{},
.behavior = .{
.prod = dep_type == .production,
.optional = dep_type == .optional,
.dev = dep_type == .development,
.peer = dep_type == .peer,
.workspace = dep_entry.workspace,
},
};
var found_package_id: ?Install.PackageID = null;
outer: for (yarn_lock_.entries.items, 0..) |entry_, yarn_idx| {
for (entry_.specs) |entry_spec| {
if (strings.eql(entry_spec, dep_spec)) {
found_package_id = yarn_entry_to_package_id[yarn_idx];
break :outer;
}
}
}
if (found_package_id) |pkg_id| {
res_buf[count] = pkg_id;
count += 1;
}
if (found_package_id) |pkg_id| {
res_buf[count] = pkg_id;
count += 1;
}
}
return res_buf[0..count];
}
}.process;
return res_buf[0..count];
}
pub fn migrateYarnLockfile(
this: *Lockfile,
@@ -956,9 +954,8 @@ pub fn migrateYarnLockfile(
});
}
const version = entry.version;
const sliced_version = Semver.SlicedString.init(version, version);
const result = Semver.Version.parse(sliced_version);
const version = try string_buf.append(entry.version);
const result = Semver.Version.parse(version.sliced(this.buffers.string_bytes.items));
if (!result.valid) {
break :blk Resolution{};
}
@@ -1033,6 +1030,7 @@ pub fn migrateYarnLockfile(
if (found_idx) |idx| {
const name_hash = stringHash(dep.name);
const dep_name_string = try string_buf.appendWithHash(dep.name, name_hash);
const version_string = try string_buf.append(dep.version);
dependencies_buf[actual_root_dep_count] = Dependency{
.name = dep_name_string,
@@ -1041,8 +1039,8 @@ pub fn migrateYarnLockfile(
allocator,
dep_name_string,
name_hash,
dep.version,
&Semver.SlicedString.init(dep.version, dep.version),
version_string.slice(this.buffers.string_bytes.items),
&version_string.sliced(this.buffers.string_bytes.items),
log,
manager,
) orelse Dependency.Version{},

View File

@@ -51,6 +51,180 @@ is-number@^7.0.0:
expect(bunLockContent).toMatchSnapshot("simple-yarn-migration");
});
test("yarn.lock with packages containing long build tags", async () => {
const tempDir = tempDirWithFiles("yarn-migration-build-tags", {
"package.json": JSON.stringify(
{
name: "build-tags-test",
version: "1.0.0",
dependencies: {
"@prisma/engines-version": "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81",
"@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
"@babel/preset-modules": "0.1.6-no-external-plugins",
},
},
null,
2,
),
"yarn.lock": `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2":
version "7.21.0-placeholder-for-preset-env.2"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703"
integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==
"@babel/preset-modules@0.1.6-no-external-plugins":
version "0.1.6-no-external-plugins"
resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a"
integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/types" "^7.4.4"
esutils "^2.0.2"
"@babel/helper-plugin-utils@^7.0.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
"@babel/types@^7.4.4":
version "7.23.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#61c5b592274a82bb7addc5073ee1d989799e75e4"
integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==
dependencies:
"@babel/helper-string-parser" "^7.22.5"
"@babel/helper-validator-identifier" "^7.22.20"
to-fast-properties "^2.0.0"
"@babel/helper-string-parser@^7.22.5":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f"
integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==
"@babel/helper-validator-identifier@^7.22.20":
version "7.22.20"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
"@prisma/engines-version@4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81":
version "4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81.tgz#5512069ca14c44af7f38e7c39d9a169480e63a33"
integrity sha512-q617EUWfRIDTriWADZ4YiWRZXCa/WuhNgLTVd+HqWLffjMSPzyM5uOWoauX91wvQClSKZU4pzI4JJLQ9Kl62Qg==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
`,
});
// Run bun pm migrate
const migrateResult = await Bun.spawn({
cmd: [bunExe(), "pm", "migrate", "-f"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
stdin: "ignore",
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(migrateResult.stdout).text(),
new Response(migrateResult.stderr).text(),
migrateResult.exited,
]);
expect(exitCode).toBe(0);
expect(fs.existsSync(join(tempDir, "bun.lock"))).toBe(true);
const bunLockContent = fs.readFileSync(join(tempDir, "bun.lock"), "utf8");
// Verify that long build tags are preserved correctly
expect(bunLockContent).toContain("4.16.1-1.4bc8b6e1b66cb932731fb1bdbbc550d1e010de81");
expect(bunLockContent).toContain("7.21.0-placeholder-for-preset-env.2");
expect(bunLockContent).toContain("0.1.6-no-external-plugins");
// Ensure no corrupted version strings
expect(bunLockContent).not.toContain("monoreporeact");
expect(bunLockContent).not.toContain("@types/react");
expect(bunLockContent).not.toContain("<22>");
// Install should work after migration
const installResult = await Bun.spawn({
cmd: [bunExe(), "install"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
stdin: "ignore",
});
const installExitCode = await installResult.exited;
expect(installExitCode).toBe(0);
});
test("yarn.lock with extremely long build tags (regression test)", async () => {
const tempDir = tempDirWithFiles("yarn-migration-extreme-build-tags", {
"package.json": JSON.stringify(
{
name: "extreme-build-tags-test",
version: "1.0.0",
dependencies: {
"test-package":
"1.0.0-alpha.beta.gamma.delta.epsilon.zeta.eta.theta.iota.kappa.lambda.mu.nu.xi.omicron.pi.rho.sigma.tau.upsilon.phi.chi.psi.omega.0123456789abcdef",
},
},
null,
2,
),
"yarn.lock": `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
test-package@1.0.0-alpha.beta.gamma.delta.epsilon.zeta.eta.theta.iota.kappa.lambda.mu.nu.xi.omicron.pi.rho.sigma.tau.upsilon.phi.chi.psi.omega.0123456789abcdef:
version "1.0.0-alpha.beta.gamma.delta.epsilon.zeta.eta.theta.iota.kappa.lambda.mu.nu.xi.omicron.pi.rho.sigma.tau.upsilon.phi.chi.psi.omega.0123456789abcdef"
resolved "https://registry.yarnpkg.com/test-package/-/test-package-1.0.0-alpha.beta.gamma.delta.epsilon.zeta.eta.theta.iota.kappa.lambda.mu.nu.xi.omicron.pi.rho.sigma.tau.upsilon.phi.chi.psi.omega.0123456789abcdef.tgz#abc123"
integrity sha512-xjEohWws8kKwCqz1IbvMrLynEfS7pFdO0CwjbPvzkt+rpL6tpnAKHS0N8d6diEBSp2BAIkUENc6H6H9SEqf0uA==
`,
});
// Run bun pm migrate
const migrateResult = await Bun.spawn({
cmd: [bunExe(), "pm", "migrate", "-f"],
cwd: tempDir,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
stdin: "ignore",
});
const exitCode = await migrateResult.exited;
expect(exitCode).toBe(0);
const bunLockPath = join(tempDir, "bun.lock");
expect(fs.existsSync(bunLockPath)).toBe(true);
const bunLockContent = fs.readFileSync(bunLockPath, "utf8");
// The entire long version string should be preserved
const expectedVersion =
"1.0.0-alpha.beta.gamma.delta.epsilon.zeta.eta.theta.iota.kappa.lambda.mu.nu.xi.omicron.pi.rho.sigma.tau.upsilon.phi.chi.psi.omega.0123456789abcdef";
expect(bunLockContent).toContain(expectedVersion);
// Should not contain any corruption artifacts
expect(bunLockContent).not.toContain("<22>");
expect(bunLockContent).not.toContain("\0");
expect(bunLockContent).not.toContain("undefined");
expect(bunLockContent).not.toContain("null");
});
test("complex yarn.lock with multiple dependencies and versions", async () => {
const tempDir = tempDirWithFiles("yarn-migration-complex", {
"package.json": JSON.stringify(