Skip to content

feat: rename apm.lock to apm.lock.yaml for IDE syntax highlighting#280

Merged
danielmeppiel merged 7 commits intomainfrom
copilot/feature-apm-lock-yaml-extension
Mar 13, 2026
Merged

feat: rename apm.lock to apm.lock.yaml for IDE syntax highlighting#280
danielmeppiel merged 7 commits intomainfrom
copilot/feature-apm-lock-yaml-extension

Conversation

Copy link
Contributor

Copilot AI commented Mar 13, 2026

The lockfile was named apm.lock (a YAML file without .yaml extension), causing IDEs to skip syntax highlighting. Renames it to apm.lock.yaml with automatic one-time migration for existing users.

Description

The lockfile is now apm.lock.yaml. Existing apm.lock files are silently renamed on the next apm install.

Core changes

  • lockfile.pyget_lockfile_path() returns apm.lock.yaml; exposes LOCKFILE_NAME / LEGACY_LOCKFILE_NAME public constants; adds migrate_lockfile_if_needed(project_root) which renames apm.lockapm.lock.yaml when the new file is absent (with OSError handling and debug logging so installs proceed even on read-only filesystems)
  • lockfile.pyinstalled_paths_for_project() falls back to reading legacy apm.lock when apm.lock.yaml is absent, ensuring transitive dependency discovery works before the first apm install after upgrade
  • install.py — calls migrate_lockfile_if_needed() at the top of the install flow before any lockfile reads; user-facing log messages updated to reference apm.lock.yaml
  • packer.py — calls migrate_lockfile_if_needed() before reading the lockfile, so apm pack works even if only a legacy apm.lock exists; writes apm.lock.yaml into bundles
  • unpacker.py — tries apm.lock.yaml first, falls back to apm.lock for backward compat with old bundles; error messages now use the actual filename read (lockfile_path.name)
  • prune.py / _helpers.py / mcp_integrator.py — replace all hardcoded "apm.lock" path strings with get_lockfile_path(); update_lockfile() docstring updated to reference apm.lock.yaml

apm-action compatibility

The microsoft/apm-action GitHub Action (pack/unpack/install modes) is fully compatible with this change. The action's TypeScript code never references apm.lock directly — all lockfile-sensitive operations are delegated to the APM CLI. The tar-based fallback restore path is also unaffected, as it extracts files without inspecting the lockfile. The one cross-version note: bundles produced by the new APM (containing apm.lock.yaml) cannot be unpacked by an old APM CLI; users pinning apm-version on their restore job should keep it in sync with their pack job.

Tests

  • All test fixtures updated to apm.lock.yaml
  • Added TestMigrateLockfileIfNeeded (migrates, no-op when new file exists, no-op when no legacy file)
  • Added test_unpack_legacy_lockfile_backward_compat for old bundles containing apm.lock
  • Assertion messages in integration tests updated to reference apm.lock.yaml

Docs / changelog

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 2 commits March 13, 2026 09:43
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Copilot AI changed the title [WIP] [FEATURE] Change apm.lock to apm.lock.yaml for readability feat: rename apm.lock to apm.lock.yaml for IDE syntax highlighting Mar 13, 2026
Copilot AI requested a review from danielmeppiel March 13, 2026 09:58
@danielmeppiel
Copy link
Collaborator

@copilot validate this does not impact our GitHub Action: found in microsoft/apm-action repository, which allows users to pack and unpack, among other capabilities

Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot validate this does not impact our GitHub Action: found in microsoft/apm-action repository, which allows users to pack and unpack, among other capabilities

The apm-action is fully compatible with this change. Here's the analysis:

The action's TypeScript code (bundler.ts, runner.ts) never references apm.lock or apm.lock.yaml directly. All lockfile-sensitive work is delegated to the APM CLI:

