Pular para o conteúdo

Rodando no CI

Terminal window
oren generate gitlab entrega
oren generate github entrega
oren generate cloudbuild entrega

O mesmo pipeline, os mesmos contratos, as mesmas imagens travadas por digest — executados no CI.

Sem --output, cada plataforma recebe o arquivo onde ela espera encontrá-lo. Com --properties <alias> você gera um ambiente só, e sem destino a saída vai para o stdout — dá para canalizar.

Quando o pipeline tem propertyFiles, o oren.yaml pode dizer que branch usa cada apelido:

propertyFiles:
main:
file: deploy/oren-properties/main.yaml
branches: [main]
develop:
file: deploy/oren-properties/develop.yaml
branches: [develop, 'feature/*']

A forma curta (main: deploy/main.yaml) continua valendo. Quem nunca gerar CI por branch nunca precisa da longa.

Com o mapa declarado, oren generate gera tudo de uma vez — mas o formato muda por plataforma, e não por escolha nossa:

Plataforma Saída Por quê
GitLab .gitlab-ci.yml, um arquivo com rules o caminho do arquivo é configuração do projeto, não da branch
GitHub .github/workflows/oren-<pipeline>.yml, um arquivo cada workflow declara os próprios gatilhos
Cloud Build cloudbuild-<apelido>.yaml, um por apelido o gatilho é que nomeia o arquivo

No GitLab isso vira uma regra por branch, definindo o apelido:

script:
- oren run entrega --properties $OREN_PROPERTIES
rules:
- if: $CI_COMMIT_BRANCH == "main"
variables: { OREN_PROPERTIES: main }

O mapa serve só ao gerador. O oren run --properties continua sendo escolha explícita de quem executa: ele nunca olha o git para adivinhar a branch, ou o mesmo comando passaria a significar coisas diferentes conforme onde é digitado.

Porque o arquivo de propriedades não tem schema — é livre de propósito, e você põe nele as chaves que quiser. Um campo reservado branch: lá dentro criaria um vocabulário reservado, e a pergunta “posso ter uma propriedade chamada branch?” passaria a ter resposta ruim.

A mesma branch em dois apelidos é erro, e não desempate. As duas condições seriam verdadeiras, a plataforma pegaria a primeira, e a entrega sairia com as propriedades do ambiente errado — em silêncio, que é o que faz disso o pior desfecho.

O --mode nativo não faz isso. Lá o conjunto de jobs é decidido na geração e muda com o apelido: os steps com enabled por expressão entram ou não. Um arquivo só não serve duas branches que geram jobs diferentes.

O job não usa container:. O GitHub sabe rodar um job dentro de uma imagem, mas para isso injeta o próprio runner nela: precisa de Node e de glibc. Uma imagem enxuta — como as alpine dos workers de referência — quebra, e quebra com um erro que não se liga à causa.

Em vez disso, o job roda docker run explícito. A imagem é executada como o oren run a executaria: só o que ela declara precisar, nada injetado.

Os segredos chegam por secrets do repositório, mapeados em env no YAML — secrets não é indexável por nome variável dentro do run, então esse mapeamento precisa existir, e é ele que torna explícito o que configurar.

Sem o mapa de branches, o workflow gerado só dispara por workflow_dispatch. Um gerador não tem como adivinhar em que evento o seu pipeline deve rodar, e supor push faria a primeira geração disparar um deploy sem ninguém pedir — acrescente o gatilho que você quer.

Com o mapa, o push sai declarado com exatamente as branches mapeadas, porque aí você disse quais são. Um passo antes do oren run escolhe o apelido pela branch, e uma branch fora do mapa falha em vez de rodar sem propriedades — que entregaria com os valores errados.

Terminal window
oren generate gitlab entrega --resume

O job passa a rodar com --resume e a plataforma guarda em cache o que a execução anterior produziu. Numa retentativa, os steps que já tinham dado certo são pulados.

Fora por padrão, e por três motivos: o cache custa transferência em toda execução para um benefício que só aparece quando algo falha; ligá-lo alarga a fronteira de confiança (ver abaixo); e ele é recusado em pipelines comuns, então um padrão faria a geração falhar para quem nunca pediu.

Dois conjuntos, e o segundo é o que costuma faltar:

  • .oren/runs/ — os outputs e o status de cada step, de onde a retomada sabe o que pode pular;
  • os diretórios mutáveis declarados no pipeline. O estado guarda a saída em JSON, mas o que o step escreveu no disco fica de fora: retomar depois de um build sem o .tar que ele gravou daria um step seguinte procurando um arquivo que não existe.

.oren/registry/ e .oren/consent.json nunca entram. Os dois são versionados e vêm do checkout.

O estado descreve o que rodou sobre um código específico, e a retomada recusa atravessar commits:

erro A execução anterior é de outro commit (062fc199)
Retomar reaproveitaria a saída de steps que rodaram sobre outro código — uma
imagem já construída seria publicada com a versão nova, sem nada indicar a troca.

A conferência não depende do arquivo de CI estar chaveado certo: ela está na CLI. Sem git, ver OREN_REVISION em Comandos.

Retomar sem execução anterior não é erro — a primeira execução no CI nunca tem estado. Ela avisa e roda tudo.

Step que escreve na raiz do projeto. Cachear a raiz restauraria .git, .oren/registry/ e .oren/consent.json de outra execução — e aí o cache decidiria que worker roda e afirmaria que o acesso foi autorizado. Aponte a dependência mutável para um subdiretório, ou gere sem --resume.

Cloud Build. Ele não tem cache nativo, e os steps param no primeiro que falha — o step que gravaria o estado não rodaria justamente quando a execução falhou, que é a única vez em que retomar interessa.

Passagem de output. No GitLab, cada job grava .oren-outputs/<id>.json como artifact e os seguintes o recebem via needs. Artifact em vez de reports:dotenv porque os outputs são JSON estruturado, e dotenv só carrega pares de string.

Dependências. Credenciais viram variáveis do projeto, com verificação que falha cedo se não estiverem definidas. O valor nunca aparece no YAML.

Diretórios mutáveis. Viram artifact entre jobs, já que runners não compartilham disco.

Sem as imagens travadas, a configuração gerada executaria o que a tag apontar em cada dia — perdendo justamente a reprodutibilidade que justifica gerar.

erro Não é possível gerar CI: alguns steps usam implementação construída localmente
Um job de CI referencia uma imagem publicada — o runner não tem o
diretório do worker. Publique a imagem e troque `build:` por `image:`.

A imagem do worker precisa ter jq quando um input vem de outro step — é ele que compõe o input.json dentro do job, inclusive a interpolação em texto misto (v${outputs['x'].y}-rc), que é suportada.

${properties.x} é suportado, e resolvido na geração — o valor entra assado no job, porque arquivo de properties não existe no runner. É por isso que o --properties é obrigatório quando o pipeline tem step condicional: o CI gerado tem conjunto fixo de jobs, e não há lá o que avalie a expressão depois.

O gerador é um compilador, não um runtime: o job roda a imagem do worker direto, e o runner não precisa de Node, nem do binário oren, nem do portal. É justamente o ponto — mas três coisas vivem na CLI e não vão junto.

O consent. Ele é a CLI perguntando a você. Um job gerado roda o que o arquivo diz. É por isso que vale commitar o .oren/consent.json: no CI ele é o registro de que alguém autorizou o acesso, e não um controle que executa.

O cache. O cache do Oren é da CLI. No CI, o que a plataforma oferecer.

A validação da saída contra o contrato. Localmente, uma saída fora do contrato falha o step mesmo com exit code zero. No job gerado a saída é copiada como veio: um output.json ausente ainda falha o step, porque a cópia falha — mas uma saída com tipo errado ou campo não declarado passa.

Essa é uma diferença de verdade, e a que vale ter em mente: o contrato é conferido no oren validate e no oren run, e são esses dois que seguram a linha. Um worker cuja saída só desvia no CI desvia em silêncio.