Skip to content

feat: marketplace-based version management (#514)#677

Open
sergio-sisternes-epam wants to merge 8 commits intomainfrom
514-marketplace-versioning
Open

feat: marketplace-based version management (#514)#677
sergio-sisternes-epam wants to merge 8 commits intomainfrom
514-marketplace-versioning

Conversation

@sergio-sisternes-epam
Copy link
Copy Markdown
Collaborator

Description

Adds marketplace-based version management for monorepo packages, allowing per-package semver versioning through versions[] arrays in marketplace.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.

Fixes #514

Type of change

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

Changes

Phase 1: Marketplace Version Schema + Semver Resolution

  • marketplace/models.py: VersionEntry dataclass, versions field on MarketplacePlugin
  • marketplace/version_resolver.py (new): Lightweight semver engine supporting ^, ~, >=, >, <, <=, !=, exact, and compound ranges. No external dependencies.
  • marketplace/resolver.py: Version-aware resolve_marketplace_plugin() returning 3-tuple (canonical, plugin, resolved_version). Falls back to single-ref flow when no versions[] present.
  • commands/install.py: Marketplace intercept with version spec parsing, resolved_version provenance in lockfile
  • commands/view.py: _display_marketplace_versions() — Rich table showing all published versions with "latest" tag
  • commands/outdated.py: _check_marketplace_versions() — range-aware update checking with "(outside range)" annotations
  • deps/lockfile.py: New fields: version_spec, resolved_version, discovered_via, marketplace_plugin_name

Phase 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 engine

Phase 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 marketplaces
  • Security warnings route through CommandLogger via warning_handler callback (visible at -v)

Documentation

  • Marketplace guide with versioning section, publishing workflow, and security model
  • CLI reference for marketplace publish and marketplace validate
  • Lockfile spec updated with new fields
  • CHANGELOG.md entry
  • Skill files updated (commands.md, dependencies.md)

Backward Compatibility

Scenario Behavior
Plugin WITH versions[] New version resolution flow
Plugin WITHOUT versions[] Existing single-ref flow (unchanged)
Direct git dependency Existing git-based flow (unchanged)
Lockfile without version_spec Treated as exact pin (safe default)

Testing

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

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):

Suite Tests Result
Install (exact, range, latest, lockfile, exit codes) 10 PASS
View / Outdated / Validate 9 PASS
Security (immutability, shadow detection, pin persistence) 6 PASS
Version Ranges (^, ~, >=, exact, compound, error) 10 PASS
Non-Marketplace Regression (init, local, compile, config, uninstall, mixed) 15 PASS

Copilot AI review requested due to automatic review settings April 11, 2026 18:13
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 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.

Sergio Sisternes and others added 7 commits April 11, 2026 20:22
Add marketplace version schema, semver range resolution, and version-aware
install/view/outdated flows for Issue #514.

- Add VersionEntry model and versions[] field to MarketplacePlugin
- Create semver range resolver (^, ~, >=, exact, compound ranges)
- Version-aware install: resolve marketplace versions with #specifier syntax
- Version-aware view: apm view plugin@marketplace versions shows version table
- Version-aware outdated: marketplace deps checked against marketplace versions
- Add version_spec field to LockedDependency for lockfile persistence
- 108 new tests across 5 test files, all 3909 tests passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 'apm marketplace publish' command that reads apm.yml defaults,
resolves git HEAD SHA, validates semver, detects version conflicts,
and updates marketplace.json with new version entries. Supports
--dry-run, --force, and auto-detection of single marketplace.

Add 'apm marketplace validate' command with validator.py module for
schema validation, semver format checks, duplicate version detection,
and duplicate name detection. Includes --check-refs placeholder and
--verbose per-plugin details.

46 new tests (21 publish + 25 validate), 3955 total passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…(Phase 3)

Add version pin cache (~/.apm/cache/marketplace/version-pins.json) that
tracks version-to-ref mappings per plugin per marketplace. Warns when a
previously pinned version's ref changes (possible ref swap attack).
Advisory only -- never blocks installation.

Add multi-marketplace shadow detection that checks all registered
marketplaces for duplicate plugin names during resolution. Warns about
potential name squatting when the same plugin exists in multiple
marketplaces.

Add security-critical provenance comments in install.py confirming
discovered_via and marketplace_plugin_name are set for all marketplace
dependencies.

35 new tests (25 immutability + 10 shadow), 3990 total passing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update marketplaces guide with versioned plugins schema, semver range
install syntax, version viewing, publish/validate commands, and
security hardening sections (immutability + shadow detection).

Update CLI reference with marketplace publish and validate subcommands,
version specifier syntax in install/view/outdated commands.

Add version_spec field to lockfile spec. Update apm-guide skill
resources with new marketplace commands and version specifier table.

Add changelog entries for all Phase 1-3 features under [Unreleased].

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use auth-first for marketplace fetch (private org repos return 404
  unauthenticated, not 403, so unauth_first swallowed the error)
- Fall back to version_spec when version field is absent in lockfile
  for marketplace outdated checks (strip range prefixes like ^, ~)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
B1: Add resolved_version field to LockedDependency and lockfile YAML.
    Populated during marketplace version resolution (3-tuple return from
    resolve_marketplace_plugin). Enables lockfile-based version verification.

B2: Exit code 1 when all packages fail validation (was silently returning 0).

B3: Show 'Resolved version: X.Y.Z' in verbose install output for marketplace
    packages alongside the resolved ref.

B4: Route 'apm view plugin@marketplace' (no subfield) to marketplace version
    display instead of failing with 'not found in apm_modules'.

B5: Show 'best in range' upgrade in outdated output when latest version is
    outside the pinned range (e.g., '2.0.0 (outside ^1.0.0; best in range: 1.2.0)').

B6: Use resolved_version as primary fallback in outdated, with robust regex
    extraction from version_spec for compound ranges.

B7: Route security warnings (immutability, shadow detection) through CLI
    CommandLogger via warning_handler callback, visible at -v level.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…crash guard

- Fix docs claiming #ref overrides unversioned plugins (it does not)
- Fix docs claiming git tag fallback for plugins without versions
- Remove -p alias from docs (not in CLI implementation)
- Add resolved_version to lockfile-spec documentation
- Add --plugin flag to skill file command reference
- Remove unused PluginNotFoundError import in view.py
- Guard caret pin hint against non-semver version entries
- Wrap _expand_specifier in try/except for raw git ref version_specs
- Add path traversal protection via ensure_path_within for source.path

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sergio-sisternes-epam sergio-sisternes-epam force-pushed the 514-marketplace-versioning branch from 135307f to d500d79 Compare April 11, 2026 18:24
The Rich rendering path already guarded the caret pin hint with
'if latest_version:', but the plain-text fallback still used
sorted_versions[0].version unconditionally, which could produce
invalid specifiers like '#^nightly' for non-semver entries.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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] Allow install to download and unpack a GitHub release automatically

3 participants