install: keep non-default ssh port in the ssh fallback clone URL
Closed
RO
1mo ago+84+84+01234567890123456789-2-2-0123456789
Fixes #36931
Problem
- A git dependency with an explicit ssh port,
git+ssh://git@host:52626/user/repo.git#v1.3.2, fails to install: the ssh clone attempt runsgit clone ssh://git@host/52626/user/repo.git, with the port turned into a path segment. Repository::try_ssh(src/install/repository.rs, thessh://branch) passes everyssh://URL throughhosted_git_info::correct_url, which replaces the last colon with a slash. That rewrite exists for scp-style input such asssh://git@github.com:user/repo, but it runs unconditionally, so it also hits a port, a[::1]:2222host, a colon inside the path, and, for a URL with no userinfo (ssh://host/user/repo.git), the colon of the scheme itself (git is handedssh///host/user/repo.git).
Fix
try_sshnow returns anssh://URL unchanged when it parses as a URL, and only runscorrect_urlwhen it does not. The check ishosted_git_info::is_parseable_url, a whole-stringWTF::URLparse, the same callhosted_git_info::parse_urlalready makes before it falls back tocorrect_url.- This is the order npm's hosted-git-info uses:
parseUrltriesnew URL(giturl)first and only callscorrectUrlwhen that throws.ssh://host:52626/pathparses, so npm never rewrites it;ssh://host:user/repodoes not parse (non-numeric port), so it is still rewritten.try_sshwas the one caller ofcorrect_urlthat skipped the parse step. - Checking "does it parse" rather than "is the colon followed by a digit" matters: scp-style paths that start with a digit (
git@github.com:1Password/op-js.git) do not parse as URLs and must still be rewritten, which npm does. A digit heuristic insidecorrect_url(the approach in #38887) stops rewriting those and also breaksHostedGitInfo::from_urlfor them, verified on a build with that change:hostedGitInfo.parseUrl("git@github.com:1Password/op-js.git")throwsInvalidGitUrl, and forgit+ssh://git@github.com:1Password/op-js.gitthe ssh fallback runsgit clone ssh://git@github.com:1Password/op-js.git, which git rejects. Both work on main. - The check has to parse the whole string. The first revision used
bun_url::origin_from_slice, which parses only the leading ASCII prefix, sossh://git@höst:user/repo.gitwas judged valid onssh://gand no longer corrected; thehösttest row below fails on that revision and passes on this one. - Verified:
test/cli/install/bun-install.test.ts, "ssh fallback clone URL for ...": shadowsgitwith a PATH shim that records its argv and asserts the exactssh://URL for eight dependency specs. On a build without the fix the five pass-through rows fail (port, IPv6 + port, port + non-ASCII path, colon in the path, no userinfo; e.g.ssh://git@localhost/52626/user/repo.git,ssh///localhost/user/repo.git) and the three rewrite rows pass; with the fix all eight pass.test/cli/install/hosted-git-info/cases.tsandparse-url.test.ts: cases for the port URL survivingparse_urland for digit-leading scp paths still being corrected. They pass on main; with #38887'scorrect_urlchange applied, all five fail, as does the digit-leadingbun-install.test.tsrow.bun bd test test/cli/install/hosted-git-info/: 662 pass.cargo clippy -p bun_installandcargo fmtare clean.
Background
- scp-style git URL:
user@host:path, whatgit clone git@github.com:foo/bartakes. The colon separates host and path, not host and port. hosted-git-info (the npm librarysrc/install/hosted_git_info.rsports) turns these into parseable URLs by replacing that colon with a slash; this iscorrectUrl/correct_url. try_https/try_ssh: when cloning a git dependency,PackageManagerTaskfirst tries an https form of the URL and, if that clone fails, an ssh form.try_sshbuilds the ssh form; for input that is alreadyssh://its only job is the scp-style correction.git+ssh:dependencies are tagged as plain git dependencies without going throughhosted_git_info::parse_url(dependency.rs), sotry_sshis the only place the issue's URL is inspected.JscUrl::from_utf8(bun_url::whatwg::URL): the WHATWG URL parser from WebKit; it returns nothing for input it rejects. For a non-special scheme such asssh:, a non-numeric port (:user/repo) is a rejection and a numeric one (:52626) is not.OwnedJscUrlis the RAII wrapper that frees the C++ object.HostedGitInfo::from_urlreturnsNonefor known hosts with an explicit port (git+ssh://user@bitbucket.org:2222/foo/bar) because the host lookup compareshost:portagainst the provider domain. This is unrelated to the bug (such a dependency is treated as a plain git URL and cloned with the port intact, which is what the issue asks for), so this PR leaves it alone; it is why #38887'scases.tsentries do not pass even with that PR's change applied.
First revision
The first revision gated the correction on bun_url::origin_from_slice(url).is_some(). origin_from_slice hands only the bytes before the first non-ASCII one to the parser (its existing caller passes an already-serialized href), so for ssh://git@höst:user/repo.git it parsed ssh://g, reported it valid, and the scp-style colon was no longer rewritten, a change from main for that input. The current revision parses the full string and adds that input as a test row.
no test proof · iteration 1 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/cli/install/bun-install.test.ts