Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Daggerverse

Visão geral

Componente Papel

orchestrator/

Módulo Dagger genérico. Lê ci/pipeline.toml e expõe as cinco funções da pipeline.

orchestrator-utils/

Utilitários de Git e registry (change detection, bump/commit, tag sha-<commit>, checagem e promoção de imagens).

maven/, npm/, uv/

Módulos Dagger por tecnologia de build. Chamados pelo orchestrator, não pelo projeto.

pipeline/

Biblioteca Go: schema da configuração (pipeline/config), orquestração genérica (PublishAll, CheckQuality) e utilitários compartilhados (pipeline/sonarargs, pipeline/imageref).

gitlabci/

Biblioteca Go: cliente de commit statuses do GitLab.

O módulo orchestrator

O orchestrator é o módulo Dagger genérico da 3.0. Ele substitui o módulo Dagger por projeto que existia até a 2.x: todo o comportamento vem do arquivo declarativo do projeto consumidor, e os targets declarados lá viram simultaneamente as estratégias de build, os checks de qualidade e o mapa de imagens de check-images/promote.

Como um projeto usa

Um projeto precisa de exatamente dois arquivos.

  1. ci/pipeline.toml — os targets do repositório (ver O schema de ci/pipeline.toml).

  2. .gitlab-ci.yml — cinco linhas incluindo o template compartilhado:

include:
  - project: 'basis/iac/ci-templates'
    ref: v1.0.0
    file: 'templates/dagger-orchestrator.gitlab-ci.yml'

O template define os stages check, build, pre-prod e promote, e chama as funções do orchestrator com as variáveis do GitLab. Duas variáveis podem ser sobrescritas no variables: local:

  • DAGGER_MODULE — default github.com/BasisTI/daggerverse/orchestrator@3.0.0. Projetos com build custom apontam para "." (módulo local, ver Projetos com build custom).

  • PIPELINE_CONFIG — default ci/pipeline.toml.

O construtor do módulo recebe --source (raiz do repositório) e --config-path:

dagger call -m github.com/BasisTI/daggerverse/orchestrator@3.0.0 \
  --source . --config-path ci/pipeline.toml \
  validate

As cinco funções

validate

Carrega e valida o arquivo declarativo e devolve um relatório textual com os targets já resolvidos (com defaults aplicados), o mapa de imagens derivado e eventuais avisos. Não faz nenhuma chamada de rede — é o job de lint da configuração, disparado no MR quando ci/pipeline.toml muda.

Os avisos cobrem o que é válido no schema mas o orchestrator genérico não consegue executar: targets type = "custom".

check-quality

Roda testes e análise estática nos targets alterados que declaram sonar = true. Recebe --base-branch, --commit-sha, --sonar-host, --sonar-token, o opcional --stop-on-first-fail e a tripla opcional --gitlab-host / --gitlab-token / --gitlab-project-id para reportar commit statuses.

Builda com a versão fixa 0.0.0-check (nada é publicado). Falha com mensagem explicativa se a configuração tiver targets custom.

publish-all

Detecta os targets alterados entre --base-branch e o commit atual, builda e publica cada imagem em --registry, aplica a tag sha-<commit> e devolve a lista de imagens publicadas (uma por linha).

Se --git-remote-url for informado, faz o bump dos arquivos de versão dos targets publicados e commita de volta na --git-branch (default develop). Também falha se houver targets custom.

check-images

Verifica se todas as imagens derivadas da configuração já existem no registry, antes da promoção. Roda no MR para main, usando --build-branch (default origin/develop) para localizar o commit em que as imagens foram construídas.

Targets custom não são ignorados aqui: suas imagens existem no registry e precisam ser verificadas como qualquer outra.

promote

Promove as imagens derivadas da configuração de --src-registry para --dst-registry (que, se vazio, é o próprio --src-registry). Assim como check-images, cobre também as imagens de targets custom.

Cada promoção percorre todas as imagens do projeto, não só as que mudaram — é assim que uma imagem antiga continua tendo sua tag production- presente. Mas a cópia só acontece quando ela muda alguma coisa: antes de copiar, o digest da tag de origem é comparado com o da tag de produção, e se forem iguais a imagem é pulada com ⏭️ Já promovida. Serviços parados há meses deixam de gerar tráfego de registry a cada merge em main, e o log passa a mostrar de relance o que de fato entrou na promoção.

O schema de ci/pipeline.toml

