Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- `apm update` and `apm install` no longer issue redundant `git clone --depth=1` calls when multiple dependencies share the same `(repo, ref)` tuple. A new four-tier resolver (per-run cache, commits API, bare `rev-parse`, legacy clone) collapses duplicate ref-to-SHA resolutions; on the reporter's 9-dep manifest with 3 unique `(repo, ref)` tuples this cuts the integrate phase from 1583s (Windows + ADO) to seconds. Reused by `install`, `update`, `outdated`, and `publish`. Emergency rollback: `APM_TIERED_RESOLVER=0`. (#1369)
- Fixed `shared/apm.md` matrix fan-out being silently stripped by the GitHub Actions cross-job secret masker when the GitHub App `private-key` was carried in the `apm-prep` matrix output; credentials are now resolved per row via `$GITHUB_ENV` in the `apm` job. (#1373)
- `apm compile --watch` now honors `targets: [claude, cursor]` (and every other multi-target / single-target configuration) on every recompile. Previously the watch path bypassed the resolver the one-shot path uses and let `CompilationConfig.from_apm_yml` fall back to the all-families default, silently regenerating `GEMINI.md` after every file edit. The watch path now resolves the effective target via the same helper the one-shot path uses and forwards it as `target=` into both the initial compile and every debounced recompile. (#1345)
- Fixed hook commands using relative paths that break for Claude target (#1310)
Expand Down
1 change: 1 addition & 0 deletions docs/src/content/docs/reference/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,5 +83,6 @@ These variables exist in the codebase but are not part of the documented contrac
| Variable | Purpose | Default | Notes |
|---|---|---|---|
| `APM_RESOLVE_PARALLEL` | Tunes parallelism in the dependency resolver. | implementation default | Subject to change. |
| `APM_TIERED_RESOLVER` | Set to `0`/`false`/`no`/`off` to disable the tiered git-ref resolver (per-run cache + commits API + bare `rev-parse` + legacy clone) and force every `install`/`update`/`outdated` ref resolution through the legacy shallow-clone path. Emergency rollback for #1369. | `1` (on) | Subject to change. Removal expected once the tiered stack has soaked through a release. |
| `APM_LEGACY_SKILL_PATHS` | Toggles legacy skill-path layout in integration targets. | unset | Compatibility shim; will be removed. |
| `APM_E2E_TESTS` | Marks the process as an end-to-end test run; relaxes some interactive guards. | unset | Test harness only. Do not set in normal use. |
33 changes: 33 additions & 0 deletions src/apm_cli/commands/outdated.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""

import logging
import os
import re
import sys
from dataclasses import dataclass, field
Expand Down Expand Up @@ -328,6 +329,38 @@ def outdated(global_, verbose, parallel_checks):
auth_resolver = AuthResolver()
downloader = GitHubPackageDownloader(auth_resolver=auth_resolver)

# #1369: wire the tiered ref resolver here too -- outdated calls
# downloader.resolve_git_reference() N times across a ThreadPoolExecutor,
# which is exactly the duplicate-resolution workload the L0 cache +
# coalesce lock are designed to collapse.
try:
from ..cache.git_cache import GitCache
from ..cache.paths import get_cache_root
from ..deps.tiered_ref_resolver import build_tiered_ref_resolver

_git_cache = None
if not os.environ.get("APM_NO_CACHE"):
try:
_git_cache = GitCache(get_cache_root(), refresh=False)
downloader.persistent_git_cache = _git_cache
except (OSError, ValueError):
pass
_tiered = build_tiered_ref_resolver(
downloader=downloader,
git_cache=_git_cache,
)
if _tiered is not None:
downloader._tiered_resolver = _tiered
except Exception as exc: # pragma: no cover - never block outdated on resolver wiring
# Non-blocking, but log so --verbose surfaces wiring failures.
import logging as _logging

_logging.getLogger(__name__).debug(
"Tiered ref resolver wiring skipped for outdated (%s): %s",
type(exc).__name__,
exc,
)

# Filter to checkable deps (skip local + Artifactory)
checkable = []
for key, dep in lockfile.dependencies.items():
Expand Down
16 changes: 15 additions & 1 deletion src/apm_cli/deps/github_downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ def __init__(
# Set by the install pipeline; None disables persistent caching.
self.persistent_git_cache = None

# #1369: tiered ref resolver. Attached by resolve.py / outdated.py
# after construction via ``build_tiered_ref_resolver``. When set,
# :meth:`resolve_git_reference` delegates to it before falling
# through to ``self._refs.resolve``. Declared here so the
# attribute is part of the documented downloader surface rather
# than a monkey-patched field.
self._tiered_resolver = None

def _git_env_dict(self) -> dict[str, str]:
"""Return a sanitized git env dict for cache-layer subprocess calls.

Expand Down Expand Up @@ -626,8 +634,14 @@ def resolve_git_reference(
) -> ResolvedReference:
"""Resolve a Git reference (branch/tag/commit) to a specific commit SHA.

Delegates to :class:`GitReferenceResolver`.
Delegates to :class:`TieredRefResolver` when one is attached
(per-run, by the install resolve phase or outdated command) for
the #1369 fast-path; falls through to the legacy
:class:`GitReferenceResolver` otherwise.
"""
tiered = getattr(self, "_tiered_resolver", None)
if tiered is not None:
return tiered.resolve(repo_ref)
return self._refs.resolve(repo_ref)

def _resolve_commit_sha_for_ref(self, dep_ref: DependencyReference, ref: str) -> str | None:
Expand Down
Loading
Loading