Files
holocron/tests/test-sync-marketplace-mirror.sh
Defame1297 9e612fd183 fix(scripts): re-inject .mcp.json as a pointer, not resolved content
reinject_mcp_servers copied .mcp.json's mcpServers into the compiled Copilot
manifest verbatim via jq. apm's own path does not: collect_mcp_servers runs
_sanitize_mcp_servers(), which drops env/headers/authorization and redacts
secret-shaped keys, because copying them into a committed manifest exfiltrates
them into the distributed artefact. The re-injection was the only route around
that sanitizer, and it wrote to a tracked, marketplace-distributed file.

Both host schemas document mcpServers as "string or object -- config path or
inline definitions", so the pointer form is valid and carries no resolved
content. It also preserves the ${VAR} indirection the sanitizer strips.

Also in this pass:
- mktemp+mv left the manifest at 0600 while --check compared content only, so
  a real sync silently demoted a mode the gate could not see
- --check --all exited 0 when the marketplace yielded zero plugins, including
  on unparseable JSON: the one gate whose work list comes from a generated file
  could be silenced by regenerating its own input
- sync_dir took an unguarded $target_dir despite a comment claiming otherwise
- basename '.'/'..' escaped $SCRATCH_ROOT and made bundle selection arbitrary
- path_manifest compared only the exec bit, so check and sync disagreed
- sync-marketplace-mirror.sh fell back to pwd outside a worktree and reported
  no drift on a tree it never identified

Mode comparison is deliberately files-only: directory modes come from umask on
one side and checkout on the other and git tracks neither, so comparing them
reports the runner's umask rather than a property of the mirror.

Tests: 44 -> 67 and 15 -> 19 assertions, each verified to fail under the
mutation it exists to catch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X7GvKuJfy2WrdBmUttV4DT
2026-08-14 11:03:45 +00:00

