Automacao de Testes com CI/CD em Zig: GitHub Actions e Pipelines

Testes automatizados so tem valor quando rodam de forma consistente em cada mudanca de codigo. CI/CD (Continuous Integration / Continuous Delivery) garante que cada commit seja testado automaticamente antes de ser aceito. Neste artigo final da serie, configuramos pipelines completos para projetos Zig usando GitHub Actions.

Continuacao do artigo sobre testes de integracao em Zig.

GitHub Actions para Zig

Pipeline Basico

O workflow mais simples para testar um projeto Zig:

# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Executar testes
        run: zig build test

      - name: Build
        run: zig build -Doptimize=ReleaseSafe

Pipeline Completo com Matrix

Teste em multiplas plataformas e versoes do Zig:

name: CI Completo

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        zig-version: ["0.14.0", "master"]
      fail-fast: false

    runs-on: ${{ matrix.os }}
    name: "${{ matrix.os }} - Zig ${{ matrix.zig-version }}"

    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: ${{ matrix.zig-version }}

      - name: Verificar formatacao
        run: zig fmt --check src/

      - name: Testes unitarios
        run: zig build test --summary all

      - name: Testes em modo Debug
        run: zig build test -Doptimize=Debug

      - name: Testes em modo ReleaseSafe
        run: zig build test -Doptimize=ReleaseSafe

      - name: Testes em modo ReleaseFast
        run: zig build test -Doptimize=ReleaseFast

      - name: Testes em modo ReleaseSmall
        run: zig build test -Doptimize=ReleaseSmall

      - name: Build de producao
        run: zig build -Doptimize=ReleaseSafe

      - name: Upload artefatos
        uses: actions/upload-artifact@v4
        with:
          name: build-${{ matrix.os }}-${{ matrix.zig-version }}
          path: zig-out/

Configurando build.zig para CI

