leestijd : 10 minutes

Ken je dat moment waarop je site lokaal eruitziet als niets, maar eenmaal online geïmplementeerd is alles tip-top? Dit is mij overkomen met mijn Gradle JBake-plugin. De taak`serve`werd weergegeven vierkante knoppen, donkere tekst op een zwarte achtergrond, en een vreemd afwezende CSS. Desondanks`publishSite`genereerde dezelfde site, hij, onberispelijk. De schuldige was noch de CSS, noch de browser, noch de cache. Het was een regel Kotlin-code — en een slechte gewoonte met command-line-argumenten.

De scène : vier screenshots, twee renders

Het was na een systeemcrash. Ik had vier schermafbeeldingen gemaakt om de lokale weergave te vergelijken (./gradlew serve`op`localhost:8820) met de inline rendering (`publishSite`op GitHub Pages). Twee schermafbeeldingen per pagina : boven en onder.

Lokale weergave

Bovenkant van de pagina— De knoppen Project en Template zijnkleine, rechthoekige, met de standaardkleuren (blauw en groen). De Start Writing kaart heeft eenegale zwarte achtergrond, zonder kader.

Rendu local - haut de page

Onderkant van de pagina— De ondertitel "Laatste artikelen en bronnen" isbijna onleesbaar, te donker op zwarte achtergrond. De data onder de blogafbeeldingen ("17 October 2013", etc.) zijnafwezig.

Rendu local - bas de page

Online weergegeven

Bovenkant van de pagina— De dezelfde knoppen zijngrote, ovaux, neon groen opvallend, tekst in hoofdletters. De kaart heeft eenafgeronde witte kader met schaduw.

Rendu en ligne - haut de page

Onderkant van de pagina— De ondertitel isgoed leesbaar. De data zijnzichtbaar. De kaarten van de artikelen hebbenafgeronde hoekenen de titels zijnvet.

Rendu en ligne - bas de page

Eerste reflex : het thema. Ik gebruik een dynamisch thema-systeem (licht, donker, high-contrast) gebaseerd op`localStorage`. Misschien activeerde de lokale winkel een high-contrast thema? Nee. Ik had het al geschrevenhttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[een heel artikel over het voorladen van thema’s], ik weet hoe het werkt. En dan, zelfs in high-contrast-modus, de CSS-structuur (boutons ovales, ombres) had moesten aanwezig zijn. Ze was er niet.

Tweede reflex: de browsercache. Nee, ik had de test uitgevoerd met schone profielen. Het verschil bleef bestaan.

Derde reflex:`serve`dient niet dezelfde bestanden aan als`publishSite`.

De diagnose : serve wees niet naar de juiste map

Mijn Gradle-plugin`bakery`heeft twee hoofdtaken:

  • bake: genereert de statische site in`build/bake/`

  • serve: start een lokale webserver

  • publishSite: duw`build/bake/`naar GitHub Pages

De divergentie kon alleen maar ontstaan uit`serve`. Si publishSite`publiceerde de juiste inhoud, dat is`build/bake/`was goed. Als`serve`toonde iets anders, het feit was dat het niet diende`build/bake/.

Laten we de code bekijken. In`SiteManager.kt`, de taak`serve`is gedefinieerd als volgt:

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`is de startpunt van de JBake CLI 2.7.0. Het idee: doorgeven-b`voor de bronmap en`-s`voor de bestemmingsmap, start dan de server-modus. Behalve dat…​

De hoofdoorzaak: JBake CLI werkt niet zo

JBake 2.7.0 wacht oppositionele argumentenvoor bron en bestemming, vervolgens optionele flags. De handtekening is:

jbake <source> <destination> [options]

Maar in mijn code schreef ik:

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

JBake interpreteerde dit als :

  1. -b→ parse de flag "bake" (bool), verbruikt

  2. /path/to/site→ wordt positioneel argument 1 (bron)

  3. -s→ parse de vlag "serve" (Booleaan), server gestartonmiddellijk

  4. /path/to/build/bake→ wordt een weesargument, genegeerd of verkeerd geïnterpreteerd

