test(m1): assert plugin manifest and pin a build stamp

This commit is contained in:
Kjell Tore Guttormsen 2026-09-05 06:52:40 +02:00
commit 0e3295f52b
4 changed files with 421 additions and 0 deletions

98
scripts/package_plugin.sh Executable file
View file

@ -0,0 +1,98 @@
#!/bin/bash
# Build jobbsok.plugin from an explicit include list.
#
# bash 3.2-clean and ASCII-only on purpose: the system bash on this Mac is 3.2.
#
# The include list is the whole point, and it is not a convenience. zip does
# not consult .gitignore, so archiving the repository root would ship .git,
# the virtualenv from Step 2, the local-only STATE.md and everything under
# .claude/ -- which holds the operator's decisions and absolute home paths.
# This archive is uploaded to Cowork once per milestone and its command is
# printed in a public README. An include list is the only form that is safe to
# publish.
#
# The virtualenv is excluded for a second reason on top of that one: a venv's
# absolute paths do not survive relocation, so an archived one would be broken
# wherever it landed. The supported route in an installed copy is to run
# scripts/bootstrap.sh once against it, which builds the environment under
# $CLAUDE_PLUGIN_DATA. README.md says so.
#
# Usage:
# bash scripts/package_plugin.sh # -> <repo root>/jobbsok.plugin
# bash scripts/package_plugin.sh --ut <sti> # -> somewhere else
set -eu
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
OUT="${REPO_ROOT}/jobbsok.plugin"
while [ $# -gt 0 ]; do
case "$1" in
--ut)
if [ $# -lt 2 ]; then
echo "package_plugin: --ut needs a path" >&2
exit 2
fi
OUT="$2"
shift 2 ;;
-h|--help)
sed -n '2,23p' "$0"
exit 0 ;;
*)
echo "package_plugin: unknown argument: $1" >&2
exit 2 ;;
esac
done
case "$OUT" in
/*) ;;
*) OUT="$(pwd)/$OUT" ;;
esac
# Regenerate first: an archive carrying a stale stamp is the exact failure the
# stamp exists to catch, and building one would be worse than not stamping.
bash "${SCRIPT_DIR}/build_stamp.sh"
# The include list. Directories are archived whole, minus the exclusions below.
# Entries that do not exist yet are reported and skipped -- hooks/, templates/
# and SECURITY.md land in later steps -- but the two the archive is worthless
# without are required outright.
INCLUDE=".claude-plugin skills scripts hooks templates .mcp.json README.md SECURITY.md LICENSE BUILD_STAMP"
REQUIRED=".claude-plugin/plugin.json BUILD_STAMP"
for path in $REQUIRED; do
if [ ! -e "${REPO_ROOT}/${path}" ]; then
echo "package_plugin: required entry missing: $path" >&2
exit 1
fi
done
PRESENT=""
SKIPPED=""
for path in $INCLUDE; do
if [ -e "${REPO_ROOT}/${path}" ]; then
PRESENT="$PRESENT $path"
else
SKIPPED="$SKIPPED $path"
fi
done
if [ -n "$SKIPPED" ]; then
echo "package_plugin: not present yet, skipped:$SKIPPED"
fi
rm -f "$OUT"
# -X drops the extra file attributes, so the archive is reproducible enough
# that two builds of the same commit differ only in timestamps. The exclusions
# are belt to the include list's braces: nothing under scripts/ or skills/ that
# is a build artefact of a local test run may ride along.
cd "$REPO_ROOT"
# shellcheck disable=SC2086 -- PRESENT is a deliberate word-split list
zip -q -r -X "$OUT" $PRESENT \
-x '*/__pycache__/*' '*.pyc' '*/.pytest_cache/*' '*/.venv/*' '*.local.md' \
'*/.DS_Store'
echo "package_plugin: $(basename "$OUT") built from $(cat "${REPO_ROOT}/BUILD_STAMP")"
echo "package_plugin: $OUT"