O build.zig deve expor steps claros para a CI:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    // Executavel principal
    const exe = b.addExecutable(.{
        .name = "minha-app",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    b.installArtifact(exe);

    // === TESTES ===

    // Testes unitarios
    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_unit_tests = b.addRunArtifact(unit_tests);

    // Testes de integracao
    const integration_tests = b.addTest(.{
        .root_source_file = b.path("tests/integration.zig"),
        .target = target,
        .optimize = optimize,
    });
    integration_tests.root_module.addImport(
        "app",
        exe.root_module,
    );

    const run_integration_tests = b.addRunArtifact(integration_tests);

    // Step "test" roda tudo
    const test_step = b.step("test", "Executar todos os testes");
    test_step.dependOn(&run_unit_tests.step);
    test_step.dependOn(&run_integration_tests.step);

    // Steps individuais
    const unit_step = b.step("test-unit", "Apenas testes unitarios");
    unit_step.dependOn(&run_unit_tests.step);

    const int_step = b.step("test-integration", "Apenas testes de integracao");
    int_step.dependOn(&run_integration_tests.step);

    // === FORMATACAO ===
    const fmt_step = b.step("fmt", "Formatar codigo");
    const fmt = b.addFmt(.{
        .paths = &.{ "src/", "tests/" },
    });
    fmt_step.dependOn(&fmt.step);

    // === CHECK (compilacao sem gerar binario) ===
    const check_step = b.step("check", "Verificar compilacao");
    const check = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    check_step.dependOn(&check.step);
}

Cross-Platform Testing

Build e Teste para Multiplos Targets

name: Cross-Platform Build

on:
  push:
    branches: [main]

jobs:
  cross-build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        target:
          - x86_64-linux-musl
          - aarch64-linux-musl
          - x86_64-macos
          - aarch64-macos
          - x86_64-windows

    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Cross-compile para ${{ matrix.target }}
        run: zig build -Dtarget=${{ matrix.target }} -Doptimize=ReleaseSafe

      - name: Upload binario
        uses: actions/upload-artifact@v4
        with:
          name: app-${{ matrix.target }}
          path: zig-out/bin/

Testando em ARM com QEMU

  test-arm:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Instalar QEMU
        run: sudo apt-get install -y qemu-user-static

      - name: Testes cross-compiled para ARM64
        run: |
          zig build test -Dtarget=aarch64-linux-musl --summary all

Code Coverage

Gerando Relatorios de Coverage

Zig suporta instrumentacao para code coverage compativel com LLVM:

  coverage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Instalar ferramentas LLVM
        run: sudo apt-get install -y llvm

      - name: Compilar testes com coverage
        run: |
          zig build test \
            -Doptimize=Debug \
            --summary all

      - name: Gerar relatorio de coverage
        run: |
          # Usando kcov para coverage de binarios Zig
          sudo apt-get install -y kcov
          kcov --include-path=./src coverage-report ./zig-out/bin/test

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          directory: coverage-report

Script Local de Coverage

#!/bin/bash
# coverage.sh — gerar relatorio de coverage localmente

set -e

echo "Compilando testes..."
zig build test -Doptimize=Debug

echo "Executando com kcov..."
kcov --include-path=./src ./coverage-report ./zig-out/bin/test

echo "Relatorio disponivel em: coverage-report/index.html"

# Extrair percentual
PERCENT=$(cat coverage-report/zig-out/bin/test/coverage.json | jq '.percent_covered' 2>/dev/null || echo "N/A")
echo "Coverage: ${PERCENT}%"

Pipeline de Release Automatizado

Workflow Completo de Release

name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  test:
    uses: ./.github/workflows/ci.yml

  release:
    needs: test
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Build para todas as plataformas
        run: |
          for target in x86_64-linux-musl aarch64-linux-musl x86_64-macos aarch64-macos x86_64-windows; do
            echo "Building para $target..."
            zig build -Dtarget=$target -Doptimize=ReleaseSafe
            tar czf "minha-app-${target}.tar.gz" -C zig-out/bin/ .
            rm -rf zig-out
          done

      - name: Criar release no GitHub
        uses: softprops/action-gh-release@v2
        with:
          files: "*.tar.gz"
          generate_release_notes: true

Linting e Qualidade de Codigo

Verificacao de Formatacao

// No build.zig, adicionar step de verificacao de formato
const fmt_check = b.addFmt(.{
    .paths = &.{ "src/", "tests/" },
    .check = true, // Verificar sem modificar
});

const fmt_step = b.step("fmt-check", "Verificar formatacao");
fmt_step.dependOn(&fmt_check.step);

Analise Estatica com Zig

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Instalar Zig
        uses: mlugg/setup-zig@v1
        with:
          version: 0.14.0

      - name: Verificar formatacao
        run: zig fmt --check src/ tests/

      - name: Verificar compilacao em todos os modos
        run: |
          zig build check -Doptimize=Debug
          zig build check -Doptimize=ReleaseSafe
          zig build check -Doptimize=ReleaseFast
          zig build check -Doptimize=ReleaseSmall

      - name: Verificar docs
        run: zig build-lib src/main.zig -femit-docs

Monitorando Resultados

Badge de Status no README

# Meu Projeto Zig

[![CI](https://github.com/usuario/projeto/actions/workflows/ci.yml/badge.svg)](https://github.com/usuario/projeto/actions/workflows/ci.yml)
[![Coverage](https://codecov.io/gh/usuario/projeto/branch/main/graph/badge.svg)](https://codecov.io/gh/usuario/projeto)

Notificacoes de Falha

  notify:
    needs: [test]
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - name: Notificar falha
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          text: "CI falhou no branch ${{ github.ref }}"
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

Boas Praticas de CI/CD para Zig

1. Fail Fast

Execute verificacoes rapidas primeiro:

steps:
  # Rapido: verificar formato (segundos)
  - run: zig fmt --check src/

  # Medio: compilar (dezenas de segundos)
  - run: zig build check

  # Lento: testes completos (minutos)
  - run: zig build test --summary all

2. Cache de Dependencias

  - name: Cache Zig
    uses: actions/cache@v4
    with:
      path: |
        ~/.cache/zig
        zig-cache
      key: ${{ runner.os }}-zig-${{ hashFiles('build.zig.zon') }}

3. Testes Paralelos

Separe testes rapidos de lentos:

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - run: zig build test-unit

  integration-tests:
    runs-on: ubuntu-latest
    steps:
      - run: zig build test-integration

  # So faz release se ambos passarem
  release:
    needs: [unit-tests, integration-tests]

Conclusao

CI/CD transforma testes de uma pratica manual em uma rede de seguranca automatica. Com Zig, a configuracao e particularmente simples: sem dependencias de runtime, cross-compilation nativa e build system integrado significam que seus pipelines sao rapidos, reproduziveis e faceis de manter. A combinacao de testes unitarios, de integracao, fuzz testing e CI/CD cria um sistema robusto de garantia de qualidade para qualquer projeto Zig.

Serie Completa

Esta e a conclusao da serie Testes Avancados em Zig:

  1. Fundamentos de Unit Tests
  2. Test Patterns
  3. Fuzz Testing
  4. Testes de Integracao
  5. CI/CD e Automacao (este artigo)

Conteudo Relacionado

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.