feat(marketplace): adopt apm pack as canonical marketplace.json builder#1570
feat(marketplace): adopt apm pack as canonical marketplace.json builder#1570danielmeppiel wants to merge 6 commits into
Conversation
There was a problem hiding this comment.
main, but PRs should target staged.
The main branch is auto-published from staged and should not receive direct PRs.
Please close this PR and re-open it against the staged branch.
You can change the base branch using the Edit button at the top of this PR,
or run: gh pr edit 1570 --base staged
Follow-up commit: CI/CD wiring +
|
03c3703 to
530351b
Compare
Rebased onto
|
|
Two follow-ups from the discussion: 1. CI fix pushed ( 2. F3 (retire Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com |
aaronpowell
left a comment
There was a problem hiding this comment.
Some initial questions/observations:
- The
sourcefor each plugin shouldn't contain./pluginsbecause that is defined in themetadata.pluginRootproperty as a root path, so tools would think they are at./plugins/plugins/... - Do we still need the
plugin.jsonfor each plugin? Aren't we just defining all of that in theapm.yml? - Similarly to the last point, shouldn't the file for external plugins be ditched and it just be part of the
apm.ymldefinition?
|
@aaronpowell -- on findings 1 and 3 of your review, filed microsoft/apm#1061 (expanded from a previous narrower issue) as a single umbrella covering both:
Both are small, additive, no-op-when-unset changes on the same code path. Once they ship upstream, this PR collapses into:
Net result: a single Pragmatic for this PR: pushing the 5-line Finding 2 ( |
|
So will there still be a need for the What I'm wondering is, if we could essentially drop the |
|
That's the goal yes, I'll be polishing this draft PR. We can also define MCP config but not LSP. Can you point me to current plugin json files that contain those ? I'll be adding schema conformance tests. It currently generates plugin and marketplace jsons as per Anthropic's spec. |
|
We don't have any plugins that ship |
Introduce APM (microsoft/apm) as the marketplace authoring substrate. Root apm.yml declares all 53 local plugins under marketplace.packages; 'apm pack' emits the Anthropic-spec marketplace.json. A small merge-external-plugins.mjs bridge appends plugins/external.json entries (kept as a separate concern this round) and re-sorts the combined list alphabetically. The legacy generator (eng/generate-marketplace.mjs) is preserved as 'npm run plugin:generate-marketplace:legacy' for parity comparisons during the transition. - npm run build: now invokes apm pack + bridge merge - 54 plugins out, name-parity with previous output verified - per-plugin plugin.json files untouched (follow-up: per-plugin apm.yml) - plugins/external.json untouched (follow-up: native external sources) - CONTRIBUTING.md: apm CLI prerequisite + apm.yml registration step - eng/README.md: marketplace generation section rewritten Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two CI changes mirroring how microsoft/apm uses microsoft/apm-action@v1
in its own self-check workflow:
1. publish.yml: add 'microsoft/apm-action@v1' step before 'npm run build'.
The build now invokes 'apm pack', which requires the apm CLI on PATH.
Without this step the publish-from-staged workflow would fail after
this PR merges.
2. validate-marketplace.yml (new): PR-time gate that runs on changes to
any marketplace.json input. Two subgates:
- Gate A: 'apm audit --ci' for supply-chain integrity (lockfile /
install fidelity, ref consistency, content-integrity scan).
Emits SARIF, uploaded to GitHub code scanning under category
'apm-audit'.
- Gate B: rebuilds marketplace.json with 'apm pack' + the merge
bridge and fails if the result differs from what's committed.
Catches contributors who edit apm.yml without re-running
'npm run build'.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
apm-action's audit-report step short-circuits when there is no
apm.lock.yaml ('No apm.lock.yaml found -- nothing to scan') and
writes no SARIF file. The unconditional upload step then failed
with 'Path does not exist: apm-audit.sarif'.
Marketplace-only manifests legitimately have no dependencies to
scan, so the absence of a SARIF file is not an error -- only its
presence-with-failures would be. Guard the upload on
hashFiles('apm-audit.sarif') != '' so the gate stays green for
marketplace-only repos and lights up the moment awesome-copilot
adds a real dependency.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
apm 0.14.0 replaced `apm pack --marketplace-output PATH` with the multi-format `--marketplace FORMAT[,FORMAT...]` plus `--marketplace-path FORMAT=PATH`. Update the npm script accordingly: apm pack --marketplace claude --marketplace-path claude=.github/plugin/marketplace.json The behavioural contract is unchanged for awesome-copilot (single Claude Code marketplace.json, same path), but the new surface is forward-compatible with multi-runtime emission (codex etc) the day this repo decides to publish for additional assistants. Also refresh `.github/plugin/marketplace.json` to the apm 0.14.0 emission shape. Two consumer-visible deltas, both Claude Code marketplace-spec compliant: - Local entries: `source` field is now emitted as a relative path prefixed with `./` (e.g. `./ai-team-orchestration` rather than bare `ai-team-orchestration`). This is the pluginRoot-subtraction artifact from microsoft/apm#1061; both forms resolve identically in Claude Code. - Local entries: field order is now alphabetised by apm's mapper (description, name, version, source) rather than the previous generator's emission order. JSON parsers are order-insensitive so this has no consumer-visible effect. External entries (merged in by eng/merge-external-plugins.mjs) are byte-identical; the bridge controls those. Workflow pinning: bump validate-marketplace.yml's actions/checkout and actions/setup-node references to the SHA-pinned v6 entries already used elsewhere in the repo (.github/workflows/contributors.yml et al), eliminating the Node.js 20 deprecation warning that fires on `@v4`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
7b4e07e to
87bea46
Compare
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
apm 0.14.0 replaces --marketplace-output PATH with --marketplace claude --marketplace-path claude=PATH. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
…ava-agent-config golden path The previous header comment pointed reviewers at microsoft/apm's own self-check workflow as the pattern reference. That workflow has diverged since this PR was first opened and is no longer the canonical APM-consumer audit pattern. The current golden path for downstream consumers is DevExpGbb/zava-agent-config's reusable apm-audit.yml, which composes microsoft/apm-action@v1 with explicit `apm audit --ci --policy org` enforcement and repo-policy override detection. awesome-copilot does not have a `dependencies:` block (marketplace-only manifest), so the audit short-circuits and we do not need the policy-enforcement layer zava ships -- but the install+audit substrate is the same. Also clarifies that `apm marketplace check` (producer-doc canonical pre-publish gate) is intentionally NOT used here today: it does not short-circuit local-path sources and would false-fail on every `source: ./plugins/<name>` entry. The drift gate is the producer-side equivalent for awesome-copilot until that upstream gap closes. No behavioural change -- comment only. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
|
🟡 Contributor Reputation Check: MEDIUM risk
Maintainers: please review this contributor before merging. |
feat(marketplace): adopt
apm packas canonical marketplace.json builderTL;DR
Replaces awesome-copilot's bespoke
marketplace-generatorscript withapm pack— the same generator microsoft/apm ships for everyone else producing a Claude Code / Copilot CLI marketplace from a folder of plugins. Adds a two-gate PR check (apm audit --ci+ drift detection) wired throughmicrosoft/apm-action@v1so contributors who editapm.ymlwithout rebuilding fail closed. External (non-local) plugin entries fromplugins/external.jsonkeep flowing in via a smalleng/merge-external-plugins.mjsbridge — fidelity to the current 81-entry shape is preserved exactly.Note
No consumer-visible artefact moves.
.github/plugin/marketplace.jsonstays at the same path, same shape, 81 entries. Anthropic / Copilot CLI install URLs are unchanged.Problem (WHY)
marketplace-generatoris a one-off script that lives only in this repo. Every fix has to be re-derived from the marketplace.json spec instead of inherited from the upstream tool that already validates it.marketplace.jsonmatches the inputs. A contributor who editsapm.ymland forgets to re-runnpm run buildships drift, and the next consumer install picks it up silently.apm audit --ci— no lockfile fidelity check, no orphan-package check, no content-integrity check. The moment awesome-copilot adopts a realdependencies:block, those gaps become real.Why these matter: the producer guide states the contract — ".claude-plugin/marketplace.json — generated by
apm pack" — and the rationale for moving to a generator-checked build pipeline is grounded in PROSE: "Grounding outputs in deterministic tool execution transforms probabilistic generation into verifiable action.". A hand-maintained or hand-regeneratable marketplace.json cannot be deterministically verified at PR time.Approach (WHAT)
apm.ymlwith 64 local packages undermarketplace.packages[]; letapm packproduce.github/plugin/marketplace.json(the renamepackages:→plugins:is the only structural transformapm packperforms).plugins/external.jsonas the registry for the 17 third-party entries; merge them post-pack viaeng/merge-external-plugins.mjs, alphabetise, re-write.microsoft/apm-action@v1into a newvalidate-marketplace.ymlPR gate: Gate A runsapm audit --ci(supply-chain), Gate B regenerates andgit diffs against the committed file (drift).apmonce inpublish.ymlso the build step uses the same binary CI does (no system-vs-local drift).CONTRIBUTING.mdandeng/README.md. Bridge stays ineng/so the build is onenpm run buildaway from contributor reach.Implementation (HOW)
apm.yml(+283) — new manifest.marketplace.packages[]lists 64 local plugins (source: ./plugins/<name>). Header comment documents the--marketplace claude --marketplace-pathflag pair (apm 0.14.0).eng/merge-external-plugins.mjs(+76) — minimal Node bridge: readsplugins/external.json(17 entries), appends to the pack-producedmarketplace.json, sorts alphabetically byname, writes back. Documents the F3 schema delta (legacysource: githubshape vs. apm 0.14.0'ssource: git-subdir) in an inline comment so future contributors do not collapse the two prematurely.package.json(+5) —buildrewired toapm pack --marketplace claude --marketplace-path claude=.github/plugin/marketplace.json && node ./eng/merge-external-plugins.mjs. The legacybuild:generatortarget is preserved for parity-diffing during the transition..github/workflows/validate-marketplace.yml(+116, new) — two-gate PR check. Action SHAs pinned (actions/checkout@v6.0.2,actions/setup-node@v6.4.0) to dodge the Node 20 deprecation. Comment block points at DevExpGbb/zava-agent-config'sapm-audit.ymlas the canonical reusable-workflow reference for repos that do have adependencies:block..github/workflows/publish.yml(+7) —microsoft/apm-action@v1step beforenpm run build; ensures the release-time build uses the sameapmbinary the PR gate does.CONTRIBUTING.md(+17) — Prerequisites + Register-in-apm.yml section.eng/README.md(+21) — explains the merge bridge for contributors landing ineng/cold..github/plugin/marketplace.json(302-line diff, mostly field reordering) — content is byte-equivalent to the previous generator output for all 81 entries; the diff is dominated by JSON key-order normalisationapm packperforms.Diagrams
Legend: build pipeline showing how the two source-of-truth files (
apm.yml,plugins/external.json) flow throughapm packand the merge bridge to produce the single committedmarketplace.json. The dashed node is the deprecated path kept for parity diffing only.flowchart LR subgraph Inputs[Source of truth] A1[apm.yml<br/>64 local packages] A2[plugins/external.json<br/>17 external entries] end subgraph Build[npm run build] B1[apm pack<br/>--marketplace claude]:::new B2[eng/merge-external-plugins.mjs<br/>append + sort]:::new end subgraph Output[Committed artefact] O1[.github/plugin/marketplace.json<br/>81 entries] end A1 --> B1 --> B2 --> O1 A2 --> B2 L1[eng/marketplace-generator<br/>legacy, parity-only]:::old -.-> O1 classDef new stroke-dasharray: 5 5; classDef old stroke-dasharray: 2 6,stroke:#888,color:#888;Legend: PR-time gate sequence — both gates run inside the same
validate-marketplacejob after a singleapm-action@v1install, then drift detection regenerates and diffs.sequenceDiagram participant PR as Pull Request participant Job as validate-marketplace job participant Action as microsoft/apm-action@v1 participant Apm as apm CLI participant Git as git diff PR->>Job: triggered on path globs Job->>Action: install apm + auth Action-->>Job: apm ready rect rgb(255, 247, 200) Note over Job,Apm: Gate A — supply chain Job->>Apm: apm audit --ci Apm-->>Job: pass — no deps today, fires on first dep Note over Job,Git: Gate B — drift Job->>Apm: apm pack + merge bridge Apm-->>Job: regenerated marketplace.json Job->>Git: diff vs committed file Git-->>Job: clean OR fail with hunk end Job-->>PR: status checkTrade-offs
eng/rather than upstreaming external plugins intoapm.yml. The legacyexternal.jsonshape (source: github) is not byte-equivalent to apm 0.14.0'ssource: git-subdir. Inlining now would force a one-shot rewrite of all 17 external entries; the bridge defers that until apm grows nativeexternal:support, keeping this PR surgical.marketplace-generatorscript andbuild:generatornpm target. Lets a maintainer run both pipelines and diff for one release cycle. Deleted in a follow-up once we have signal from real contributors.apm audit --ci, notapm marketplace check, as Gate A. The producer-side validatorapm marketplace checkis documented as "the gate to run in CI", but in apm 0.14.0 it does not short-circuit local-path sources — it tries togit ls-remoteevery./plugins/<name>entry and false-fails.apm audit --cishort-circuits cleanly today and fires automatically when awesome-copilot adopts a realdependencies:block. We flagged the marketplace-check gap upstream.actions/checkout@v6.0.2/actions/setup-node@v6.4.0rather than floating@v4. Upstreamawesome-copilotworkflows still use@v4, which Node 20 deprecation will break. Pinning a moving floating tag here is the smaller change than rewriting every other workflow.Benefits
apm.yml, not editing two files.npm run buildsees a red check with the exact hunk, not a silent broken install onstaged.dependencies:block,apm audit --cienforces lockfile fidelity, ref consistency, and content integrity without any further CI work.Validation
CI on this PR (head
27fba11):Local build verification (apm 0.14.0)
Scenario Evidence
apm.ymland runsnpm run build;.github/plugin/marketplace.jsonis updated and consumer install paths are unchanged.validate-marketplace.ymlGate B (drift) — green CI on this PR is the proofapm.ymland forgets to rebuild; CI fails closed with a readable diff hunk.validate-marketplace.ymlGate B (drift) — locally verified by mutatingapm.ymland confirming Gate B reports the diffdependencies:block, lockfile / orphan-package / content-integrity violations fail the PR.validate-marketplace.ymlGate A (apm audit --ci) — short-circuits cleanly today; fires on first dep.github/plugin/marketplace.jsonpath unchanged; 81 entries preserved by byte-diff against pre-PR fileHow to test
npm run build; confirmgit statusis clean (drift gate green locally).apm.yml'smarketplace.packages[], push, observevalidate-marketplaceGate B fail with the diff hunk.npm run build, push, observe Gate B green..github/plugin/marketplace.json— confirm 81 entries, alphabetised, install URLs unchanged.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com