Skip to content

feat: surface installed hook actions during apm install#409

Open
harshitlarl wants to merge 3 commits intomicrosoft:mainfrom
harshitlarl:feat/hook-install-transparency-316
Open

feat: surface installed hook actions during apm install#409
harshitlarl wants to merge 3 commits intomicrosoft:mainfrom
harshitlarl:feat/hook-install-transparency-316

Conversation

@harshitlarl
Copy link
Copy Markdown

Summary

  • show concise per-event hook action summaries during
  • include the full rewritten hook JSON in verbose mode so developers can review deployed hook content
  • add focused tests covering the new install-time transparency output

Testing

  • . .venv/bin/activate && pytest tests/unit/test_install_hook_transparency.py tests/unit/integration/test_hook_integrator.py -n0

Closes #316

@harshitlarl
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds install-time transparency for hook packages so developers can see what hook actions will run and (in verbose mode) inspect the deployed hook JSON content, addressing supply-chain visibility concerns from #316.

Changes:

  • Emit per-event hook action summaries during apm install for integrated hooks.
  • In --verbose mode, print the fully rewritten hook JSON that will be deployed/merged.
  • Add focused unit tests covering the new hook transparency output.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/apm_cli/integration/hook_integrator.py Adds hook action flattening/summarization and returns CLI display payloads alongside integration results.
src/apm_cli/commands/install.py Logs hook action summaries and verbose hook JSON content during install.
tests/unit/test_install_hook_transparency.py New unit tests validating the new install-time hook transparency output (normal + verbose).

Comment on lines +135 to +141
return {
"target_label": target_label,
"output_path": output_path,
"source_hook_file": source_hook_file.name,
"actions": actions,
"rendered_json": json.dumps(rewritten, indent=2, sort_keys=True),
}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

rendered_json is precomputed for every integrated hook via json.dumps(...), even when apm install is not in --verbose mode. This adds avoidable work/memory on the hot install path. Consider deferring JSON rendering until verbose logging is actually requested (e.g., store the rewritten dict, or store a callable/None and render in the logger code only when needed).

Copilot uses AI. Check for mistakes.
"output_path": output_path,
"source_hook_file": source_hook_file.name,
"actions": actions,
"rendered_json": json.dumps(rewritten, indent=2, sort_keys=True),
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

The verbose display payload uses json.dumps(..., sort_keys=True), but the deployed JSON files are written with json.dump(..., indent=2) (no sort_keys). This means --verbose will show a key order that can differ from what was actually deployed, which undermines the goal of letting developers review the exact hook content. Align the verbose rendering with the on-disk serialization (or reuse the same serialization helper for both).

Suggested change
"rendered_json": json.dumps(rewritten, indent=2, sort_keys=True),
"rendered_json": json.dumps(rewritten, indent=2),

Copilot uses AI. Check for mistakes.
display_payloads.append(
self._build_display_payload(
".github/hooks/",
target_filename,
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

For VSCode/GitHub hooks, the display payload sets output_path to target_filename only. This makes the verbose line render as hooks.json -> <filename> without the .github/hooks/ prefix, unlike the Claude/Cursor cases that include the full target path. Consider storing the full relative destination path (e.g. .github/hooks/<filename>) or using target_label when formatting the destination so the output is unambiguous.

Suggested change
target_filename,
rel_path,

Copilot uses AI. Check for mistakes.
Comment on lines +794 to +799
if logger.verbose:
logger.verbose_detail(
f" Hook JSON ({source_name} -> {payload['output_path']}):"
)
for line in payload["rendered_json"].splitlines():
logger.verbose_detail(f" {line}")
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

The new verbose hook transparency (Hook JSON ... + full rewritten JSON) changes what --verbose emits during apm install, but the CLI docs currently describe verbose mode only in terms of file paths + diagnostic details. Please update the Starlight docs (e.g. docs/src/content/docs/reference/cli-commands.md under apm install) to mention that verbose mode also prints the full rewritten hook JSON so users can review deployed hook content.

Copilot uses AI. Check for mistakes.
@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

Great work on this, @harshitlarl! Hook transparency is a P0 security feature (#316) and we want to get this merged. A few things to address:

Rebase required

main has moved significantly since this PR was opened. The install pipeline was refactored and this PR now has merge conflicts. Key changes you'll need to adapt to:

  1. HookIntegrationResult is now a subclass of IntegrationResult (in base_integrator.py), not a standalone @dataclass. Your display_payloads field needs to be wired through the new class hierarchy.
  2. _integrate_package_primitives() moved to src/apm_cli/install/services.py and uses a generic target-dispatch loop — the three separate per-target hook blocks no longer exist. Your _log_hook_details() call needs to hook into the single dispatch loop (around line 124-136 in services.py).
  3. Tests will need updated imports and the new function signature.

Code feedback

  1. Defer rendered_json to verbose-only path. Currently json.dumps(rewritten, indent=2, sort_keys=True) runs for every hook even in non-verbose mode. Store the rewritten dict and serialise lazily only when logger.verbose is true — avoids unnecessary work on the hot install path.
  2. Drop sort_keys=True — the deployed JSON doesn't sort keys, so --verbose would show a different key order than what's on disk, which undermines the transparency goal.
  3. Missing CHANGELOG entry — please add one under [Unreleased] > Added.
  4. Missing docs update--verbose now shows hook JSON content; the Starlight docs page for apm install should mention this.

We can help

We know the rebase is non-trivial given the architecture changes. If you'd like, we're happy to push the rebase to your branch directly (maintainer edits are enabled). Just let us know — otherwise, take your time and reach out if you hit any blockers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hook installation transparency — display hook contents during install

3 participants