diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 4646a0f0..9e9902e4 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -30,3 +30,4 @@ jobs: ./scripts/test-install.sh bash scripts/test-install-openclaw-registration.sh bash scripts/test-install-link-ownership.sh + bash scripts/test-install-parallel-logs.sh diff --git a/scripts/install.sh b/scripts/install.sh index 1c6af93b..2bf82c52 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -1726,16 +1726,17 @@ main() { local installed=0 t i=0 rc local failed=() if $use_parallel; then - local install_out_dir + local install_out_dir install_status=0 install_out_dir="$(mktemp -d)" export AGENCY_INSTALL_OUT_DIR="$install_out_dir" export AGENCY_INSTALL_SCRIPT="$SCRIPT_DIR/install.sh" export AGENCY_INSTALL_EXTRA="$(worker_flags)" - printf '%s\n' "${SELECTED_TOOLS[@]}" | xargs -P "$parallel_jobs" -I {} sh -c 'AGENCY_INSTALL_WORKER=1 "$AGENCY_INSTALL_SCRIPT" --tool "{}" --no-interactive $AGENCY_INSTALL_EXTRA > "$AGENCY_INSTALL_OUT_DIR/{}" 2>&1' + printf '%s\n' "${SELECTED_TOOLS[@]}" | xargs -P "$parallel_jobs" -I {} sh -c 'AGENCY_INSTALL_WORKER=1 "$AGENCY_INSTALL_SCRIPT" --tool "{}" --no-interactive $AGENCY_INSTALL_EXTRA > "$AGENCY_INSTALL_OUT_DIR/{}" 2>&1' || install_status=$? for t in "${SELECTED_TOOLS[@]}"; do [[ -f "$install_out_dir/$t" ]] && cat "$install_out_dir/$t" done rm -rf "$install_out_dir" + [[ "$install_status" -eq 0 ]] || return "$install_status" installed=$n_selected else for t in "${SELECTED_TOOLS[@]}"; do diff --git a/scripts/test-install-parallel-logs.sh b/scripts/test-install-parallel-logs.sh new file mode 100755 index 00000000..1d8f000e --- /dev/null +++ b/scripts/test-install-parallel-logs.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +scratch="$(mktemp -d)" +trap 'rm -rf "$scratch"' EXIT +mkdir -p "$scratch/repo/scripts" "$scratch/repo/engineering" "$scratch/repo/integrations" "$scratch/home" +cp "$SCRIPT_DIR/install.sh" "$SCRIPT_DIR/lib.sh" "$scratch/repo/scripts/" +cp "$SCRIPT_DIR/../divisions.json" "$scratch/repo/" +export HOME="$scratch/home" +export CLAUDE_CONFIG_DIR="$HOME/.claude" COPILOT_AGENT_DIR="$HOME/.github/agents" +for agent in first second; do + printf '%s\n' '---' "name: $agent" 'description: Example agent' 'color: blue' '---' '# Example agent' > "$scratch/repo/engineering/$agent.md" +done +mkdir -p "$scratch/bin" "$scratch/tmp" +cat > "$scratch/bin/cp" <<'EOF' +#!/usr/bin/env bash +if [[ "$*" == *"/.claude/agents/"* && "${FAIL_WORKER:-yes}" == yes ]]; then + echo 'fixture worker copy failed' >&2 + exit 47 +fi +exec /bin/cp "$@" +EOF +chmod +x "$scratch/bin/cp" +export PATH="$scratch/bin:$PATH" TMPDIR="$scratch/tmp" +expected_status=0 +printf 'failure\n' | xargs -P 2 -I {} sh -c 'exit 47' || expected_status=$? +status=0 +bash "$scratch/repo/scripts/install.sh" --tool claude-code,copilot --division engineering \ + --no-convert --parallel --jobs 2 > "$scratch/failure.log" 2>&1 || status=$? +[[ "$status" == "$expected_status" && "$status" != 0 ]] || { cat "$scratch/failure.log"; echo "FAIL: worker failure status changed: $status"; exit 1; } +grep -q 'fixture worker copy failed' "$scratch/failure.log" || { + echo 'FAIL: buffered worker failure is hidden' >&2; exit 1; +} +[[ -f "$HOME/.github/agents/first.md" ]] +! grep -q 'Done! Installed' "$scratch/failure.log" +[[ -z "$(ls -A "$scratch/tmp")" ]] || { echo 'FAIL: worker output directory leaked'; exit 1; } +export FAIL_WORKER=no +bash "$scratch/repo/scripts/install.sh" --tool claude-code,copilot --division engineering \ + --no-convert --parallel --jobs 2 > "$scratch/success.log" 2>&1 +grep -q 'Done! Installed 2 tool(s)' "$scratch/success.log" +[[ -f "$HOME/.claude/agents/first.md" ]] +[[ -z "$(ls -A "$scratch/tmp")" ]] +echo 'PASS: failed parallel worker logs are replayed, status is preserved, and buffers are removed'