Implementado em pipeline/config. O parse é estrito: qualquer campo desconhecido é erro.

Nível raiz

Campo Tipo Descrição

schema-version

int

Obrigatório, hoje sempre 1.

[project]

tabela

Metadados do repositório.

[defaults.*]

tabela

Defaults por tipo de build.

[targets.<nome>]

tabela

Um bloco por artefato buildável. Ao menos um é obrigatório.

[project]

Campo Tipo Descrição

group

string

Obrigatório. Grupo no registry: <registry>/<group>/<image>.

[defaults.maven], [defaults.npm], [defaults.uv]

Bloco Campo Tipo Descrição

defaults.maven

image

string

Imagem base dos builds Maven.

defaults.maven

use-docker

bool

Liga um daemon Docker no build (Testcontainers).

defaults.npm

build-image

string

Imagem de build (ex: node:22-alpine).

defaults.npm

run-image

string

Imagem de runtime (ex: nginx:alpine).

defaults.uv

build-image

string

Imagem de build (ex: ghcr.io/astral-sh/uv:python3.12-bookworm-slim).

defaults.uv

run-image

string

Imagem de runtime (ex: python:3.12-slim-bookworm).

[targets.<nome>] — campos comuns

Campo Tipo Descrição

type

string

Obrigatório. Um de maven, npm, uv, dockerfile, custom.

path

string

Path de change detection. Default: o nome do target.

source-path

string

Diretório montado no build. Default: o path efetivo, ou "." (raiz do repositório) quando reactor = true.

image

string

Nome da imagem no registry, sem grupo e sem tag. Default: o nome do target. Os nomes efetivos precisam ser únicos entre os targets.

version-file

string

Arquivo de versão bumpado pelo publish-all. Default por tipo: pom.xml (maven), package.json (npm), pyproject.toml (uv e dockerfile). Quando há quality-type, o default segue ele — é ele que descreve o código. Targets custom não têm default.

root-version-file

bool

Indica que version-file é relativo à raiz do repositório, e não ao path do target. Caso típico do reactor Maven multi-módulo, em que vários targets compartilham o pom.xml da raiz.

extra-trigger-paths

lista de string

Paths adicionais que também disparam o build/check deste target (ex: uma lib compartilhada, o pom.xml da raiz).

sonar

bool

O target participa do check-quality. Em targets type = "dockerfile" exige quality-type; em targets custom, o check fica a cargo do módulo Dagger do projeto.

quality-type

string

Build system que analisa o código do target, quando ele difere de como a imagem é construída. Um de maven, npm, uv — nunca dockerfile nem custom, que não têm build system. Só é aceito junto de sonar = true. Default: o próprio type. Ver quality-type: analisar o código de um target dockerfile.

quality-type: analisar o código de um target dockerfile

type responde "como a imagem é construída"; quality-type responde "com que build system o código é testado e analisado". Na esmagadora maioria dos targets os dois são a mesma coisa, e quality-type é omitido.

Eles divergem quando um projeto precisa de um Dockerfile próprio para o runtime — display virtual, browser headless, drivers nativos — mas o código continua sendo um projeto uv/Maven/npm normal, com testes e análise estática. Sem quality-type esse target teria de escolher entre publicar a imagem certa e ter qualidade medida; com ele, publish-all builda pelo Dockerfile e check-quality roda pelo build system declarado.

É o caso dos scrapers do licitacao, que rodam navegador no runtime:

[targets.scrapers_mte]
type = "dockerfile"          # a imagem sai do Dockerfile do scraper
path = "scrapers/mte"
sonar = true
quality-type = "uv"          # o código é Python: testes e Sonar pelo módulo uv

Os campos do build system de quality (run-subdir, uv-build-image, customizations, maven-image, …) valem normalmente no target e são o que o check-quality consome — e o validate os mostra num bloco quality (<tipo>) separado.

Duas regras de validação mantêm o campo honesto:

  • quality-type sem sonar = true é erro: seria um campo inerte, que engana quem lê o arquivo;

  • sonar = true num target dockerfile sem quality-type é erro de validação, com a mensagem indicando exatamente o campo que falta — não uma falha tardia na hora de rodar o check.

Campos de type = "maven"

Campo Tipo Descrição

maven-image

string

Sobrescreve defaults.maven.image.

use-docker

bool

Sobrescreve defaults.maven.use-docker.

reactor

bool

Build multi-módulo: monta a raiz e builda com -pl <module> -am. Ver Modo reactor do Maven.

