#!/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 # -> /jobbsok.plugin # bash scripts/package_plugin.sh --ut # -> 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"