Action mode How it works Impact
Install (apm install) CLI subprocess → migrate_lockfile_if_needed() renames apm.lockapm.lock.yaml on first run ✅ Transparent migration
Pack (apm pack) CLI subprocess → packer.py now writes apm.lock.yaml into bundle ✅ No action-side change needed
Restore via apm unpack CLI subprocess → unpacker.py tries apm.lock.yaml first, falls back to apm.lock for legacy bundles ✅ Backward compat preserved
Restore via tar fallback tar xzf --strip-components=1 → extracts everything, action only counts files in .github//.claude/, never reads the lockfile ✅ Unaffected

The one cross-version note: bundles packed by new APM (containing apm.lock.yaml) can't be unpacked by an old APM CLI (which only looks for apm.lock). Since apm-action downloads the CLI version on each run (controlled by apm-version input, default: latest), users on old pinned versions who upgrade their bundle-producing job should also upgrade apm-version on the restore job — same coordination already required for any breaking change.

@danielmeppiel danielmeppiel marked this pull request as ready for review March 13, 2026 10:05
Copilot AI review requested due to automatic review settings March 13, 2026 10:05
Copy link
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

Renames the APM lockfile to apm.lock.yaml (to enable YAML syntax highlighting in IDEs) and updates the CLI/test/docs surface area accordingly, including a one-time migration path for existing apm.lock users and backward compatibility for unpacking older bundles.

Changes:

  • Introduces LOCKFILE_NAME / _LEGACY_LOCKFILE_NAME, updates get_lockfile_path(), and adds migrate_lockfile_if_needed() for apm.lockapm.lock.yaml.
  • Updates CLI flows to read/write the new lockfile name, and adds bundle unpack fallback for legacy bundles.
  • Updates tests and Starlight documentation to reference apm.lock.yaml, and adds a changelog entry.

Reviewed changes

