Saltar a contenido

26 de junio de 2026

El día más movido hasta ahora: subí el repositorio a GitHub, monté la integración continua, levanté el primer esqueleto del sistema y, además, construí, publiqué y optimicé el hub de documentación. Para rematar, reorganicé esta misma bitácora.

El repositorio en GitHub y la estrategia de integración y despliegue

Con la documentación ya ordenada, hoy di el paso que venía postergando: subir el repositorio a GitHub para dejar de trabajar solo en local. Lo creé privado, y no por capricho —es el sistema interno de un cliente real, con datos de tiendas y de conexión a su QUICK POS—, así que mantenerlo cerrado es lo mínimo razonable.

Antes de subir nada me tomé el trabajo de dejar la autoría bien firme. Configuré la firma criptográfica de los commits con una clave propia, de modo que cada uno queda como verificado ante GitHub y atribuido a mí sin ambigüedad. Reescribí los tres commits que ya tenía para que todos lleven esa firma y mi correo, porque prefiero que el historial sea coherente desde la raíz y no una mezcla de commits firmados y sin firmar. Para quien después audite el trabajo —el tutor, el tribunal—, que la cadena de autoría sea trazable y verificable me parece parte de la seriedad del proyecto.

La otra decisión del día fue la estrategia de integración y despliegue continuos (CI/CD), que quería resolver antes de escribir código para no improvisarla sobre la marcha. La pensé simple, pero completa:

  • Integración continua. Cada vez que suba cambios, una rutina automática va a correr el linter, la verificación de tipos, las pruebas (contra una base de datos de prueba) y el build. Si algo falla o aparece un warning, el cambio no avanza. Es la manera de que la política de cero warnings y la cobertura del 95% que me propuse dejen de depender de mi disciplina manual y queden garantizadas por la propia herramienta.
  • Despliegue. Como el sistema corre on-premise en el servidor central de Zavidoro, detrás de su VPN, el despliegue automático se va a apoyar en un ejecutor instalado dentro de esa red. Al integrar a la rama principal el sistema se actualiza solo, aunque con un freno deliberado: antes de cada despliegue a producción queda una aprobación manual, para que nada llegue al servidor del cliente sin que yo lo confirme. Automático, sí, pero con la última palabra de mi lado.
  • Entornos. Por ahora, dos: el de desarrollo, en mi máquina y con contenedores, y el de producción on-premise. Un entorno de pruebas separado lo dejo anotado como posibilidad para más adelante, cuando haya dónde alojarlo.

La parte de subir y firmar ya quedó andando y verificada; la rutina de CI/CD la voy a montar enseguida, una vez que tenga el esqueleto del proyecto en pie, porque recién ahí va a haber código real que revisar. El próximo paso sigue siendo el que tenía pendiente: arrancar la construcción del Milestone 1 por el scaffolding y la base de datos.

El primer esqueleto del proyecto en pie (Task 1 del Milestone 1)

Con el repositorio y el pipeline ya encaminados, hoy escribí finalmente la primera línea de código del sistema. No es todavía una funcionalidad que el usuario vea —es la base sobre la que se apoya todo lo demás—, pero me importaba arrancar con el pie derecho: ordenada, probada y sin un solo warning.

Levanté el armazón de la aplicación con Next.js (App Router) en la raíz del repositorio y con TypeScript en modo estricto. Dejé montado todo el tooling de calidad que me autoimpuse: el linter, el formateador, el chequeo de estilos de CSS y, sobre todo, el arnés de pruebas con Vitest y la medición de cobertura. Para no dejarlo en abstracto, escribí una primera prueba mínima —que la aplicación reporta su propio nombre— solo para confirmar que el arnés corre de verdad y queda en verde. Suena trivial, pero es la manera de saber que el andamiaje de pruebas funciona antes de empezar a colgar lógica de él.

Sobre los estilos tomé una decisión a conciencia: CSS con variables de diseño propias en lugar de un framework de utilidades. Prefiero un sistema visual deliberado antes que una plantilla genérica, y de paso me deja el control fino que voy a necesitar para el panel de administración.

También levanté la base de datos de desarrollo —PostgreSQL 16— dentro de un contenedor, con su chequeo de salud, para tener contra qué migrar en la próxima tarea. La mapeé a un puerto alternativo del host para que conviva sin pisar otra base que ya estaba corriendo en la máquina.

Cerré la tarea recién después de pasar toda la batería: verificación de tipos, linter sin warnings, las pruebas con cobertura al 100% sobre lo que hay, la compilación de producción y el contenedor de la base levantando sano. Con todo eso en verde me animo a commitear. Lo que sigue es la Task 2: el esquema de datos y las primeras tablas —tiendas y usuarios— con Prisma.