module

string

Path do módulo dentro do reactor. Obrigatório quando reactor = true.

extra-options

lista de string

Opções extras passadas ao mvn.

Campos de type = "uv"

Campo Tipo Descrição

uv-build-image

string

Sobrescreve defaults.uv.build-image.

uv-run-image

string

Sobrescreve defaults.uv.run-image.

run-subdir

string

Subdiretório de execução dentro do build (ex: "src").

customizations

lista de string

Customizações de build do módulo uv: "dbt", "dlt".

Campos de type = "dockerfile"

Campo Tipo Descrição

dockerfile

string

Path do Dockerfile relativo ao source-path efetivo. Default Dockerfile. Quando source-path = ".", precisa trazer o caminho completo a partir da raiz (ex: apps/judge-api/Dockerfile).

type = "npm" não tem campos próprios: as imagens vêm exclusivamente de [defaults.npm].

Exemplo simples

Quatro targets — três Maven e um frontend Angular — todos com o nome do diretório igual ao nome do target, o que dispensa path e image:

schema-version = 1

[project]
group = "contavinculada"

[defaults.maven]
image = "maven:3.9.11-eclipse-temurin-21"
use-docker = false

[defaults.npm]
build-image = "node:22-alpine"
run-image = "nginx:alpine"

[targets.contavinculada]
type = "maven"
sonar = true

[targets.integracaosgo]
type = "maven"
sonar = true

[targets.snf]
type = "maven"
sonar = true

[targets.frontend]
type = "npm"
sonar = true

Exemplo de reactor Maven multi-módulo

Dois módulos de um mesmo reactor. Ambos montam a raiz (source-path = "."), compartilham o pom.xml da raiz como arquivo de versão (root-version-file = true) e são reconstruídos quando o contrato comum ou o pom da raiz mudam:

schema-version = 1

[project]
group = "triagem"

[defaults.maven]
image = "maven:3.9.11-eclipse-temurin-25"
use-docker = true

[targets.triagem-core]
type = "maven"
source-path = "."
reactor = true
module = "triagem-core"
version-file = "pom.xml"
root-version-file = true
extra-trigger-paths = ["triagem-contracts", "pom.xml"]
sonar = true

[targets.triagem-ingestao]
type = "maven"
source-path = "."
reactor = true
module = "triagem-ingestao"
version-file = "pom.xml"
root-version-file = true
extra-trigger-paths = ["triagem-contracts", "pom.xml"]
sonar = true

Outros exemplos reais estão em pipeline/config/testdata/.

A garantia anti-drift

Esta é a regra central do schema.

O mapa de imagens consumido por check-images e promote não é declarado: ele é derivado dos mesmos targets que descrevem o build, via Config.ProjectImages(), que devolve "<group>/<image efetiva>" → "<path efetivo>" para todos os targets.

Antes da 3.0, a lista de imagens a verificar e promover era mantida à mão, em paralelo aos build targets do orchestrator do projeto. Bastava adicionar um serviço novo e esquecer da segunda lista para a promoção silenciosamente pular a imagem — ou apontar para uma que não existia mais.

Com uma única fonte de verdade, é impossível divergir: o conjunto do que se builda e o conjunto do que se promove são literalmente a mesma coleção de targets. Targets type = "custom" entram no mapa normalmente — eles não são buildados pelo orchestrator genérico, mas suas imagens existem no registry e precisam ser verificadas e promovidas como as outras.

Projetos com build custom

Alguns projetos têm builds que nenhum dos tipos genéricos cobre. Eles mantêm um módulo Dagger próprio, mas não duplicam a configuração: esse módulo carrega o mesmo ci/pipeline.toml via pipeline/config e faz o merge dos targets custom implementados à mão com os targets genéricos derivados do arquivo.

No schema, esses targets são marcados com type = "custom":

[targets.beneficios]
type = "maven"
path = "apps/beneficios"
sonar = true

[targets.rh-dp]
type = "custom"

[targets.lightdash-content]
type = "custom"
extra-trigger-paths = ["rh-dp/dbtrh"]

O orchestrator genérico não sabe construir esses targets e falha com uma mensagem clara em publish-all e check-quality, indicando que o projeto precisa apontar DAGGER_MODULE para o módulo local. validate apenas emite um aviso, e check-images/promote continuam funcionando pelo módulo genérico, inclusive para as imagens desses targets.

Modo reactor do Maven

