feat: marketplace UX, security hardening, and lockfile provenance (#514)#677
Conversation
There was a problem hiding this comment.
Pull request overview
Adds marketplace-based, per-plugin semver versioning and resolution to APM so monorepo packages can publish and install independently versioned releases via marketplace.json (versions[]), with supporting CLI UX (install/view/outdated/publish/validate) and supply-chain advisories (immutability pins + shadow detection).
Changes:
- Introduces
versions[]schema (VersionEntry) and a built-in semver range resolver for marketplace plugins. - Updates resolution + install/outdated/view flows to understand
NAME@MARKETPLACE[#version_spec], record provenance in the lockfile, and display version-aware UX. - Adds
apm marketplace publish/validate, plus security advisories (ref immutability pins; cross-marketplace shadow detection), and expands tests/docs/changelog accordingly.
Reviewed changes
Copilot reviewed 30 out of 30 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_view_versions.py | Adds CLI tests for apm view NAME@MARKETPLACE versions output/sorting/badges. |
| tests/unit/test_view_command.py | Ensures apm view NAME@MARKETPLACE routes to marketplace versions display. |
| tests/unit/test_version_resolver.py | Unit tests for semver spec detection and range resolution. |
| tests/unit/test_outdated_marketplace.py | Unit tests for marketplace-aware apm outdated behavior and range annotations. |
| tests/unit/marketplace/test_versioned_resolver.py | Tests version-aware marketplace resolution + lockfile field round-trips + warning routing. |
| tests/unit/marketplace/test_version_pins.py | Tests version pin cache persistence, atomic writes, and fail-open behavior. |
| tests/unit/marketplace/test_shadow_detector.py | Tests multi-marketplace shadow detection and resolver integration warnings. |
| tests/unit/marketplace/test_marketplace_validator.py | Tests manifest validator + apm marketplace validate output/exit codes. |
| tests/unit/marketplace/test_marketplace_resolver.py | Updates parsing tests for the new #... fragment support. |
| tests/unit/marketplace/test_marketplace_publish.py | Tests apm marketplace publish defaults/options/conflicts/dry-run and JSON updates. |
| tests/unit/marketplace/test_marketplace_models.py | Tests VersionEntry + parsing of versions[] from marketplace.json. |
| tests/unit/marketplace/test_marketplace_install_integration.py | Tests install interception changes + exit code + verbose resolved-version logging. |
| src/apm_cli/marketplace/version_resolver.py | New semver parser/range resolver and is_version_specifier() heuristic. |
| src/apm_cli/marketplace/version_pins.py | New local cache for version->ref pins (immutability advisory). |
| src/apm_cli/marketplace/validator.py | New manifest validation engine (schema/versions/duplicates). |
| src/apm_cli/marketplace/shadow_detector.py | New advisory scan for same plugin name across registered marketplaces. |
| src/apm_cli/marketplace/resolver.py | Extends parsing to NAME@MKT#...; resolves semver to refs; emits advisories via handler. |
| src/apm_cli/marketplace/models.py | Adds VersionEntry + MarketplacePlugin.versions and parsing support. |
| src/apm_cli/marketplace/client.py | Switches marketplace fetch to auth-first to avoid private-repo 404 swallowing. |
| src/apm_cli/deps/lockfile.py | Adds version_spec + resolved_version fields to lockfile entries. |
| src/apm_cli/commands/view.py | Adds marketplace version display and routes apm view NAME@MKT to it. |
| src/apm_cli/commands/outdated.py | Adds marketplace-aware update checks and a “Source” column. |
| src/apm_cli/commands/marketplace.py | Adds marketplace validate and marketplace publish (plus helper functions). |
| src/apm_cli/commands/install.py | Parses #version_spec, resolves marketplace versions, records provenance/lockfile fields, fixes all-failed exit code. |
| packages/apm-guide/.apm/skills/apm-usage/dependencies.md | Documents marketplace semver specifiers in dependency syntax. |
| packages/apm-guide/.apm/skills/apm-usage/commands.md | Updates skill command reference for new marketplace/version commands. |
| docs/src/content/docs/reference/lockfile-spec.md | Documents new lockfile field(s) for marketplace versioning. |
| docs/src/content/docs/reference/cli-commands.md | Documents new CLI syntax/commands and updated outdated/view behaviors. |
| docs/src/content/docs/guides/marketplaces.md | Adds versioning/publishing/validation/security sections to marketplace guide. |
| CHANGELOG.md | Adds Unreleased entries for marketplace version management feature set. |
135307f to
d500d79
Compare
|
This seems to extend the plugin/marketplace spec with the new versions array? In my opinion, we should not extend or modify it since the spec is evolving rapidly and thus we may introduce difficult issues to reconcile down the line @sergio-sisternes-epam |
|
@danielmeppiel Yes. leverage the marketplace.json and add a versions[] array, and add a "publish" command helping to manage the edits on the file, rather than letting users do it manually, is the only idea I could come up to mitigate the current gap in mono-repo version management. When designing it, we ensure retro-compatibility: If versions[] is absent, APM follows the default behaviour it has today. When present, the marketplace versions supersede the default behaviour. This should mitigate/prevent any problems down the line. This branch has been tested in closed preview in a couple of scenarios, and feedback has been positive (pre-merge). |
096f491 to
3230c01
Compare
|
Thanks for pushing this forward, @sergio-sisternes-epam — and for the patience while I caught up with the broader marketplace cluster (see threads on #514, #676, #722). I want to land the parts of this PR that are unambiguously valuable while pulling back on the schema extension. Concretely: Where I land on
|
danielmeppiel
left a comment
There was a problem hiding this comment.
Requesting changes to gate merge until the scope reduction in #677 (comment) is addressed.
Summary of asks (full detail in the linked comment):
- Drop the
versions[]schema extension onMarketplacePlugin. It diverges from Anthropic'smarketplace.jsonspec and creates cross-tool drift (Claude CLI and APM would resolve different versions from the samemarketplace.json). - Drop
version_resolver.py— heuristic version selection on top of a non-standard schema compounds the drift. - Redirect
apm marketplace publishwork to #722 (Marketplace authoring suite) so the publish/validate UX lands in the right home with consistent UX alongsideapm marketplace generate/lint.
Keepers (this PR is the right vehicle for these):
MarketplaceValidator— schema validation against the standard spec- Shadow detector
view/outdatedUX additionsLockedDependency.source_url/source_digestprovenance- Auth-first client fix
version_pins.py(separate from version_resolver — pin enforcement, not invented schema)
Once the versions[] work is split out and either dropped or moved behind a feature gate, happy to re-review and approve the slimmer PR. The non-controversial pieces above are valuable and we want them in.
|
Thanks for the detailed and thoughtful review, Daniel. Pushed the scope reduction you requested: Dropped:
Kept and refactored:
The 3,907 tests pass. Ready for re-review when you have a moment. |
3230c01 to
4fc3a22
Compare
4fc3a22 to
ce9aeb8
Compare
APM Review Panel: PR #677 (post-scope-reduction)Re-reviewing the slimmed PR after the April-20 scope reduction. Verdict at the bottom. Context refresh: this PR was originally a What's in scope for this re-review: 26 files / +1357/-81 LOC. Six concrete deliverables:
supply-chain-security-expertStrong agree on direction; one P1 regression introduced by the reframe. The scope-reduction is the right call from a supply-chain perspective: extending Three findings: P1 --
|
| # | Owner | Item | Fix size |
|---|---|---|---|
| 1 | sergio-sisternes-epam | version_pins.py -- key on (marketplace, plugin, version) using the standard version field; only warn when same version resolves to a different ref |
~30 LOC + test |
| 2 | sergio-sisternes-epam | commands/install.py:198 -- route security warnings through logger.warning (not verbose_detail) |
~3 LOC |
| 3 | sergio-sisternes-epam | marketplace/resolver.py parse_marketplace_ref -- reject semver-range characters with a friendly error linking to docs |
~5 LOC + test |
Deferred to follow-up issues:
- 3-tuple -> 2-tuple cleanup of
resolve_marketplace_plugin(drop the always-Noneelement) OutdatedRowdataclass to replace the 6-tupleview NAME@MKT"Resolved" line + Source/Status column reorder- Shadow detector cache-hit measurement under N-marketplace setups
- Validator
--check-refsimplementation (already documented as planned)
Verdict
Conditional ship. Three concrete P1 fixes, then approve. Sergio has earned the trust to land this iteration with a final review pass rather than another cycle of changes-requested.
CEO addendum -- promoting the P2s to requiredReconsidered after panel deliberation. Sergio has demonstrated throughput on this PR (1,800-LOC scope reduction landed in a working day, clean diff, accurate PR description). Trust isn't the bottleneck -- iteration cost is. Given the throughput, batching the cleanup now is cheaper for everyone than carrying it as follow-up debt across the marketplace cluster (#722, #757, #676 all touch this surface). Promoting the following P2s to required for this PR:
Explicitly still deferred to follow-up issues (genuine new work, not cleanup):
Updated final action listP1 (security/correctness):
P2 (now required, batched while contributor is hot): Total estimated diff: ~80 LOC + a couple of test updates. Well within a single-pass iteration for someone with Sergio's throughput on this surface. Once landed, this PR earns a clean approval and we close the marketplace-foundations chapter cleanly. Sergio -- thank you for the velocity on the scope reduction. The expanded ask isn't a trust signal, it's the inverse: you're moving fast enough that batching the cleanup now is the higher-leverage path for both you and the reviewers. |
ebe5196 to
4eb1df8
Compare
Panel Feedback AddressedAll 8 items from the APM Review Panel (3 P1s + 5 promoted P2s) have been implemented. Summary: P1 -- Security & Correctness
P2 -- Promoted Cleanup
Test coverage
|
- Marketplace-aware view and outdated commands (standard spec) - Shadow detector for cross-marketplace supply-chain advisories - Ref immutability pins (version_pins.py) - MarketplaceValidator (schema + duplicate names) - Auth-first client fix for private repos - Lockfile provenance (discovered_via, marketplace_plugin_name) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
4eb1df8 to
9041eb1
Compare
Description
Adds marketplace-based version management for monorepo packages, allowing per-package semver versioning through
versions[]arrays inmarketplace.json. This solves the monorepo versioning problem (repo-wide tags vs. tag pollution vs. poly-repos) by leveraging the marketplace as a version registry.Users can now install specific versions (
apm install plugin@marketplace#^2.0.0), view available versions (apm view plugin@marketplace), check for updates with range awareness (apm outdated), and publish new versions (apm marketplace publish). Security hardening includes advisory immutability warnings when version refs change and multi-marketplace shadow detection.Fully backward compatible: plugins without
versions[]use the existing single-ref flow. Direct git dependencies are completely unaffected.Related to #514
Type of change
Changes
Phase 1: Marketplace Version Schema + Semver Resolution
marketplace/models.py:VersionEntrydataclass,versionsfield onMarketplacePluginmarketplace/version_resolver.py(new): Lightweight semver engine supporting^,~,>=,>,<,<=,!=, exact, and compound ranges. No external dependencies.marketplace/resolver.py: Version-awareresolve_marketplace_plugin()returning 3-tuple(canonical, plugin, resolved_version). Falls back to single-ref flow when noversions[]present.commands/install.py: Marketplace intercept with version spec parsing,resolved_versionprovenance in lockfilecommands/view.py:_display_marketplace_versions()— Rich table showing all published versions with "latest" tagcommands/outdated.py:_check_marketplace_versions()— range-aware update checking with "(outside range)" annotationsdeps/lockfile.py: New fields:version_spec,resolved_version,discovered_via,marketplace_plugin_namePhase 2: Publishing Tooling
commands/marketplace.py:apm marketplace publish— publishes current package version to marketplace.json with SHA-pinned refs.apm marketplace validate— validates marketplace integrity (4 checks: refs, duplicates, semver format, source URLs).marketplace/validator.py(new): Marketplace validation enginePhase 3: Security Hardening
marketplace/version_pins.py(new): Advisory immutability — caches version-to-ref mappings, warns on ref changes (potential ref-swap attacks)marketplace/shadow_detector.py(new): Multi-marketplace shadow detection — warns when the same plugin name appears in multiple registered marketplacesCommandLoggerviawarning_handlercallback (visible at-v)Documentation
marketplace publishandmarketplace validateBackward Compatibility
versions[]versions[]version_specTesting
Unit Tests
4017 tests passing (+227 new tests across 12 test files covering version resolver, models, publish, validate, shadow detector, version pins, versioned resolver, outdated marketplace, view versions, and install integration).
Integration Tests (50 tests against a live marketplace)
End-to-end tested against a private marketplace with 68 versioned plugins (including multi-version packages with 5-6 versions each):
^,~,>=, exact, compound, error)