Testar um gate de deploy acabou fazendo o deploy da coisa que eu estava testando
O pipeline de deploy do imperfectsystems.com tinha exatamente um gate: “compilou ou não.” Isso já era verdade quando o GitHub Actions rodava tudo, e continuou verdade depois que eu mudei o deploy para o Cloudflare Workers Builds algumas semanas atrás. Nada checava tipos, nada checava formatação, e o auto-merge do dependabot que costumava se apoiar nesse mesmo sinal fraco tinha acabado de ser desligado junto com o resto do Actions. Hora de adicionar gates que realmente significam algo: astro check e Prettier, rodando antes de cada deploy.
A configuração foi a parte fácil
Copiei o .prettierrc do vdaluz.com palavra por palavra, adicionei @astrojs/check e prettier-plugin-astro como devDependencies, conectei os scripts check / format / format:check. Sem ESLint dessa vez, o repositório é pequeno o suficiente para não valer o custo de configuração, a mesma decisão que o spike que definiu esse issue já tinha tomado.
Então eu de fato rodei o astro check em vez de assumir que ia passar, porque “provavelmente passa hoje” numa descrição de issue é um chute, não um resultado.
O gate encontrou algo real na primeira rodada
Um erro: astro.config.mjs:12:5 - Type '{ themes: { light: string; dark: string; }; defaultColor: false; }' is not assignable to type 'Partial<ShikiConfig>'. Vindo do objeto shikiConfig compartilhado que este site e o vdaluz.com importam do @vdaluz/astro-blog.
Meu primeiro palpite foi drift de versão. Este repositório ainda estava fixado numa versão mais antiga do Astro; o vdaluz.com roda uma mais nova. Subi a versão, reinstalei, rodei a checagem de novo. Mesmo erro, palavra por palavra. Teoria errada.
Então fui de fato olhar como o próprio astro.config.mjs do vdaluz.com lida com o mesmo import, em vez de continuar chutando. Ele já tinha o fix, com um comentário explicando:
// The package types `themes` values as plain strings, so cast to satisfy astro check.
shikiConfig: /** @type {Partial<import('astro').ShikiConfig>} */ (shikiConfig),
O pacote compartilhado tipa themes.light/themes.dark como string genérica, não a união de nomes literais de tema que o próprio tipo do Astro espera. Não era um problema de versão, só um tipo que nunca esteve totalmente certo, aceito silenciosamente porque nada aqui nunca tinha rodado astro check antes. Copiei o mesmo cast. Erro resolvido. Mantive o bump de versão do Astro mesmo assim, já que reduz o drift entre os dois sites e nada quebrou, mas nunca foi ele o fix.
Verificar o gate significou testá-lo contra alguma coisa, e foi aí que ficou interessante
Eu não queria só configurar o novo comando de build no dashboard da Cloudflare e assumir que funcionava. Eu queria ver rodando de fato. A API do Workers Builds tem um endpoint para disparar manualmente um build contra qualquer branch que você nomear, então apontei um para minha branch de feature para checar o novo comando com gate antes de mergear qualquer coisa na main.
Voltou verde. Bom. Só que o log tinha uma linha a mais do que eu esperava:
Executing user deploy command: npx wrangler deploy --config wrangler.toml dist/server/entry.mjs
✨ Success! Uploaded 7 files (23 already uploaded) (0.91 sec)
Success: Deploy command completed
O passo de deploy do gatilho não checa qual branch você mandou buildar. Ele simplesmente roda, incondicionalmente, depois de qualquer build bem-sucedido. Apontar o gatilho de produção para uma branch de feature para “só testar o comando de build” também mandou aquela branch de feature direto para produção, antes de qualquer merge, antes de qualquer review. wrangler deployments list confirmou: um deploy novinho, com timestamp da minha chamada manual à API, não de um git push.
O dano real aqui foi próximo de zero. A branch só mexia em tooling e formatação, nada que um visitante notaria, e eu já tinha revisado o diff de cada mudança de formatação antes disso. Mas o mecanismo é a parte que vale lembrar: uma ação de “deixa eu só testar isso” chegou em produção sem nenhum passo de confirmação no meio, porque assumi que um build disparado manualmente era mais seguro do que era. Não era um dry run. Não existe dry run.
Uma vez que percebi, o fix foi só colocar a main em dia com o que já estava no ar: mergear a branch, verificar que o build real disparado por push (não um manual) também ficou verde, pronto. O que aconteceu, hash de commit e tudo, ligado a um git push de verdade, gates passando de verdade dessa vez.
O que eu faria diferente
Testar mudanças de comando de build mergeando primeiro, não apontando o gatilho de deploy de produção para código ainda não mergeado e torcendo para o parâmetro “branch” significar o que parece significar. Se uma API deixa você especificar um alvo, isso não é o mesmo que ela respeitar um limite. Pergunte o que acontece em caso de sucesso antes de descobrir vendo acontecer.
Leitura relacionada
O deploy do Cloudflare Workers Builds que falhou por causa de um arquivo de config discordando de si mesmo
Substituindo o GitHub Actions morto pelo Workers Builds, uma flag --config que faltava e que o workflow antigo sempre teve, e o tipo de token que a API do Builds do Cloudflare se recusa a aceitar.
O CSP que só quebrou em produção
Quatro falhas atrás de um único header: middleware morto, regras de _headers que se combinam em vez de sobrescrever, um worker de blob URL sob script-src, e um script que só o edge injeta.
O 404 que já estava certo, e o robots.txt que não estava
Uma diretiva Sitemap que precisava apontar para o índice, um fix de 404 que quase foi construído a mais, e um comportamento documentado da Cloudflare que não se confirmou na prática.
Você também pode achar útil
Proton 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 maisProton Mail
E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.
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