Reactor multi-módulo é o caso em que um módulo depende de módulos irmãos do mesmo pom.xml de raiz. Buildar o diretório do módulo isoladamente não funciona: as dependências irmãs não estão no disco.

Com reactor = true o módulo maven (opção ReactorMode):

  • monta a raiz do reactor em vez do diretório do módulo;

  • builda com -pl <módulo> -am, para que o Maven construa o módulo alvo e todos os que ele precisa;

  • injeta -Drevision=<versão> em cada invocação, em vez de rodar versions:set.

O versions:set é evitado de propósito: reescrever os POMs brigaria com o flatten-maven-plugin e quebraria a resolução das dependências irmãs que o -am acabou de construir.

O que o projeto precisa ter no pom

  1. O pom.xml da raiz declara <version>${revision}</version> e a property <revision> com um valor de desenvolvimento local:

    <version>${revision}</version>
    <properties>
      <revision>0.0.1-SNAPSHOT</revision>
    </properties>
  2. O flatten-maven-plugin configurado, para que os artefatos instalados/publicados tenham a versão resolvida e não o literal ${revision}.

  3. O maven-install-plugin ativo no verify. Isso é obrigatório: o stage do Jib roda sem -am, ou seja, sem reconstruir os módulos irmãos — eles precisam já estar no repositório local, instalados pelo stage de build e teste.

Quem escreve o número da versão

Ninguém escreve esse número à mão. O valor da property <revision> no arquivo não é usado pela pipeline: cada invocação do Maven recebe -Drevision=<CalVer>, e propriedade de linha de comando vence a do POM. O que estiver commitado ali só vale para quem roda mvn na própria máquina, sem passar -Drevision — por isso o valor inicial deve ser um SNAPSHOT de desenvolvimento, e não uma CalVer.

Depois de publicar as imagens, o publish-all grava a CalVer de volta no arquivo e commita: quando o <version> do pom é ${revision}, o orchestrator-utils edita a property <revision> do pom da raiz, e não o elemento <version>. É por isso que targets reactor declaram version-file = "pom.xml" com root-version-file = true.

Ou seja, o número que você encontra no repositório é o rastro da última pipeline bem-sucedida, não uma entrada que alguém precisa manter.

Breaking changes da 3.0

Módulo maven

  • ParentPom e FullBuildModules foram removidos. Multi-módulo agora é ReactorMode (reactor = true no schema), que builda com -pl <módulo> -am a partir da raiz. Ver Modo reactor do Maven.

  • O default de buildImage deixou de ser a variante -alpine e passou a ser maven:3.9.11-eclipse-temurin-21. Alpine é baseada em musl e o binário do node que o frontend-maven-plugin baixa é linkado contra glibc, de modo que builds de frontend morriam nela.

Biblioteca pipeline

  • EntryPointInfo foi removido do BuildTarget. O subdiretório de execução de targets Python passou a ser o campo run-subdir da configuração declarativa, repassado ao módulo uv.

  • BuildStrategy e QualityStrategy não carregam mais campos de configuração opacos: as estratégias fecham sobre o target resolvido via closure.

Promote

  • O parâmetro registryPass foi renomeado para registryPassword (--registry-password na CLI), alinhando com publish-all e check-images.

Esquema de tags

O repositório usa dois esquemas de tag em paralelo, porque tem dois tipos de artefato.

Artefato Formato da tag Exemplo

Módulos Dagger (orchestrator, orchestrator-utils, maven, npm, uv)

Tag global do repositório, sem prefixo

3.0.0

Bibliotecas Go (pipeline, gitlabci)

Tag com prefixo do diretório, exigido pelo Go modules

pipeline/v0.9.1, gitlabci/v0.2.1

Na prática: DAGGER_MODULE referencia …​daggerverse/orchestrator@3.0.0, enquanto os go.mod referenciam github.com/BasisTI/daggerverse/pipeline v0.9.1. As duas numerações evoluem independentemente — uma release 3.0.0 dos módulos pode conviver com a pipeline/v0.9.1.

ci.skip

O publish-all faz bump dos arquivos de versão e commita de volta na branch. Esse commit não pode disparar uma nova pipeline, sob pena de loop infinito.

A supressão é feita exclusivamente pelo push option, em CommitAndPush (orchestrator-utils):

git push -o ci.skip <remote> HEAD:<branch>

