Actions do Projeto¶
Mapa Geral e Dependências¶
Push → develop
└─▶ versioning_dev.yml (tag dev-v*)
└─▶ [PR aberta para develop/main]
└─▶ security_review.yml (security_check.py --mode pre-push)
Push → main (qualquer arquivo exceto CHANGELOG.md)
└─▶ production_pipeline.yml
├─▶ Job: test (pytest)
└─▶ Job: release_prod (git-cliff + tag + CHANGELOG)
└─ (trigger por_calls)
├─▶ deploy_hugging_face.yml
└─▶ deploy_pages.yml
Push → main (apenas CHANGELOG.md)
└─▶ deploy_pages.yml (deploy do dashboard)
└─▶ changelog_dev.yml (sync CHANGELOG → develop)
Push → supabase/migrations/**
└─▶ deploy_db.yml (supabase db push)
workflow_dispatch
├─▶ manual_release.yml (release manual major/minor/patch)
└─▶ push_homo.yml (atualiza branch homo)
production_pipeline.yml — Pipeline Principal¶
Arquivo: .github/workflows/production_pipeline.yml
Trigger: push para main, excluindo mudanças apenas em CHANGELOG.md
on:
push:
branches: [main]
paths-ignore:
- 'CHANGELOG.md'
Permissões necessárias:
permissions:
contents: write # Para criar tags e commitar CHANGELOG
Job 1: test¶
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "🐍 Setup Python 3.13"
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: "📦 Instalar uv"
run: pip install uv
- name: "📥 Instalar dependências"
run: uv pip install -r requirements.txt --system
- name: "🧪 Executar pytest"
run: pytest tests/ -v
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
Por que
--systemno uv? O ambiente do GitHub Actions não tem um venv ativo.--systeminstrui ouva instalar no Python do sistema em vez de tentar criar/ativar um venv.
Job 2: release_prod¶
release_prod:
needs: test # Só executa se test passou
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # CRÍTICO: busca TODO o histórico de tags
- name: "🔢 Calcular próxima versão"
id: version
run: |
LAST=$(git tag --list 'v*' --sort=-version:refname | head -n 1)
# Fallback se não houver nenhuma tag ainda
LAST=${LAST:-v0.0.0}
IFS='.' read -r MAJOR MINOR PATCH <<< "${LAST#v}"
echo "new_version=v${MAJOR}.${MINOR}.$((PATCH+1))" >> $GITHUB_OUTPUT
- name: "📝 Gerar CHANGELOG com git-cliff"
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose --tag ${{ steps.version.outputs.new_version }}
env:
OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }}
- name: "💾 Commit CHANGELOG e criar tag"
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add CHANGELOG.md
git commit -m "chore(release): ${{ steps.version.outputs.new_version }} [skip ci]"
git tag ${{ steps.version.outputs.new_version }}
git push origin main --follow-tags
fetch-depth: 0: Sem isso, oactions/checkoutfaz um shallow clone (apenas o commit mais recente). Ogit-cliffe o cálculo da versão precisam de todo o histórico de tags para funcionar corretamente.
security_review.yml — Code Review com IA¶
Arquivo: .github/workflows/security_review.yml
Trigger: pull_request apontando para develop ou main
on:
pull_request:
branches: [develop, main]
Diferencial técnico: Este workflow usa um requirements.txt isolado (gatekeep/requirements-gatekeep.txt) para instalar apenas as dependências necessárias para o script de segurança, sem instalar as dependências pesadas da aplicação principal (Streamlit, etc.).
jobs:
security-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
# fetch-depth: 0 necessário para que o git diff
# contra GITHUB_BASE_REF funcione corretamente
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: "📦 Instalar deps do gatekeep"
run: pip install -r gatekeep/requirements-gatekeep.txt
- name: "🔒 Executar security_check"
run: python gatekeep/security_check.py --mode pre-push
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GITHUB_ACTIONS: "true"
# GITHUB_BASE_REF é injetado automaticamente pelo GitHub
# em eventos de pull_request (ex: "develop" ou "main")
Comportamento em fork PRs: O GitHub não injeta secrets em PRs de forks por segurança. Nesses casos, GEMINI_API_KEY será vazio e o script entra em modo fail-open, permitindo a PR sem revisão da IA.
deploy_db.yml — Migrations do Banco de Dados¶
Arquivo: .github/workflows/deploy_db.yml
Trigger: Push para main que modifique arquivos em supabase/migrations/**
on:
push:
branches: [main]
paths:
- 'supabase/migrations/**'
Fluxo de execução:
jobs:
deploy-migrations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "🛠️ Instalar Supabase CLI"
uses: supabase/setup-cli@v1
with:
version: latest
- name: "🔗 Linkar ao projeto de produção"
run: supabase link --project-ref ${{ secrets.SUPABASE_PROJECT_ID }}
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
- name: "🚀 Aplicar migrations"
run: supabase db push
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
Como o Supabase CLI rastreia migrations:
O CLI mantém uma tabela interna supabase_migrations.schema_migrations no banco de produção. Ao executar supabase db push, ele compara os arquivos .sql presentes em supabase/migrations/ com os timestamps já registrados nessa tabela, e executa apenas os arquivos novos em ordem cronológica.
⚠️ ATENÇÃO: O
supabase db pushexecuta SQL diretamente em produção. Não há rollback automático. Uma migration mal-escrita pode corromper dados ou bloquear a aplicação.
deploy_hugging_face.yml — Espelhamento no HF Spaces¶
Arquivo: .github/workflows/deploy_hugging_face.yml
Trigger: Conclusão bem-sucedida do production_pipeline.yml
on:
workflow_run:
workflows: ["production_pipeline"]
types: [completed]
branches: [main]
Técnica do branch orfão:
O Hugging Face Spaces espera um repositório git com apenas os arquivos do app — sem o histórico de desenvolvimento, sem binários pesados da pasta docs/imgs. Para isso, o workflow cria um snapshot limpo:
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: "🧹 Preparar snapshot limpo"
run: |
# Cria branch orfão (sem histórico)
git checkout --orphan deploy-snapshot
# Remove binários pesados da documentação
git rm -rf docs/imgs/
# Adiciona tudo o que restou
git add -A
git commit -m "deploy: snapshot for HF Spaces"
- name: "🚀 Push para Hugging Face"
run: |
git remote add huggingface \
https://emn-f:${{ secrets.HF_TOKEN }}@huggingface.co/spaces/emn-f/vox-ai
git push huggingface deploy-snapshot:main --force
O --force é necessário porque cada deploy é um commit único sem histórico relacionado ao anterior. O HF Spaces detecta o push e reinicia o container automaticamente.
deploy_pages.yml — Dashboard de Transparência¶
Arquivo: .github/workflows/deploy_pages.yml
Trigger: Conclusão do production_pipeline.yml OU push direto no CHANGELOG.md da main
on:
workflow_run:
workflows: ["production_pipeline"]
types: [completed]
branches: [main]
push:
branches: [main]
paths:
- 'CHANGELOG.md'
Injeção segura de credenciais:
O arquivo pages/dashboard.js no repositório contém placeholders em vez de credenciais reais:
// Em pages/dashboard.js (no repositório)
const SUPABASE_URL = "__SUPABASE_URL_DEV__";
const SUPABASE_ANON_KEY = "__SUPABASE_ANON_KEY_DEV__";
O workflow substitui os placeholders antes do deploy usando sed:
- name: "🔑 Injetar credenciais Supabase Dev"
run: |
sed -i \
's|__SUPABASE_URL_DEV__|${{ secrets.SUPABASE_URL_DEV }}|g' \
pages/dashboard.js
sed -i \
's|__SUPABASE_ANON_KEY_DEV__|${{ secrets.SUPABASE_ANON_KEY_DEV }}|g' \
pages/dashboard.js
Por que
DEVe não produção? O Dashboard é público (GitHub Pages). Usar aANON_KEYdo banco de desenvolvimento limita o impacto de eventuais abusos — os dados exibidos são os de homologação, não de produção.
versioning_dev.yml — Tagging na Branch develop¶
Arquivo: .github/workflows/versioning_dev.yml
Trigger: Push para develop
Objetivo: Criar uma tag dev-v* em cada commit da branch de desenvolvimento para rastreabilidade, sem gerar CHANGELOG ou release notes.
jobs:
tag-dev:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: "🔢 Calcular tag dev"
id: devtag
run: |
# Busca a última tag de produção para manter o MAJOR.MINOR em sync
PROD_TAG=$(git tag --list 'v*' --sort=-version:refname | head -n 1)
PROD_TAG=${PROD_TAG:-v0.0.0}
IFS='.' read -r MAJOR MINOR PATCH <<< "${PROD_TAG#v}"
# Busca a última tag dev para incrementar o PATCH
LAST_DEV=$(git tag --list 'dev-v*' --sort=-version:refname | head -n 1)
if [ -z "$LAST_DEV" ]; then
NEW_TAG="dev-v${MAJOR}.${MINOR}.$((PATCH+1))"
else
IFS='.' read -r _ _ DEV_PATCH <<< "${LAST_DEV#dev-v}"
NEW_TAG="dev-v${MAJOR}.${MINOR}.$((DEV_PATCH+1))"
fi
echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
- name: "🏷️ Criar e push da tag"
run: |
git tag ${{ steps.devtag.outputs.tag }}
git push origin ${{ steps.devtag.outputs.tag }}
As tags dev-v* são visíveis no repositório e são usadas pela função git_version() em src/utils.py para exibir a versão correta no rodapé da interface quando o app está rodando a partir da branch develop.
manual_release.yml — Release Manual¶
Arquivo: .github/workflows/manual_release.yml
Trigger: workflow_dispatch (acionamento manual pela interface do GitHub ou CLI)
Input de usuário:
on:
workflow_dispatch:
inputs:
bump_type:
description: "Tipo de incremento de versão"
required: true
type: choice
options:
- patch
- minor
- major
default: patch
Lógica de cálculo por tipo:
LAST=$(git tag --list 'v*' --sort=-version:refname | head -n 1)
LAST=${LAST:-v0.0.0}
IFS='.' read -r MAJOR MINOR PATCH <<< "${LAST#v}"
case "${{ inputs.bump_type }}" in
patch)
NEW_VERSION="v${MAJOR}.${MINOR}.$((PATCH+1))"
;;
minor)
NEW_VERSION="v${MAJOR}.$((MINOR+1)).0"
;;
major)
NEW_VERSION="v$((MAJOR+1)).0.0"
;;
esac
Casos de uso para minor e major:
minor: Novas features significativas (ex: novo módulo de busca, nova fonte de dados na KB).major: Quebras de compatibilidade (ex: mudança no schema do banco que requer migração de dados, refatoração completa da API).
O restante do fluxo é idêntico ao release_prod do production_pipeline.yml: git-cliff gera o CHANGELOG, commita com [skip ci] e cria a tag.
push_homo.yml — Atualização da Branch de Homologação¶
Arquivo: .github/workflows/push_homo.yml
Trigger: workflow_dispatch (manual)
Objetivo: Sincronizar a branch homo com o estado atual da develop para criar um ambiente de homologação estável.
jobs:
push-to-homo:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: develop
fetch-depth: 0
- name: "🔄 Atualizar homo com develop"
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git push origin develop:homo --force
A branch
homonão tem proteção de branch e aceita--force. Ela representa um snapshot dadevelopem um momento específico escolhido pela equipe para testes de homologação.
changelog_dev.yml — Sincronização do CHANGELOG¶
Arquivo: .github/workflows/changelog_dev.yml
Trigger: Push para main que modifique apenas o CHANGELOG.md
on:
push:
branches: [main]
paths:
- 'CHANGELOG.md'
Objetivo: Quando o production_pipeline.yml commita o CHANGELOG.md na main, este workflow propaga essa mudança para a develop, mantendo o histórico de releases acessível na branch de desenvolvimento.
steps:
- uses: actions/checkout@v4
with:
ref: develop
fetch-depth: 0
- name: "🔄 Cherry-pick CHANGELOG da main"
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
# Busca o SHA do commit mais recente da main (o commit do CHANGELOG)
CHANGELOG_COMMIT=$(git log origin/main -1 --pretty=format:"%H")
# Cherry-pick apenas aquele commit
git cherry-pick "$CHANGELOG_COMMIT" --no-commit
# Commita na develop com [skip ci] para não disparar outros workflows
git commit -m "chore: sync CHANGELOG from main [skip ci]"
git push origin develop
Por que
cherry-picke nãomerge? Adeveloppode ter commits que ainda não foram para amain. Um merge traria todos esses commits de volta para amainna próxima PR, criando um ciclo. Ocherry-picktraz apenas o arquivoCHANGELOG.mdatualizado, sem misturar históricos.
Apêndice — Diagrama de Segredos e Variáveis de Ambiente¶
Todos os segredos são armazenados como GitHub Repository Secrets e nunca aparecem nos logs:
| Secret | Usado em | Descrição |
|---|---|---|
GEMINI_API_KEY |
production_pipeline, security_review, app |
Chave da Google AI Studio |
SUPABASE_URL |
production_pipeline, app |
URL do projeto Supabase de produção |
SUPABASE_ANON_KEY |
production_pipeline, app |
Chave anônima do Supabase de produção |
SUPABASE_URL_DEV |
deploy_pages |
URL do projeto Supabase de desenvolvimento |
SUPABASE_ANON_KEY_DEV |
deploy_pages |
Chave anônima do Supabase de desenvolvimento |
SUPABASE_PROJECT_ID |
deploy_db |
ID do projeto para o CLI do Supabase |
SUPABASE_ACCESS_TOKEN |
deploy_db |
Token de acesso admin para o CLI |
HF_TOKEN |
deploy_hugging_face |
Token de acesso ao Hugging Face |