Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/apm_cli/marketplace/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
import requests

from .errors import MarketplaceFetchError
from .models import MarketplaceManifest, MarketplacePlugin, MarketplaceSource, parse_marketplace_json
from .models import (
MarketplaceManifest,
MarketplacePlugin,
MarketplaceSource,
parse_marketplace_json,
)
from .registry import get_registered_marketplaces

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -173,7 +178,9 @@ def _try_proxy_fetch(
except (json.JSONDecodeError, ValueError):
logger.debug(
"Proxy returned non-JSON for %s/%s %s",
source.owner, source.repo, file_path,
source.owner,
source.repo,
file_path,
)
return None

Expand Down Expand Up @@ -213,7 +220,9 @@ def _fetch_file(
if cfg is not None and cfg.enforce_only:
logger.debug(
"PROXY_REGISTRY_ONLY blocks direct GitHub fetch for %s/%s %s",
source.owner, source.repo, file_path,
source.owner,
source.repo,
file_path,
)
return None

Expand All @@ -229,6 +238,12 @@ def _do_fetch(token, _git_env):
headers["Authorization"] = f"token {token}"
resp = requests.get(url, headers=headers, timeout=30)
if resp.status_code == 404:
if not token:
# Unauthenticated 404 is ambiguous: could be a genuinely
# missing file *or* a private repo hiding its existence.
# Raise so that ``try_with_fallback`` retries with a token.
resp.raise_for_status()
Comment on lines 240 to +245
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description and new tests describe an unauthenticated-first flow (unauth 404, then authenticated retry) to preserve rate-limit optimization for public repos, but the current implementation details shown elsewhere indicate try_with_fallback(..., unauth_first=False) (auth-first). Since this 404-raise logic only helps if an unauthenticated attempt happens before an authenticated one, please align the actual try_with_fallback call behavior with the PR description/tests (either switch to unauth-first + this exception strategy, or update the description/tests if auth-first is the intended behavior).

Copilot uses AI. Check for mistakes.
# Authenticated 404 means the file genuinely does not exist.
return None
Comment on lines 239 to 247
Copy link

Copilot AI Apr 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raising on unauthenticated 404s can break the documented contract that 404 => None when no credentials are available (e.g., public repo where the candidate path genuinely doesn’t exist). This can also break _auto_detect_path probing because a missing first candidate would raise instead of returning None to continue to the next path. A concrete fix is to translate an ultimate 404 exception (after try_with_fallback has exhausted available credentials) back into None in _fetch_file (e.g., catch requests.HTTPError, inspect exc.response.status_code == 404, and return None), while still allowing the initial unauthenticated 404 to raise to trigger the authenticated retry.

Copilot uses AI. Check for mistakes.
resp.raise_for_status()
return resp.json()
Expand Down
Loading
Loading