A mensagem do commit é apenas Bump versão para <versão>, sem o prefixo [skip ci]. O prefixo foi removido de propósito: o [skip ci] na mensagem é avaliado pelo GitLab também nas pipelines de merge request, então um MR contendo o commit de bump teria sua pipeline bloqueada — inclusive o check-quality. O push option, ao contrário, afeta só o push que o produziu.

O orchestrator-utils reconhece os dois formatos ao procurar o commit de build (--grep=^\[skip ci\] e --grep=^Bump versão para `, com `--invert-grep), de modo que check-images e promote ignoram commits de bump ao localizar a build branch.

Integração com o GitLab

O diretório gitlabci/ é uma biblioteca Go independente (github.com/BasisTI/daggerverse/gitlabci).

Commit statuses

O gitlabci.Client reporta o progresso de cada stage como commit status, visível na UI de MR/commit. Chama POST /api/v4/projects/:id/statuses/:sha para transicionar cada stage de running para success ou failed.

client := &gitlabci.Client{
    BaseURL:   "https://gitlab.example.com",
    Token:     "glpat-...",
    ProjectID: "12345",
}

client.SetCommitStatus(sha, gitlabci.StateRunning, "my-app: Build", "")
// ... roda o stage ...
client.SetCommitStatus(sha, gitlabci.StateSuccess, "my-app: Build", "")

Os nomes seguem o padrão "{projeto}: {stage}", por exemplo portal-web: Install Dependencies ou api-gateway: Docker Build and Push.

O orchestrator monta o cliente a partir de --gitlab-host, --gitlab-token e --gitlab-project-id. Quando qualquer um deles está ausente, os módulos se comportam exatamente igual, apenas sem reportar statuses.

Bibliotecas Go

pipeline

Orquestração genérica e type-safe. Usa generics para abstrair sobre os tipos do SDK do Dagger, permitindo que orchestrators com versões diferentes do SDK compartilhem a mesma lógica.

  • BuildStrategy[Dir, Secret] / QualityStrategy[Dir, Secret] — assinaturas que qualquer módulo implementa para se plugar na orquestração.

  • DaggerOps[Dir, Secret] — callbacks que isolam as operações específicas do SDK (change detection, acesso a diretórios, tagging de imagem).

  • PublishAll — detecta os projetos alterados, roda suas estratégias de build em sequência, agrega as imagens publicadas e as tageia com sha-<commit>.

  • CheckQuality — roda testes e análise estática nos projetos alterados, com fail-fast opcional.

pipeline/config

Leitura e validação de ci/pipeline.toml, e a derivação anti-drift do mapa de imagens (ProjectImages, ProjectImagesJSON). Consumido tanto pelo orchestrator genérico quanto pelos módulos custom dos projetos.

pipeline/sonarargs

BuildOptions(host, token, projectKey, waitForQualityGate, extra) monta as opções -Dsonar.* na ordem canônica. Os módulos maven, npm e uv delegam para cá; cada um mantém apenas suas bordas (leitura do *dagger.Secret e o tratamento de config nula).

pipeline/imageref

Ref(registry, group, image, tag) monta <registry>/<group>/<image>:<tag>; OCILabels(commitSha, version) devolve os labels org.opencontainers.image.* aplicados às imagens publicadas.

Módulos por tecnologia

Estes módulos são chamados pelo orchestrator; um projeto normalmente não os invoca diretamente.

maven

Build e teste (clean verify), análise Sonar (sonar:sonar) e publicação da imagem via plugin Jib. Suporta Maven Wrapper, cache do repositório local, daemon Docker opcional para Testcontainers, modo reactor e reporte de commit statuses.

Fora do modo reactor, a versão é aplicada com versions:set; no modo reactor, com -Drevision.

npm

Projetos Node.js/Angular. Roda npm ci, npm run build, análise SonarQube opcional e publica o dist/ em um container nginx. Suporta override de versão via patch do package.json e reporte de commit statuses.

uv

Projetos Python com uv. Sync de dependências em dois estágios (uv sync), análise SonarQube opcional e publicação de uma imagem Python slim. Suporta run-subdir e customizações de build para projetos dlt e dbt.

orchestrator-utils

Utilitários de Git e registry usados pelo orchestrator: change detection entre branches, lookup de commit SHA, tagging de imagem com sha-<commit>, bump e commit dos arquivos de versão, checagem de existência de imagens e promoção entre registries.

Todas as operações Git rodam dentro de um container alpine/git; as operações de imagem usam crane.

About

Daggerverse Basis - Opiniatated common libs for our pipelines

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages