feat(deps): update revision pins#1738
Open
danielmeppiel wants to merge 3 commits into
Open
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds first-class handling for full 40-character SHA revision pins in the dependency workflow so that apm update / apm outdated can treat them as updateable (by comparing against the commit behind the latest annotated stable semver tag) while preserving the “no branch / no lightweight tag” fence.
Changes:
- Introduces a revision-pin resolver + manifest rewriter that maps full SHA pins to the latest annotated semver tag’s commit and annotates
apm.ymllines with# <tag>. - Extends git remote ref parsing to distinguish annotated vs lightweight tags (via peeled
^{}refs) and uses that in bothupdateandoutdated. - Updates lockfile regeneration to preserve existing
resolved_tagmetadata for unchanged SHA pins, plus adds unit coverage and docs updates.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/deps/revision_pins.py |
New helper module for detecting full-SHA pins, selecting latest annotated stable semver tag, rendering a plan, and rewriting apm.yml atomically. |
src/apm_cli/commands/update.py |
Adds pre-update revision-pin resolution / consent flow, applies manifest rewrite, and annotates lockfile with resolved_tag. |
src/apm_cli/commands/outdated.py |
Adds SHA-pin check path to compare pinned SHA against latest annotated semver tag commit. |
src/apm_cli/deps/git_remote_ops.py |
Detects annotated tags by tracking peeled refs/tags/<tag>^{} output and sets RemoteRef.annotated. |
src/apm_cli/models/dependency/types.py |
Extends RemoteRef with an annotated: bool = False field. |
src/apm_cli/utils/yaml_io.py |
Adds a sibling-temp-file + os.replace helper for atomic text replacement. |
src/apm_cli/install/phases/lockfile.py |
Preserves resolved_tag across installs when SHA pins are unchanged. |
tests/unit/deps/test_revision_pin_resolver.py |
New unit tests for peeled-tag detection, annotated-only selection, prerelease skipping, and atomic manifest rewrite rollback. |
tests/unit/commands/test_update_command.py |
Adds unit tests for revision-pin dry-run behavior, --yes apply path, non-TTY abort, and lockfile tag annotation. |
tests/unit/test_outdated_command.py |
Adds unit coverage for SHA pins reporting against latest annotated tag candidates. |
packages/apm-guide/.apm/skills/apm-usage/dependencies.md |
Documents new SHA-pin update behavior and tag annotation. |
packages/apm-guide/.apm/skills/apm-usage/commands.md |
Updates apm update / apm outdated command docs to mention SHA-pin semantics. |
docs/src/content/docs/reference/cli/update.md |
Documents revision-pin behavior and --dry-run semantics. |
docs/src/content/docs/reference/cli/outdated.md |
Documents SHA-pin comparison against annotated tags. |
docs/src/content/docs/enterprise/security.md |
Extends security model docs to describe revision-pin update fences + lockfile tagging. |
docs/src/content/docs/consumer/manage-dependencies.md |
Adds user-facing explanation + example of SHA pin rewrite and # <tag> annotation. |
CHANGELOG.md |
Adds an Unreleased entry for the revision-pin support. |
Copilot's findings
- Files reviewed: 17/17 changed files
- Comments generated: 8
Comment on lines
+456
to
+457
| if dry_run and revision_pin_updates: | ||
| return |
Comment on lines
+470
to
+495
| @@ -351,6 +489,10 @@ def _plan_callback(plan: UpdatePlan) -> bool: | |||
| ) | |||
| return False | |||
|
|
|||
| if revision_pin_updates: | |||
| plan_state["proceeded"] = True | |||
| return True | |||
|
|
|||
|
|
||
| def _resolve_and_maybe_apply_revision_pin_updates( | ||
| *, | ||
| all_declared_deps, |
| current_ref: str, | ||
| package_name: str, | ||
| package_basename: str, | ||
| remote_refs, |
|
|
||
| def resolve_revision_pin_updates( | ||
| dependencies: Iterable[DependencyReference], | ||
| downloader, |
Comment on lines
+200
to
+202
| def dependency_ref_from_locked(locked) -> DependencyReference: | ||
| """Rebuild a DependencyReference suitable for authoritative upstream checks.""" | ||
| return locked.to_dependency_ref() |
Comment on lines
+133
to
+136
| lines = ["[i] Revision pin updates for apm.yml", ""] | ||
| for update in ordered: | ||
| lines.append(f" [~] {update.display_name}") | ||
| lines.append(f" ref: {update.old_sha[:7]} -> {update.new_sha[:7]} (# {update.tag})") |
|
|
||
| ### Added | ||
|
|
||
| - `apm update` and `apm outdated` now understand full-SHA revision pins, resolving the latest annotated semver tag SHA and annotating updated manifest pins with the tag. (#1209) |
Folds copilot-pull-request-reviewer follow-ups: --dry-run prints full plan, preserve consent gate, type hints, STATUS_SYMBOLS, changelog PR number. Refs #1209 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(deps): update revision pins
TL;DR
apm updatenow has an additive SHA-pin path: it resolves full 40-character revision pins to the latest annotated semver tag SHA, rewritesapm.ymlwith a# <tag>annotation, and then runs the existing install/update pipeline.apm outdatednow reports SHA-pinned deps against that same annotated-tag target instead of treating them as unknown branch/commit refs. The capability-surface behavior-diff idea from the issue discussion is deliberately out of scope for this PR.Note
Closes #1209. The board-approved fences are preserved: authoritative upstream ref fetch, branch/lightweight-tag rejection, atomic manifest writes, and no behavior-diff tooling.
Problem (WHY)
apm outdated"no longer offers updates for those revision-pinned dependencies" ([FEATURE] Update revision-pins for external dependencies #1209).Approach (WHAT)
git ls-remoterefs.apm updateinstall path only when direct manifest deps contain full-SHA refs.apm.ymlthrough a sibling temp file andos.replace, then reload the manifest before install.--dry-runnever writes.apm.lock.yamlafter the install pipeline writes the new SHA.apm outdatedto compare full-SHA pins against the same annotated-tag candidate.Implementation (HOW)
src/apm_cli/deps/revision_pins.py# <tag>.src/apm_cli/commands/update.pysrc/apm_cli/commands/outdated.pysrc/apm_cli/deps/git_remote_ops.py+src/apm_cli/models/dependency/types.pyannotatedbit on remote tag refs by detecting peeledrefs/tags/<tag>^{}lines fromgit ls-remote.src/apm_cli/utils/yaml_io.pysrc/apm_cli/install/phases/lockfile.pyresolved_tagmetadata for unchanged SHA pins during normal lockfile regeneration.tests/unit/deps/test_revision_pin_resolver.pytests/unit/commands/test_update_command.py--yesmanifest rewrite, non-TTY abort, and lockfile tag annotation failure checks.tests/unit/test_outdated_command.pypackages/apm-guide/.apm/skills/apm-usage/*+CHANGELOG.mdDiagrams
Legend: the dashed nodes are the new revision-pin path layered ahead of the existing update pipeline.
flowchart LR subgraph Detect[Detect] A["apm.yml full SHA pin"] --> B["git ls-remote upstream refs"] B --> C{"stable annotated semver tag?"} end subgraph Apply[Apply] C -->|"yes"| D["atomic apm.yml rewrite with tag comment"] C -->|"no"| E["fail closed"] D --> F["existing install update pipeline"] F --> G["record resolved_tag in lockfile"] end classDef new stroke-dasharray: 5 5; class B,C,D,G new;Trade-offs
rcupgrades for SHA pins.apm.ymldiffs.Benefits
apm update --dry-runplan instead of staying stale silently.apm.ymldiffs show both the immutable SHA and the human-readable tag (# vX.Y.Z) for review.apm outdatedreports full-SHA pins using the same annotated-tag rule as update, so preview and apply agree.Validation
uv run --extra dev pytest tests/unit/deps/test_revision_pin_resolver.py tests/unit/test_outdated_command.py tests/unit/commands/test_update_command.py -q:Full validation commands
uv run --extra dev pytest tests/unit tests/test_console.py -q:uv run --extra dev ruff check src/ tests/ && uv run --extra dev ruff format --check src/ tests/ && ... && bash scripts/lint-auth-signals.sh:Scenario Evidence
apm update --dry-runpreviews a SHA/tag rewrite without changingapm.yml.tests/unit/commands/test_update_command.py::TestUpdateDryRun::test_dry_run_renders_revision_pin_plan_without_writing_manifestapm update --yesrewrites the SHA pin, annotates it with the tag, and records the tag in the lockfile.tests/unit/commands/test_update_command.py::TestUpdateAssumeYes::test_yes_updates_revision_pin_manifest_before_installtests/unit/commands/test_update_command.py::TestRevisionPinLockfileAnnotation::test_annotates_lockfile_with_resolved_tagtests/unit/deps/test_revision_pin_resolver.py::test_latest_revision_pin_tag_ignores_branches_and_lightweight_tagstests/unit/deps/test_revision_pin_resolver.py::test_latest_revision_pin_tag_ignores_prereleases_by_defaulttests/unit/deps/test_revision_pin_resolver.py::test_apply_revision_pin_updates_keeps_old_pin_when_replace_failsapm outdatedreports SHA-pinned deps against the latest annotated release tag.tests/unit/test_outdated_command.py::TestOutdatedCommand::test_commit_ref_reports_latest_annotated_tagHow to test
apm.ymldependency pinned to a full 40-character SHA from an older annotated release tag; runapm update --dry-run; expect a revision-pin plan and no file changes.apm update --yes; expect the manifest entry to change to the latest annotated tag's SHA with# <tag>appended.apm.lock.yaml; expectresolved_committo match the new SHA andresolved_tagto match the annotation.apm outdated; expect the same latest tag/SHA target in the table.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com