Skip to content

fix(lockfile): parse git and github deps that carry an integrity hash - #104

Closed
robertodr wants to merge 1 commit into
nix-community:masterfrom
robertodr:fix/github-deps-with-integrity-hash
Closed

fix(lockfile): parse git and github deps that carry an integrity hash#104
robertodr wants to merge 1 commit into
nix-community:masterfrom
robertodr:fix/github-deps-with-integrity-hash

Conversation

@robertodr

Copy link
Copy Markdown

🤖 AI text below 🤖

Problem

deserialize_package dispatches lockfile entries purely on their arity. Newer versions of bun append an integrity hash to github: and git+ entries, which gives them arity 4 — the same shape as an npm package:

// arity 3, handled correctly today
"dayjs": ["dayjs@github:iamkun/dayjs#45bf6a3", {}, "iamkun-dayjs-45bf6a3"],

// arity 4, routed to deserialize_npm_package
"@kenn-io/kata-ui": ["kata@github:kenn-io/kata#c668572", {}, "kenn-io-kata-c668572", "sha512-WiP4Se…"],

Those entries reach deserialize_npm_package, which asks to_npm_url to build a registry url out of a git specifier. There are two outcomes:

  • Scoped package — silently produces a bogus https://registry.npmjs.org/@scope/pkg/-/pkg-github:owner/repo#rev.tgz, which fails much later at fetch time.
  • Unscoped package — fails outright. to_npm_url looks for the scope separator by splitting on the first /, so for kata@github:kenn-io/kata#c668572 it consumes the / between owner and repo and is left with kata#c668572, which has no @ to split on:
[ERROR bun2nix]
    Failed to parse lockfile related JSON as rust type:
    Failed to deserialize package: Missing @ for package name and version declaration.

    Make sure all versions in your bun lockfile are formatted properly or try
    deleting it and running `bun install` to produce a fresh one

The advice in that message can't help: the lockfile is valid and a fresh bun install reproduces it. Any project with an unscoped github dependency is stuck.

Fix

  1. Dispatch arity-4 entries on their specifier — a new 4 if deserializer.has_git_or_github_specifier() arm sends them to deserialize_tarball_git_or_github_package, so git and github packages are recognised whether or not bun recorded a hash for them. That method only reads the identifier, so the trailing hash is harmlessly ignored. Backed by a new public is_git_or_github_identifier helper with doctests.

  2. Harden to_npm_url — it now finds the name/version separator the way drain_package_specifier already does, instead of inferring the scope from a /, so a specifier containing a slash can no longer be misread. This is defence in depth: after (1) a git specifier no longer reaches it.

Testing

Built and tested against rustc 1.95.

  • cargo test --locked — 7 doctests pass, 2 of them new.

  • rustfmt --check clean on the touched files. cargo clippy --all-targets reports one collapsible_if warning in an untouched block; warning count is unchanged from the base commit.

  • No regression: generated output for templates/git-deps/bun.lock (npm + git+ + two arity-3 github: deps) is byte-identical before and after this change.

  • Reproduced and fixed, using a synthetic lockfile with arity-4 github entries, scoped and unscoped:

    "packages": {
      "scoped":   ["@scope/pkg@github:iamkun/dayjs#45bf6a3", {}, "iamkun-dayjs-45bf6a3", "sha512-AAAA…"],
      "unscoped": ["dayjs@github:iamkun/dayjs#45bf6a3",      {}, "iamkun-dayjs-45bf6a3", "sha512-BBBB…"]
    }

    Before, this fails with Missing @ for package name and version declaration. After, it produces the same entry the arity-3 form does:

    "github:iamkun-dayjs-45bf6a3" = fetchFromGitHub {
      owner = "iamkun";
      repo = "dayjs";
      rev = "45bf6a3";
      hash = "sha256-E3r6M5Ydy7TtH4BrpnWT57BztbLn0HJXMklto4zMJ6w=";
    };

I did not add a template or nix flake check fixture for the arity-4 shape, since the lockfile bun generates depends on the bun version in the build environment — happy to add one if you'd prefer it pinned as a static fixture.

Newer versions of bun append an integrity hash to `github:` and `git+`
package entries, giving them arity 4 - the same shape as an npm package.
`deserialize_package` dispatches purely on arity, so those entries were
handed to `deserialize_npm_package`, which asked `to_npm_url` to build a
registry url out of a git specifier.

For a scoped package that silently produced a bogus registry.npmjs.org
url. For an unscoped one it failed outright, because `to_npm_url` looks
for the scope separator by splitting on the first `/` - which for
`kata@github:kenn-io/kata#c668572` consumes the `/` between owner and
repo, leaving `kata#c668572` with no `@` left to split on:

    Failed to deserialize package: Missing @ for package name and
    version declaration.

Dispatch arity 4 entries on their specifier, so git and github packages
reach the git/github deserializer whether or not bun recorded a hash for
them. `to_npm_url` now finds the name/version separator the same way
`drain_package_specifier` does instead of inferring the scope from a
`/`, so a specifier containing a slash can no longer be misread.

Assisted-by: ClaudeCode:claude-opus-5
@typedrat

Copy link
Copy Markdown
Contributor

Is this different than what #101 resolves?

@robertodr

Copy link
Copy Markdown
Author

No, I think this is a duplicate. Should have looked at the open PRs before submitting...

@robertodr robertodr closed this Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants