Voltar ao portfolio

Projeto pessoal / Validação de software e pipelines de build

Preflight

Preflight verifica se uma máquina e uma alteração atendem aos requisitos do projeto antes do commit ou do build. Serve a estúdios de jogos e empresas de software em geral: detecta SDKs ausentes, arquivos acima do limite e violações de política, com regras explícitas e diagnósticos que indicam o que corrigir.

Por que executar antes de um commit?

Uma textura de origem ou um binário gerado grande demais pode entrar no Git e só ser descoberto na CI. O stage pre-submit verifica arquivos alterados contra a política antes do envio: o autor recebe o caminho, o limite, o tamanho encontrado e uma ação corretiva enquanto ainda pode ajustar a alteração. A mesma lógica pode ser chamada por um hook de commit e pela CI; o hook precisa ser configurado pela equipe.

Determinístico significa que os mesmos arquivos, ambiente verificado, regras, política e target produzem o mesmo veredito e a mesma ordem de achados. A decisão vem de condições verificáveis, como comparar bytes com um limite, sem interpretação por IA. Isso não garante que um build ou o software funcione: antecipa problemas conhecidos. Durações e identificadores de execução variam entre execuções.

  • C#
  • .NET
  • JSON
  • SARIF
  • Windows
Diagnóstico ilustrativo, com uma regra de tamanho de arquivo da documentação pública. É um exemplo de leitura, não uma captura de execução.
preflight run --stage pre-submit --changed-from origin/main --platform win64

core.presubmit.large-file   Failed
  at        Art/Characters/hero_diffuse.tga
  expected  <= 2,621,440 bytes
  actual    11,400,000 bytes
  fix       Remova o arquivo do controle de versão ou peça ao responsável pela pipeline para revisar o limite.

preflight explain core.presubmit.large-file --platform win64

O arquivo tem 11.400.000 bytes e excede o limite Win64 de 2.621.440 bytes. A regra compara o tamanho com maxBytes; não decide se a textura tem valor artístico ou se o código está correto. preflight explain permite conferir por que esse limite vale para a execução.

01 / Contexto

Feedback tardio, erros em cascata e scripts que divergem

Num estúdio, o problema pode ser um asset acima do orçamento; numa empresa de software, um SDK ausente, um caminho proibido ou um artefato de build enviado ao repositório. São condições verificáveis antes das etapas caras da pipeline. Colocar a validação só na CI adia o diagnóstico até a fila de execução; manter um script diferente no computador do desenvolvedor cria outra fonte de divergência.

Separei a verificação, compilada em C#, da política, escrita em JSON, para que a lógica exista uma vez e os requisitos possam variar por projeto. Quem cuida da infraestrutura define regras, limites e bloqueios e publica um pacote versionado. Quem desenvolve instala esse pacote e executa a ferramenta, sem precisar implementar verificações. A CI continua sendo o ponto de controle da equipe e executa a mesma lógica.

O que acontece numa execução de pre-submit

  1. 01

    Resolução da política

    preflight run --stage pre-submit --changed-from origin/main --platform win64 seleciona a pipeline declarada no checkout e uma versão instalada aceita. O argumento de plataforma seleciona sua camada de política.

  2. 02

    Seleção das regras e dependências

    --changed-from origin/main define a referência do diff Git; não é um filtro exclusivo do staging. O stage pre-submit seleciona as regras raiz e inclui seus pré-requisitos, mesmo quando pertencem a outros stages.

  3. 03

    Execução e relatório

    As regras executam por níveis do grafo, com paralelismo limitado. O relatório informa localização, esperado, encontrado e correção. preflight explain core.presubmit.large-file --platform win64 mostra a origem do limite aplicado.

workspace
Verifica a máquina e o ambiente de desenvolvimento.
pre-submit
Verifica os arquivos alterados contra a política do projeto.
build-readiness
Verifica os pré-requisitos de um build.

O comando avalia arquivos rastreados no diff Git contra a referência escolhida. Ele não faz commit nem instala automaticamente um hook.

02 / Design e arquitetura

Decisões de arquitetura e suas consequências

Regra e política

Uma implementação, limites diferentes por projeto

Uma regra sabe como verificar algo. Sua política decide se ela executa, seus parâmetros, severidade e comportamento de bloqueio. Dois projetos podem usar o mesmo assembly com limites de tamanho diferentes, sem criar forks do código. Isso permite testar a lógica de validação separadamente e alterar requisitos sem recompilar a ferramenta.

