fix(install): support FQDN monorepo subpath on GHES hosts (closes #1673)#1684
fix(install): support FQDN monorepo subpath on GHES hosts (closes #1673)#1684sergio-sisternes-epam wants to merge 2 commits into
Conversation
#1673) Extend is_github_hostname() to check the GITHUB_HOST env var, mirroring the GHES detection already present in AuthResolver.classify_host(). This closes the classification gap that caused _detect_virtual_package() and _resolve_shorthand_to_parsed_url() to treat configured GHES hosts as generic hosts -- embedding subpaths into the git URL instead of splitting into git: + path:. Before: ghe.example.com/org/repo/packages/skill -> repo_url='org/repo/packages/skill', virtual_path=None (broken) After (with GITHUB_HOST=ghe.example.com): -> repo_url='org/repo', virtual_path='packages/skill' (correct) Guards against false positives: - ADO hosts (dev.azure.com, *.visualstudio.com) excluded - gitlab.com excluded - Invalid FQDNs rejected - Only activates when GITHUB_HOST matches the hostname Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes GHES monorepo-subpath shorthand parsing by extending is_github_hostname() to recognize a custom GitHub Enterprise Server host configured via GITHUB_HOST, so inputs like ghe.example.com/org/repo/packages/skill split into repo_url=org/repo and virtual_path=packages/skill rather than embedding the subpath into the clone URL.
Changes:
- Extend
is_github_hostname()to treat theGITHUB_HOSTvalue as a GitHub-family hostname (with guards for ADO/GitLab/FQDN validity). - Add unit tests for GHES hostname classification via
GITHUB_HOST. - Add end-to-end parsing tests for GHES FQDN shorthand with virtual subpaths (including
#ref).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/apm_cli/utils/github_host.py | Adds GITHUB_HOST-aware GHES detection to is_github_hostname() to enable owner/repo vs subpath splitting for GHES FQDN shorthand. |
| tests/unit/test_github_host.py | Adds focused tests validating GHES hostname classification behavior via GITHUB_HOST (and negative guards). |
| tests/unit/test_generic_git_urls.py | Adds parsing tests verifying GHES FQDN shorthand correctly yields repo_url + virtual_path when GITHUB_HOST matches. |
Drop .strip().split('/')[0] from the GITHUB_HOST env var check in
is_github_hostname() and use .lower() only, matching the normalization
in AuthResolver.classify_host(). This prevents a misconfigured value
like GITHUB_HOST=ghe.example.com/extra from being accepted at parse
time but rejected at install time.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
APM Review Panel:
|
| Persona | B | R | N | Takeaway |
|---|---|---|---|---|
| Python Architect | 0 | 1 | 1 | Correct targeted fix; GHES env detection now duplicated 5 times with divergent guard sets -- extract _is_ghes_configured_host() helper as follow-up. |
| CLI Logging Expert | 0 | 2 | 1 | No user-facing output paths changed; two silent failure modes exposed by the new GITHUB_HOST lookup deserve diagnostic coverage. |
| DevX UX Expert | 0 | 2 | 1 | Fix works for GITHUB_HOST-aware users; silent misbehavior and missing docs leave unaware GHES users with no recovery path. |
| Supply Chain Security | 0 | 2 | 2 | PR is sound in scope; two cross-cutting normalization inconsistencies with classify_host() should be fixed before merge to prevent silent auth fallback on misconfigured GITHUB_HOST values. |
| OSS Growth Hacker | 0 | 1 | 2 | Solid GHES enterprise fix missing CHANGELOG entry; unlocks a high-value enterprise segment but the story stays invisible at upgrade time without a changelog line. |
| Auth Expert | 0 | 2 | 2 | Parse-time fix is correct for happy path; classify_host() diverges on strip/split normalization, creating a narrow misroute for malformed GITHUB_HOST values. |
| Doc Writer | 0 | 3 | 1 | CHANGELOG missing fix entry; GITHUB_HOST env-var doc and FQDN shorthand dep guide both omit new virtual-path-split behavior for GHES; docstring references are accurate. |
| Test Coverage Expert | 0 | 1 | 1 | 17 unit-tier tests cover parse path well; install-pipeline integration floor not met; GITHUB_HOST scheme-prefix guard gap untested. |
B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.
Top 5 follow-ups
-
[Auth Expert] Align
classify_host()GITHUB_HOST normalization to.strip().lower().split('/')[0]to matchis_github_hostname()-- Flagged independently by supply-chain-security, auth-expert, and cli-logging: whitespace or path-component in GITHUB_HOST causes URL parsing to succeed as GHES while token routing fails as generic, producing silent auth breakage after a fix that appears to work. One-line change inauth.py:262. Highest-signal pre-merge ask. -
[OSS Growth Hacker] Add CHANGELOG entry under
[Unreleased] ### Fixedcrediting the contributor and closing apm install: FQDN monorepo subpath on GHES fails; hoping for CLI-only install without manual apm.yml edit #1673 -- Without a CHANGELOG entry the fix is invisible to enterprise GHES users at upgrade time -- the exact audience that would convert on this fix. Zero-effort, high-visibility pre-merge ask. -
[Test Coverage Expert] Add integration test for
apm installGHES FQDN subpath throughAPMPackage.from_apm_yml()(outcome: missing, tier: integration-with-fixtures) -- No test pins the end-to-end contractdep.repo_url == 'org/repo'anddep.virtual_path == 'packages/skill'through the full install path. Future refactors can silently regress the fix with no automated catch. -
[DevX UX Expert] Emit an actionable error naming
GITHUB_HOSTwhen a GHES FQDN subpath install falls into the generic-host branch -- WithoutGITHUB_HOSTset, the user receives a cryptic git clone failure with no mention of the required env var. APM already has scaffolding for env-var hints viaunsupported_host_error(). -
[Python Architect] Extract a module-private
_is_ghes_configured_host()helper inutils/github_host.pyto unify five diverging GHES env-detection copies -- The ADO guard added in this PR is correct but creates a detectable inconsistency across five call sites with divergent guard sets. The next change to GHES detection will again diverge without a single source of truth.
Architecture
classDiagram
direction LR
class GithubHostUtils {
<<IOBoundary>>
+is_github_hostname(hostname) bool
+is_azure_devops_hostname(hostname) bool
+is_gitlab_hostname(hostname) bool
+is_valid_fqdn(hostname) bool
+has_github_gitlab_host_env_conflict(hostname) bool
}
note for GithubHostUtils "utils/github_host.py -- GHES env check added to is_github_hostname (PR, has ADO guard). Pre-existing sibling copies in is_gitlab_hostname and has_github_gitlab_host_env_conflict lack the ADO guard. AuthResolver.classify_host and DownloadDelegate._is_configured_ghes are further diverging copies."
class DependencyReference {
<<ValueObject>>
+host str
+repo_url str
+virtual_path str
+is_virtual bool
+parse(dep_str) DependencyReference
+_detect_virtual_package(dep_str)
+_resolve_virtual_shorthand_repo(repo_url, host, virtual_path)
}
class AuthResolver {
<<Strategy>>
+classify_host(host, port) HostInfo
}
class DownloadDelegate {
+_is_configured_ghes(host) bool
}
DependencyReference ..> GithubHostUtils : is_github_hostname, is_azure_devops_hostname
AuthResolver ..> GithubHostUtils : is_azure_devops_hostname, is_valid_fqdn
DownloadDelegate ..> GithubHostUtils : is_github_hostname
class GithubHostUtils:::touched
classDef touched fill:#fff3b0,stroke:#d47600
flowchart TD
A["apm install ghe.example.com/org/repo/packages/skill"] --> B
B["DependencyReference.parse(dep_str)"] --> C
C["_detect_virtual_package\nextracts hostname=ghe.example.com"] --> D
D{"is_github_hostname(ghe.example.com)"}
D -- "h==github.com? No\nh.endswith(.ghe.com)? No" --> E
E["os.environ.get(GITHUB_HOST)"] --> F
F{"ghes_host==h AND\nnot is_azure_devops_hostname AND\nis_valid_fqdn?"}
F -- "env unset or mismatch: False\npre-PR behavior" --> G
F -- "env matches + guards pass: True\npost-PR fix" --> H
G["is_generic_host=True\nmin_base_segments=4"] --> J
J["is_virtual=False\nBUG: repo_url embeds packages/skill"]
H["is_generic_host=False\nmin_base_segments=2"] --> L
L["is_virtual=True\nFIX: repo_url=org/repo, virtual_path=packages/skill"]
Recommendation
Approve and merge after the author completes two pre-merge asks: (1) add a CHANGELOG entry under [Unreleased] ### Fixed crediting @sergio-sisternes-epam and closing #1673, and (2) apply the one-line normalization fix in classify_host() at auth.py:262 (.strip().lower().split('/')[0]) to prevent the parse/auth misroute on malformed GITHUB_HOST values. All other findings -- integration test floor, actionable error UX, env-detection deduplication, docs expansion -- are tracked as follow-up issues rather than merge blockers. The unit-tier test coverage is strong, the fix is narrow and correct, and the contributor is an enterprise adopter whose PR should close promptly: a contributor kept waiting is a worse outcome than a follow-up issue left open.
Full per-persona findings
Python Architect
-
[recommended] GHES env detection pattern is duplicated 5 times with divergent guard sets at
src/apm_cli/utils/github_host.py:190
The patternos.environ.get("GITHUB_HOST", "").strip().lower().split("/")[0]now appears in: (1)is_github_hostname()[PR, has ADO guard], (2)is_gitlab_hostname()[no ADO guard], (3)has_github_gitlab_host_env_conflict()[no ADO guard], (4)AuthResolver.classify_host()[no ADO guard, no .strip()/.split()], (5)DownloadDelegate._is_configured_ghes()[no guards at all]. The ADO guard added in this PR is correct but creates a detectable inconsistency. Future GHES detection changes need coordinated edits in 5 places.
Suggested: Extract a module-private helper_is_ghes_configured_host(hostname: str) -> boolencapsulating the guard set. Call it fromis_github_hostname(), and use the negation inis_gitlab_hostname()andhas_github_gitlab_host_env_conflict()to also gain the ADO guard. -
[nit]
is_github_hostname()does not strip path segments from incoming hostname;is_gitlab_hostname()does atsrc/apm_cli/utils/github_host.py:184
is_gitlab_hostname()normalises the incoming hostname withh = hostname.strip().lower().split("/")[0]butis_github_hostname()usesh = hostname.lower()without stripping. Quiet sibling inconsistency.
Suggested: Changeh = hostname.lower()toh = hostname.strip().lower().split("/")[0].
CLI Logging Expert
-
[recommended] Silent fallback when GITHUB_HOST fails
is_valid_fqdn()with no user diagnostic atsrc/apm_cli/utils/github_host.py:190
is_github_hostname()now checksGITHUB_HOSTbut returnsFalsesilently when the value failsis_valid_fqdn(). If a user setsGITHUB_HOSTto a bare label (e.g.localhost) the FQDN guard rejects it, the host is treated as generic, and the user gets a cryptic git clone error with no indication thatGITHUB_HOSTwas seen but rejected.
Suggested: Whenghes_hostis non-empty butis_valid_fqdn(ghes_host)isFalse, emit a_rich_warning:'[!] GITHUB_HOST value is not a valid FQDN -- custom GHES host not recognised. Check your GITHUB_HOST setting.' -
[recommended]
_is_configured_ghesdoes not strip path fromGITHUB_HOST, creating parse/auth inconsistency atsrc/apm_cli/deps/download_strategies.py:945
The newis_github_hostname()applies.split('/')[0]to strip path segments fromGITHUB_HOST. But_is_configured_ghesindownload_strategies.pydoes not. IfGITHUB_HOST=ghe.example.com/api/v3,is_github_hostnamereturnsTrue(parse succeeds, subpath splits correctly) but_is_configured_ghesreturnsFalse(auth token not forwarded on download). Before this PR both returnedFalse-- consistent. After this PR they diverge.
Suggested: Apply the same.split('/')[0].strip()normalization to_is_configured_ghes. -
[nit] No
--verbosetrace at the GHES recognition branch in_detect_virtual_packageatsrc/apm_cli/models/dependency/reference.py:1192
Whenis_github_hostname(validated_host)returnsTruebecauseGITHUB_HOSTmatched, there is no verbose-mode log of that decision.
Suggested: Add a--verboselog entry afteris_github_hostname()returnsTruein_detect_virtual_package.
DevX UX Expert
-
[recommended] No actionable error emitted when
GITHUB_HOSTis missing for a GHES FQDN subpath install atsrc/apm_cli/models/dependency/reference.py:1227
A user who typesapm install ghe.example.com/org/repo/packages/skillwithoutGITHUB_HOSTset falls into the generic-host branch where all segments are embedded in the clone URL. The downstream git failure says "repository not found" with no mention ofGITHUB_HOST. APM already has scaffolding for actionable env-var hints viaunsupported_host_error().
Suggested: In theis_generic_hostbranch, whenlen(path_segments) >= 4and the host is a valid unclassified FQDN, raise aValueError:'FQDN shorthand with a monorepo subpath on a GHES host requires GITHUB_HOST. Set GITHUB_HOST=<host> so APM can split the repo boundary from the package subpath, then retry.' -
[recommended] Docs not updated: FQDN subpath-splitting dependency on
GITHUB_HOSTis undocumented atdocs/src/content/docs/reference/cli/install.md:20
docs/src/content/docs/reference/cli/install.mddescribes FQDN shorthand ashost/owner/repowith no mention that monorepo subpaths on GHES silently requireGITHUB_HOSTto split correctly.
Suggested: Append a sentence to thePACKAGE_REFdescription covering GHES FQDN subpath withGITHUB_HOSTrequirement. Also add a parallel note to theGITHUB_HOSTrow inenvironment-variables.md. -
[nit] GHES-precedence guard in
is_gitlab_hostnamemissingis_azure_devops_hostnamecheck atsrc/apm_cli/utils/github_host.py:69
Asymmetry betweenis_github_hostname()(has ADO guard) andis_gitlab_hostname()(does not). Maintenance trap for future readers comparing the two functions.
Suggested: Addand not is_azure_devops_hostname(ghes_host)inis_gitlab_hostname()GHES-precedence block.
Supply Chain Security Expert
-
[recommended]
GITHUB_HOSTwhitespace not stripped inclassify_host(), causing parse/auth split on GITHUB_HOST with leading or trailing spaces atsrc/apm_cli/core/auth.py:262
is_github_hostname()applies.strip()before matchingGITHUB_HOST.classify_host()readsos.environ.get('GITHUB_HOST', '').lower()with no strip. ForGITHUB_HOSTwith accidental surrounding whitespace,is_github_hostnamereturnsTruebutclassify_hostreturnskind='generic'. Result: correct URL parsing, silent auth failure with no diagnostic.
Suggested: Change toghes_host = os.environ.get('GITHUB_HOST', '').strip().lower()atauth.py:262. -
[recommended] Path-strip divergence between
is_github_hostname()andclassify_host()creates silent GHES parse/auth mismatch whenGITHUB_HOSTcontains a path component atsrc/apm_cli/utils/github_host.py:190
is_github_hostname()strips path components via.split('/')[0].classify_host()does not. WithGITHUB_HOST=ghe.corp.com/api/v3: parse sees GHES (correct), auth sees generic (wrong). Silent functional auth breakage with no error.
Suggested: Remove.split('/')[0]fromis_github_hostname()so both functions consistently reject path-componentGITHUB_HOSTvalues, OR add the same normalization toclassify_host(). -
[nit]
_supports_gh_cli_host()intoken_manager.pynow has unreachable code forGITHUB_HOSThosts afteris_github_hostname()returnsTrueearly atsrc/apm_cli/core/token_manager.py:156
Harmless dead code. Track as cleanup candidate in a follow-up. -
[nit] Exclusion denylist incomplete: non-GitHub FQDN git hosts accidentally set as
GITHUB_HOSTreceive GitHub tokens atsrc/apm_cli/utils/github_host.py:168
Pre-existing design consequence, not introduced here. Docstring should warn explicitly.
Suggested: AddWARNINGblock in docstring:GITHUB_HOSTmust be set only for actual GHES instances; setting it to any other git host will cause APM to route GitHub PATs to that host.
OSS Growth Hacker
-
[recommended] No CHANGELOG entry added for this fix at
CHANGELOG.md
The[Unreleased] ### Fixedsection has entries for apm install never updates existing .claude/rules/*.md from local .apm/instructions/ (deployed rule files missing from lockfile local_deployed_files) #1662, Audit: auth.py exception cascade -- 15 clauses need security-aware review #935/refactor(auth): debug-log all exception handlers in credential cascade (closes #935) #1664 but nothing for apm install: FQDN monorepo subpath on GHES fails; hoping for CLI-only install without manual apm.yml edit #1673. The fix is invisible to enterprise GHES users at upgrade time.
Suggested: Add under[Unreleased] ### Fixed:- apm install now correctly splits FQDN monorepo subpath shorthand (e.g. ghe.example.com/org/repo/packages/skill) when GITHUB_HOST matches the queried host. (by @sergio-sisternes-epam, closes #1673) -
[nit] GHES FQDN subpath usage is not illustrated in the environment-variables doc at
docs/src/content/docs/reference/environment-variables.md
GITHUB_HOSTis documented but gives no example of the FQDN shorthand with subpath form the fix enables.
Suggested: Add an example:GITHUB_HOST=ghe.example.com apm install ghe.example.com/org/repo/packages/skill -
[nit] Gitea/Bitbucket/generic-host gap not tracked as a follow-up issue
The PR body defers generic host FQDN subpath support with no linked follow-up issue. Without a tracking issue, this gap silently dies.
Suggested: Open a follow-up issue titledfeat(install): probe-based FQDN subpath split for non-GHES generic git hosts (Gitea, Bitbucket Server).
Auth Expert
-
[recommended]
classify_host()andis_github_hostname()normalizeGITHUB_HOSTdifferently; diverge on whitespace or embedded-path values atsrc/apm_cli/core/auth.py:262
classify_host()atauth.py:262does.lower()only. The newis_github_hostname()does.strip().lower().split('/')[0]. ForGITHUB_HOSTwith leading/trailing whitespace or embedded path,is_github_hostnamereturnsTruewhileclassify_hostreturnskind='generic'. Token routing goes entirely throughclassify_host(), so correct URL parsing and wrong auth is the result. Contradicts the PR body claim that this mirrors existing GHES detection inclassify_host().
Suggested: Apply the same normalization inclassify_host():ghes_host = os.environ.get('GITHUB_HOST','').strip().lower().split('/')[0] -
[recommended] No test asserts
is_github_hostname()andclassify_host()agree for the same GHES host attests/unit/test_github_host.py:484
The fix's value depends on parse-time and auth-time routing reaching the same conclusion. No test pins this cross-module invariant. Future drift between the two paths would go undetected.
Suggested: Add a test importingAuthResolverand assertingclassify_host()returnskind='ghes'for the sameGITHUB_HOSTconfiguration. -
[nit]
DownloadDelegate._is_configured_ghes()is now largely superseded byis_github_hostname()but theORremains atsrc/apm_cli/deps/download_strategies.py:725
TheORon line 725 can still returnTruefor an ADO host when_is_configured_ghesfires (weaker guards). Track as cleanup candidate after this PR lands. -
[nit]
GITHUB_HOSTwith a port (e.g.ghe.example.com:8080) silently fails the equality check atsrc/apm_cli/utils/github_host.py:190
.split('/')[0]strips path separators but not colons. No test documents this known limitation.
Suggested: Add a test assertingis_github_hostname('ghe.example.com')isFalsewhenGITHUB_HOST='ghe.example.com:8080'.
Doc Writer
-
[recommended] No CHANGELOG entry for the GHES FQDN monorepo subpath fix (closes apm install: FQDN monorepo subpath on GHES fails; hoping for CLI-only install without manual apm.yml edit #1673) at
CHANGELOG.md
The[Unreleased] Fixedsection has two entries but nothing for apm install: FQDN monorepo subpath on GHES fails; hoping for CLI-only install without manual apm.yml edit #1673. This is a user-visible behavior change that GHES operators will look for in the changelog when assessing whether to upgrade.
Suggested: Add under[Unreleased] ### Fixed:`apm install` now correctly splits FQDN shorthand with monorepo subpaths (e.g. `ghe.example.com/org/repo/packages/skill`) into `git: org/repo` + `path: packages/skill` for GHES hosts configured via `GITHUB_HOST`. Previously the subpath was embedded in the git URL, causing install failures. (by @sergio-sisternes-epam, closes #1673) -
[recommended]
GITHUB_HOSTrow inenvironment-variables.mddoes not mention FQDN dependency parsing atdocs/src/content/docs/reference/environment-variables.md
After this fix,GITHUB_HOSTalso gates whether FQDN shorthand dependency strings split intorepo_url + virtual_path. A GHES admin who reads only the env-var reference will not discover this requirement.
Suggested: Extend the Notes cell:"Process-wide; affects clone URLs, host detection, and FQDN shorthand dependency parsing (enables host/org/repo/virtual-path to split correctly into repo + virtual path for monorepo subpaths)." -
[recommended]
dependencies.mdFQDN shorthand block omits GHES virtual-path behavior and may mislead withnon-GitHub hostscomment atpackages/apm-guide/.apm/skills/apm-usage/dependencies.md
The comment# FQDN shorthand (non-GitHub hosts keep the domain)implies GHES (a GitHub host) cannot use FQDN form, which is now incorrect after this fix.
Suggested: Add a GHES sub-block with examples (requiresGITHUB_HOSTset) and revise the comment to distinguish GHES from truly generic hosts. -
[nit] GHES section in
authentication.mdshowsowner/reposhorthand but not FQDN monorepo form atpackages/apm-guide/.apm/skills/apm-usage/authentication.md
Now that FQDN monorepo shorthand resolves correctly, a cross-reference here would help discoverability.
Suggested: Append FQDN monorepo example to the GHES block with a note thatGITHUB_HOSTmust be set for the virtual-path split to work.
Test Coverage Expert
-
[recommended] Install-pipeline integration floor missing: no fixture-backed test runs
apm installfor a GHES FQDN subpath dependency end-to-end attests/integration/test_ghes_fqdn_subpath_install.py
Evidence: missing, integration-with-fixtures -- proves:apm install ghe.example.com/org/repo/packages/skillinstallspackages/skillsubpath, not the whole repo as a single path component.
assert dep.repo_url == 'org/repo' and dep.virtual_path == 'packages/skill' and dep.is_virtual is True
Probedtests/integration/-- zero GHES FQDN subpath integration tests found. The user-facing promise lives in the full install pipeline, not only inDependencyReference.parse().
Suggested: Add@pytest.mark.integrationclassTestGHESFQDNSubpathInstallPipelinethat stubs the git-clone seam and verifies the end-to-end contract throughAPMPackage.from_apm_yml(). No PAT required. -
[nit] No test covers
GITHUB_HOSTset with a URL scheme prefix ((ghe.example.com/redacted)), which silently fails GHES recognition attests/unit/test_github_host.py
Evidence: missing, unit --GITHUB_HOSTset withhttps://prefix does not silently enable GHES for the wrong host value.
monkeypatch.setenv('GITHUB_HOST', '(ghe.example.com/redacted)'); assert github_host.is_github_hostname('ghe.example.com') is False
All 10 new unit tests verified present -- none cover scheme-prefixedGITHUB_HOST.
Suggested: Add a test documenting current behavior; optionally add a guard detecting scheme prefix and raisingValueErrorwith an actionable message.
This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
pypi.org
To allow these domains, add them to the
network.allowedlist in your workflow frontmatter:
network:
allowed:
- defaults
- "pypi.org"See Network Configuration for more information.
Generated by PR Review Panel for issue #1684 · sonnet46 20.4M · ◷
TL;DR
Extend
is_github_hostname()to recognise custom GitHub Enterprise Server hosts configured viaGITHUB_HOST, so FQDN shorthand with monorepo subpaths (e.g.ghe.example.com/org/repo/packages/skill) correctly splits intogit: org/repo+path: packages/skillinstead of embedding the subpath into the git URL.Problem
When installing a monorepo subpath package from a GHES host using FQDN shorthand:
The parser treats ALL path segments as the git repository URL, producing an invalid clone URL (
https://ghe.example.com/org/repo.git/packages/skill/). This works ongithub.combecauseis_github_hostname()returnsTruefor it, triggering a 2-segmentowner/reposplit. Custom GHES hosts fall through to the generic-host branch which treats all segments as repo path.Root cause
Classification gap between two host-detection paths:
is_github_hostname()(parse-time) -- only matchesgithub.comand*.ghe.comAuthResolver.classify_host()(install-time) -- also checksGITHUB_HOSTenv var and correctly returnskind="ghes"This means
_detect_virtual_package()setsmin_base_segments = len(path_segments)for GHES hosts, and_resolve_shorthand_to_parsed_url()includes all segments inuser_repo.Fix
Add a
GITHUB_HOSTenv var check tois_github_hostname(), mirroring the GHES detection inclassify_host(). Guards against false positives:dev.azure.com,*.visualstudio.com) excludedgitlab.comexcluded*.ghe.comexcluded (already matched by earlier check)is_valid_fqdn()GITHUB_HOSTvalue matches the queried hostnameBefore (with
GITHUB_HOST=ghe.example.com):After:
Test evidence
is_github_hostname()GHES classification (positive + negative guards)Files changed
src/apm_cli/utils/github_host.pyGITHUB_HOSTenv var check tois_github_hostname()tests/unit/test_github_host.pyTestIsGitHubHostnameGHESclass (10 tests)tests/unit/test_generic_git_urls.pyTestGHESFQDNSubpathParsingclass (7 tests)Out of scope
Generic boundary probe for non-GHES hosts (Gitea, Bitbucket, etc.) where
GITHUB_HOSTis not set. These hosts still require the dict format ({git: ..., path: ...}). A probe-based solution for truly generic hosts is a separate design effort.Fixes: #1673