#!/usr/bin/bash set -euo pipefail IFS=$'\n\t' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" if [[ -d "${SCRIPT_DIR}/../../.git" ]] || [[ -f "${SCRIPT_DIR}/../../nodo/config/etc/mirrors.env" ]]; then REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)" else REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" fi if [[ -f "${REPO_ROOT}/nodo/config/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/nodo/config/etc/mirrors.env" elif [[ -f "${REPO_ROOT}/config/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/config/etc/mirrors.env" elif [[ -f "${REPO_ROOT}/etc/mirrors.env" ]]; then # shellcheck source=/dev/null source "${REPO_ROOT}/etc/mirrors.env" fi MIRRORS_STAGING_DIR="${MIRRORS_STAGING_DIR:-$HOME/ipfs/staging}" IPFS_USE_NOCOPY="${IPFS_USE_NOCOPY:-true}" log() { local level="$1" shift local timestamp timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" echo "{\"timestamp\":\"${timestamp}\",\"level\":\"${level}\",\"message\":\"$*\"}" >&2 } usage() { cat << USAGE Uso: $0 [OPCIONES] [DIRECTORIO_STAGING] Ingesta el directorio de staging en Kubo (IPFS) generando el grafo UnixFS DAG y retornando el CID raíz. Opciones: --nocopy Utiliza la extensión filestore (--nocopy) para evitar duplicación en disco --portal-only Actualiza portal web, documentación y metadatos sobre el árbol MFS existente --quick Sinónimo de --portal-only -h, --help Muestra este mensaje de ayuda USAGE exit 1 } main() { local use_nocopy="${IPFS_USE_NOCOPY}" local portal_only="false" local staging_dir="${MIRRORS_STAGING_DIR}" while [[ $# -gt 0 ]]; do case "$1" in --nocopy) use_nocopy="true" shift ;; --portal-only|--quick) portal_only="true" shift ;; -h|--help) usage ;; *) staging_dir="$1" shift ;; esac done if [[ ! -d "${staging_dir}" ]]; then log "ERROR" "Directorio de staging no encontrado: ${staging_dir}" >&2 exit 1 fi # Limpiar artefactos innecesarios en la raíz de staging rm -rf "${staging_dir}/README.rst" \ "${staging_dir}/logo.svg" \ "${staging_dir:?}/bin" \ "${staging_dir}/config" \ "${staging_dir:?}/etc" \ "${staging_dir}/systemd" \ "${staging_dir}/guardianes.json" 2>/dev/null || true # Asegurar que las páginas web y metadatos estén presentes en la raíz de staging for doc in README.html index.html modules.json bitacora-cids.json cover.jpg; do if [[ -f "${REPO_ROOT}/${doc}" ]]; then cp -f "${REPO_ROOT}/${doc}" "${staging_dir}/${doc}" fi done # Asegurar que los artefactos web de Vigía estén sincronizados si existen local vigia_dist="${MIRRORS_VIGIA_DIST_DIR:-${REPO_ROOT}/../vigia/web/dist}" if [[ -d "${vigia_dist}" ]] && [[ -f "${vigia_dist}/vigia.html" ]]; then mkdir -p "${staging_dir}/vigia" cp -f "${vigia_dist}/vigia.html" "${staging_dir}/vigia/index.html" for f in favicon.svg favicon.ico logo.svg guardianes.json modules.json; do if [[ -f "${vigia_dist}/${f}" ]]; then cp -f "${vigia_dist}/${f}" "${staging_dir}/vigia/${f}" fi done fi # Asegurar que el directorio de herramientas del nodo esté sincronizado if [[ -d "${REPO_ROOT}/nodo" ]]; then mkdir -p "${staging_dir}/nodo" cp -rf "${REPO_ROOT}/nodo/"* "${staging_dir}/nodo/" fi # Asegurar que la documentación técnica Sphinx esté presente if [[ -d "${REPO_ROOT}/docs/_build/html" ]]; then mkdir -p "${staging_dir}/docs" cp -rf "${REPO_ROOT}/docs/_build/html/"* "${staging_dir}/docs/" fi local root_cid local backup_path="/mxos-root-backup-$$" cleanup_and_restore() { local exit_code=$? if [[ "${exit_code}" -ne 0 ]]; then log "ERROR" "Fallo durante la ingesta (código ${exit_code}). Verificando restauración de respaldo..." if ipfs files stat "${backup_path}" >/dev/null 2>&1; then log "WARN" "Restaurando /mxos-root desde respaldo transaccional ${backup_path}..." ipfs files rm -rf /mxos-root 2>/dev/null || true ipfs files cp "${backup_path}" /mxos-root 2>/dev/null || true log "INFO" "Restauración completada." fi fi ipfs files rm -rf /mxos-staging-build 2>/dev/null || true ipfs files rm -rf "${backup_path}" 2>/dev/null || true exit "${exit_code}" } trap cleanup_and_restore EXIT INT TERM HUP if [[ "${portal_only}" == "true" ]]; then if ! ipfs files stat /mxos-root >/dev/null 2>&1; then log "ERROR" "No existe un arbol /mxos-root previo en MFS. Se requiere una ingesta completa inicial." exit 1 fi log "INFO" "Iniciando ingesta agil de portal y metadatos sobre arbol MFS existente..." ipfs files rm -rf /mxos-staging-build 2>/dev/null || true ipfs files cp /mxos-root /mxos-staging-build # Limpiar artefactos obsoletos o desacoplados ipfs files rm -rf /mxos-staging-build/guardianes.json 2>/dev/null || true # Actualizar activos web estaticos for doc in README.html index.html modules.json bitacora-cids.json cover.jpg; do local doc_source="" if [[ -f "${staging_dir}/${doc}" ]]; then doc_source="${staging_dir}/${doc}" elif [[ -f "${REPO_ROOT}/${doc}" ]]; then doc_source="${REPO_ROOT}/${doc}" fi if [[ -n "${doc_source}" && -f "${doc_source}" ]]; then local doc_cid doc_cid="$(ipfs add --pin=false --cid-version=1 --hash=sha2-256 -Q "${doc_source}")" ipfs files rm -rf "/mxos-staging-build/${doc}" 2>/dev/null || true ipfs files cp "/ipfs/${doc_cid}" "/mxos-staging-build/${doc}" fi done # Actualizar artefactos estáticos de Vigía si existen if [[ -d "${staging_dir}/vigia" ]]; then local vigia_cid vigia_cid="$(ipfs add -r --pin=false --cid-version=1 --hash=sha2-256 -Q "${staging_dir}/vigia")" ipfs files rm -rf /mxos-staging-build/vigia 2>/dev/null || true ipfs files cp "/ipfs/${vigia_cid}" "/mxos-staging-build/vigia" fi # Actualizar herramientas de nodo si existen if [[ -d "${staging_dir}/nodo" ]]; then local nodo_cid nodo_cid="$(ipfs add -r --pin=false --cid-version=1 --hash=sha2-256 --hidden -Q "${staging_dir}/nodo")" ipfs files rm -rf /mxos-staging-build/nodo 2>/dev/null || true ipfs files cp "/ipfs/${nodo_cid}" "/mxos-staging-build/nodo" fi # Actualizar documentacion tecnica si existe if [[ -d "${staging_dir}/docs" ]]; then local docs_cid docs_cid="$(ipfs add -r --pin=false --cid-version=1 --hash=sha2-256 --hidden -Q "${staging_dir}/docs")" ipfs files rm -rf /mxos-staging-build/docs 2>/dev/null || true ipfs files cp "/ipfs/${docs_cid}" "/mxos-staging-build/docs" fi # Asegurar enlaces DAG directos para compatibilidad UnixFS local fedora_linux_releases_cid fedora_linux_releases_cid="$(ipfs files stat --hash /mxos-staging-build/fedora/linux/releases 2>/dev/null || true)" if [[ -n "${fedora_linux_releases_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora/releases 2>/dev/null || true ipfs files cp "/ipfs/${fedora_linux_releases_cid}" /mxos-staging-build/fedora/releases fi local fedora_linux_updates_cid fedora_linux_updates_cid="$(ipfs files stat --hash /mxos-staging-build/fedora/linux/updates 2>/dev/null || true)" if [[ -n "${fedora_linux_updates_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora/updates 2>/dev/null || true ipfs files cp "/ipfs/${fedora_linux_updates_cid}" /mxos-staging-build/fedora/updates fi local fedora_cid fedora_cid="$(ipfs files stat --hash /mxos-staging-build/fedora 2>/dev/null || true)" if [[ -n "${fedora_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora-enchilada 2>/dev/null || true ipfs files cp "/ipfs/${fedora_cid}" /mxos-staging-build/fedora-enchilada fi # Validacion de pre-vuelo estricta antes de promover a /mxos-root local required_paths=( "index.html" "README.html" "modules.json" "bitacora-cids.json" "centos-stream/10-stream/BaseOS" "fedora/linux" ) log "INFO" "Validando integridad estructural del nuevo arbol en /mxos-staging-build..." for rel_p in "${required_paths[@]}"; do if ! timeout 5s ipfs files stat "/mxos-staging-build/${rel_p}" >/dev/null 2>&1; then log "ERROR" "Fallo de integridad: La ruta requerida '/${rel_p}' no existe en el nuevo arbol ensamblado." log "ERROR" "Abortando promocion. El arbol canonico /mxos-root se mantiene intacto." exit 1 fi done log "INFO" "Integridad estructural 100% verificada. Promoviendo atomicamente hacia /mxos-root..." ipfs files rm -rf /mxos-root 2>/dev/null || true ipfs files cp /mxos-staging-build /mxos-root ipfs files rm -rf /mxos-staging-build 2>/dev/null || true if timeout 5s ipfs files stat /mxos-root/index.html >/dev/null 2>&1; then ipfs files rm -rf "${backup_path}" 2>/dev/null || true trap - EXIT INT TERM HUP fi root_cid="$(ipfs files stat --hash /mxos-root)" ipfs pin add --recursive=false "${root_cid}" >/dev/null 2>&1 || true log "INFO" "Ingesta agil completada exitosamente. CID raiz: ${root_cid}" echo "${root_cid}" return 0 fi if [[ "${use_nocopy}" == "true" ]]; then log "INFO" "Iniciando ingesta UnixFS atomica en MFS provisional (/mxos-staging-build)..." # Preparar respaldo transaccional del arbol actual si existe if ipfs files stat /mxos-root >/dev/null 2>&1; then ipfs files rm -rf "${backup_path}" 2>/dev/null || true ipfs files cp /mxos-root "${backup_path}" 2>/dev/null || true fi # Preparar nodo de ensamblado provisional en MFS sin alterar el arbol canonico activo ipfs files rm -rf /mxos-staging-build 2>/dev/null || true ipfs files mkdir -p /mxos-staging-build # Iterar sobre las entradas de staging para ensamblar el arbol for entry_path in "${staging_dir}"/*; do [[ -e "${entry_path}" ]] || continue local entry_name entry_name="$(basename "${entry_path}")" # Omitir archivos y directorios de desarrollo excluidos case "${entry_name}" in README.rst|logo.svg|bin|config|etc|systemd|guardianes.json|.*) continue ;; esac if [[ "${entry_name}" == "fedora" || "${entry_name}" == "centos-stream" ]]; then log "INFO" "Ingestando arbol de paquetes para ${entry_name} con filestore (--nocopy, --fscache, --pin=false)..." local repo_cid repo_cid="$(ipfs add -r --nocopy --fscache --pin=false --cid-version=1 --hash=sha2-256 --hidden -Q "${entry_path}")" ipfs files cp "/ipfs/${repo_cid}" "/mxos-staging-build/${entry_name}" elif [[ -d "${entry_path}" ]]; then log "INFO" "Ingestando directorio estático ${entry_name} en almacén de bloques directo (--pin=false)..." local sdir_cid sdir_cid="$(ipfs add -r --pin=false --cid-version=1 --hash=sha2-256 --hidden -Q "${entry_path}")" ipfs files cp "/ipfs/${sdir_cid}" "/mxos-staging-build/${entry_name}" elif [[ -f "${entry_path}" ]]; then log "INFO" "Ingestando archivo estático ${entry_name} en almacén de bloques directo (--pin=false)..." local file_source="${entry_path}" if [[ -f "${REPO_ROOT}/${entry_name}" ]]; then file_source="${REPO_ROOT}/${entry_name}" fi local doc_cid doc_cid="$(ipfs add --pin=false --cid-version=1 --hash=sha2-256 -Q "${file_source}")" ipfs files cp "/ipfs/${doc_cid}" "/mxos-staging-build/${entry_name}" fi done # Asegurar enlaces DAG directos para compatibilidad UnixFS sin depender de symlinks rotos local fedora_linux_releases_cid fedora_linux_releases_cid="$(ipfs files stat --hash /mxos-staging-build/fedora/linux/releases 2>/dev/null || true)" if [[ -n "${fedora_linux_releases_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora/releases 2>/dev/null || true ipfs files cp "/ipfs/${fedora_linux_releases_cid}" /mxos-staging-build/fedora/releases fi local fedora_linux_updates_cid fedora_linux_updates_cid="$(ipfs files stat --hash /mxos-staging-build/fedora/linux/updates 2>/dev/null || true)" if [[ -n "${fedora_linux_updates_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora/updates 2>/dev/null || true ipfs files cp "/ipfs/${fedora_linux_updates_cid}" /mxos-staging-build/fedora/updates fi local fedora_cid fedora_cid="$(ipfs files stat --hash /mxos-staging-build/fedora 2>/dev/null || true)" if [[ -n "${fedora_cid}" ]]; then ipfs files rm -rf /mxos-staging-build/fedora-enchilada 2>/dev/null || true ipfs files cp "/ipfs/${fedora_cid}" /mxos-staging-build/fedora-enchilada fi # Validación de pre-vuelo estricta antes de promover a /mxos-root local required_paths=( "index.html" "README.html" "modules.json" "bitacora-cids.json" "centos-stream/10-stream/BaseOS" "fedora/linux" ) log "INFO" "Validando integridad estructural del nuevo árbol en /mxos-staging-build..." for rel_p in "${required_paths[@]}"; do if ! timeout 5s ipfs files stat "/mxos-staging-build/${rel_p}" >/dev/null 2>&1; then log "ERROR" "Fallo de integridad: La ruta requerida '/${rel_p}' no existe en el nuevo árbol ensamblado." log "ERROR" "Abortando promoción. El árbol canónico /mxos-root se mantiene intacto." exit 1 fi done log "INFO" "Integridad estructural 100% verificada. Promoviendo atómicamente hacia /mxos-root..." # Promoción atómica ipfs files rm -rf /mxos-root 2>/dev/null || true ipfs files cp /mxos-staging-build /mxos-root ipfs files rm -rf /mxos-staging-build 2>/dev/null || true # Si /mxos-root quedó íntegro, eliminamos el respaldo de seguridad if timeout 5s ipfs files stat /mxos-root/index.html >/dev/null 2>&1; then ipfs files rm -rf "${backup_path}" 2>/dev/null || true trap - EXIT INT TERM HUP fi root_cid="$(ipfs files stat --hash /mxos-root)" else log "INFO" "Iniciando ingesta UnixFS convencional de ${staging_dir} en Kubo (--pin=false)..." root_cid="$(ipfs add -r --pin=false --cid-version=1 --hash=sha2-256 --hidden -Q "${staging_dir}")" fi if [[ -z "${root_cid}" ]]; then log "ERROR" "Fallo en la generación del CID raíz." >&2 exit 1 fi log "INFO" "Ingesta completada exitosamente. CID raíz: ${root_cid}" # Enclavamiento local no recursivo del bloque raíz ipfs pin add --recursive=false "${root_cid}" >/dev/null 2>&1 || true # Salida limpia del CID por stdout echo "${root_cid}" } main "$@"