Deploy de Repositorio Privado

Este tutorial mostra como deployar uma aplicacao a partir de um repositorio GitHub privado. Requer um Personal Access Token (PAT) para autenticacao.

Cenario

Voce tem um repo privado (devborlot/meu-app-sys) com um backend Flask que precisa ser deployado em producao. O repo usa Dockerfile para build.

Pre-requisitos

  • Runner instalado (runner init executado)
  • Docker e Traefik rodando no servidor
  • Dominio apontando pro servidor
  • GitHub Personal Access Token (Fine-grained ou Classic)

Passo 1: Criar GitHub Token

Fine-grained PAT (recomendado)

  1. Acesse GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens
  2. Clique em "Generate new token"
  3. Configure:
    • Token name: runner-deploy
    • Expiration: 90 days (ou custom)
    • Repository access: Only select repositories → selecione meu-app-sys
    • Permissions:
      • Contents: Read-only
      • Metadata: Read-only
  4. Copie o token gerado (comeca com github_pat_...)

Classic PAT (alternativa)

  1. GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
  2. Generate new token com scope repo
  3. Copie o token (comeca com ghp_...)

O Runner armazena o token encriptado (AES-GCM) no state file e usa GIT_ASKPASS para autenticar — o token nunca aparece em ps aux, .git/config ou logs.

Passo 2: Preparar o Repositorio

.deploy.yml

project: meu-app
system: sys
port: 8000

# Modo BUILD com Dockerfile
build:
  context: .
  dockerfile: Dockerfile

# NOTA: bloco `github:` foi DEPRECATED em v2.16.0 — o repo é resolvido
# automaticamente do state file (registrado via `runner add --repo --branch`).

version:
  source: file              # opcional desde v2.18.2
  file: version.json
  field: version
# Desde v2.18.2, generator default emite `source: git-commit`
# (deploys auto-versionados via SHA).

networks:
  - public
  - mysql
  - redis

environment:
  FLASK_ENV: production
  PYTHONPATH: /app/src

healthcheck:
  path: /health
  start_period: 45s    # Grace period para Flask cold start
  interval: 10s
  timeout: 5s
  retries: 5

instances:
  production:
    domain: sys.meusite.com.br
    source:
      type: dist
    keep_versions: 3

Dockerfile

FROM python:3.12-slim
WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY src/ ./src/

ENV PYTHONPATH=/app/src
ENV FLASK_ENV=production

EXPOSE 8000

HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
  CMD python -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')" || exit 1

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "main:app"]

Dica: declarar HEALTHCHECK no Dockerfile evita problemas com imagens que nao tem curl/wget. O Runner respeita o healthcheck da imagem.

Build e Push para dist

git checkout dist 2>/dev/null || git checkout -b dist

# Copiar artefatos (backend Python = src/ direto)
cp -r src/ ./src/
cp requirements.txt Dockerfile .deploy.yml version.json ./

git add .
git commit -m "deploy: v1.0.0"
git push origin dist

Passo 3: Registrar no Runner (com Token)

No servidor de producao:

runner add --repo devborlot/meu-app-sys --branch main --token ghp_xxxYYYzzz123

O Runner:

  1. Usa o token para clonar o repo privado (via GIT_ASKPASS)
  2. Valida o .deploy.yml (parse + validacao semantica — verifica que Dockerfile existe, port > 0, healthcheck path absoluto)
  3. Encripta e salva o token no state file
  4. Registra a app

Com ckey (para proteger secrets)

Se o app precisa de variaveis sensiveis (.env com senhas de DB, API keys):

runner add --repo devborlot/meu-app-sys --branch main \
  --token ghp_xxxYYYzzz123 \
  --ckey "minha-chave-de-encriptacao-forte"

A ckey permite recuperacao zero-touch dos secrets em caso de perda do servidor.

Verificar

runner list
# Deve mostrar: meu-app (sys) — up_to_date

# Verificar config
runner validate /data/apps/meu-app/.deploy.yml

Passo 4: Configurar Environment

Antes do deploy, configure as variaveis de ambiente:

# Variaveis normais (vao para .env)
runner env set meu-app --key DB_HOST --value mysql
runner env set meu-app --key DB_NAME --value meu_app_db

# Secrets (vao para .secrets, encriptados se ckey configurada)
runner env set meu-app --key DB_PASSWORD --value "senha_segura" --secret
runner env set meu-app --key JWT_SECRET --value "chave_jwt_longa" --secret

# Verificar
runner env status meu-app

Passo 5: Deploy

runner deploy meu-app -V

Output esperado:

[1/7] FETCH     Baixando codigo fonte...
[2/7] PREPARE   Preparando versao v1.0.0...
[3/7] SECURITY  Verificando seguranca...
[4/7] BUILD     Building image meu-app:v1.0.0...
[5/7] CONTAINER Iniciando container...
[DEPLOY] Honoring HEALTHCHECK declared by image 'meu-app:v1.0.0'
[6/7] HEALTH    Verificando saude...
Health check wait: 135s (start=45s, interval=10s, timeout=5s, retries=5)
Container meu-app_production_v1-0-0 is healthy
[7/7] ROUTING   Configurando Traefik...

Passo 6: Verificar

# Health check
curl -s https://sys.meusite.com.br/health | python3 -m json.tool

# Container
docker ps | grep meu-app

# Logs
docker logs meu-app_production_v1-0-0 --tail 20

Passo 7: Automatizar

Cron (detecta pushes)

*/5 * * * * /opt/runner/runner fetch --deploy --json >> /var/log/runner-fetch.json 2>&1

Ou service mode (recomendado v2.2.0+)

runner service init --port 9090
runner service start
# Scheduler faz fetch automaticamente + gerencia canary + signals

Rotacionar Token

Quando o token expirar:

# Gerar novo token no GitHub
# Atualizar no Runner:
runner edit meu-app --token ghp_novoToken456

# Verificar que o fetch funciona:
runner fetch meu-app

A partir da v2.1.0, se o token expirar, o Runner mostra: Falha de autenticacao no git: o token salvo esta ausente ou expirou. Rode: runner edit <app> --token <ghp_...>

Exemplo Completo: Fluxo de Release

# 1. Desenvolvimento (branch dev)
git checkout dev
# ... commits ...
git push origin dev

# 2. Release (merge para main + tag)
git checkout main
git merge dev --no-ff -m "release: v1.1.0"
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin main --tags

# 3. Build + push para dist
git checkout dist
rm -rf src/ && cp -r ../src ./src/
cp requirements.txt Dockerfile .deploy.yml version.json ./
git add . && git commit -m "deploy: v1.1.0"
git push origin dist

# 4. Runner detecta e deploya (via cron ou service mode)
#    Ou force: ssh servidor "runner fetch --deploy"

# 5. Voltar para dev
git checkout dev && git merge main && git push origin dev

Troubleshooting

"could not read Username"

Token ausente ou expirado:

runner edit meu-app --token ghp_novoToken
runner fetch meu-app --force

"Config invalido em .deploy.yml at line N"

Erro de YAML com localizacao exata. Corrija o campo indicado na branch dist.

Build falha (Dockerfile)

# Testar build localmente:
docker build -t test .
docker run --rm test

# Verificar que Dockerfile esta na dist:
ls /data/apps/meu-app/pull/Dockerfile

Health check falha

Ver o guia de healthcheck para diagnostico detalhado.

By Borlot.com.br on 16/04/2026