mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 19:01:54 +00:00
The lint fan-out listed lint-php twice, once before and once after lint-playwright. Two commits added it independently:82218087c1for the PHP and Ruby syntax checks,6fee7ca136for the per-language unit suites, and the second did not see the first because lint-playwright had since been inserted between them. The wasted slot was the harmless half. parallel.sh keys target_start, target_dur, target_rc and target_log by target NAME, so a repeat overwrites the first run's log path and exit code, and the final pass reads each name once. Two consequences: drain_one prints the second run's log twice and the first run's output never appears, and a failing first invocation is reported green as soon as the second one passes. A duplicate therefore turns a target into a blind spot rather than into a slow one. The gate showed the symptom plainly, two lint-php rows with an identical 0m 03s, which reads as a rendering quirk rather than as one target run twice. Both halves are fixed: the list loses the repeat, and parallel.sh refuses a duplicate before it loads the environment or starts a single make, naming the offender and why a repeat cannot be tolerated. The two callers are Makefile:440 and Makefile:822; the test list has no duplicates, so the guard is inert there. Negative control: `parallel.sh lint-php lint-python lint-php` exits 2 with "duplicate target(s): lint-php" and launches nothing. The full quality-high run afterwards dispatches 15 lint targets with lint-php once and stays green. Residual: the guard covers scripts/make/parallel.sh only. scripts/tests/dns/ parallel.sh is a separate script with its own argument handling and was not examined. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
161 lines
4.1 KiB
Bash
Executable File
161 lines
4.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Generic parallel-make wrapper. Takes one or more make targets as
|
|
# args, runs each as its own background `make` invocation (capped at
|
|
# INFINITO_WORKER_CPU concurrent workers), and at the end prints a
|
|
# per-target wall-clock breakdown plus the overall elapsed time.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
cd "${REPO_ROOT}"
|
|
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "scripts/make/parallel.sh: at least one make target required" >&2 # nocheck: self-path-reference
|
|
exit 2
|
|
fi
|
|
|
|
duplicates="$(printf '%s\n' "$@" | sort | uniq -d)"
|
|
if [[ -n "${duplicates}" ]]; then
|
|
echo "scripts/make/parallel.sh: duplicate target(s): ${duplicates//$'\n'/ }; each table here is keyed by target name, so a repeat overwrites the first run's exit code and log" >&2 # nocheck: self-path-reference
|
|
exit 2
|
|
fi
|
|
|
|
# shellcheck source=scripts/meta/env/load.sh
|
|
source scripts/meta/env/load.sh
|
|
|
|
: "${INFINITO_WORKER_CPU:?INFINITO_WORKER_CPU must be set (provided by default.env via the env loader)}"
|
|
|
|
n_targets=$#
|
|
if ((n_targets < INFINITO_WORKER_CPU)); then
|
|
jobs="${n_targets}"
|
|
else
|
|
jobs="${INFINITO_WORKER_CPU}"
|
|
fi
|
|
|
|
format_duration() {
|
|
local seconds="$1"
|
|
printf '%dm %02ds' "$((seconds / 60))" "$((seconds % 60))"
|
|
}
|
|
|
|
echo
|
|
echo "🚀 parallel make dispatch"
|
|
echo " 📋 targets: $*"
|
|
echo " ⚙️ jobs: ${jobs} (cpu cap: ${INFINITO_WORKER_CPU})"
|
|
echo
|
|
|
|
tmpdir="$(mktemp -d)"
|
|
trap 'rm -rf "${tmpdir}"' EXIT
|
|
|
|
declare -A pid_to_target=()
|
|
declare -A target_start=()
|
|
declare -A target_dur=()
|
|
declare -A target_rc=()
|
|
declare -A target_log=()
|
|
|
|
start_ts="$(date +%s)"
|
|
|
|
active=0
|
|
to_launch=("$@")
|
|
launch_idx=0
|
|
|
|
drain_one() {
|
|
local pid=""
|
|
local rc=0
|
|
wait -n -p pid || rc=$?
|
|
local tgt="${pid_to_target[${pid}]}"
|
|
target_rc["${tgt}"]="${rc}"
|
|
target_dur["${tgt}"]=$(($(date +%s) - target_start["${tgt}"]))
|
|
if ((rc == 0)); then
|
|
echo "✅ ${tgt} ($(format_duration "${target_dur[${tgt}]}"))"
|
|
else
|
|
echo "❌ ${tgt} ($(format_duration "${target_dur[${tgt}]}")) exit=${rc}"
|
|
fi
|
|
cat "${target_log[${tgt}]}"
|
|
active=$((active - 1))
|
|
}
|
|
|
|
for tgt in "${to_launch[@]}"; do
|
|
while ((active >= jobs)); do
|
|
drain_one
|
|
done
|
|
log="${tmpdir}/${launch_idx}.log"
|
|
target_log["${tgt}"]="${log}"
|
|
target_start["${tgt}"]="$(date +%s)"
|
|
(make -- "${tgt}") >"${log}" 2>&1 &
|
|
pid_to_target[$!]="${tgt}"
|
|
active=$((active + 1))
|
|
launch_idx=$((launch_idx + 1))
|
|
done
|
|
|
|
while ((active > 0)); do
|
|
drain_one
|
|
done
|
|
|
|
total=$(($(date +%s) - start_ts))
|
|
|
|
echo
|
|
echo "📊 per-target wall-clock:"
|
|
overall_rc=0
|
|
for tgt in "$@"; do
|
|
rc="${target_rc[${tgt}]}"
|
|
if ((rc == 0)); then
|
|
status="✅"
|
|
else
|
|
status="❌"
|
|
overall_rc="${rc}"
|
|
fi
|
|
printf ' %s %-30s %s\n' "${status}" "${tgt}" "$(format_duration "${target_dur[${tgt}]}")"
|
|
done
|
|
|
|
echo
|
|
if ((overall_rc == 0)); then
|
|
echo "✨ total $(format_duration "${total}") 🎉"
|
|
else
|
|
echo "💥 total $(format_duration "${total}") (exit=${overall_rc})"
|
|
fi
|
|
echo
|
|
|
|
failed_targets=()
|
|
for tgt in "$@"; do
|
|
((target_rc["${tgt}"] == 0)) || failed_targets+=("${tgt}")
|
|
done
|
|
|
|
if ((${#failed_targets[@]} > 0)); then
|
|
for tgt in "${failed_targets[@]}"; do
|
|
echo "──────── output of failed target: ${tgt} ────────"
|
|
cat "${target_log[${tgt}]}"
|
|
echo "──────── end of ${tgt} ────────"
|
|
echo
|
|
done
|
|
echo "FAILED TARGETS: ${failed_targets[*]}"
|
|
echo
|
|
fi
|
|
|
|
if [[ -n "${GITHUB_STEP_SUMMARY:-}" ]]; then
|
|
{
|
|
echo "## 🚀 Parallel make targets"
|
|
echo
|
|
echo "| Target | Status | Duration |"
|
|
echo "|---|:---:|---:|"
|
|
for tgt in "$@"; do
|
|
if ((target_rc[${tgt}] == 0)); then
|
|
status="✅"
|
|
else
|
|
status="❌ exit=${target_rc[${tgt}]}"
|
|
fi
|
|
# shellcheck disable=SC2016 # backticks are markdown, not expansion
|
|
printf '| `%s` | %s | %s |\n' \
|
|
"${tgt}" "${status}" "$(format_duration "${target_dur[${tgt}]}")"
|
|
done
|
|
if ((overall_rc == 0)); then
|
|
printf '| **total** | ✨ | %s |\n' "$(format_duration "${total}")"
|
|
else
|
|
printf '| **total** | 💥 exit=%s | %s |\n' \
|
|
"${overall_rc}" "$(format_duration "${total}")"
|
|
fi
|
|
echo
|
|
} >>"${GITHUB_STEP_SUMMARY}"
|
|
fi
|
|
|
|
exit "${overall_rc}"
|