Copilot reviewed 43 out of 43 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/apm_cli/deps/lockfile.py Defines new lockfile name constants, updates lockfile path helper, adds legacy→new migration helper.
src/apm_cli/commands/install.py Runs migration early in install and switches lockfile reads to get_lockfile_path().
src/apm_cli/commands/prune.py Reads lockfile via get_lockfile_path() instead of hardcoded filename.
src/apm_cli/commands/_helpers.py Reads lockfile via get_lockfile_path() instead of hardcoded filename.
src/apm_cli/integration/mcp_integrator.py Defaults MCP lockfile persistence to get_lockfile_path().
src/apm_cli/bundle/packer.py Packs using apm.lock.yaml and reads lockfile via get_lockfile_path().
src/apm_cli/bundle/unpacker.py Unpacks preferring apm.lock.yaml, falling back to legacy apm.lock for older bundles.
tests/test_lockfile.py Updates get_lockfile_path() expectations and adds migration unit tests.
tests/unit/test_unpacker.py Updates bundle fixtures to write apm.lock.yaml and adds legacy-bundle unpack test.
tests/unit/test_packer.py Updates packer fixtures and error expectations to apm.lock.yaml.
tests/unit/test_transitive_deps.py Updates transitive deps tests to write/read apm.lock.yaml.
tests/unit/test_transitive_mcp.py Updates transitive MCP tests to use apm.lock.yaml.
tests/unit/test_uninstall_transitive_cleanup.py Updates uninstall transitive cleanup tests to use apm.lock.yaml.
tests/unit/test_mcp_lifecycle_e2e.py Updates MCP lifecycle e2e tests to use apm.lock.yaml.
tests/test_enhanced_discovery.py Updates enhanced discovery tests to write apm.lock.yaml.
tests/integration/test_selective_install_mcp.py Updates selective MCP install integration tests to use apm.lock.yaml.
tests/integration/test_plugin_e2e.py Updates plugin e2e tests to assert apm.lock.yaml existence and parse it.
tests/integration/test_pack_unpack_e2e.py Updates pack/unpack integration test to assert apm.lock.yaml.
tests/integration/test_diff_aware_install_e2e.py Updates helper to read apm.lock.yaml.
tests/integration/test_deployed_files_e2e.py Updates helper and lockfile deletion paths to apm.lock.yaml.
docs/src/content/docs/reference/primitive-types.md Updates references from apm.lock to apm.lock.yaml.
docs/src/content/docs/reference/manifest-schema.md Updates schema text to require apm.lock.yaml.
docs/src/content/docs/reference/lockfile-spec.md Updates lockfile spec wording to apm.lock.yaml.
docs/src/content/docs/reference/cli-commands.md Updates CLI reference to apm.lock.yaml.
docs/src/content/docs/introduction/why-apm.md Updates introductory references to the new lockfile name.
docs/src/content/docs/introduction/what-is-apm.md Updates introductory references to the new lockfile name.
docs/src/content/docs/integrations/ide-tool-integration.md Updates integration docs to reference apm.lock.yaml.
docs/src/content/docs/integrations/github-rulesets.md Updates rulesets docs to reference apm.lock.yaml.
docs/src/content/docs/integrations/gh-aw.md Updates GH Agentic Workflows docs to reference apm.lock.yaml.
docs/src/content/docs/integrations/ci-cd.md Updates CI/CD docs to reference apm.lock.yaml.
docs/src/content/docs/guides/plugins.md Updates plugin guide references to apm.lock.yaml.
docs/src/content/docs/guides/pack-distribute.md Updates pack/distribute guide to reference apm.lock.yaml in examples.
docs/src/content/docs/guides/org-packages.md Updates org packages guide references to apm.lock.yaml.
docs/src/content/docs/guides/dependencies.md Updates dependencies guide section headings and examples to apm.lock.yaml.
docs/src/content/docs/guides/agent-workflows.md Updates workflow guide references to apm.lock.yaml.
docs/src/content/docs/getting-started/quick-start.md Updates quick start directory layout and text to apm.lock.yaml.
docs/src/content/docs/getting-started/migration.md Updates migration doc instructions to commit/remove apm.lock.yaml.
docs/src/content/docs/enterprise/teams.md Updates enterprise teams doc to reference apm.lock.yaml.
docs/src/content/docs/enterprise/security.md Updates security doc references to apm.lock.yaml.
docs/src/content/docs/enterprise/making-the-case.md Updates “making the case” doc references to apm.lock.yaml.
docs/src/content/docs/enterprise/governance.md Updates governance doc references and examples to apm.lock.yaml.
docs/src/content/docs/enterprise/adoption-playbook.md Updates adoption playbook references to apm.lock.yaml.
CHANGELOG.md Adds an Unreleased entry describing the lockfile rename/migration.
Comments suppressed due to low confidence (1)

src/apm_cli/bundle/unpacker.py:98

  • When neither lockfile exists in the bundle, the error message always says apm.lock.yaml not found even if the code just checked both apm.lock.yaml and legacy apm.lock. Consider mentioning both filenames or using the attempted filename(s) in the message to reduce confusion for users unpacking older bundles.
            if not lockfile_path.exists():
                raise FileNotFoundError(
                    "apm.lock.yaml not found in the bundle  -- the bundle may be incomplete."
                )

Comment on lines 285 to 289
try:
lockfile = cls.read(project_root / "apm.lock")
lockfile = cls.read(get_lockfile_path(project_root))
if not lockfile:
return []
return lockfile.get_installed_paths(project_root / "apm_modules")
Comment on lines +318 to +323
new_path = get_lockfile_path(project_root)
legacy_path = project_root / _LEGACY_LOCKFILE_NAME
if not new_path.exists() and legacy_path.exists():
legacy_path.rename(new_path)
return True
return False
Comment on lines 11 to 13
from ..deps.lockfile import LockFile, LOCKFILE_NAME, _LEGACY_LOCKFILE_NAME


