fix(deploy): stage installer download and copy runtime assets no-clobber (FW-D1)
deploy/install.sh extracted the repo archive in-place with `tar --strip-components=1`, which inside an existing project could silently overwrite the target's own README.md/FUTURE_WORKS.md/etc and litter it with this repo's dev docs. Rebuild the fetch path: - stage the clone/extract into a `mktemp -d` dir, never in-place - verify `.agents/skills/lib.sh` is present before copying anything - copy only runtime assets (.agents/, AGENT.md, .env.example) into the target with per-file no-clobber guards (`[ ! -e ]`), so existing files always win - post-fetch sanity check now tests a file, not just the directory - fail fast when neither git nor curl is available Use explicit `[ ! -e ]` guards + a POSIX find merge rather than `cp -n` (non-portable; emits a deprecation warning on GNU coreutils 9.x). The earlier `tar --exclude` denylist idea was rejected in review: non-portable and the unanchored `--exclude="scripts"` pattern stripped the skills' own nested scripts/ dirs, yielding a silently broken install. Mark FW-D1 resolved and FW-D2 partially addressed in FUTURE_WORKS.md/.ko.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+63
-13
@@ -54,24 +54,74 @@ echo "✅ PyYAML (system dependency) detected."
|
||||
mkdir -p "$TARGET_DIR"
|
||||
cd "$TARGET_DIR"
|
||||
|
||||
# Download .agents/skills if missing (for curl one-liner installs)
|
||||
if [ ! -d ".agents/skills" ]; then
|
||||
echo "📥 .agents/skills not found. Fetching from Gitea repository..."
|
||||
if command -v git &>/dev/null && [ -z "$(ls -A 2>/dev/null || echo "")" ]; then
|
||||
echo "🌐 Cloning Gitea repository..."
|
||||
git clone "https://git.godopu.com/tmpl/multi-agent-mux.git" .
|
||||
REPO_URL="https://git.godopu.com/tmpl/multi-agent-mux.git"
|
||||
ARCHIVE_URL="https://git.godopu.com/tmpl/multi-agent-mux/archive/main.tar.gz"
|
||||
|
||||
# Fetch the orchestration assets if missing (for curl one-liner installs).
|
||||
#
|
||||
# Safety model (FW-D1): we NEVER extract the repo archive directly into the
|
||||
# target. Running inside an existing project must not overwrite the target's
|
||||
# own files (README.md, FUTURE_WORKS.md, …) or litter it with this repo's
|
||||
# development docs. Instead we stage the download into a throwaway temp dir,
|
||||
# verify it, then copy ONLY the runtime assets (.agents/, AGENT.md, .env.example)
|
||||
# into the target with per-file no-clobber guards so a pre-existing target file
|
||||
# always wins. We key off lib.sh (a file), not the .agents/skills directory, so
|
||||
# an empty/partial dir can't masquerade as a valid install.
|
||||
if [ ! -f ".agents/skills/lib.sh" ]; then
|
||||
echo "📥 Orchestration skills not found. Fetching from Gitea repository..."
|
||||
STAGE_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$STAGE_DIR"' EXIT
|
||||
|
||||
if command -v git &>/dev/null; then
|
||||
echo "🌐 Cloning repository (shallow) into a staging area..."
|
||||
git clone --depth 1 "$REPO_URL" "$STAGE_DIR"
|
||||
elif command -v curl &>/dev/null; then
|
||||
echo "🌐 Downloading and extracting archive into a staging area..."
|
||||
curl -fsSL "$ARCHIVE_URL" | tar -xz --strip-components=1 -C "$STAGE_DIR"
|
||||
else
|
||||
echo "🌐 Downloading and extracting skills archive..."
|
||||
curl -fsSL "https://git.godopu.com/tmpl/multi-agent-mux/archive/main.tar.gz" | tar -xz --strip-components=1
|
||||
fi
|
||||
|
||||
if [ ! -d ".agents/skills" ]; then
|
||||
echo "❌ Error: Fetch completed but '.agents/skills' is still missing. Target layout might be invalid." >&2
|
||||
echo "❌ Error: neither 'git' nor 'curl' is available to fetch the skills." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Skills downloaded successfully."
|
||||
|
||||
# Verify the staged tree before we trust and copy from it.
|
||||
if [ ! -f "$STAGE_DIR/.agents/skills/lib.sh" ]; then
|
||||
echo "❌ Error: fetched source is missing '.agents/skills/lib.sh'. Aborting (no files copied)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Copy ONLY runtime assets into the target, never overwriting an existing
|
||||
# target file. We merge per-file (POSIX find + an explicit "[ ! -e ]" guard)
|
||||
# instead of `cp -n`: `cp -n` is non-portable and now prints a deprecation
|
||||
# warning on GNU coreutils 9.x, whereas the explicit guard is portable to
|
||||
# BSD/macOS and makes the no-clobber intent obvious.
|
||||
mkdir -p .agents
|
||||
( cd "$STAGE_DIR/.agents" && find . -type f -print ) | while IFS= read -r rel; do
|
||||
dest=".agents/${rel#./}"
|
||||
if [ ! -e "$dest" ]; then
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp "$STAGE_DIR/.agents/$rel" "$dest"
|
||||
fi
|
||||
done
|
||||
if [ -f "$STAGE_DIR/AGENT.md" ] && [ ! -e "AGENT.md" ]; then
|
||||
cp "$STAGE_DIR/AGENT.md" .
|
||||
fi
|
||||
if [ -f "$STAGE_DIR/.env.example" ] && [ ! -e ".env.example" ]; then
|
||||
cp "$STAGE_DIR/.env.example" .
|
||||
fi
|
||||
|
||||
rm -rf "$STAGE_DIR"
|
||||
trap - EXIT
|
||||
echo "✅ Skills staged into workspace (existing files preserved)."
|
||||
fi
|
||||
|
||||
# Sanity check: verify a FILE, not just the directory — an empty .agents/skills
|
||||
# would otherwise pass and yield a silently broken install.
|
||||
if [ ! -f ".agents/skills/lib.sh" ]; then
|
||||
echo "❌ Error: '.agents/skills/lib.sh' missing after setup. Target layout might be invalid." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Orchestration skills present."
|
||||
|
||||
echo "📂 Ensuring metadata directory structure (.mam/)..."
|
||||
mkdir -p .mam/jobs .mam/delegate_job_logs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user