Exemplo de política JSON: a mesma regra com um limite geral e outro para Win64.
{
    "schemaVersion": 1,
    "pipeline": "projecta",
    "rules": {
        "core.presubmit.large-file": {
            "settings": {
                "maxBytes": 5242880
            }
        }
    },
    "targets": {
        "win64": {
            "rules": {
                "core.presubmit.large-file": {
                    "settings": {
                        "maxBytes": 2621440
                    }
                }
            }
        }
    }
}

Sem target explícito, maxBytes é 5.242.880 (5 MiB). Com --platform win64, passa a 2.621.440 (2,5 MiB). A implementação C# não muda; muda o requisito. Esse trecho ilustra a seleção de parâmetros, sem repetir todo o manifesto de distribuição.

Grafo de execução

Dependências executadas em níveis do grafo

As regras declaram dependências e executam por níveis topológicos. Verificações independentes compartilham um nível; níveis posteriores aguardam os pré-requisitos. Se a verificação da toolchain falha, uma sonda de compilação dependente pode ser pulada em vez de gerar outra falha previsível.

A atribuição de causa raiz percorre a cadeia de dependências, atravessando skips intermediários. A mensagem final aponta para a toolchain ausente, oferecendo um problema para resolver em vez de uma lista de sintomas. O stage seleciona as raízes desse grafo, sem descartar dependências de outros stages.

Escolhi uma barreira entre níveis para simplificar a propagação de falhas e a coordenação. Uma regra lenta segura o próximo nível, mesmo que parte dele já pudesse começar. Esse custo é uma escolha consciente: a execução é mais simples de auditar, e o relatório é ordenado por nível e ID, independentemente de qual tarefa termina primeiro.

Dois controles independentes

blocking e gating respondem a perguntas diferentes

Uma convenção de nomes pode bloquear um submit sem tornar a compilação inútil. Uma sonda opcional pode ser um pré-requisito técnico mesmo quando sua falha não deve reprovar o envio. Um único booleano não expressa os dois casos. Preflight separa blocking, que afeta o resultado e o exit code, de gating, que interrompe regras dependentes. A severidade continua sendo o nível de comunicação, sem substituir essas decisões.

Proveniência da política

Origem e precedência dos valores da política

Políticas podem herdar parâmetros, aplicar targets explícitos de plataforma/configuração e selar chaves contra alterações nas camadas posteriores. Os selos se acumulam na cadeia de herança, impedindo um projeto de remover silenciosamente uma restrição da organização. Overlays locais da máquina ficam fora da CI.

O comando explain registra a origem dos valores efetivos, incluindo pacote, arquivo, linha e valores substituídos. Os eixos de target precisam ser informados explicitamente para casar com um bloco. Assim, um default de configuração não escolhe silenciosamente outra política de produção.

A precedência precisa ser visível porque um valor em JSON, sozinho, não explica a configuração efetiva. Sem proveniência, investigar uma diferença entre a máquina local e a CI exigiria reconstruir a herança à mão. O comando preflight explain transforma essa investigação em dados da própria resolução.

Fronteira dos plugins

Um contrato comum para regras embutidas e externas

Regras implementam IValidationRule usando Preflight.Abstractions. Acesso a arquivos, processos, alterações e política chega pelos serviços de RuleContext, permitindo testar verificações sem o workspace real. O assembly de regras embutidas não tem uma dependência privilegiada de Core.

Plugins carregam em contextos de assembly separados e coletáveis, compartilhando o assembly do contrato com o host. Versões de dependências podem coexistir; IDs duplicados e contratos incompatíveis geram recusas identificadas em vez de um vencedor arbitrário. É isolamento de dependências, não uma sandbox de segurança: os plugins são código confiado por quem publica a pipeline.

Distribuição

Regras e política distribuídas como um pacote versionado

Um pacote de pipeline contém política, assemblies de regras e um manifesto com SHA-256 por arquivo e faixa de contrato suportada. Ordem estável das entradas e timestamps fixos tornam os bytes do arquivo reproduzíveis. A instalação verifica o pacote antes de consolidá-lo no armazenamento instalado.