Comment on lines 99 to 101
raise FileNotFoundError(
"apm.lock in the bundle could not be parsed -- the bundle may be corrupt."
"apm.lock.yaml in the bundle could not be parsed -- the bundle may be corrupt."
)
Comment on lines 65 to 69
# 1. Read lockfile
lockfile_path = project_root / "apm.lock"
lockfile_path = get_lockfile_path(project_root)
lockfile = LockFile.read(lockfile_path)
if lockfile is None:
raise FileNotFoundError(

# Lock file created
assert (temp_project / "apm.lock").exists(), "apm.lock should be created"
assert (temp_project / "apm.lock.yaml").exists(), "apm.lock should be created"
CHANGELOG.md Outdated
Comment on lines +11 to +13
### Changed

- Lockfile renamed from `apm.lock` to `apm.lock.yaml` for IDE syntax highlighting; existing `apm.lock` files are automatically migrated to `apm.lock.yaml` on the next `apm install` (#TBD)
"""
if lock_path is None:
lock_path = Path.cwd() / "apm.lock"
lock_path = get_lockfile_path(Path.cwd())
Comment on lines 21 to 24
## Abstract

`apm.lock` records the exact resolved state of every dependency in an APM
`apm.lock.yaml` records the exact resolved state of every dependency in an APM
project. It is the receipt of what was installed — commit SHAs, source URLs,
Comment on lines +395 to 405
# Migrate legacy apm.lock → apm.lock.yaml if needed (one-time, transparent)
migrate_lockfile_if_needed(Path.cwd())

# Capture old MCP servers and configs from lockfile BEFORE
# _install_apm_dependencies regenerates it (which drops the fields).
# We always read this — even when --only=apm — so we can restore the
# field after the lockfile is regenerated by the APM install step.
old_mcp_servers: builtins.set = builtins.set()
old_mcp_configs: builtins.dict = {}
_lock_path = Path.cwd() / "apm.lock"
_lock_path = get_lockfile_path(Path.cwd())
_existing_lock = LockFile.read(_lock_path)
@danielmeppiel
Copy link
Collaborator

@copilot review the PR comments and fix if needed and appropriate. In addition, integration tests that ran seem to be failing at https://github.com/microsoft/apm/actions/runs/23045967973, ensure integration tests do pass.

Copilot AI and others added 2 commits March 13, 2026 10:35
…blic constant, messages

Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Co-authored-by: danielmeppiel <51440732+danielmeppiel@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 13, 2026

@copilot review the PR comments and fix if needed and appropriate. In addition, integration tests that ran seem to be failing at https://github.com/microsoft/apm/actions/runs/230459679...

All 10 PR review comments addressed in commits 0f5c07f and 5e32d00:

  • Legacy fallback in installed_paths_for_project() and pack_bundle() — pre-migration reads now fall back to apm.lock
  • OSError handling in migrate_lockfile_if_needed() — catches permission errors, logs debug, returns False
  • _LEGACY_LOCKFILE_NAMELEGACY_LOCKFILE_NAME (public constant)
  • Unpacker error messages use actual filename via lockfile_path.name
  • Log messages in install.py updated to say apm.lock.yaml
  • Docstring in mcp_integrator.py updated
  • CHANGELOG #TBD#280
  • lockfile-spec.md migration section now documents the filename rename
  • test_plugin_e2e.py assertion message corrected

Re: integration test failure — test_auto_install_virtual_prompt_first_run is a pre-existing issue on main: script_runner.py outputs * Package installed and ready to run but the test assertion checks for ✨ Package installed and ready to run. Not caused by the lockfile rename.

@danielmeppiel danielmeppiel merged commit 09fef91 into main Mar 13, 2026
9 checks passed
@danielmeppiel danielmeppiel deleted the copilot/feature-apm-lock-yaml-extension branch March 13, 2026 12:57
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.

[FEATURE] apm.lock should be apm.lock.yaml for linting and readability

3 participants