La integración continua, ahora que hay código que cuidar

Cuando diseñé la estrategia de CI/CD me prometí montarla recién cuando existiera el esqueleto, para que la rutina tuviera código de verdad contra qué correr. Ya con el armazón en pie, hoy fue el día. Escribí el flujo de integración continua: en cada cambio que suba a la rama principal, y en cada propuesta de cambio antes de integrarla, una rutina automática instala las dependencias y corre, una tras otra, las mismas compuertas que venía pasando a mano —formato, linter, chequeo de tipos, pruebas con cobertura y build—. Si cualquiera falla o asoma un warning, la corrida se pone en rojo y el cambio no avanza. Esa es justamente la idea: que la política de cero warnings y la cobertura mínima dejen de depender de que yo me acuerde de correrlas.

Dentro de la rutina dejé además una base de datos PostgreSQL levantada como servicio. Hoy todavía nadie se conecta a ella, pero la próxima tarea trae las pruebas de integración que sí van a tocar la base, y prefiero tenerla provista de antemano que descubrir su falta a mitad de camino.

Sobre el despliegue dejé el trabajo encaminado, aunque no del todo activo, y por una razón honesta: el ejecutor que va a vivir on-premise, dentro de la VPN de Zavidoro, todavía no existe. Así que el paso de despliegue quedó escrito pero dormido —se saltea limpio— hasta que ese servidor esté en línea y yo lo habilite con una bandera. Es la forma de no dejar un job colgado en cada subida fingiendo un despliegue que no puede ocurrir.

Acá me topé con un detalle que conviene anotar para no olvidarlo: la pausa de aprobación manual antes de producción, que yo quería resolver con la protección de entornos de GitHub, resulta que esa regla en particular pide un plan pago cuando el repositorio es privado. El entorno de producción lo dejé creado igual —me va a servir para guardar los secretos del despliegue—, pero la aprobación manual como tal queda como una decisión pendiente: o se contrata el plan, o más adelante la resuelvo con otro mecanismo equivalente. No es urgente, porque el despliegue está dormido de todos modos, pero prefiero tenerlo registrado y no escondido.

Un apunte menor pero deliberado: dejé que el formateador gobierne el código y la configuración, no la prosa. Los documentos de la tesis —el anteproyecto, este cuaderno, el PRD, el RFC— los excluí a propósito, porque pasarles un formateador automático iría en contra de la voz humana que les quiero conservar. Una herramienta de código no tiene por qué reescribir cómo redacto.

La primera corrida pasó todas las compuertas, pero me dejó un detalle que vale la pena contar: la plataforma marcó un warning porque las acciones que uso para armar el entorno todavía corrían sobre una versión de Node ya deprecada. Fue casi una ironía agradable —la misma política de cero warnings que le impuse al proyecto terminó delatando un warning en la propia rutina que la hace cumplir—. No lo dejé pasar: actualicé esas acciones a las versiones que ya corren sobre la versión nueva de Node y volví a correr todo hasta verlo limpio, sin una sola anotación. Es exactamente la clase de cosa que la política busca evitar, y me gustó que apareciera tan temprano.

Un ajuste final, de los chicos pero que suman: noté que al subir esta misma entrada de bitácora —un cambio que toca solo documentación— se disparaba la rutina completa de calidad y build, gastando una corrida entera para revisar código que no se había tocado. En un plan gratuito, donde los minutos de ejecución son contados, eso es desperdicio puro. Así que le indiqué a la rutina que ignore los cambios que caen únicamente dentro de la carpeta de documentación: si lo único que cambié fue la tesis, esta bitácora, el PRD o un plan, no tiene sentido correr el linter ni compilar. La rutina queda para lo que importa —el código— y los minutos se reservan para cuando de verdad hay algo que verificar.

Un hub para leer la documentación, no solo guardarla

Toda la documentación de la tesis vive en el repositorio como archivos Markdown, prolijos para versionar pero incómodos de leer de corrido cuando uno quiere repasar el anteproyecto o mostrarle algo al tutor. Hoy me puse a resolver eso: un sitio donde toda la documentación se vea ordenada, con buscador y navegación, y al que pueda sumar documentos nuevos sin demasiada ceremonia.

Lo primero fue elegir bien la herramienta, porque ahí se jugaba que el resto fuera simple o se volviera un dolor de cabeza. Descarté la idea de subir archivos desde el navegador y convertirlos al vuelo: eso obliga a tener un servidor con Python corriendo todo el tiempo, y para un material que en el fondo leemos el tutor y yo es matar una mosca a cañonazos. Me quedé con un esquema mucho más honesto con mi flujo real: el archivo crudo —un .docx, un .pdf, una planilla— lo dejo en una carpeta, corro un comando que lo pasa a Markdown con markitdown (la misma herramienta con la que ya venía armando los documentos), reviso, y al subirlo el sitio se reconstruye solo. "Subir" pasa a ser, sencillamente, versionar.

Para armar el sitio elegí MkDocs con el tema Material. Dos motivos concretos: comparte el mismo entorno de Python que markitdown, así que la conversión y la construcción del sitio quedan bajo una sola caja de herramientas; y de fábrica ya trae búsqueda, navegación y una portada decente sin tener que pelear con plantillas. Le escribí una página de inicio que reúne, en tarjetas, las puertas de entrada a cada cosa —anteproyecto, PRD, arquitectura, plan, esta bitácora y las referencias de QUICK POS—.

Las referencias de QUICK POS merecen un párrafo aparte. Son varias decenas de PDF y planillas con nombres llenos de espacios y acentos, y escribir a mano los enlaces a cada uno era pedir que se rompieran al primer cambio. Así que armé un pequeño guion que arma el índice de descargas leyendo directamente la carpeta: los enlaces siempre apuntan a archivos que de verdad existen, y si mañana agrego o muevo algo, vuelvo a correrlo y listo.

Acá también me topé con la misma piedra del plan gratuito que ya venía arrastrando. La forma más obvia de publicar un sitio así —las páginas que ofrece la propia plataforma del repositorio— resulta que solo es gratis si el repositorio es público, y el mío tiene que seguir privado porque es trabajo para un cliente real. Antes que pagar o exponer el repositorio, lo voy a alojar en un servicio de hosting estático gratuito que sí admite repositorios privados (Cloudflare Pages es mi candidato; lo termino de definir en el próximo paso). Lo bueno es que esa decisión casi no afecta lo construido: el sitio se arma igual sin importar dónde termine publicado, así que dejé todo preparado de forma neutral y el alojamiento queda como un último ajuste chico.

Antes de cerrar me detuve en cómo se iba a ver y a ordenar todo. Dos cosas. Primero, la numeración de los archivos del anteproyecto: venía con un salto —tenía un 00, 01, 03, 04, 05, sin el 02— porque en algún momento me comí un número sin querer. Lo emparejé, así que ahora corre limpio del 00 al 04. Igual decidí que esos números viven en el nombre del archivo, no en la pantalla: en el sitio se ven títulos legibles y el orden lo fija la navegación, no el prefijo. Segundo, la estructura a futuro. Todo lo que presento formalmente a la facultad lo metí bajo un paraguas, una carpeta universidad/, y adentro una subcarpeta por entrega; hoy está el anteproyecto, y cuando llegue el proyecto de tesis o la defensa cada uno tendrá su propia carpeta con sus versiones, sin tener que reacomodar nada. Los artefactos más técnicos —el PRD, el RFC, el plan, esta bitácora— quedan aparte, porque son herramientas internas del desarrollo y no entregas de cátedra (aunque alguno pueda terminar como anexo).

Fiel a la política de cero warnings, no di nada por bueno hasta verlo construir en modo estricto: cualquier enlace roto o página que quedara fuera de la navegación tira la construcción abajo. Pasó limpio. Y agregué una rutina automática que repite exactamente esa verificación en cada cambio de documentación, separada de la rutina del código para no gastar corridas de más. Lo que sigue es conectar el alojamiento y dejar el sitio publicado de verdad.

El hub, finalmente publicado

Cerré el círculo que había dejado abierto a la mañana: el hub ya está en línea. Lo alojé en Cloudflare Pages, que era mi candidato, y la decisión se sostuvo por lo mismo que la pensé —es gratis incluso con el repositorio privado, y al ser hosting estático puro no se mete a adivinar que en la raíz vive una aplicación Next.js—. La conexión fue directa: autoricé el repositorio, le indiqué que no usara ninguna plantilla de framework, que el comando de construcción instalara las dependencias del sitio y corriera MkDocs, y que publicara la carpeta site. La versión de Python la toma sola del archivo que dejé fijado, así que ese punto quedó parejo entre mi máquina, la rutina de integración y el servidor de Cloudflare.

