Construindo um scan de posts sensível a localidade para o blog-manager
Eu rodo um app Rails chamado blog-manager que escaneia meus dois sites Astro em busca de posts novos, importa e os sindica para o Medium e o Dev.to. Isso ficou plano o tempo todo: cada post mora direto em src/content/blog/<slug>.md, um arquivo por post, sem subdiretórios.
Isso está prestes a mudar. Eu quero traduções em espanhol dos meus posts, e o plano que saiu de um spike há alguns dias se firmou numa abordagem do lado do git: o canônico em inglês em src/content/blog/en/<slug>.md, o irmão em espanhol em es/<slug>.md, escritos no mesmo commit para que qualquer divergência apareça nos diffs em vez de se esconder em algum banco de dados. O blog-manager e a sindicação continuam em inglês para sempre, eles só precisam saber onde procurar quando os posts se mudarem para en/.
Esse issue era o trabalho pré-requisito: ensinar o scanner a encontrar posts no novo layout, ensinar as skills de publicação a escrever lá, e construir uma checagem de CI que pega uma tradução faltando. Nenhuma migração de site de fato acontece aqui, isso é um trabalho separado e maior. Isso é só garantir que o blog-manager não quebre quando ela acontecer.
A mudança no scanner
O núcleo do scanner do blog-manager é uma chamada à GitHub Contents API: lista os arquivos em src/content/blog/, compara com o que está no banco, faz upsert do que mudou, soft-delete do que sumiu. Simples, e funcionou bem por um ano.
Minha primeira tentativa de sensibilidade a localidade foi: sondar src/content/blog/en/ primeiro. Se existir e tiver arquivos markdown, escanear ali em vez do diretório plano. Senão, cair de volta para o plano, sem mudança. Dois modos de falha que eu já tinha pensado: um diretório en/ ausente devolve um 404 do GitHub, que eu tive que capturar em vez de deixar propagar (o handler discard_on do meu job trata um 404 não capturado como um blog quebrado e marca como falho), e um diretório en/ que existe mas ainda está vazio precisava do mesmo fallback, senão uma migração pela metade pareceria que todo post foi apagado.
Escrevi testes para os dois casos. Tudo passou. Abri o PR.
O que a revisão de código pegou
Eu rodo uma revisão de múltiplos ângulos antes de mesclar qualquer coisa nesse repositório, oito passagens independentes sobre o diff procurando bugs de correção, código redundante e violações de convenção. Quatro delas, trabalhando de forma independente, convergiram para o mesmo achado: minha lógica de “prefira en/, caia para o plano” assumia que a migração era tudo ou nada. No momento em que en/ tivesse até um único arquivo, eu parava de olhar para o diretório plano por completo.
Isso está errado para o plano de verdade. Meu arquivo de 166 posts não vai se mudar para en/ de uma vez só, são muitos arquivos para traduzir e eu prefiro fazer isso aos poucos, talvez começando só com posts novos e preenchendo os antigos depois, se é que vou me dar ao trabalho. O que significa que, por um tempo, en/ vai ter alguns posts e o diretório plano vai ter outros, ao mesmo tempo, de propósito. Meu scanner teria silenciosamente feito soft-delete de todo post ainda no diretório plano na primeira vez que eu movesse um único post para en/. Não é perda de dados permanente, já que os posts se restauram sozinhos se o arquivo deles reaparecer, mas eles teriam sumido do meu painel e saído da sindicação até eu terminar uma migração que talvez levasse meses.
A correção foi parar de tratar um en/ não vazio como totalmente autoritativo e, em vez disso, unir com a listagem do diretório plano: en/ ganha se um slug aparecer nos dois, mas nada que já migrou para o plano é descartado só porque comecei a mover arquivos por aí. É uma mudança de código pequena. Encontrá-la não foi pequeno, levou quatro ângulos de revisão diferentes chegando de forma independente no mesmo bug antes de eu confiar o bastante para reescrever a lógica principal e os testes em volta dela.
Duas dessas quatro passagens de revisão também pegaram algo que eu tinha deixado passar por um motivo bem diferente: eu tinha escrito comentários de código novos com travessões longos, o que viola uma regra de formatação que tenho para literalmente tudo que escrevo, incluindo código. Pego pela mesma passagem automatizada, na mesma revisão, bem do lado do bug de correção de verdade. Gravidade diferente, mesma lição: uma passagem de revisão não sabe qual achado importa mais até você olhar.
Um bug que eu não escrevi
Enquanto eu conectava a checagem de CI na configuração de pre-commit do meu blog principal, uma sessão de agente completamente sem relação estava ativa no mesmo diretório de trabalho, lançando uma funcionalidade de feed RSS. Nenhum de nós dois estava usando um worktree do git, a convenção daquele projeto é “trabalhe direto na main, sem branches paralelas”, o que é seguro desde que seja verdade mesmo que só uma coisa mexa no repositório por vez.
Não era, dessa vez. Eu tinha adicionado uma linha ao package.json, um script npm apontando para meu novo script de checagem de tradução, e deixei sem commit por alguns minutos enquanto testava as coisas. A outra sessão fez commit das próprias mudanças com um git add simples e levou minha linha junto. Nada quebrou, a linha está correta e funciona, mas agora git log -- package.json atribui ela a um commit sobre uma funcionalidade de feed RSS que não tem nada a ver com traduções. Eu não reescrevi o commit mesclado, isso parecia mais risco do que uma linha mal atribuída justificava. Anotei em vez disso, para pegar da próxima vez antes que aconteça, não depois.
O que vem a seguir
O scanner e as skills de publicação estão prontos e mesclados. A migração de fato, mover meu arquivo para en/, montar o roteamento de i18n do Astro, e lançar traduções reais em espanhol, é trabalho separado, começando pelo meu site menor como piloto antes de eu tocar no maior.
Leitura relacionada
O que acontece quando um job faz broadcast para ninguém
Fechando o loop da imagem de destaque: frontmatter só por inserção, uma guarda que pegou divergência real na primeira execução, e um broadcast sem ouvinte.
Uma correção de desvio de documentação que não foi tão chata quanto parecia
Três itens da auditoria que cada um virou outra coisa: uma alegação meio corrigida, uma recuperação de senha silenciosamente morta, e um e-mail de staging que apontaria para produção.
Um 500 escondido dentro das rotas isoladas de uma engine montada
O painel de jobs retornava um 500 em vez de uma página de login: helpers de rota sem qualificação resolvem contra a engine, não contra a aplicação. Uma linha, mais o gêmeo dormente dela.
Você também pode achar útil
RackNerd VPS
Hospedagem VPS econômica para serviços leves que funcionam continuamente.
Como afiliado da RackNerd, ganho com compras qualificadas.
Saiba maisAdGuard para iOS
Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.
Como afiliado da AdGuard, ganho com compras qualificadas.
Saiba maisNordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba mais