tiempo de lectura : 10 minutes

¿Conoces ese momento en que tu sitio local parece nada, pero una vez desplegado en línea todo está perfecto? Eso es lo que me pasó con mi plugin Gradle JBake. La tarea`serve`mostraba botones cuadrados, texto oscuro sobre fondo negro, y un CSS extrañamente ausente. Sin embargo`publishSite`generaba el mismo sitio, él, impecable. El culpable no era ni el CSS, ni el navegador, ni la caché. Era una línea de código Kotlin — y un mal hábito con los argumentos de línea de comandos.

toc

[]

La escena : cuatro screenshots, dos renderizados

Era después de un fallo del sistema. Había hecho cuatro capturas de pantalla para comparar el renderizado local (./gradlew serve`sobre`localhost:8820) con el renderizado en línea (`publishSite`en GitHub Pages). Dos capturas de pantalla por página: la parte superior e inferior.

Renderizado local

Principio de página— Los botones Project y Template sonpequeños, rectangulares, a los colores estándar (azul y verde). La tarjeta Start Writing tiene unfondo negro uniforme, sin marco.

Rendu local - haut de page

pie de página— El subtítulo "Últimos artículos y recursos" escasi ilegible, demasiado oscuro sobre fondo negro. Las fechas debajo de las imágenes del blog ("17 October 2013", etc.) sonausentes.

Rendu local - bas de page

Renderizado en línea

Encabezado de página— Los mismos botones songrandes, ovino, verde neón llamativo, texto en mayúsculas. La tarjeta tiene unmarco blanco redondeado con sombra proyectada.

Rendu en ligne - haut de page

Pie de página— El subtítulo esbien legible. Las fechas sonvisibles. Las tarjetas de los artículos tienenesquinas redondeadasy los títulos sonen negrita.

Rendu en ligne - bas de page

Primer reflejo: el tema. Uso un sistema de tema dinámico (claro, oscuro, alto contraste) basado en`localStorage`. Tal vez el local store activaba un tema high-contrast? No. Ya había escritohttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[un artículo completo sobre la precarga de temas], sé cómo funciona. Además, incluso en modo alto contraste, la estructura CSS (botones ovales, sombras) debía estar allí. No estaba.

Segundo reflejo: el caché del navegador. No, había hecho la prueba con perfiles limpios. El diff persistía.

Tercer reflejo:`serve`no servía los mismos archivos que`publishSite`.

El diagnóstico: serve no apuntaba al directorio correcto

Mi plugin Gradle`bakery`a deux tâches principales :

  • bake: genera el sitio estático en`build/bake/`

  • serve: lanza un servidor web local

  • `publishSite`empuja`build/bake/`a GitHub Pages

La divergencia solo podía provenir de`serve`. Si publishSite`publicaba el contenido correcto, es que`build/bake/`estaba bien. Si`serve`mostraba otra cosa, es que no servía`build/bake/.

Miremos el código. En`SiteManager.kt`, la tarea`serve`está definida así :

SiteManager.kt — La tâche serve (version buggy)
tasks.register("serve", JavaExec::class.java) { task ->
    task.apply {
        mainClass.set("org.jbake.launcher.Main")
        classpath = jbakeRuntime
        environment("GEM_PATH", jbakeRuntime.asPath)
        jvmArgs(/* ... */)
        args = listOf(
            "-b", file(site.bake.srcPath).absolutePath,
            "-s", layout.buildDirectory.get()
                .asFile.resolve(site.bake.destDirPath)
                .absolutePath
        )
    }
}

org.jbake.launcher.Main`es el punto de entrada de la CLI JBake 2.7.0. La idea : pasar-b`para el directorio de origen y`-s`para el directorio de destino, lanzar el modo servidor. Excepto que…​

La causa raíz: JBake CLI no funciona así

JBake 2.7.0 está esperandoargumentos posicionalespara origen y destino, luego flags opcionales. La firma es :

jbake <source> <destination> [options]

Pero, en mi código, escribía:

args = listOf("-b", "/path/to/site", "-s", "/path/to/build/bake")

