fix: preserve ssh:// dependency URLs for Bitbucket Datacenter repositories#665
fix: preserve ssh:// dependency URLs for Bitbucket Datacenter repositories#665edenfunf wants to merge 4 commits intomicrosoft:mainfrom
Conversation
Fixes microsoft#661. When a user specifies an explicit ssh:// URL in apm.yml (e.g. ssh://git@bitbucket.domain.ext:7999/project/repo.git), APM was silently stripping the port during ssh:// → git@ normalisation and then falling back to https:// after the portless SSH clone attempt failed. Store the original ssh:// string in DependencyReference.original_ssh_url before normalisation, and pass it verbatim to git clone in _clone_with_fallback so the port and protocol are preserved.
sergio-sisternes-epam
left a comment
There was a problem hiding this comment.
Clean, focused fix — the "preserve original URL, don't fix normalization" approach is the right call. Minimal risk, no side effects on existing URL handling. The PR description is excellent with clear root cause analysis and before/after examples.
The test coverage is thorough: custom port, standard ssh, https exclusion, git@ exclusion, and clone URL ordering all covered.
One request before merge: please add a CHANGELOG entry under ## [Unreleased]:
### Fixed
- Preserve `ssh://` dependency URLs with custom ports for Bitbucket Datacenter repositories instead of silently falling back to HTTPS (#661)
Thanks for another solid contribution!
There was a problem hiding this comment.
Pull request overview
This PR fixes cloning failures for self-hosted Git servers (notably Bitbucket Datacenter) when dependencies are specified with an explicit ssh://...:PORT/... URL by preserving the original SSH URL and using it for the SSH clone attempt.
Changes:
- Add
DependencyReference.original_ssh_urland populate it when parsingssh://dependency strings. - Update
_clone_with_fallbackto preferoriginal_ssh_urlfor the SSH clone method. - Add regression tests covering parsing and clone URL selection for
ssh://URLs with custom ports.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/models/dependency/reference.py |
Adds original_ssh_url and captures it during parsing for ssh:// inputs. |
src/apm_cli/deps/github_downloader.py |
Uses original_ssh_url directly for the SSH clone attempt to preserve custom ports. |
tests/unit/test_generic_git_urls.py |
Adds parsing regression tests for Bitbucket Datacenter-style ssh:// URLs with ports. |
tests/unit/test_auth_scoping.py |
Adds downloader regression tests to assert the SSH clone attempt uses the original ssh:// URL verbatim. |
Copilot's findings
- Files reviewed: 4/4 changed files
- Comments generated: 3
Raw dependency strings can include '#ref' or '@alias' suffixes which are not valid git clone URL syntax. Strip these from original_ssh_url at capture time so the port-preserving clone path does not silently fail and re-trigger the https fallback. Also narrows the over-broad except clause in regression tests from (RuntimeError, Exception) to (RuntimeError, GitCommandError), and adds the CHANGELOG entry requested in review.
Fixes #661.
Root Cause
When APM parses a dependency URL that starts with
ssh://,_normalize_ssh_protocol_urlconverts it to thegit@host:pathformat and silently drops any custom port:_clone_with_fallbackthen tries SSH on the reconstructed URL (default port 22, wrong) which fails, and falls back to:This affects Bitbucket Datacenter and any other self-hosted git server that uses a non-standard SSH port.
Fix
Add an
original_ssh_urlfield toDependencyReference. Whenparse_from_strencounters a URL that begins withssh://, the verbatim string is stored in this field before the normalisation step.In
_clone_with_fallback, Method 2 (SSH) now usesdep_ref.original_ssh_urldirectly when it is set, instead of rebuilding the URL from host + repo_ref (which loses the port):Before
After
Changes
src/apm_cli/models/dependency/reference.pyoriginal_ssh_urldataclass field; capture it inparse_from_strbefore normalisation; pass to constructorsrc/apm_cli/deps/github_downloader.pydep_ref.original_ssh_urlverbatim in SSH clone attempt when presenttests/unit/test_generic_git_urls.pyTestBitbucketDatacenterSSHclass with 5 regression testsRegression Protection
Added
TestBitbucketDatacenterSSHcovering:ssh://with custom port stores the original URL inoriginal_ssh_urlrepo_urlfields are still correctly parsedssh://without a port also preserves the original URLoriginal_ssh_urlgit@shorthand URLs do not setoriginal_ssh_urlAll 3 763 existing unit tests continue to pass.