198 lines
7.2 KiB
Bash
Executable File
198 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Barrido de compatibilidad componente <-> core real (change verify-core-compat).
|
|
# Monta un proyecto temporal por paquete contra el core real publicado, lo
|
|
# ejercita segun su nivel de evidencia (tests/compat/levels.json) con criterios
|
|
# estrictos (cualquier Deprecated/Warning/Notice/Fatal cuenta FAIL) y escribe
|
|
# tests/compat/report.json.
|
|
#
|
|
# Uso: scripts/compat-sweep [--only <paquete> ...]
|
|
# Env: DUCKBRAIN_CORE_URL repositorio del core (default: Config::coreUrl())
|
|
# DUCKBRAIN_CACHE_DIR caché donde CoreProvider mantiene el clon del core
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
COMPAT="$ROOT/tests/compat"
|
|
LEVELS="$COMPAT/levels.json"
|
|
REPORT="$COMPAT/report.json"
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
|
|
ONLY=()
|
|
args=("$@")
|
|
i=0
|
|
while [ "$i" -lt "${#args[@]}" ]; do
|
|
case "${args[i]}" in
|
|
--only) i=$((i + 1)); ONLY+=("${args[i]}") ;;
|
|
*) echo "Uso: scripts/compat-sweep [--only <paquete> ...]" >&2; exit 2 ;;
|
|
esac
|
|
i=$((i + 1))
|
|
done
|
|
|
|
# --- resolver el core real (clon + checkout del mayor tag semver) --------------
|
|
CORE_INFO="$(php -d error_reporting=-1 -r '
|
|
require "'"$ROOT"'/src/autoload.php";
|
|
try {
|
|
$p = Toolbox\CoreProvider::fromUrl(
|
|
Toolbox\Config::coreUrl(), Toolbox\Config::cacheDir(), new Toolbox\Console()
|
|
);
|
|
$v = $p->version();
|
|
if ($v === null) {
|
|
fwrite(STDERR, "El core no tiene tags semver publicados; nada que verificar.\n");
|
|
exit(1);
|
|
}
|
|
echo $p->dir(), "\n", $v, "\n", (string) $p->commit(), "\n";
|
|
} catch (Throwable $e) {
|
|
fwrite(STDERR, "No se pudo acceder al core real: " . $e->getMessage() . "\n");
|
|
exit(1);
|
|
}' 2>"$WORK/core.err")" || {
|
|
echo "== ERROR: barrido abortado ==" >&2
|
|
cat "$WORK/core.err" >&2
|
|
exit 1
|
|
}
|
|
CORE_DIR="$(printf '%s\n' "$CORE_INFO" | sed -n 1p)"
|
|
CORE_VER="$(printf '%s\n' "$CORE_INFO" | sed -n 2p)"
|
|
CORE_SHA="$(printf '%s\n' "$CORE_INFO" | sed -n 3p)"
|
|
echo "== core real: v$CORE_VER ($CORE_SHA) en $CORE_DIR =="
|
|
echo
|
|
|
|
LEVELS_PATH="$LEVELS" php -r '
|
|
$levels = json_decode((string) file_get_contents(getenv("LEVELS_PATH")), true);
|
|
echo implode("\n", array_keys($levels)), "\n";' > "$WORK/pkgs"
|
|
|
|
RESULTS="$WORK/results"
|
|
: > "$RESULTS"
|
|
FAILED=0
|
|
|
|
wanted() {
|
|
[ "${#ONLY[@]}" -eq 0 ] && return 0
|
|
local w
|
|
for w in "${ONLY[@]}"; do [ "$w" = "$1" ] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
stop_server() {
|
|
if [ -n "${SERVER_PID:-}" ]; then
|
|
kill "$SERVER_PID" 2>/dev/null || true
|
|
wait "$SERVER_PID" 2>/dev/null || true
|
|
SERVER_PID=""
|
|
fi
|
|
}
|
|
|
|
# --- por paquete ---------------------------------------------------------------
|
|
while read -r PKG; do
|
|
wanted "$PKG" || continue
|
|
PROJ="$WORK/proj-$PKG"
|
|
LOG="$WORK/$PKG.server.log"
|
|
echo "── $PKG"
|
|
|
|
php -d error_reporting=-1 "$ROOT/bin/duckbrain" install "$PROJ" --core-dir="$CORE_DIR" --yes >/dev/null
|
|
php -d error_reporting=-1 "$ROOT/bin/duckbrain" add "$PKG" --root="$PROJ" --yes >/dev/null \
|
|
|| { echo "$PKG:add|fail" >> "$RESULTS"; echo " add fallo"; FAILED=1; continue; }
|
|
|
|
# Config del proyecto temporal: sqlite, clave para crypto y db local.
|
|
PROJ_PATH="$PROJ" php -r '
|
|
$proj = getenv("PROJ_PATH");
|
|
$f = "$proj/config.php";
|
|
$cfg = (string) file_get_contents($f);
|
|
$cfg = str_replace("define(\x27DB_TYPE\x27, \x27mysql\x27);", "define(\x27DB_TYPE\x27, \x27sqlite\x27);", $cfg);
|
|
$cfg = str_replace("define(\x27DB_NAME\x27, \x27\x27);", "define(\x27DB_NAME\x27, \x27$proj/app.sqlite\x27);", $cfg);
|
|
if (!str_contains($cfg, "PRIVATE_KEY")) {
|
|
$cfg .= "\ndefine(\x27PRIVATE_KEY\x27, \x27compat-sweep-clave-de-prueba-32!\x27);\n";
|
|
}
|
|
file_put_contents($f, $cfg);'
|
|
|
|
# Router de compatibilidad del fixture (si lo hay).
|
|
if [ -f "$COMPAT/$PKG/router.php" ]; then
|
|
mkdir -p "$PROJ/src/Routers"
|
|
sed "s|@COMPAT@|$COMPAT/$PKG|g" "$COMPAT/$PKG/router.php" > "$PROJ/src/Routers/Compat.php"
|
|
fi
|
|
|
|
# Servidor con diagnósticos al máximo y capturados.
|
|
BASE=""
|
|
: > "$LOG"
|
|
for PORT in $(seq 18100 18120); do
|
|
PHP_CLI_SERVER_WORKERS=4 php -d error_reporting=-1 -d display_errors=1 \
|
|
-d log_errors=1 -d error_log="$LOG" -S "127.0.0.1:$PORT" -t "$PROJ" "$PROJ/index.php" \
|
|
>/dev/null 2>&1 &
|
|
SERVER_PID=$!
|
|
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
|
|
sleep 0.15
|
|
if curl -s -o /dev/null "http://127.0.0.1:$PORT/compat/ping"; then
|
|
BASE="http://127.0.0.1:$PORT"
|
|
break 2
|
|
fi
|
|
done
|
|
stop_server
|
|
done
|
|
if [ -z "$BASE" ]; then
|
|
echo " no hubo servidor en 18100-18120" >&2
|
|
echo "$PKG:server|fail" >> "$RESULTS"; FAILED=1; continue
|
|
fi
|
|
|
|
# Aserciones del fixture (si lo hay; los niveles "none" no tienen).
|
|
if [ -f "$COMPAT/$PKG/assert.sh" ]; then
|
|
BASE="$BASE" PROJ="$PROJ" PKG="$PKG" LOG="$LOG" RESULTS="$RESULTS" COMPAT="$COMPAT" \
|
|
bash "$COMPAT/$PKG/assert.sh" || true
|
|
fi
|
|
stop_server
|
|
|
|
# Criterio estricto: cualquier diagnostico en el log del servidor es fallo.
|
|
if grep -qE 'Deprecated|Warning:|Notice:|Fatal error|Parse error' "$LOG"; then
|
|
echo " diagnostico PHP detectado en el servidor (ver $LOG):"
|
|
grep -E 'Deprecated|Warning:|Notice:|Fatal error|Parse error' "$LOG" | sed 's/^/ /'
|
|
FAILED=1
|
|
fi
|
|
done < "$WORK/pkgs"
|
|
|
|
# --- merge de resultados con levels.json y reporte ------------------------------
|
|
LEVELS_PATH="$LEVELS" RESULTS_PATH="$RESULTS" REPORT_PATH="$REPORT" \
|
|
CORE_VER="$CORE_VER" CORE_SHA="$CORE_SHA" php -r '
|
|
$levels = json_decode((string) file_get_contents(getenv("LEVELS_PATH")), true);
|
|
$results = [];
|
|
foreach (file(getenv("RESULTS_PATH"), FILE_IGNORE_NEW_LINES) as $line) {
|
|
[$id, $status] = array_pad(explode("|", $line, 2), 2, "");
|
|
$results[$id] = $status;
|
|
}
|
|
// Corre parciales (--only): hereda del reporte previo los ok del mismo core.
|
|
$prior = is_file(getenv("REPORT_PATH"))
|
|
? (json_decode((string) file_get_contents(getenv("REPORT_PATH")), true) ?: [])
|
|
: [];
|
|
$stale = ($prior["core"]["version"] ?? null) !== getenv("CORE_VER");
|
|
$out = [];
|
|
$fail = 0;
|
|
foreach ($levels as $pkg => $comps) {
|
|
foreach ($comps as $comp => $level) {
|
|
$reason = null;
|
|
if (is_array($level)) { $reason = $level["reason"] ?? null; $level = $level["level"]; }
|
|
$id = "$pkg:$comp";
|
|
$status = $results[$id] ?? ($level === "none" ? "skipped" : "not_attempted");
|
|
if ($status === "not_attempted" && !$stale && ($prior["components"][$id]["status"] ?? "") === "ok") {
|
|
$status = "ok";
|
|
}
|
|
if (!in_array($status, ["ok", "skipped"], true)) { $fail++; }
|
|
$out[$id] = array_filter([
|
|
"status" => $status,
|
|
"level" => $level,
|
|
"reason" => $reason,
|
|
], fn ($v) => $v !== null);
|
|
}
|
|
}
|
|
$report = [
|
|
"core" => ["version" => getenv("CORE_VER"), "commit" => getenv("CORE_SHA")],
|
|
"generated" => gmdate("Y-m-d\TH:i:s\Z"),
|
|
"components" => $out,
|
|
];
|
|
file_put_contents(getenv("REPORT_PATH"), json_encode($report, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n");
|
|
foreach ($out as $id => $r) {
|
|
printf(" %-24s %-13s %s%s\n", $id, $r["status"], $r["level"], isset($r["reason"]) ? " (" . $r["reason"] . ")" : "");
|
|
}
|
|
exit($fail === 0 ? 0 : 1);'
|
|
|
|
STATUS=$?
|
|
if [ "$STATUS" = 0 ]; then
|
|
echo "OK: barrido de compatibilidad completo contra core v$CORE_VER"
|
|
else
|
|
echo "FAIL: hay componentes sin verificar o con fallos (ver $REPORT)" >&2
|
|
fi
|
|
exit $STATUS
|