bootstrap: iniciar bitácora operativa

This commit is contained in:
2026-08-13 00:43:15 -06:00
commit 1ecfb022b1
85 changed files with 7967 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REF="${ZOLA386_REF:-d9c9726435d1347341f2171f9aa751291f1db93d}"
DEST="$ROOT/themes/zola.386"
SOURCE="${ZOLA386_SOURCE:-}"
TMP=""
cleanup() {
if [[ -n "$TMP" && -d "$TMP" ]]; then
rm -rf "$TMP"
fi
}
trap cleanup EXIT
if [[ -z "$SOURCE" ]]; then
TMP="$(mktemp -d)"
SOURCE="$TMP/source"
git init -q "$SOURCE"
git -C "$SOURCE" remote add origin https://github.com/lopes/zola.386.git
git -C "$SOURCE" fetch -q --depth 1 origin "$REF"
git -C "$SOURCE" checkout -q --detach FETCH_HEAD
else
SOURCE="$(cd "$SOURCE" && pwd)"
fi
for required in sass static theme.toml; do
if [[ ! -e "$SOURCE/$required" ]]; then
echo "Falta $required en el tema fuente: $SOURCE" >&2
exit 1
fi
done
rm -rf "$DEST"
mkdir -p "$DEST"
mkdir -p "$DEST/templates"
cp -R "$SOURCE/sass" "$DEST/sass"
cp -R "$SOURCE/static" "$DEST/static"
cp "$SOURCE/theme.toml" "$DEST/theme.toml"
[[ -f "$SOURCE/LICENSE" ]] && cp "$SOURCE/LICENSE" "$DEST/LICENSE"
[[ -f "$SOURCE/README.md" ]] && cp "$SOURCE/README.md" "$DEST/UPSTREAM_README.md"
printf '%s\n' "$REF" > "$DEST/.upstream-revision"
cat <<MSG
Tema zola.386 preparado en themes/zola.386
Revisión fijada: $REF
Se conservaron Sass y archivos estáticos; las plantillas locales modernas tienen precedencia.
MSG
+166
View File
@@ -0,0 +1,166 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CLI="${CLUSTERLOG_BIN:-$ROOT/bin/clusterlog}"
for command in jq mktemp cp find; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Falta el comando requerido para la prueba E2E: $command" >&2
exit 1
fi
done
if [[ ! -x "$CLI" ]]; then
echo "No existe un binario ejecutable en $CLI; ejecute make build." >&2
exit 1
fi
TMP="$(mktemp -d)"
cleanup() { rm -rf "$TMP"; }
trap cleanup EXIT
cp -a "$ROOT/." "$TMP/"
CLI="$TMP/bin/clusterlog"
for section in memorias tareas proyectos; do
find "$TMP/content/$section" -maxdepth 1 -type f -name '*.md' ! -name '_index.md' -delete
done
find "$TMP/data/review-events" -type f ! -name '.gitkeep' -delete
find "$TMP/data/task-events" -type f ! -name '.gitkeep' -delete
cat > "$TMP/data/admins.json" <<'JSON'
{
"version": 1,
"review_policy": {"default": "all-active", "include_author": true},
"admins": []
}
JSON
: > "$TMP/config/allowed_signers"
# Material público ficticio: sólo ejercita resolución de huella, nunca firma.
cat > "$TMP/ana.pub" <<'KEY'
ssh-ed25519 ZmFrZS1rZXktbWF0ZXJpYWwtYW5hLTAwMDAwMDAwMDAwMDAwMA== ana@test
KEY
cat > "$TMP/bob.pub" <<'KEY'
ssh-ed25519 ZmFrZS1rZXktbWF0ZXJpYWwtYm9iLTAwMDAwMDAwMDAwMDAwMA== bob@test
KEY
run_json() {
local output
output=$("$@")
jq -e '.ok == true' <<<"$output" >/dev/null
printf '%s' "$output"
}
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" \
admin add --id ana --name 'Ana Admin' --email ana@example.org \
--key "$TMP/ana.pub" --roles admin >/dev/null
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" \
admin add --id bob --name 'Bob Admin' --email bob@example.org \
--key "$TMP/bob.pub" --roles admin >/dev/null
cat > "$TMP/memory.json" <<'JSON'
{
"title": "Actualizar kubelet de nodos de prueba",
"description": "Cambio controlado en dos nodos",
"tags": ["kubernetes", "mantenimiento"],
"systems": ["worker-test-01", "worker-test-02"],
"risk": "medium",
"review_required": true,
"review_policy": "all-active",
"context": "Validar la actualización antes de la ventana productiva.",
"changes": ["Drenar nodos", "Actualizar paquete", "Reintegrar nodos"],
"validation": ["Nodos Ready", "Pods reprogramados"],
"rollback": "Reinstalar la versión anterior."
}
JSON
memory=$(run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" \
memory create --from-json "$TMP/memory.json")
mem_id=$(jq -er '.data.entry_id' <<<"$memory")
mem_path=$(jq -er '.data.path' <<<"$memory")
queue=$(run_json "$CLI" --root "$TMP" --json review list)
[[ $(jq -er '.data.count' <<<"$queue") -eq 1 ]]
[[ $(jq -er '.data.items[0].required_count' <<<"$queue") -eq 2 ]]
[[ $(jq -er '.data.items[0].reviewed_count' <<<"$queue") -eq 0 ]]
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" \
review mark "$mem_id" --note 'Revisado por Ana' >/dev/null
queue=$(run_json "$CLI" --root "$TMP" --json review list)
[[ $(jq -er '.data.items[0].reviewed_count' <<<"$queue") -eq 1 ]]
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" \
review mark "$mem_id" --note 'Revisado por Bob' >/dev/null
queue=$(run_json "$CLI" --root "$TMP" --json review list)
[[ $(jq -er '.data.count' <<<"$queue") -eq 0 ]]
# Cambiar el contenido invalida las confirmaciones de la versión anterior.
printf '\n## Seguimiento\n\nSe agregó una observación posterior.\n' >> "$mem_path"
run_json "$CLI" --root "$TMP" --json sync >/dev/null
queue=$(run_json "$CLI" --root "$TMP" --json review list)
[[ $(jq -er '.data.count' <<<"$queue") -eq 1 ]]
[[ $(jq -er '.data.items[0].reviewed_count' <<<"$queue") -eq 0 ]]
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" review mark "$mem_id" >/dev/null
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" review mark "$mem_id" >/dev/null
queue=$(run_json "$CLI" --root "$TMP" --json review list)
[[ $(jq -er '.data.count' <<<"$queue") -eq 0 ]]
cat > "$TMP/project.json" <<'JSON'
{
"title": "Renovar plataforma de monitoreo",
"description": "Actualizar y normalizar la observabilidad",
"objective": "Reducir puntos ciegos operativos.",
"owners": ["ana"],
"target_date": "2099-12-01",
"status": "active",
"tags": ["observabilidad"],
"systems": ["prometheus"],
"scope": ["Métricas y alertas"],
"out_of_scope": ["Mesa de ayuda"],
"milestones": ["Inventario", "Migración"],
"success_criteria": ["Cobertura completa de nodos"]
}
JSON
project=$(run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/ana.pub" \
project create --from-json "$TMP/project.json")
project_id=$(jq -er '.data.project_id' <<<"$project")
cat > "$TMP/task.json" <<JSON
{
"title": "Migrar alertas de nodos",
"description": "Mover reglas al repositorio estándar",
"project_id": "$project_id",
"priority": "high",
"assignees": ["bob"],
"estimate_minutes": 60,
"due": "2099-11-01",
"tags": ["alertas"],
"systems": ["prometheus"],
"objective": "Centralizar reglas.",
"acceptance_criteria": ["Reglas validadas"],
"notes": "Sin cambios productivos."
}
JSON
task=$(run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" \
task create --from-json "$TMP/task.json")
task_id=$(jq -er '.data.task_id' <<<"$task")
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" \
task start "$task_id" --note 'Inicio' >/dev/null
run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" \
task adjust "$task_id" --minutes 15 --note 'Trabajo previo' >/dev/null
done=$(run_json "$CLI" --root "$TMP" --json --ssh-key "$TMP/bob.pub" \
task done "$task_id" --note 'Terminado')
[[ $(jq -er '.data.task.status' <<<"$done") == done ]]
[[ $(jq -er '.data.task.actual_minutes' <<<"$done") -ge 15 ]]
[[ $(jq -er '.data.task.running_by | length' <<<"$done") -eq 0 ]]
tasks=$(run_json "$CLI" --root "$TMP" --json task list --project "$project_id")
[[ $(jq -er '.data.count' <<<"$tasks") -eq 1 ]]
[[ $(jq -er '.data.items[0].status' <<<"$tasks") == done ]]
projects=$(run_json "$CLI" --root "$TMP" --json project list --status active)
[[ $(jq -er '.data.count' <<<"$projects") -eq 1 ]]
run_json "$CLI" --root "$TMP" --json sync --check >/dev/null
validation=$(run_json "$CLI" --root "$TMP" --json validate --skip-zola)
[[ $(jq -er '.data.valid' <<<"$validation") == true ]]
[[ $(find "$TMP/data/review-events" -type f -name '*.json' | wc -l) -eq 4 ]]
[[ $(find "$TMP/data/task-events" -type f -name '*.json' | wc -l) -ge 4 ]]
printf 'E2E OK: memoria %s, proyecto %s, tarea %s; revisión por hash y eventos independientes verificados.\n' \
"$mem_id" "$project_id" "$task_id"
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BIN="${CLUSTERLOG_BIN:-$ROOT/bin/clusterlog}"
"$BIN" --root "$ROOT" --json sync >/dev/null
"$BIN" --root "$ROOT" --json sync --check >/dev/null
"$BIN" --root "$ROOT" --json validate --skip-zola >/dev/null
"$BIN" --json schema memory >/dev/null
"$BIN" --json schema task >/dev/null
"$BIN" --json schema project >/dev/null
"$BIN" --json schema content >/dev/null
echo "Smoke test correcto."
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CLI="${CLUSTERLOG_BIN:-$ROOT/bin/clusterlog}"
BASE_SHA="${BASE_SHA:-}"
HEAD_SHA="${HEAD_SHA:-HEAD}"
for command in git jq; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Falta el comando requerido para verificar firmas: $command" >&2
exit 1
fi
done
if [[ ! -x "$CLI" ]]; then
echo "No existe un binario ejecutable en $CLI; ejecute make build." >&2
exit 1
fi
active_admins=$(jq '[.admins[] | select(.active == true)] | length' "$ROOT/data/admins.json")
if [[ "$active_admins" -eq 0 ]]; then
echo "Firmas: bootstrap sin administradores activos; comprobación omitida."
exit 0
fi
if [[ -z "$BASE_SHA" ]]; then
echo "BASE_SHA es obligatorio cuando existen administradores activos." >&2
exit 1
fi
if ! git -C "$ROOT" cat-file -e "$BASE_SHA^{commit}" 2>/dev/null; then
echo "El commit base no está disponible: $BASE_SHA" >&2
exit 1
fi
if ! git -C "$ROOT" cat-file -e "$HEAD_SHA^{commit}" 2>/dev/null; then
echo "El commit de cabeza no está disponible: $HEAD_SHA" >&2
exit 1
fi
mapfile -t commits < <(git -C "$ROOT" rev-list --reverse "$BASE_SHA..$HEAD_SHA")
if [[ ${#commits[@]} -eq 0 ]]; then
echo "Firmas: no hay commits nuevos en el rango."
exit 0
fi
for commit in "${commits[@]}"; do
"$CLI" --root "$ROOT" verify commit "$commit" >/dev/null
echo "Firma SSH autorizada: $commit"
done