"""Versión de los .css y .js propios en cada liga (21-oct-2026). Sin esto, después de un deploy el navegador sigue usando el tablero.css o el kanban.js anteriores un buen rato: el nombre del archivo no cambia y el servidor no le pide revisarlo. Pasó el 21-oct: la página de inicio nueva salió con la ruta a producción sin estilos hasta recargar a la fuerza. Aquí, al construir, cada liga a un .css o .js que vive en docs/ recibe ?v=. Si el archivo cambió, la dirección cambia y el navegador lo baja otra vez; si no cambió, sigue en caché. Se aplica al HTML de las páginas de MkDocs (on_post_page) y a las páginas sueltas de docs/ (formularios, cuenta, admin, salir…, en on_post_build). Los archivos de docs/ no se tocan. Los de Material ya traen su huella en el nombre (main..min.css) y no se tocan. """ import hashlib import logging import os import posixpath import re log = logging.getLogger("mkdocs.hooks.versiones") _HUELLAS = {} # href="…x.css" o src="…x.js" sin parámetros (si ya traen ?algo, se respetan) _LIGA = re.compile(r'(\b(?:href|src)=")([^"#?]+\.(?:css|js))(")') def on_pre_build(config, **kwargs): _HUELLAS.clear() docs = config["docs_dir"] for raiz, _, archivos in os.walk(docs): for f in archivos: if f.endswith((".css", ".js")): ruta = os.path.join(raiz, f) with open(ruta, "rb") as fh: _HUELLAS[os.path.relpath(ruta, docs).replace(os.sep, "/")] = hashlib.sha256(fh.read()).hexdigest()[:10] def _base(url_pagina): u = "/" + (url_pagina or "") return u if u.endswith("/") else posixpath.dirname(u) + "/" def _marcar(html, base): def cambia(m): url = m.group(2) if url.startswith(("http:", "https:", "//", "data:")): return m.group(0) rel = (url if url.startswith("/") else posixpath.normpath(posixpath.join(base, url))).lstrip("/") huella = _HUELLAS.get(rel) return m.group(1) + url + "?v=" + huella + m.group(3) if huella else m.group(0) return _LIGA.sub(cambia, html) def on_post_page(output, page, config, **kwargs): return _marcar(output, _base(page.url)) def on_post_build(config, **kwargs): # páginas sueltas: el HTML de docs/ que MkDocs copia tal cual (formularios, cuenta, redirecciones…) docs, site, n = config["docs_dir"], config["site_dir"], 0 for raiz, _, archivos in os.walk(docs): for f in archivos: if not f.endswith(".html"): continue rel = os.path.relpath(os.path.join(raiz, f), docs).replace(os.sep, "/") destino = os.path.join(site, rel) if not os.path.isfile(destino): continue with open(destino, encoding="utf-8") as fh: html = fh.read() nuevo = _marcar(html, _base(rel)) if nuevo != html: with open(destino, "w", encoding="utf-8") as fh: fh.write(nuevo) n += 1 log.info("versiones: %d archivos .css/.js con huella; %d páginas sueltas marcadas", len(_HUELLAS), n)