305 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SCRIPT="$REPO_ROOT/scripts/sync-marketplace-mirror.sh"
PASS=0
FAIL=0
pass() { echo " PASS: $1"; PASS=$((PASS + 1)); }
fail() { echo " FAIL: $1"; FAIL=$((FAIL + 1)); }
# Fixture: a temp directory that is its own git worktree. Since
# scripts/sync-marketplace-mirror.sh resolves REPO_ROOT via
# `git rev-parse --show-toplevel 2>/dev/null || pwd`, isolation has to come
# from that call answering "the fixture" -- so the fixture owns a real .git.
#
# Relying instead on `git rev-parse` FAILING inside a bare `mktemp -d` (its
# previous form) is not isolation: two ordinary conditions make it succeed and
# resolve to the LIVE repo, at which point every test writes to this repo's own
# tracked .github/plugin/marketplace.json. Both are reproduced and fixed here:
# - TMPDIR pointing inside a git worktree, which puts the fixture in one.
# - An inherited GIT_DIR/GIT_WORK_TREE, which re-targets `git -C` and
# `git rev-parse` regardless of cwd. tests/run-tests.sh is itself a pre-push
# hook, and git hooks export exactly those variables -- the same leak
# tests/test-git-hooks-install.sh:6-10 already defends against.
# run_script() strips GIT_DIR/GIT_WORK_TREE for the second; `git init` here
# covers the first and makes the resolution positive rather than accidental.
# run-tests.sh:52-53 asserts no test writes back into the live repo tree, and
# that claim now carries the concurrent runner.
make_fixture() {
local dir
dir="$(mktemp -d)"
# Physical path: `git rev-parse --show-toplevel` reports the resolved path,
# and on macOS `mktemp -d` hands back one under the /tmp -> /private/tmp
# symlink. Without -P the script's REPO_ROOT and the assertions' $FIXTURE
# would name the same directory differently and every diff would compare
# against a path the script never wrote.
dir="$(cd "$dir" && pwd -P)"
# Checked, not best-effort: this init IS the isolation invariant. `echo "$dir"`
# is the last statement, so a silently-failed init would return a plain temp
# dir, `git rev-parse --show-toplevel` would walk up to whatever repo encloses
# it, and the suite would go back to writing into the live tree.
if ! env -u GIT_DIR -u GIT_WORK_TREE git -C "$dir" init -q >/dev/null 2>&1; then
echo "make_fixture: 'git init' failed in $dir — every test would then resolve REPO_ROOT to an enclosing repo and write outside the fixture" >&2
exit 1
fi
echo "$dir"
}
SRC_REL=".claude-plugin/marketplace.json"
DST_REL=".github/plugin/marketplace.json"
CONTENT_A='{"plugins":[{"name":"demo","version":"1.0.0"}]}'
CONTENT_B='{"plugins":[{"name":"demo","version":"2.0.0"}]}'
write_src() {
local dir="$1" content="$2"
mkdir -p "$dir/.claude-plugin"
printf '%s' "$content" > "$dir/$SRC_REL"
}
write_dst() {
local dir="$1" content="$2"
mkdir -p "$dir/.github/plugin"
printf '%s' "$content" > "$dir/$DST_REL"
}
# `env -u GIT_DIR -u GIT_WORK_TREE` mirrors tests/test-git-hooks-install.sh:10:
# under a git hook (run-tests.sh runs as pre-push) those are exported, and the
# script's `git rev-parse --show-toplevel` would then answer with the LIVE repo
# no matter which directory it was invoked from.
run_script() {
local dir="$1"
shift
(cd "$dir" && env -u GIT_DIR -u GIT_WORK_TREE bash "$SCRIPT" "$@")
}
CLEANUP_DIRS=()
trap 'rm -rf ${CLEANUP_DIRS[@]+"${CLEANUP_DIRS[@]}"}' EXIT
track() { CLEANUP_DIRS+=("$1"); }
# --- 1. No .claude-plugin/marketplace.json at all: real-sync mode is a no-op, exit 0 ---
echo ""
echo "--- missing source: real sync exits 0 and creates nothing ---"
FIXTURE1="$(make_fixture)"; track "$FIXTURE1"
if run_script "$FIXTURE1" > /dev/null 2>&1 && [[ ! -e "$FIXTURE1/$DST_REL" ]]; then
pass "missing source exits 0 in real-sync mode without creating a mirror"
else
fail "missing source should exit 0 and create nothing in real-sync mode"
fi
# --- 2. No source: --check mode is also a no-op, exit 0 (nothing to drift-check) ---
echo ""
echo "--- missing source: --check exits 0 (nothing to compare) ---"
FIXTURE2="$(make_fixture)"; track "$FIXTURE2"
if run_script "$FIXTURE2" --check > /dev/null 2>&1; then
pass "missing source exits 0 in --check mode"
else
fail "missing source should exit 0 in --check mode, not report drift"
fi
# --- 2b. Source missing but a mirror still present: --check must FAIL ---
# --check used to exit 0 on any missing source, so deleting
# .claude-plugin/marketplace.json left a stale .github/plugin/marketplace.json
# reported as "no drift" -- a mirror of a file that no longer exists. That is
# the silent divergence this script's header says it prevents ("keeps that
# legacy mirror byte-identical ... instead of letting it silently drift"), and
# scripts/sync-plugin-content.sh --check --all already errors on the same
# condition. Case 2 above still holds: neither file present stays a no-op.
echo ""
echo "--- missing source with a surviving mirror: --check reports drift ---"
FIXTURE2B="$(make_fixture)"; track "$FIXTURE2B"
write_dst "$FIXTURE2B" "$CONTENT_A"
if run_script "$FIXTURE2B" --check > /dev/null 2>&1; then
fail "exited 0 with a stale mirror and no source -- expected drift (exit 1)"
else
pass "a mirror with no source left to mirror is reported as drift"
fi
# Real-sync mode keeps its no-op: it has nothing to copy, and deleting a
# tracked file is not this script's call to make.
if run_script "$FIXTURE2B" > /dev/null 2>&1 && [[ -f "$FIXTURE2B/$DST_REL" ]]; then
pass "real-sync mode still no-ops on a missing source, leaving the mirror alone"
else
fail "real-sync mode should no-op on a missing source, not fail or delete the mirror"
fi
# --- 3. Source exists, mirror missing entirely: --check reports drift (exit 1) ---
echo ""
echo "--- --check reports drift when the mirror file does not exist yet ---"
FIXTURE3="$(make_fixture)"; track "$FIXTURE3"
write_src "$FIXTURE3" "$CONTENT_A"
if run_script "$FIXTURE3" --check > /dev/null 2>&1; then
fail "exited 0 with no mirror file present -- expected drift (exit 1)"
else
pass "missing mirror file is reported as drift"
fi
# --- 4. Real sync creates the mirror (and its parent dir) byte-identical to the source ---
echo ""
echo "--- real sync creates .github/plugin/marketplace.json byte-identical to source ---"
FIXTURE4="$(make_fixture)"; track "$FIXTURE4"
write_src "$FIXTURE4" "$CONTENT_A"
if run_script "$FIXTURE4" > /dev/null 2>&1 \
&& [[ -f "$FIXTURE4/$DST_REL" ]] \
&& diff -q "$FIXTURE4/$SRC_REL" "$FIXTURE4/$DST_REL" > /dev/null 2>&1; then
pass "real sync creates the mirror file identical to the source"
else
fail "real sync did not create a byte-identical mirror file"
fi
# --- 5. --check is clean immediately after a real sync ---
echo ""
echo "--- --check is clean right after syncing ---"
if run_script "$FIXTURE4" --check > /dev/null 2>&1; then
pass "no drift reported immediately after syncing"
else
fail "drift reported right after syncing -- sync and check disagree"
fi
# --- 6. Editing the source after a sync introduces drift; re-sync clears it ---
echo ""
echo "--- source edited after sync is detected as drift, cleared by re-sync ---"
write_src "$FIXTURE4" "$CONTENT_B"
if run_script "$FIXTURE4" --check > /dev/null 2>&1; then
fail "no drift reported after editing the source -- expected drift"
else
pass "editing the source after a sync is detected as drift"
run_script "$FIXTURE4" > /dev/null 2>&1
if run_script "$FIXTURE4" --check > /dev/null 2>&1; then
pass "re-sync clears the drift"
else
fail "re-sync did not clear the drift"
fi
fi
# --- 7. A mirror that was hand-seeded already identical to the source: --check is clean ---
echo ""
echo "--- --check is clean when the mirror already matches, without ever syncing ---"
FIXTURE7="$(make_fixture)"; track "$FIXTURE7"
write_src "$FIXTURE7" "$CONTENT_A"
write_dst "$FIXTURE7" "$CONTENT_A"
if run_script "$FIXTURE7" --check > /dev/null 2>&1; then
pass "an already-in-sync mirror reports no drift"
else
fail "an already-in-sync mirror should report no drift"
fi
# --- 8. A stale, pre-existing mirror that differs from source: --check flags it, sync fixes it ---
echo ""
echo "--- a stale pre-existing mirror is flagged by --check and overwritten by sync ---"
FIXTURE8="$(make_fixture)"; track "$FIXTURE8"
write_src "$FIXTURE8" "$CONTENT_A"
write_dst "$FIXTURE8" "$CONTENT_B"
if run_script "$FIXTURE8" --check > /dev/null 2>&1; then
fail "a stale mirror was not flagged as drift"
else
pass "a stale pre-existing mirror is flagged as drift"
fi
run_script "$FIXTURE8" > /dev/null 2>&1
if diff -q "$FIXTURE8/$SRC_REL" "$FIXTURE8/$DST_REL" > /dev/null 2>&1; then
pass "real sync overwrites the stale mirror to match the source"
else
fail "real sync did not overwrite the stale mirror"
fi
# --- 9. An unknown/extra positional argument is rejected with a usage error ---
echo ""
echo "--- an unrecognized argument is rejected (usage, exit 1) ---"
FIXTURE9="$(make_fixture)"; track "$FIXTURE9"
write_src "$FIXTURE9" "$CONTENT_A"
if run_script "$FIXTURE9" --bogus > /dev/null 2>&1; then
fail "exited 0 with an unrecognized argument -- expected a usage error"
else
pass "an unrecognized argument is rejected"
fi
# --- 10. --check followed by a stray extra argument is also rejected ---
echo ""
echo "--- --check plus a trailing extra argument is rejected (usage, exit 1) ---"
if run_script "$FIXTURE9" --check extra > /dev/null 2>&1; then
fail "exited 0 with --check plus a trailing argument -- expected a usage error"
else
pass "--check plus a trailing extra argument is rejected"
fi
# --- 11. Real sync is idempotent: running it twice in a row leaves the mirror unchanged ---
echo ""
echo "--- running real sync twice in a row is idempotent ---"
FIXTURE11="$(make_fixture)"; track "$FIXTURE11"
write_src "$FIXTURE11" "$CONTENT_A"
run_script "$FIXTURE11" > /dev/null 2>&1
run_script "$FIXTURE11" > /dev/null 2>&1
if run_script "$FIXTURE11" --check > /dev/null 2>&1; then
pass "running sync twice in a row is idempotent"
else
fail "a second sync run introduced unexpected drift"
fi
# --- 12. Fixture isolation survives an inherited GIT_DIR/GIT_WORK_TREE ---
# The whole suite's isolation is REPO_ROOT resolving to the fixture. With
# GIT_DIR/GIT_WORK_TREE exported -- which is every git-hook context, and
# run-tests.sh runs as pre-push -- `git rev-parse --show-toplevel` answers with
# THAT repo from any cwd, so the script wrote to the live tree and 5 of these
# cases failed. Point both variables at a decoy repo (never the live one, so
# this assertion cannot itself write where it must not) and assert the fixture
# still wins: the mirror lands in the fixture and the decoy stays untouched.
echo ""
echo "--- fixture isolation holds with GIT_DIR/GIT_WORK_TREE inherited from elsewhere ---"
FIXTURE12="$(make_fixture)"; track "$FIXTURE12"
DECOY="$(make_fixture)"; track "$DECOY"
write_src "$FIXTURE12" "$CONTENT_A"
if (export GIT_DIR="$DECOY/.git" GIT_WORK_TREE="$DECOY"; run_script "$FIXTURE12" > /dev/null 2>&1) \
&& [[ -f "$FIXTURE12/$DST_REL" ]] && [[ ! -e "$DECOY/$DST_REL" ]]; then
pass "an inherited GIT_DIR/GIT_WORK_TREE does not redirect writes out of the fixture"
else
fail "an inherited GIT_DIR/GIT_WORK_TREE redirected the sync outside the fixture"
fi
# --- 13. Outside any git worktree, REPO_ROOT cannot be guessed: hard error ---
# `git rev-parse --show-toplevel 2>/dev/null || pwd` used to fall back to $PWD.
# Both of this script's exit-0 paths are "the two files agree" or "neither file
# exists", so a REPO_ROOT that is not this repo reports "no drift" over a tree it
# never inspected — run --check from an empty non-worktree directory and that was
# the literal outcome. Case 2 above (a real worktree with no source file) still
# exits 0; the difference is whether the tree was identified at all.
#
# GIT_CEILING_DIRECTORIES rather than trusting `mktemp -d` to land outside a
# worktree: TMPDIR may itself sit inside one (the same hazard make_fixture's
# header documents), in which case rev-parse would succeed and this case would
# quietly test nothing. The ceiling stops git's upward walk at the fixture's
# parent, and the precondition below asserts it actually did.
echo ""
echo "--- outside a git worktree, --check errors instead of guessing \$PWD ---"
NOREPO_PARENT="$(mktemp -d)"; track "$NOREPO_PARENT"
NOREPO_PARENT="$(cd "$NOREPO_PARENT" && pwd -P)"
NOREPO="$NOREPO_PARENT/not-a-worktree"
mkdir -p "$NOREPO"
if (cd "$NOREPO" && env -u GIT_DIR -u GIT_WORK_TREE GIT_CEILING_DIRECTORIES="$NOREPO_PARENT" \
git rev-parse --show-toplevel > /dev/null 2>&1); then
fail "precondition: git rev-parse still resolves a worktree under the ceiling — this case would test nothing"
else
for MODE in "--check" ""; do
RC13=0
OUT13="$( (cd "$NOREPO" && env -u GIT_DIR -u GIT_WORK_TREE \
GIT_CEILING_DIRECTORIES="$NOREPO_PARENT" bash "$SCRIPT" ${MODE:+"$MODE"}) 2>&1 )" || RC13=$?
case "$RC13:$OUT13" in
0:*)
fail "'${MODE:-real sync}' exited 0 outside a git worktree — it reported on a tree it never identified" ;;
*"not inside a git worktree"*)
pass "'${MODE:-real sync}' errors with a not-a-worktree message instead of falling back to \$PWD" ;;
*)
fail "'${MODE:-real sync}' failed for an unexpected reason (rc=$RC13): $OUT13" ;;
esac
done
# And it must not have written anything into the directory it refused to trust.
if [[ ! -e "$NOREPO/.github" ]]; then
pass "nothing is written into the unidentified directory"
else
fail "the script created files under a directory it could not identify as the repo root"
fi
fi
echo ""
echo "Results: $PASS passed, $FAIL failed"
[[ $FAIL -eq 0 ]]