Por que `serve` exibía um site feio enquanto `publishSite` era perfeito
Publié le 22 April 2026
Você conhece aquele momento em que o seu site local parece nada, mas uma vez colocado online tudo fica impecável? Isso aconteceu comigo com meu plugin Gradle JBake. A tarefa`serve`exibia botões quadrados, texto escuro sobre fundo preto, e um CSS estranhamente ausente. Porém`publishSite`gerava o mesmo site, ele, impecável. O culpado não era nem o CSS, nem o navegador, nem o cache. Era uma linha de código Kotlin — e um mau hábito com os argumentos na linha de comando.
- tic
-
[]
A cena: quatro screenshots, dois renders
Era depois de uma falha do sistema. Eu tinha tirado quatro capturas de tela para comparar o renderizado local (./gradlew serve`sobre`localhost:8820) com a renderização online (`publishSite`no GitHub Pages). Dois screenshots por página: topo e base.
Renderização local
Topo da página— Os botões Project e Template sãopequenos, retangulares, nas cores padrão (azul e verde). O cartão Start Writing tem umfundo preto uniforme, sem moldura.
Rodapé— O subtítulo "Últimos artigos e recursos" équase ilegível, muito escuro sobre fundo preto. As datas sob as imagens do blog ("17 October 2013", etc.) sãoausentes.
Renderizado online
Topo da página— Os mesmos botões sãograndes, ovinos, verde neon flashy, texto em maiúsculas. O cartão tem umquadro branco arredondado com sombra projetada.
rodapé— O subtítulo ébem legível. As datas sãovisíveis. Os cartões dos artigos têmcantos arredondadose os títulos sãoem negrito.
Primeiro reflexo: o tema. Utilizo um sistema de tema dinâmico (claro, escuro, alto contraste) baseado em`localStorage`. Talvez o local store desencadeava um tema high-contrast? Não. Já havia escritohttps://cheroliv.com/blog/2025/0098_preloading_css_variable_post.html[um artigo inteiro sobre o pré-carregamento dos temas], eu sei como isso funciona. E então, mesmo em modo high-contrast, a estrutura CSS (botões ovais, sombras) devia estar lá. Ela não estava lá.
Segundo reflexo: o cache do navegador. Não, eu tinha feito o teste com perfis limpos. O diff persistia.
Terceiro reflexo:`serve`não servia os mesmos arquivos que`publishSite`.
O diagnóstico: serve não apontava para a pasta correta
Meu plugin Gradle`bakery`tem duas tarefas principais:
-
bake: gera o site estático em`build/bake/` -
serve: inicia um servidor web local -
publishSite: empurra`build/bake/`para GitHub Pages
A divergência só poderia vir de`serve`. Si publishSite`publicava o conteúdo correto, é que`build/bake/`estava bom. Se`serve`exibia outra coisa, o que significa que não servia`build/bake/.
Vamos olhar o código. Em`SiteManager.kt`, a tarefa`serve`é definida assim :
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`é o ponto de entrada da CLI JBake 2.7.0. A ideia: passar-b`para a pasta de origem e`-s`para a pasta de destino, então iniciar o modo servidor. Mas…
A causa raiz: O CLI do JBake não funciona assim
JBake 2.7.0 aguarda algunsargumentos posicionaispara a fonte e o destino, seguido de flags opcionais. A assinatura é :
jbake <source> <destination> [options]
Mas no meu código, eu escrevia :
args = listOf("-b", "/path/to/site", "-s", "/path/to/build/bake")
JBake interpretava isso :
-
-b→ analisar o sinalizador \"bake\" (booleano), consumido -
/path/to/site→ torna-se argumento posicional 1 (fonte) -
-s→ analisar o opção "serve" (booleano), servidor iniciadoimediatamente -
/path/to/build/bake→ torna-se um argumento órfão, ignorado ou mal interpretado
Resultado: JBake estava tomando`/path/to/site`como fonte,não levava em conta o destinofornecida (build/bake), e utilizava seu diretório padrão (frequentemente`./output`ou uma cópia temporária). O arquivo`css/styles.css`o build era portanto sobrescrito ou ignorado, e é um velho CSS padrão (sem as variáveis personalizadas, sem os arredondamentos, sem as sombras) que estava sendo servido.
Em comparação,bake et publishSite`utilizam oplugin Gradle JBake oficial(`jbake-gradle-plugin) que escreve bem em`build/bake/`via a API Gradle. Eles não passam pela CLI.
A solução: argumentos posicionais, não flags
A correção é trivial — uma vez que se sabe. Basta passar a fonte e o destino como argumentos posicionais, depois`-s`por último:
args = listOf(
file(site.bake.srcPath).absolutePath,
layout.buildDirectory.get()
.asFile.resolve(site.bake.destDirPath)
.absolutePath,
"-s"
)
É isso. Três linhas alteradas, bug resolvido.
Após publicação local do plugin (publishToMavenLocal), un `./gradlew serve`lança JBake com a sintaxe correta :
jbake /home/user/project/site /home/user/project/build/bake -s
E desta vez, o servidor Jetty integrado ao JBake serve bem o conteúdo de`build/bake/`— idêntico ao que é publicado online.
Verificação rápida com curl
Para garantir que o servidor esteja servindo o conteúdo correto, um simples`curl`confirma que`index.html`contém`data-bs-theme="light"`e que`build/bake/css/styles.css`pesa bem 31 402 bytes — idêntico ao arquivo gerado 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
O render local agorapixel-idênticoao renderizado implantado.
Por que esse bug era sutil
Você vê por que era difícil de pegar?
-
Nenhum erro explícitoJBake não travava. Ele "funcionava", simplesmente com uma pasta errada.
-
O build funcionava:`./gradlew bake`gerava corretamente os arquivos em`build/bake/`.
-
O despliegue estava funcionando:`publishSite`empurrava o bom conteúdo online
-
Só
serveestava quebrado: a tarefa de desenvolvimento, aquela que usamos constantemente para iterar. -
O hábito de
-key value: como desenvolvedor, somos condicionados por anos de CLI Unix (-o output`,-i input). O CLI do JBake é uma exceção — a fonte e o destino são posicionais.
Conclusão
Se você mantém um plugin Gradle que envolve JBake (ou qualquer ferramenta CLI),leia a documentação dos argumentosmesmo se você pensa que os conhece. Uma hipótese implícita (-s = "set destination") pode custar horas de depuração visual.
Aqui, o fix era literalmente para mudar:
args = listOf("-b", src, "-s", dest) // ❌ BUG
en :
args = listOf(src, dest, "-s") // ✅ FIX
Três tokens deslocados, e meu site local volta a ficar tão bonito quanto o site em produção.
Lição: quando a renderização difere entre local e produção sem razão óbvia, suspeite primeiro do pipeline — não o CSS, não o navegador, e muito menos o framework. Frequentemente é a etapa logo antes da renderização que está trapaceando.