Resultaat : JBake nam`/path/to/site`als bron,nam geen rekening mee met de bestemmingverstrekt(build/bake), en gebruikte zijn standaardmap (vaak`./output`of een tijdelijke kopie). Het bestand`css/styles.css`de build werd dus overschreven of genegeerd, en het was een oude standaard-CSS (zonder de aangepaste variabelen, zonder de afgeronde hoeken, zonder de schaduwen) die werd geserveerd.

In vergelijking hiermee,bake et publishSite`gebruiken deofficiële Gradle JBake plugin(`jbake-gradle-plugin) die schoon schrijft in`build/bake/`via de Gradle-API. Ze gaan niet via de CLI.

De oplossing: positieargumenten, geen vlaggen

De correctie is triviaal — eenmaal je het weet. Het is voldoende om de bron en bestemming als positionele argumenten door te geven, dan`-s`ten slotte:

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"
)

Dat is alles. Drie regels gewijzigd, bug opgelost.

Na lokale publicatie van de plugin (publishToMavenLocal), un `./gradlew serve`Start JBake met de juiste syntaxis:

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

En deze keer serveert de Jetty-server geïntegreerd in JBake goed de inhoud van`build/bake/`— identiek aan wat online wordt gepubliceerd.

@startuml
!define RECTANGLE class

package "AVANT (fout)" {
  RECTANGLE JBakeCLI1 {
    + args = ["-b", "plaats", "-s", "gebouw/bake"]
  }

  RECTANGLE Source1 {
    + site/
  }

  RECTANGLE Dest1 {
    + ??? (dossier temporaire)
  }

  RECTANGLE Jetty1 {
    + sert mauvais fichiers
  }

  JBakeCLI1 --> Source1 : bed
  JBakeCLI1 -[#red]x Dest1 : destination ignorée
  Jetty1 --> Dest1 : dient
}

package "APRÈS (oplossing)" {
  RECTANGLE JBakeCLI2 {
    + args = ["plaats", "gebouw/bake", "-s"]
  }

  RECTANGLE Source2 {
    + site/
  }

  RECTANGLE Dest2 {
    + build/bake/
  }

  RECTANGLE Jetty2 {
    + sert les bons fichiers
  }

  JBakeCLI2 --> Source2 : bed
  JBakeCLI2 --> Dest2 : geschreven in
  Jetty2 --> Dest2 : dient OK
}
@enduml

Snelle controle met curl

Om er zeker van te zijn dat de server de juiste inhoud serveert, een eenvoudig`curl`bevestigt dat`index.html`bevat`data-bs-theme="light"`python s = input() parts = s.split(‘') for i in range(len(parts)): if i % 2 == 0: parts[i] = parts[i].replace("et", "en").replace("que", "dat") s = '’.join(parts) print(s, end='')build/bake/css/styles.css`weegt inderdaad 31 402 bytes — identiek aan het bestand dat gegenereerd wordt door`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

De lokale weergave is nuidentieke pixelaan het geïmplementeerde weergave.

Waarom was deze bug verraderlijk?

Ziet u waarom het moeilijk was om te vangen?

  1. Geen expliciete fout: JBake crasht niet. Het "werkte", gewoon met een verkeerde map.

  2. De build werkte:`./gradlew bake`genereerde correct de bestanden in`build/bake/`.

  3. De implementatie werkte:`publishSite`duwde de goede content online

  4. Alleen serve was kapot: de ontwikkeltaak, die men constant gebruikt om te itereren.

  5. De gewoonte van -key valueals ontwikkelaar wordt men gevormd door jaren van Unix CLI (-o output`, -i input). JBake CLI is een uitzondering — bron en bestemming zijn positioneel.

Conclusie

Als je een Gradle-plugin onderhoudt die JBake omwikkelt (of een ander CLI-hulpmiddel),lees de documentatie van de argumentenzelfs als je denkt ze te kennen. Een impliciete hypothese (-s= "set destination") kan je uren visueel debuggen kosten.

Hier was de fix letterlijk om te veranderen :

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

en :

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

Drie tokens verplaatst, en mijn lokale site wordt weer zo mooi als de productiesite.

Leswanneer het renderen verschilt tussen lokaal en prod zonder duidelijke reden, verdenk eerst de pipeline — niet de CSS, niet de browser, en nog minder het framework. Vaak is de stap net voor het renderen die vals speelt.

Gerelateerde artikelen