English · Español
Solución 01 — referencia de just manifest¶
Léelo solo tras completar
../lab/01-write-the-justfile.mdy commitear tu intento. Compara contra esta referencia, no antes.
Receta de referencia¶
Añade a /Justfile:
# Record a per-experiment manifest. Used by every numeric experiment from Phase 4+.
# Usage: just manifest <topic>
manifest topic:
#!/usr/bin/env bash
set -euo pipefail
safe_topic=$(printf '%s' "{{topic}}" | tr -c '[:alnum:]._-' '-' )
dir="experiments/$(date +%F)-${safe_topic}"
mkdir -p "$dir"
versions=$(uv run python -c "from utils.seeding import log_versions; import json; print(json.dumps(log_versions()))")
git_sha=$(git rev-parse HEAD 2>/dev/null || echo "no-git")
if git diff-index --quiet HEAD -- 2>/dev/null; then
git_dirty=false
else
git_dirty=true
fi
cpu=$(awk -F: '/^model name/{print $2; exit}' /proc/cpuinfo | sed 's/^ //')
kernel=$(uname -r)
os=$(. /etc/os-release && echo "$PRETTY_NAME")
cat > "$dir/manifest.json" <<JSON
{
"id": "$(date +%F)-${safe_topic}",
"git_sha": "${git_sha}",
"git_dirty": ${git_dirty},
"seed": 42,
"versions": ${versions},
"hardware": { "cpu": "${cpu}", "kernel": "${kernel}", "os": "${os}" },
"started_at": "$(date -u +%FT%TZ)"
}
JSON
echo "$dir/manifest.json"
Decisiones tomadas en esta referencia¶
set -euo pipefail— falla ruidosamente ante cualquier error, variable indefinida o fallo de pipeline. Sin esto, ungitausente produciría silenciosamentegit_sha: "".safe_topic— reemplaza cualquier cosa no alfanumérica (más.,_,-) por-. Previene quetopic=foo;rm -rf .inyecte comandos.#!/usr/bin/env bash— la receta es un único script bash (just soporta shebangs). Esto es más limpio que encadenar comandos con&&./proc/cpuinfo— extracción amigable con Fedora del modelo de CPU. Degrada elegantemente si el archivo no está (produciríacpuvacío, peroset -eestá activo así que fallaríamos antes de producir un medio-manifiesto — aceptable).started_atsolamente, sinfinished_at— esta receta registra el manifiesto de inicio; el script del experimento que lo consume actualizafinished_atywall_secondscuando termina. Acoplarlos forzaría a cada experimento a invocarjust manifesty un paso de fin, lo cual es incómodo.- Idempotencia: reejecutar la receta con el mismo topic el mismo día sobrescribe el manifiesto. Defendible: una reejecución es una reejecución. Si quieres historial, usa otro topic.
Qué marcaría en una review¶
- Las
versionsse capturan fuera del proceso del experimento — así que si tu script de experimento cambia su estado de venv, el manifiesto queda obsoleto. Elrecord_manifestreal ensrc/utils/se llamará dentro del experimento, que es correcto. Esta receta es una herramienta rápida ad-hoc. - Sin campos
pid,user,host. Añádelos si colaboras o corres en hardware compartido. - La receta bloquea por la disponibilidad de
git. Para un checkout sin git (raro), cae a"no-git"— aceptable para un currículo, no para un entorno regulado.
Test de referencia¶
Crea tests/test_justfile.py:
import json
import re
import subprocess
from pathlib import Path
def test_manifest_recipe(tmp_path: Path, monkeypatch) -> None:
# The recipe writes inside the repo; we'd ideally redirect to tmp_path.
# For a curriculum exercise, asserting "the file exists and parses" is enough.
result = subprocess.run(
["just", "manifest", "lab-test"],
capture_output=True,
text=True,
check=True,
)
path = Path(result.stdout.strip())
assert path.exists()
assert path.suffix == ".json"
data = json.loads(path.read_text())
assert data["versions"]["python"].startswith("3.11")
assert re.fullmatch(r"[0-9a-f]{40}", data["git_sha"]) or data["git_sha"] == "no-git"
assert isinstance(data["git_dirty"], bool)
assert data["seed"] == 42
assert data["id"].endswith("lab-test")
Por qué este test es "suficientemente bueno" en vez de "perfecto"¶
Un test perfecto redirigiría la receta a escribir bajo tmp_path. Eso requiere o bien:
- Un parámetro --dir en la receta (over-engineering para el currículo).
- Un chdir a tmp_path y una receta que resuelva rutas relativas a pwd (ya es el caso — pruébalo).
Cualquiera vale. La versión de arriba es un smoke test; la segunda es un unit test. Elige según cuánta fricción tolerarás.
Checklist de comparación¶
Al diffear tu versión contra esta, comprueba:
- ¿Quoteaste
{{topic}}correctamente? Un${topic}sin quotes permite path traversal (../../something). - ¿Usaste
mkdir -p(sin error si existe) en vez demkdir? - ¿Pusiste
git_dirtycomo booleano real en el JSON (no el string"true")? - ¿Manejaste el caso sin-git?
- ¿Embebiste las versiones como objeto anidado, no como JSON serializado a string?
Si algún "no" — anótalo en learners/borja/phase-00/notes/justfile-notes.md y decide si lo arreglas. A veces la respuesta es "mi forma vale para el currículo" — esa es una conclusión válida.