Gerador de site estático com "Make" (2022)
(karl.berlin)- Sites estáticos são fáceis de hospedar, rápidos e exigem pouca manutenção; para um site simples, algumas linhas de Makefile podem bastar, sem precisar aprender um construtor separado
- A estrutura central mantém a disposição dos arquivos em
source, adicionaheader.htmlaos arquivos HTML e copia os demais arquivos parabuildsem alterações make buildusafind source -type fepatsubstpara mapear arquivos de entrada e saída, aplicando regras do make diferentes para.htmle para os outros arquivos- Destaque da página atual, conversão de Markdown, servidor local, rebuild automático ao detectar mudanças e deploy no GitHub Pages podem ser adicionados como targets extras quando necessário
- Como tem poucas dependências e deixa claro o que precisa ser alterado, em sites com requisitos pequenos um fluxo de build feito à mão pode ser mais rápido do que um gerador genérico
Site estático básico feito só com Makefile
- Geradores de sites estáticos produzem resultados fáceis de hospedar, rápidos e com carga de manutenção muito baixa
- Para um site simples, pode ser mais rápido e mais satisfatório escrever seus próprios scripts do que aprender e adaptar um construtor de sites existente
- Se for um site comum, sem timestamp de atualização automática nem feed RSS, uma configuração ainda mais simples do que scripts voltados para blog já é suficiente
-
Requisitos básicos
- Todos os arquivos de entrada ficam no diretório
source, e a saída mantém a mesma estrutura de diretórios header.htmlé adicionado no início de todos os arquivos HTML- Arquivos que não são HTML são copiados para o diretório
buildsem modificação
- Todos os arquivos de entrada ficam no diretório
-
Regras do Makefile
- O target
buildtem dependências que associam todos os arquivos sobsourceaos arquivos de saída correspondentes sobbuild - Ele não executa trabalho por conta própria, mas serve como ponto de entrada para que a regra apropriada seja aplicada a cada arquivo
- A regra
build/%.htmldepende desource/%.html,header.htmleMakefile mkdir -p $(dir $@)cria o diretório de saída, ecat header.html $< > $@combina o header com o HTML de entrada para gerar a saída- A regra
build/%copia diretamente arquivos que não são HTML comcp $< $@ - Com apenas esse
header.htmle essas regras, executarmake buildjá gera um diretóriobuildque pode ser aberto localmente ou enviado para um servidor web
- O target
Exemplos de extensão e targets auxiliares
-
Indicar a página atual
- Destacar a página atual na navegação ajuda o visitante a ver rapidamente onde está dentro do site
- No exemplo, os links de navegação são localizados para adicionar a classe
current sed -E 's|(href="$(subst source,,$<))|class="current" \1|' header.html | cat - $< > $@- A forma real da substituição deve variar conforme a estrutura de markup usada
-
Gerar HTML a partir de Markdown
- Se você não quiser escrever HTML diretamente, ou se o conteúdo existente estiver em Markdown, pode encadear um conversor de Markdown para HTML por pipe
- O exemplo usa smu
smu $< | cat header.html - > $@- A regra básica ainda assume que
build/foo.htmlé gerado a partir desource/foo.html - Para arquivos Markdown, você pode manter o sufixo
.htmlou alterar a regra para procurar arquivos.mdcomo entrada
-
Pré-visualização local
- Em alguns sites, abrir o arquivo local diretamente no navegador não permite uma prévia correta, e um motivo comum é o uso de links absolutos em vez de relativos
- Python já vem instalado em muitos sistemas e inclui um servidor web adequado para esse uso
- Target
serve:python -m http.server -d build
-
Rebuild automático ao mudar arquivos
- Durante o trabalho no site, reconstruir manualmente toda vez é incômodo
- Com
entr, é possível executarmake buildautomaticamente quando houver mudanças emsource,header.htmlouMakefile - Target
watch:find source header.html Makefile | entr make build - Se você quiser evitar dependências, também pode usar
inotifywait
-
Deploy no GitHub Pages
- Se o repositório já estiver no GitHub, hospedar o HTML resultante no GitHub Pages é uma escolha natural
- Ajustar o comando de deploy para não precisar se preocupar com detalhes do git é um pouco trabalhoso, mas dá para resolver com
git worktree - O método é baseado neste post de Sangsoo Nam
- Adiciona-se a branch
gh-pagescomo worktree empublic_html - Copia-se
build/*parapublic_html - Executam-se
git add --all,git commit -m "Deploy to github pages"egit push origin gh-pages - No fim, remove-se o worktree com
git worktree remove public_html
-
Exemplo real
- Uma página feita com essa abordagem pode ser vista em karlb/astridbartel.de
- Um gerador de site estático completo pode começar com seis linhas de Makefile e ser adaptado rapidamente ao que você precisa, sem dependências incomuns nem carga de manutenção
1 comentários
Opiniões no Hacker News
Depois acrescentei recursos como notícias e feed RSS, listas automáticas de artigos de pesquisa e materiais de aula, e uma lista de livros filtrável por tags. Ainda é um Makefile, mas o Makefile em si acabou ficando até um pouco mais simples; em vez disso, ele chama scripts Bash que usam os excelentes utilitários xml2 e 2xml para manipular HTML linha a linha. Uso principalmente utilitários essenciais como
grepesedTambém adicionei alguns git hooks que chamam
makeautomaticamente quando necessário, especialmente no servidor remoto onde o site é hospedado: ao dar push no repositório, a versão pública é reconstruídaFunciona muito bem há anos, e o histórico do git remonta a 2009. Olhando os primeiros commits, eram
beccad7 (FIRST_VERSION) Initial commit,d1cc6d7 adding link to Google Reader shared items,6ccfd0c fix typo,d337959 adding link to Identi.ca account; realmente se passaram 15 anosxml2e2xmlparecem ser um projeto meio abandonadohttps://github.com/cryptorick/xml2
https://manpages.debian.org/unstable/xml2/2xml.1.en.html
source/, ele não é apagado embuild/Nos meus projetos, reconstruir o site inteiro ainda é rápido o bastante, então optei por apagar toda a pasta build antes de reconstruir
https://github.com/jez/jez.github.io/blob/source/Makefile#L1...
Isso neutraliza boa parte dos builds incrementais, que são um dos grandes motivos para usar um sistema de build, mas, se você souber qual página quer regenerar, ainda pode chamar
makediretamente só para aquele arquivoGostaria de saber se existe algum workaround comum para esse padrão em Makefiles
Uso a função
shelldo GNU Make para listar os arquivos e a funçãofilter-outpara filtrar as saídas “esperadas”. É uma gambiarra feia, mas faço o comando ser executado como parte da expansão de uma variável via funçãoshell, garantindo que rode sempreLink do Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
Além da opção nuclear como
make clean, você pode ter alvos (targets) específicos do Make para excluir e renomear. Por exemplo, fazermake rm sourcefileoumake mv sourcefile newsourcefiletratar tanto a exclusão/renomeação do original quanto dos artefatos geradosNa prática, mesmo em blogs ou projetos online bem grandes, o ciclo
make clean/make allnormalmente é rápido o bastante, e muitas vezes é necessário ao alterar templates ou elementos de design do site. Se a escala for grande a ponto de o tempo de rebuild virar preocupação, talvez faça mais sentido usar um CMS de verdade, com as fontes gerenciadas em um banco de dados e geração dinâmica no acesso do clientemake cleannão resolveria?rm/%.html:@rm -f source/%.html build/%.htmlPara executar, use
$ make rm/page.htmlpruneexatamente para esse propósitohttp://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
Mas acho que a melhor solução que também funciona com
makeé ter um alvomake distque gere o arquivo final.tar.gzdos artefatos. Se as regras forem escritas corretamente, arquivos antigos não entram. Em projetos grandes pode ser lento, mas durante o desenvolvimento você nem precisa usar isso; basta usar na hora do release. O build incremental continua possível, e só o.tar.gzfinal precisa ser criado do zeroblog.shmencionado neste textoPeguei aquilo e adaptei para criar o
barf, meu gerador de site estático minimalista. Ele não existiria se o Karl não tivesse publicado aquele ótimo trabalho[0]: https://github.com/karlb/karl.berlin/blob/master/blog.sh
[1]: https://barf.bt.ht
shite, e é com ele que gero meu site. O nome sugere a qualidade do software :)O que mais gosto é que, até agora, nunca precisei atualizar nada, e parece que nunca vou precisar. A segunda melhor coisa é que ele tem hot reload sem JavaScript
[1] https://github.com/adityaathalye/shite
[2] https://evalapply.org
m4permite manter a mesma abordagem esquelética e ainda ganhar um pouco mais de flexibilidadeMantive um site pequeno feito desse jeito uns 20 anos atrás. Mas, fora sites pessoais, não sei se esse modelo se encaixa bem hoje em dia. Isso porque esse método, por natureza, força um papel de Web 1.0. Todos os usuários que contribuem precisam ser proficientes em HTML, ou alguém precisa assumir a tarefa ingrata de “webmaster”
[1] https://en.wikipedia.org/wiki/M4_(computer_language)
m4”Você começa um projeto limpo prometendo que desta vez não vai mexer com
m4. Aí, para reduzir código repetido, acaba colocando uma pequena chamada am4Um ano depois, está vasculhando cinco camadas de expansão de macros para descobrir por que a palavra “cat” desaparece silenciosamente do site, e no fim descobre que um desenvolvedor júnior tentou implementar um loop for por conta própria, em vez de copiar do manual, e estragou as aspas
Depois de resolver o problema imediato, você conclui que depurar a DSL é difícil demais, então traz aquele arquivo de macros M4 que vinha copiando de projeto em projeto. Depois passa um dia inteiro trocando todos os usos de
definepor macros que geram macros para adicionar comentários na saída, de modo que o script de geração de stack trace funcioneNo próximo projeto, você vai estabelecer uma regra absoluta. Nada de
m4! Claro, talvez aquela única ocorrência possa ser uma exceçãom4ouperl, recomendo usar SGML, que é a base e o superconjunto comum de HTML e XMLSGML oferece nativamente macros de texto com verificação simples de tipos, isto é, expansão de entidades, e também permite expansão de macros parametrizadas com conhecimento de tipos. Aqui, tipos significam o tipo geral de conteúdo de elementos de marcação, ou seja, os elementos filhos permitidos e sua ordem, além de considerar expansão ou escape conforme contextos como atributos, CDATA e RCDATA
Expandir e escapar adequadamente comentários de usuários potencialmente maliciosos, aplicando regras de usuário que, por exemplo, permitam marcação em nível inline e proíbam o elemento
script, é algo que só SGML consegue expandir corretamente. Também dá para expandir Markdown ou sintaxe wiki para HTML, importar conteúdo HTML externo/de sindicação, criar RSS e outlines para navegação etc. Funciona bem para tarefas bastante complexas de preparação de sites estáticos na linha de comando[1]: https://sgmljs.net/docs/producing-html-tutorial/producing-ht...
[2]: https://sgmljs.net/docs/sgmlproc-manual.html
m4ou busca-e-substituição comsed, vale experimentarenvsubstÉ um programa que substitui referências a variáveis no estilo Bash, por exemplo
$TITLE, pelos valores das variáveis de ambienteexport CURRENT="..."cat page.html | envsubtm4”? Por favor, nuncam4nem é lá grande coisa como linguagem de programação esotéricaO fato de ter rodado no sendmail não é justificativa suficiente para nada
Toda vez que leio um texto interessante por aqui, assino o feed. Seja um site Wordpress, Bear Blog, Micro.blog, um blog Havenweb ou o feed de um site feito à mão, eu adiciono ao módulo
Really Social Sitesda Hey HomepageNo fim das contas, quero tornar pública essa lista de blogs, como a Kagi faz na iniciativa Small Web. Mas, para acrescentar qualidade, a curadoria é essencial, e quando penso em curadoria, parece natural começar uma espécie de revista online
De onde as pessoas tiram essa “sensação de legitimidade”, no bom sentido, de que podem compartilhar algo com os outros? Fico curioso por que presumem que outras pessoas vão se interessar pelo que elas estão fazendo. Às vezes parece uma competição. Algo como “para conseguir likes ou exposição no blog, preciso criar a coisa mais legal possível”
Colaboração obviamente é boa, e só é possível se tudo for público. Mas não entendo bem onde fica a linha entre “faço isso porque parece legal” e “me esforço para compartilhar com outras pessoas para obter reação”
Ele disse que, mesmo mudando apenas um arquivo de teste, conseguia regenerar o artigo inteiro com um único comando, incluindo a execução dos testes e a geração dos gráficos
https://pages.github.com/
O meu ficou um pouco superdimensionado porque eu queria hot reload sem JavaScript, e foi uma divertida sessão de yak shaving
A ideia básica é a mesma. Uso heredoc nos templates e um compilador para transformar texto simples em HTML; no meu caso,
pandoc. Uso um CSV intermediário para gerar o índice e também tenho uma técnica útil comsedpara extrair front matter. Clássico e bom[1] https://github.com/karlb/karl.berlin/blob/master/blog.sh
[2] https://github.com/adityaathalye/shite
[3] Meu jeito de fazer: https://github.com/adityaathalye/shite/blob/master/bin/templ...
Ainda assim, há algumas limitações. Eu organizo os posts por namespace e coloco a data na URL, e o
makenão lida muito bem com isso diretamentemakeé que, em programas grandes compilados com compiladores lentos, ele torna a recompilação incremental muito mais rápida quando há uma pequena alteraçãoAlgo que levaria 40 minutos para uma recompilação completa pode terminar em cerca de 3 segundos
Se um site estático é gerado do zero em menos de 1 segundo apenas fazendo
catde um cabeçalho comum e algumas centenas de arquivos HTML, não há vantagem em usarmakeem vez de um script. Isso só cria o risco de fazer builds incompletos por causa de bugs de dependência.phonyAinda assim, dá para manter distinções como
make buildemake pushEm outro projeto pequeno, usei um script de shell bem pequeno como bundler improvisado. Ele inseria CSS e JS dentro do HTML, e o objetivo era permitir que arquivos não compilados também fossem servidos localmente