feat: add general-purpose lifecycle hooks framework (#1529)#1798
Open
sergio-sisternes-epam wants to merge 9 commits into
Open
feat: add general-purpose lifecycle hooks framework (#1529)#1798sergio-sisternes-epam wants to merge 9 commits into
sergio-sisternes-epam wants to merge 9 commits into
Conversation
Implement a lifecycle hook framework that fires custom actions at install, update, and uninstall time. Three hook types are supported: shell commands, HTTP webhooks, and executable scripts. Hooks can be configured at project level (apm.yml), global level (~/.apm/config.json), and policy level (apm-policy.yml). Policy hooks run first and cannot be removed by the project. All hooks are fire-and-forget with error isolation -- failures never block the CLI. New files: - src/apm_cli/core/lifecycle_hooks.py: event model, hook definitions, runner, discovery, and build_runner_from_context convenience function - src/apm_cli/core/hook_executors.py: webhook (HTTPS-only, 2s timeout, daemon thread), command (subprocess, 30s timeout), and script (path-traversal guard) executors Wiring: - InstallService.run(): pre-install / post-install hooks - uninstall/cli.py: pre-uninstall / post-uninstall hooks - update.py: pre-update / post-update hooks Supporting changes: - apm_package.py: lifecycle_hooks field and parsing - policy/schema.py: LifecycleHooksPolicy (require, deny_types) - policy/parser.py: lifecycle_hooks policy parsing - config.py: get/set/unset_lifecycle_hooks for global config Tests: 50 unit tests covering models, runner, executors, collection, deduplication, error isolation, and security guards. Docs: enterprise/lifecycle-hooks.md guide page. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace inline lifecycle_hooks config (apm.yml, config.json, policy) with standalone JSON hook files discovered from well-known directories: - Policy: /etc/apm/policy.d/*.json - User: ~/.apm/hooks/*.json - Project: .apm/hooks/*.json Key changes: - Two hook types: command (stdin delivery) and http (HTTPS POST) - Drop script type (subsumed by command with bash field) - Drop token_env (replaced by headers with $ENV_VAR expansion) - Event payload delivered via stdin for commands (not env var) - Add working_directory field to event payload - Remove lifecycle_hooks from APMPackage, ApmPolicy, config.py - Rewrite executors, tests (58 passing), and documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Hook stdout, stderr, exit code, and execution status are now appended to ~/.apm/logs/hooks.log (or $APM_HOME/logs/hooks.log) after every hook execution. This gives administrators an audit trail without requiring verbose CLI output. Log writing is fire-and-forget -- failures are silently swallowed to ensure logging never breaks the CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three new sub-commands under 'apm hooks': - hooks init -- scaffold a starter hook JSON file - hooks test -- dry-run a synthetic event through all hooks - hooks validate -- check all hook files for schema errors Includes 26 tests and documentation updates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Change project-level hook discovery from a directory (.apm/hooks/*.json) to a single file (.apm/hooks.json). Update init, validate commands, all tests, and documentation accordingly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a lifecycle hooks framework to APM so installs/updates/uninstalls can emit structured events to user/admin-defined “command” and “http” hooks, plus a new apm hooks CLI group and accompanying enterprise documentation.
Changes:
- Introduces hook discovery + event models + runner (
lifecycle_hooks.py) and executors with stdout/stderr logging (hook_executors.py). - Fires lifecycle events from install/update/uninstall flows and registers the new CLI command group.
- Adds unit tests covering parsing/discovery/execution and documentation for enterprise usage.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/core/test_lifecycle_hooks.py | New unit tests for hook models, discovery, and runner behavior. |
| tests/unit/core/test_hook_executors.py | New unit tests for HTTP/command executors, env expansion, cwd, and hooks log output. |
| tests/unit/commands/test_hooks.py | New unit tests for apm hooks list/init/test/validate commands. |
| src/apm_cli/install/service.py | Fires pre-install/post-install hooks around the install pipeline and adds helper builders. |
| src/apm_cli/core/lifecycle_hooks.py | Adds lifecycle event schema, hook entry model, JSON parsing, and discovery across policy/user/project. |
| src/apm_cli/core/hook_executors.py | Implements command + HTTP hook execution and appends results to hooks.log. |
| src/apm_cli/commands/update.py | Fires pre-update/post-update hooks around the update pipeline. |
| src/apm_cli/commands/uninstall/cli.py | Fires pre-uninstall/post-uninstall hooks around uninstall. |
| src/apm_cli/commands/hooks.py | New apm hooks command group (list/init/test/validate). |
| src/apm_cli/cli.py | Registers the new hooks command group. |
| docs/src/content/docs/enterprise/lifecycle-hooks.md | Adds enterprise documentation for hook format, discovery, security, logging, and CLI usage. |
12 tasks
- Add credential denylist to _expand_env_vars: blocks TOKEN, SECRET, PAT, KEY, PASSWORD, CREDENTIAL pattern vars from HTTP header expansion - Strip credential-pattern vars from _build_hook_env so command hooks cannot inherit GITHUB_APM_PAT, ADO_APM_PAT, etc. - Fix console import in hooks.py: replace nonexistent 'console' symbol with _get_console() calls (fixes dead Rich display layer) - Add HTTP thread drain in 'apm hooks test' so log entries are written before CLI exits (bounded 15s join per thread) - Update security.md: retract absolute 'no network calls' and 'no code execution' claims to reflect opt-in lifecycle hooks reality - Update execute_hook/fire() signatures to return threads for drain - Change verbose message from 'sent' to 'dispatched' (accurate wording) - Add comprehensive tests for credential denylist and env stripping Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nes-epam/feat-installation-analytics-hooks # Conflicts: # docs/src/content/docs/enterprise/security.md
- Move pre-install hooks after import check so hooks do not fire when the install pipeline is not available - Add return type annotations to _build_hook_runner and _build_event - Fix effective_command to prefer command over bash on Windows - Use urlparse for URL validation, reject embedded credentials - Add hooks_for_event() public API, stop reaching into _hooks - Remove Rich markup in panel title that renders as italic - Clarify synchronous execution in _execute_command docstring - Add type annotations to _fire_update_hooks parameters - Redact URL credentials before writing to hooks.log - Add apm hooks commands to commands.md reference Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nt (#1529) - Move hooks commands into dedicated 'Lifecycle hooks' section in commands.md - Add tests for _redact_url_credentials (plain, user:pass, user-only) - Add tests for hooks_for_event public API (matching, empty result) 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.
TL;DR
Adds a Copilot CLI-aligned lifecycle hooks framework that fires events on install, update, and uninstall -- enabling enterprise analytics, CI integrations, and custom automation via standalone JSON hook files.
Problem (WHY)
Enterprise users need opt-in analytics and extensibility around package lifecycle events (install, update, uninstall). There was no event system to hook into -- users could not build integrations like Slack notifications, audit logging, or telemetry collection on top of APM's package operations. See #1529.
Approach (WHAT)
Follow the Copilot CLI hooks model: standalone JSON hook files discovered from well-known locations, with two hook types (command and HTTP). All hooks are additive across sources and policy hooks cannot be disabled.
Discovery sources (priority order)
/etc/apm/policy.d/*.json~/.apm/hooks/*.json.apm/hooks.jsonLifecycle events
pre-install,post-install,pre-update,post-update,pre-uninstall,post-uninstallHook types
CLI commands
apm hooks-- list all discovered hooksapm hooks init-- scaffold a starter.apm/hooks.jsonapm hooks test-- dry-run a synthetic eventapm hooks validate-- check all hook files for errorsHook output logging
All hook execution output is logged to
~/.apm/logs/hooks.logwith timestamps, event name, hook type, target, status, and stdout/stderr.Implementation (HOW)
New files
src/apm_cli/core/lifecycle_hooks.py-- HookEntry model, discovery, runnersrc/apm_cli/core/hook_executors.py-- command/HTTP executors, log file writersrc/apm_cli/commands/hooks.py-- CLI group with init/test/validate subcommandsdocs/src/content/docs/enterprise/lifecycle-hooks.md-- full documentationModified files
src/apm_cli/install/service.py-- fire pre/post-install hookssrc/apm_cli/commands/uninstall/cli.py-- fire pre/post-uninstall hookssrc/apm_cli/commands/update.py-- fire pre/post-update hookssrc/apm_cli/cli.py-- register hooks command groupTests
tests/unit/core/test_lifecycle_hooks.py-- 33 tests (schema, parsing, discovery, runner)tests/unit/core/test_hook_executors.py-- 35 tests (executors, env expansion, cwd, logging)tests/unit/commands/test_hooks.py-- 25 tests (CLI commands)Validation
Closes #1529
apm-spec-waiver: lifecycle hooks are opt-in fire-and-forget observers that do not alter install/update/uninstall contract behaviour