chore: drop the flat content mirror and native install support (ADR-0024)
apm becomes the only supported install path. The flat mirror at each plugin root existed solely so Claude Code's native `claude plugin install` could convention-scan plugin content (ADR-0017). With no native consumers, it cost ~20,000 tracked lines plus ~2,100 lines of sync tooling and ~88s of every push to guard content apm never reads — and its only automated gate, `claude plugin validate --strict`, passes on a plugin with zero content, so it could not detect the defect ADR-0017 was created to fix. Removes the mirror (213 files), the six per-plugin manifest pairs, sync-plugin-content.sh, its 1,289-line test, the orphaned marketplace-plugins.sh, and the check-plugin-content-sync and validate-plugins pre-push hooks. The root `marketplace:` block and .claude-plugin/ catalogue stay: apm's own marketplace consumers read that same file, so `<name>@holocron` short names keep working. tests/run-bats.sh now excludes .claude/skills/. apm installs from .apm/, which carries the tests/ dirs the mirror stripped, so deployed .bats files would otherwise be discovered and double-run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YR2CjVumUbEGWcMikcoXBD
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared bounded-batch concurrent job runner. Sourced by
|
||||
# scripts/sync-plugin-content.sh, tests/run-tests.sh, and tests/run-bats.sh so
|
||||
# their concurrency-cap and per-item log/status handling can't silently
|
||||
# diverge -- previously the same batching logic (core-count cap, per-item
|
||||
# log/status files, batched `wait`) was hand-implemented independently in all
|
||||
# three places.
|
||||
# Shared bounded-batch concurrent job runner. Sourced by tests/run-tests.sh and
|
||||
# tests/run-bats.sh so their concurrency-cap and per-item log/status handling
|
||||
# can't silently diverge -- previously the same batching logic (core-count cap,
|
||||
# per-item log/status files, batched `wait`) was hand-implemented independently
|
||||
# in each caller.
|
||||
#
|
||||
# Batches (not a rolling pool) because a bounded rolling pool needs `wait -n`,
|
||||
# which is bash 4.3+ -- all three callers are explicitly bash-3.2-safe.
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared helper: enumerates the local (string-source) plugin entries declared in
|
||||
# .claude-plugin/marketplace.json. Sourced by scripts/sync-plugin-content.sh
|
||||
# (--all), kept as its own file so a future marketplace.json schema change (e.g.
|
||||
# a new source type) has one place to land if a second caller needs the walk.
|
||||
#
|
||||
# Requires jq. Not meant to be executed directly -- source it.
|
||||
|
||||
# assert_marketplace_manifest_usable <marketplace_json_path>
|
||||
#
|
||||
# Checks the preconditions list_marketplace_local_plugins depends on but cannot
|
||||
# report on. The caller runs the walk inside a process substitution
|
||||
# (`done < <(list_marketplace_local_plugins ...)`), which is its own subshell: a
|
||||
# jq abort in there kills only that subshell, so an unparseable manifest yields
|
||||
# zero lines and reads exactly like "this marketplace declares no local plugins".
|
||||
# sync-plugin-content.sh --all's own empty-set check (see there) is what catches
|
||||
# that reading; it cannot tell an empty manifest from an unparseable one on its
|
||||
# own, which is why this assert has to run first, in the caller's own shell.
|
||||
#
|
||||
# It also rejects an entry whose `source` is neither a local path string nor a
|
||||
# remote source object. Only those two shapes are classifiable: the walk below
|
||||
# takes the string ones, and the object ones are remote. Anything else -- absent
|
||||
# (`null`), a number, an array, a boolean -- is neither, so it silently drops out
|
||||
# of every marketplace-derived work list: this walk's and, through it,
|
||||
# sync-plugin-content.sh --all's.
|
||||
#
|
||||
# The check is deliberately typed as "not string AND not object" rather than
|
||||
# enumerating `.source == null`. Rejecting null specifically left every other
|
||||
# malformed value (`"source": 42`, `"source": []`) passing the assert while
|
||||
# silently dropping out of the walk below -- i.e. exactly the defect the null
|
||||
# case was fixed for, reached with a different value.
|
||||
#
|
||||
# Exits 1 with a specific message on any violation, so call it from the caller's
|
||||
# own shell -- never inside `< <(...)`, which is the exact swallowing this guards.
|
||||
assert_marketplace_manifest_usable() {
|
||||
local marketplace="$1" root_type plugins_type unclassifiable
|
||||
|
||||
if ! jq empty "$marketplace" >/dev/null 2>&1; then
|
||||
echo "Error: $marketplace is not valid JSON -- every marketplace-derived check reads as \"no plugins declared\" until it parses. Fix it, or recompile it with \`apm pack\`." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# `jq empty` passes on any valid JSON document, including `[]`, `"x"` and `123`.
|
||||
# The `.plugins` lookup on the next line then aborts with a raw
|
||||
# `jq: error: Cannot index array with string "plugins"` and rc=5, attributed to
|
||||
# nothing -- so assert the root shape here, where it can be named.
|
||||
root_type="$(jq -r 'type' "$marketplace")"
|
||||
if [[ "$root_type" != "object" ]]; then
|
||||
echo "Error: $marketplace is a JSON $root_type at its top level; expected an object with a \`plugins\` array. Recompile it with \`apm pack\`." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
plugins_type="$(jq -r '.plugins | type' "$marketplace")"
|
||||
if [[ "$plugins_type" != "array" && "$plugins_type" != "null" ]]; then
|
||||
echo "Error: $marketplace has a \`plugins\` field of type $plugins_type; expected an array of plugin entries." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unclassifiable="$(jq -r '[.plugins[]?
|
||||
| select((.source | type) as $t | $t != "string" and $t != "object")
|
||||
| "\(.name // "<unnamed>") (source: \(.source | type))"] | join(", ")' "$marketplace")"
|
||||
if [[ -n "$unclassifiable" ]]; then
|
||||
echo "Error: $marketplace has entries whose \`source\` is neither a local path string nor a remote source object: $unclassifiable. Such an entry is neither local nor remote, so it is skipped by every marketplace-derived check while still claiming its name. Fix it in root apm.yml's marketplace.packages[] and recompile." >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# list_marketplace_local_plugins <repo_root> <marketplace_json_path>
|
||||
#
|
||||
# Prints one "<name>\t<absolute_plugin_dir>" line per local (string `source:`)
|
||||
# marketplace entry. Remote sources (github/git/npm objects) have no local
|
||||
# directory to walk and are skipped.
|
||||
list_marketplace_local_plugins() {
|
||||
local repo_root="$1" marketplace="$2"
|
||||
local plugin_count i name source_type source
|
||||
|
||||
plugin_count="$(jq '.plugins | length' "$marketplace")"
|
||||
for ((i = 0; i < plugin_count; i++)); do
|
||||
source_type="$(jq -r ".plugins[$i].source | type" "$marketplace")"
|
||||
[[ "$source_type" == "string" ]] || continue
|
||||
name="$(jq -r ".plugins[$i].name" "$marketplace")"
|
||||
source="$(jq -r ".plugins[$i].source" "$marketplace")"
|
||||
source="${source#./}"
|
||||
printf '%s\t%s\n' "$name" "$repo_root/$source"
|
||||
done
|
||||
}
|
||||
Reference in New Issue
Block a user