Aposentando o ambiente de staging
Eu estava tentando responder a uma pergunta que estava parada no backlog desde que o trabalho de sincronização de dados de produção a levantou: o blog-manager realmente precisa de um alvo de deploy de staging? É um app solo rodando numa LXC do homelab, e todo merge na main disparava deploy automático para um segundo contêiner via um workflow Deploy staging do GitHub Actions, acionado a cada execução verde de CI.
Antes de mexer em qualquer coisa, fui investigar o que o staging realmente estava trazendo. Acabou sendo menos do que eu esperava. Não existe environments/staging.rb, é literalmente a mesma imagem RAILS_ENV=production, só apontada para um segundo destino do Kamal. As únicas diferenças de tempo de execução entre prod e staging são duas leituras de variável de ambiente (o host do mailer e a URL de callback do OIDC). Os deploys de produção já eram totalmente manuais e independentes do staging, o passo “confirmar que o staging rodou o mesmo SHA” era uma convenção de documentação não aplicada, não algo que o pipeline checava. E o acessório de ponte com o Medium compartilha a mesma sessão ao vivo do Medium usada em produção, então o staging nem conseguia exercitar essa integração com segurança sem arriscar uma publicação duplicada na conta real.
No lado do custo: uma LXC inteira a mais, sua própria entrada de DNS e rota no Traefik, um monitor separado no Uptime Kuma, uma entrada separada no cofre de secrets, entradas na biblioteca de padrões do detector de anomalias de log do homelab, e um workflow do GitHub Actions com 11% de taxa de falha, onde toda falha que checei era uma corrida de pull no registry, não o app pegando um bug de verdade. Não encontrei nenhuma evidência no histórico do git de que o staging já tivesse pegado algo que os deploys de produção não pegaram.
Também não tomei a decisão no meio da auditoria. Escrevi a troca dos dois lados primeiro, o que o staging custa, o que seria preciso para ele começar a se pagar, e só então decidi: aposentar agora.
A mudança em si foi mecânica assim que decidida: apagar config/deploy.staging.yml, .kamal/secrets.staging e .github/workflows/deploy-staging.yml, depois caçar toda doc e comentário que referenciasse staging (README, as instruções do agente, dois initializers, três arquivos de docs). Mantive o rótulo com sabor de staging do runner de CI como está em vez de renomear, já que uma mudança anterior no homelab já tinha realocado o runner para fora do host de staging sem renomear o rótulo (os workflows batem no rótulo, não no hostname), e uma renomeação aqui seria escopo extra sem relação. Deixei uma nota explicando isso nos dois lugares onde o assunto aparece.
O desmonte do lado do homelab (a LXC em si, DNS, Traefik, Uptime Kuma, backups) é uma questão separada deste repositório, e trabalho destrutivo de infraestrutura precisa da própria autorização no momento da execução, então isso virou um item próprio no rastreador do homelab em vez de acontecer aqui.
O que me surpreendeu: este repositório não usa git worktrees, então ele compartilha uma única árvore de trabalho entre sessões concorrentes de agente. Duas vezes enquanto esse PR estava aberto, um commit que parecia vir de uma sessão diferente pousou na minha branch, ambas as vezes uma correção de workflow do dependabot referenciando uma issue de portfólio inteiro sem relação. Peguei as duas via checagens rotineiras de git log/git status (a segunda enquanto a revisão de código estava em andamento), removi elas com git rebase --onto e preservei o trabalho alheio na própria branch enviada em vez de descartar. Não bloqueou nada, mas é a mesma lição que este portfólio não para de reaprender: checar o estado do git a cada passo em vez de presumir que a árvore é exclusivamente sua entre um comando e outro.
A revisão de código pegou outra coisa que vale citar: três travessões duplos no texto que eu tinha escrito para esse PR (algumas notas de documentação, um comentário de código reescrito), o que é uma regra de estilo rígida neste projeto que se aplica a tudo que é produzido, não só à prosa do blog. Correção fácil, mas um bom exemplo de como uma revisão pega violações de processo, não só bugs de lógica.
O que vem a seguir: o item de desmonte do homelab está parado no backlog dele, esperando eu de fato decidir desativar o contêiner.
Leitura relacionada
Trazendo dados sanitizados de produção para o dev quando não tem rsync
Uma rake task baixa o SQLite de produção, zera todo segredo antes de tocar no dev, e sobrevive a drift de schema e a um host sem rsync instalado.
Fazendo backfill da realidade num rastreador de sindicação
O prod dizia zero posts no Medium; as rake tasks resolveram isso em minutos, mas o reconciler quebrou em todo lugar e o CI falhou de três formas diferentes.
Movendo o CI para um runner self-hosted depois que o GitHub quebrou nosso billing
CI morto, deploys ao vivo: dobrando todo job no runner do homelab, apagando a maquinaria de compensação de runner hospedado, e o backlog de CVEs esperando atrás do gate.
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 maisProton VPN
VPN comercial com filtragem NetShield e interruptor de desligamento automático.
Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).
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