Waarom `serve` een lelijke website toonde, terwijl `publishSite` perfect was?
Gepubliceerd op 22 April 2026
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.
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.
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.
Onderkant van de pagina— De ondertitel isgoed leesbaar. De data zijnzichtbaar. De kaarten van de artikelen hebbenafgeronde hoekenen de titels zijnvet.
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:
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 :
-
-b→ parse de flag "bake" (bool), verbruikt -
/path/to/site→ wordt positioneel argument 1 (bron) -
-s→ parse de vlag "serve" (Booleaan), server gestartonmiddellijk -
/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:
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?
-
Geen expliciete fout: JBake crasht niet. Het "werkte", gewoon met een verkeerde map.
-
De build werkte:`./gradlew bake`genereerde correct de bestanden in`build/bake/`.
-
De implementatie werkte:`publishSite`duwde de goede content online
-
Alleen
servewas kapot: de ontwikkeltaak, die men constant gebruikt om te itereren. -
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.