3 pontos por GN⁺ 2023-09-11 | 1 comentários | Compartilhar no WhatsApp
  • 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, adiciona header.html aos arquivos HTML e copia os demais arquivos para build sem alterações
  • make build usa find source -type f e patsubst para mapear arquivos de entrada e saída, aplicando regras do make diferentes para .html e 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 build sem modificação
  • Regras do Makefile

    • O target build tem dependências que associam todos os arquivos sob source aos arquivos de saída correspondentes sob build
    • 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/%.html depende de source/%.html, header.html e Makefile
    • mkdir -p $(dir $@) cria o diretório de saída, e cat 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 com cp $< $@
    • Com apenas esse header.html e essas regras, executar make build já gera um diretório build que pode ser aberto localmente ou enviado para um servidor web

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 de source/foo.html
    • Para arquivos Markdown, você pode manter o sufixo .html ou alterar a regra para procurar arquivos .md como 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 executar make build automaticamente quando houver mudanças em source, header.html ou Makefile
    • 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-pages como worktree em public_html
    • Copia-se build/* para public_html
    • Executam-se git add --all, git commit -m "Deploy to github pages" e git 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

 
GN⁺ 2023-09-11
Opiniões no Hacker News
  • Antigamente, eu gerava meu site pessoal (https://pablo.rauzy.name/) com um simples Makefile
    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 grep e sed
    Também adicionei alguns git hooks que chamam make automaticamente quando necessário, especialmente no servidor remoto onde o site é hospedado: ao dar push no repositório, a versão pública é reconstruída
    Funciona 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 anos
  • O problema desse método é que, se você apaga um arquivo em source/, ele não é apagado em build/
    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 make diretamente só para aquele arquivo
    Gostaria de saber se existe algum workaround comum para esse padrão em Makefiles
    • Não sei se é um padrão comum, mas a minha solução foi executar, a cada vez, um comando que remove os arquivos “inesperados”
      Uso a função shell do GNU Make para listar os arquivos e a função filter-out para 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ção shell, garantindo que rode sempre
      Link do Makefile: https://github.com/jaredkrinke/make-blog/blob/main/Makefile
    • Exclusão e renomeação de arquivos são problemas comuns em vários sistemas de controle de versão/build
      Além da opção nuclear como make clean, você pode ter alvos (targets) específicos do Make para excluir e renomear. Por exemplo, fazer make rm sourcefile ou make mv sourcefile newsourcefile tratar tanto a exclusão/renomeação do original quanto dos artefatos gerados
      Na prática, mesmo em blogs ou projetos online bem grandes, o ciclo make clean / make all normalmente é 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 cliente
    • make clean não resolveria?
    • Acho que algo assim funcionaria
      rm/%.html:
      @rm -f source/%.html build/%.html
      Para executar, use $ make rm/page.html
    • O sistema de build genérico Shake tem um recurso prune exatamente para esse propósito
      http://neilmitchell.blogspot.com/2015/04/cleaning-stale-file...
      Mas acho que a melhor solução que também funciona com make é ter um alvo make dist que gere o arquivo final .tar.gz dos 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.gz final precisa ser criado do zero
  • Fui diretamente inspirado pelo trabalho do Karl no script shell blog.sh mencionado neste texto
    Peguei 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
    • Temos gostos parecidos. O meu se chama 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
  • Misturar um pouco de m4 permite manter a mesma abordagem esquelética e ainda ganhar um pouco mais de flexibilidade

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

  • Não existe “um pouco de 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 a m4
    Um 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 define por macros que geram macros para adicionar comentários na saída, de modo que o script de geração de stack trace funcione
    No próximo projeto, você vai estabelecer uma regra absoluta. Nada de m4! Claro, talvez aquela única ocorrência possa ser uma exceção
  • Em vez de depender de substituição genérica de texto como m4 ou perl, recomendo usar SGML, que é a base e o superconjunto comum de HTML e XML
    SGML 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
  • Em vez de m4 ou busca-e-substituição com sed, vale experimentar envsubst
    É um programa que substitui referências a variáveis no estilo Bash, por exemplo $TITLE, pelos valores das variáveis de ambiente
    export CURRENT="..."
    cat page.html | envsubt
  • “Um pouco de m4”? Por favor, nunca
    m4 nem é lá grande coisa como linguagem de programação esotérica
  • Já tentei uma vez, nunca mais
    O fato de ter rodado no sendmail não é justificativa suficiente para nada
  • Gosto do fato de que quase todos os blogs de desenvolvedores que encontro no HN têm feed RSS
    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 Sites da Hey Homepage
    No 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
    • Estou tentando entender se eu, como desenvolvedor, sou estranho por não querer ter meu próprio blog
      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”
    • Também há o https://prose.sh, parecido com o Bear Blog
  • Um amigo certa vez explicou como ele gerava artigos científicos com make
    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
  • É uma ideia elegante, mas se você já está fazendo push para o GitHub, dá para subir só o código-fonte e deixar que o GitHub publique o Markdown como páginas hospedadas
    https://pages.github.com/
    • Aí você passa a depender do GitHub para mais do que simples hospedagem. É melhor, desde o começo, manter a geração do site executável localmente
  • Gostei do código
    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 com sed para 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...
    • Achei a abordagem GEMINI dele bem engraçada. Ela remove a maior parte da formatação com regex

Ainda assim, há algumas limitações. Eu organizo os posts por namespace e coloco a data na URL, e o make não lida muito bem com isso diretamente

  • Ao ver scripts desse tipo, fica claro exatamente por que usamos coisas como esbuild ou vite em vez de fazer tudo nós mesmos
  • A vantagem do make é que, em programas grandes compilados com compiladores lentos, ele torna a recompilação incremental muito mais rápida quando há uma pequena alteração
    Algo 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 cat de um cabeçalho comum e algumas centenas de arquivos HTML, não há vantagem em usar make em vez de um script. Isso só cria o risco de fazer builds incompletos por causa de bugs de dependência
    • Se as dependências de arquivos não forem realmente importantes, basta marcar os alvos de build como .phony
      Ainda assim, dá para manter distinções como make build e make push
  • Uau, isso é quase exatamente o que eu estava pensando em fazer no meu site
    Em 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