O checkout declara uma faixa de versões aceitas; uma máquina pode fixar uma versão para rollback. Instalar um pacote não altera esse pin. Preflight não busca atualizações sozinho, deixando a distribuição para o canal de artefatos do estúdio e evitando mudanças inesperadas nas regras das máquinas.

Saída confiável

Estados de resultado com significados distintos

Passed, Warning, Failed, Errored, Skipped e NotApplicable têm significados distintos. Uma regra sem algo aplicável para inspecionar não afirma sucesso; uma regra que caiu é diferenciada de um defeito no workspace. Console, JSON e SARIF apresentam os mesmos dados de relatório. Exit codes distinguem uma alteração bloqueada de configuração inválida e erro interno.

O histórico é NDJSON local com escrita append-only; estatísticas de duração aparecem apenas com observações suficientes. A ferramenta pode medir um comando de build, mas medir sua duração não comprova que ele foi validado ou que o software funciona.

O determinismo vale para o veredito e a ordem dos achados quando os inputs são iguais, incluindo o ambiente inspecionado e a versão da pipeline. A duração e o runId variam por construção; comparar relatórios byte a byte exige controlar esses campos. Uma política igual sobre máquinas com SDKs diferentes pode corretamente produzir resultados diferentes.

Cache incremental

Cache condicionado à identidade dos inputs

O cache é opt-in e só atende regras que fornecem um fingerprint explícito dos inputs. A chave também inclui a política efetiva da regra, stage, target, geração do contrato e identidade do assembly. Alterar um limite ou recompilar um plugin invalida o resultado anterior. Resultados de cache são identificados visivelmente, sem apresentar trabalho reutilizado como uma nova verificação.

O custo dessa escolha é exigir que o autor da regra descreva os inputs relevantes. Para uma sonda que não consiga representar seu ambiente com segurança, executar de novo é preferível a reutilizar evidência incompleta. O cache fica desligado por padrão.

03 / Por dentro do código

Fronteiras entre contrato, execução, regras e interface

Preflight.Abstractions

O vocabulário dos plugins: descritores, resultados, contexto e interfaces de serviços. Depende da biblioteca base, mantendo o contrato pequeno para regras externas.

Preflight.Core

Resolução de políticas, grafos de dependências, execução, carregamento de plugins, cache e histórico. Calcula os dados do relatório sem depender de volta da CLI.

Preflight.Rules

Verificações embutidas usam o contrato público, como um plugin da equipe. Demonstram validação de workspace, alterações e preparação de build sem incorporar os requisitos de cada projeto na ferramenta.

Preflight.Cli

Comandos, parsing, empacotamento de pipelines e apresentação da saída. A linha de comando hospeda o core; outra integração pode consumir os dados sem extrair texto do terminal.

Como o comportamento é verificado

O repositório reúne testes unitários, verificações do contrato de plugins, testes de bytes exatos da saída e cenários Gherkin que executam o binário publicado. Testes de fronteira impedem regras embutidas de depender de Core e Core de depender da CLI. O script de verificação confere formatação, compila com warnings como erros, executa as suítes e coleta cobertura. São verificações da ferramenta, distintas das regras de validação definidas para cada projeto de software ou jogo.

04 / Trabalho em andamento

Implementação atual e limites do contrato

Em desenvolvimento

Preflight é público sob licença MIT e permanece abaixo da versão 1.0. A implementação atual usa .NET 10 e é desenvolvida no Windows. Regras, políticas, pacotes e relatórios já funcionam juntos; a API pública ainda pode mudar.

A direção é fortalecer verificações e evidências para pipelines de jogos e de software em geral. Regras específicas pertencem à equipe que conhece os requisitos do projeto. Compilação e testes continuam com suas ferramentas; uma integração de IDE ou build farm poderia hospedar o mesmo core. Esta página descreve a CLI existente.

Explore o projeto

O README documenta instalação, uso diário, autoria de regras, políticas e distribuição de pipelines. O código permite acompanhar como esses contratos se conectam à execução e aos relatórios.

Leia a documentação no GitHub

Contato

Cidade de Québec, QC, Canadá / Disponível para oportunidades

Estou aberto a oportunidades em desenvolvimento de software, programação de ferramentas e gameplay, além de vagas de artista 3D júnior. Se minha experiência fizer sentido para sua equipe, será um prazer conversar pelos meus perfis nas redes sociais.