JBake lo interpretaba como :

  1. -b→ parse el flag "bake" (booleano), consumido

  2. /path/to/site→ se convierte en argumento posicional 1(fuente)

  3. -s→ parsear la bandera "serve" (booleana), servidor iniciadoinmediatamente

  4. /path/to/build/bake→ se convierte en un argumento huérfano, ignorado o malinterpretado

Resultado : JBake estaba tomando`/path/to/site`como fuente,no tenía en cuenta el destinoproporcionada (build/bake), y utilizaba su repertorio por defecto (a menudo`./output`o una copia temporal). El archivo`css/styles.css`del build estaba entonces sobrescrito o ignorado, y era un CSS predeterminado antiguo (sin las variables personalizadas, sin los bordes redondeados, sin las sombras) que se servía.

En comparación,bake et publishSite`utilizan elplugin oficial de Gradle JBake(`jbake-gradle-plugin) quien escribe ordenadamente en`build/bake/`mediante la API de Gradle. No pasan por la CLI.

La solución: argumentos posicionales, no banderas

La corrección es trivial — una vez que se sabe. Basta con pasar la fuente y el destino como argumentos posicionales, entonces`-s`finalmente

SiteManager.kt — La tâche serve (version corrigée)
args = listOf(
    file(site.bake.srcPath).absolutePath,
    layout.buildDirectory.get()
        .asFile.resolve(site.bake.destDirPath)
        .absolutePath,
    "-s"
)

Eso es todo. Tres líneas cambiadas, bug resuelto.

Después de la publicación local del plugin (publishToMavenLocal), un `./gradlew serve`lanza JBake con la sintaxis correcta :

jbake /home/user/project/site /home/user/project/build/bake -s

Y esta vez, el servidor Jetty integrado a JBake sirve bien el contenido de`build/bake/`— idéntico a lo que se publica en línea.

serve vs publish diagram

Verificación rápida con curl

Para asegurarse de que el servidor sirva bien el contenido correcto, un simple`curl`confirma que`index.html`contiene`data-bs-theme="light"`y que`build/bake/css/styles.css`pesa bien 31 402 octets — idéntico al archivo generado por`bake`.

$ ./gradlew serve
# Dans un autre terminal :
$ curl -s http://localhost:8820/ | grep data-bs-theme
<html ... data-bs-theme="light" ...>

$ curl -I http://localhost:8820/css/styles.css
HTTP/1.1 200 OK
Content-Length: 31402
Content-Type: text/css

El renderizado local ahora espixel idénticoal renderizado desplegado.

¿Por qué este bug era vicioso?

¿Ves por qué era difícil de atrapar?

  1. Sin error explícito: JBake no fallaba. Simplemente estaba funcionando con una carpeta incorrecta.

  2. El build funcionaba:`./gradlew bake`generaba correctamente los archivos en`build/bake/`.

  3. El despliegue funcionaba:`publishSite`publicaba el buen contenido en línea.

  4. Sólo serve estaba roto: la tarea de desarrollo, la que utilizamos constantemente para iterar.

  5. La costumbre de -key value: como desarrollador, se condiciona por años de CLI Unix (-o output`, -i input). JBake CLI es una excepción — el origen y el destino son posicionales.

Conclusión

Si usted mantiene un plugin Gradle que envuelve JBake(o cualquier herramienta CLI),lee la documentación de los argumentosaunque los conozcas. Una hipótesis implícita (-s = "set destination"` puede costarte horas de depuración visual.

Aquí, el fix era literalmente cambiar :

args = listOf("-b", src, "-s", dest)   // ❌ BUG

en :

args = listOf(src, dest, "-s")         // ✅ FIX

Tres tokens desplazados, y mi sitio local vuelve a ser tan bonito como el sitio en producción.

Lección: cuando el renderizado difiere entre local y prod sin razón evidente, sospecha primero del pipeline — no el CSS, no el navegador, y mucho menos el framework. Suele ser el paso justo antes del renderizado el que engaña.

Articles connexes