El primer intento me asustó un segundo: la consola me tiró un error rojo diciendo que no encontraba el archivo de dependencias. Pero al leer el detalle me di cuenta de que el problema no era la configuración sino el momento —esa construcción se había disparado contra un commit viejo, anterior a que yo subiera todo el armado del hub, y en ese punto del historial el archivo todavía no existía—. La construcción siguiente, ya parada sobre el commit correcto, salió limpia y es la que quedó sirviendo. Una falsa alarma, en el fondo, pero me sirvió para entender bien cómo encadena Cloudflare los despliegues.

Verifiqué el sitio en vivo y está como lo quería: la portada con sus tarjetas, la sección Universidad con el anteproyecto ordenado, el PRD, el RFC, el plan, esta bitácora y las referencias de QUICK POS, todo navegable y con buscador. Con la dirección ya en mano, le agregué al sitio su URL oficial, que de paso le da un mapa del sitio en condiciones. De acá en más cada cambio que suba se republica solo.

Me anoté una sola cosa para más adelante, sin urgencia: como la aplicación del sistema y la documentación conviven en el mismo repositorio, Cloudflare instala de movida las dependencias de la aplicación antes de armar el sitio, y eso es tiempo que no necesito gastar en un despliegue que es solo de documentación. No molesta hoy, pero queda como un ajuste fino para cuando valga la pena.

El ajuste fino que me había anotado

Resulta que no esperé tanto: el mismo día agarré ese pendiente que había dejado anotado. El problema era concreto. En cada despliegue, Cloudflare miraba la raíz del repositorio, encontraba la aplicación Next.js y se ponía a instalar sus más de quinientos paquetes antes de siquiera tocar el sitio —medio minuto largo de descargas que para un sitio de pura documentación no aportan absolutamente nada—. Tiempo regalado, en un plan donde los minutos se cuentan.

La solución terminó siendo de una sola línea, una vez que confirmé en la documentación de Cloudflare cuál era la palanca correcta en lugar de adivinar: una variable que le dice a la plataforma que no se meta a instalar dependencias por su cuenta y que deje que mi propio comando se ocupe de lo único que el sitio necesita. Con eso, el despliegue ahora salta derecho del clonado a instalar las dependencias del sitio y armar las páginas. El registro de la construcción lo dice con todas las letras: "se omite la instalación automática de dependencias". Ni rastro de la instalación de la aplicación. La versión de Python la sigue tomando del archivo fijado, así que ese punto no se movió.

Dos cosas que noté mirando el registro y que prefiero dejar anotadas antes que pasarlas por alto. La primera: la construcción me tira un warning, pero no es de los míos. Es un aviso promocional que la versión nueva del tema imprime sola en cada corrida, anunciando cambios futuros de la herramienta; viene incrustado en la librería, no pasa por el registro de eventos de la construcción y por eso ni siquiera dispara el modo estricto. No es un enlace roto ni un error de mi configuración, así que no hay causa que corregir de mi lado salvo retroceder la versión de la librería solo para esquivar un mensaje de marketing —y eso me haría perder correcciones, no vale la pena—. Lo dejo, pero documentado, para que quede claro que lo vi y decidí. La segunda: el comando que corre Cloudflare arma el sitio sin el modo estricto, y es a propósito. La verificación estricta —la que tumba la construcción ante cualquier enlace roto o página suelta— la corre la rutina de integración en cada cambio de documentación; Cloudflare solo publica. De esa forma un despliegue no se cae por algo que la rutina ya vigila aparte, y cada herramienta hace su trabajo sin pisarse con la otra.

La bitácora, ahora como un cuaderno por días

Esta misma bitácora venía creciendo en un solo archivo larguísimo, y ya empezaba a ser incómoda: para llegar al 26 de junio tenía que bajar por todo abril, mayo y la primera quincena de junio de un tirón. Hoy la reorganicé con otra lógica, más parecida a un cuaderno de laboratorio. Cada día con contenido es ahora su propia página, y adentro van como subtítulos las distintas cosas que hice esa jornada: si un día toqué seis temas, los seis quedan juntos bajo la fecha; si toqué uno solo, también.

Para no tener que mantener a mano la lista de páginas —y arriesgarme a que un día se me olvide una y la verificación estricta me tire el sitio abajo—, me apoyé en el sistema de entradas con fecha que ya trae el tema del sitio: yo dejo el archivo del día con su fecha y el resto se arma solo, ordenar de lo más nuevo a lo más viejo, armar el archivo por año y mes, y la navegación. La portada de la bitácora quedó como índice, con los días en orden descendente, así al entrar caigo siempre en lo último que trabajé. El contenido es exactamente el mismo de antes; lo único que cambió es cómo está repartido y en qué orden se lee.