Skip to content

Guia de CI/CD — middag-io

Referência completa para GitHub Actions, secrets 1Password, deploy AWS e migração legada do Bitbucket.

Sumário


1. Visão Geral

Arquitetura

Desenvolvedor ──push──> GitHub (middag-io/{repo})


                   GitHub Actions
                   (workflows reutilizáveis de middag-io/.github-private)

                ┌────────────┼───────────┐
                ▼            ▼           ▼
            1Password      GHCR      Cloudflare
            (secrets)     (imagens)    (Pages)
                │            │
                ▼            ▼
             EC2 + 1Password Connect
             (produção / staging)

Decisões Chave

DecisãoReferência
Nomenclatura de reposADR-001
Modelo de branches (main + develop)ADR-002
Estratégia de release (release-please)ADR-003
Estratégia de migraçãoADR-004
Integração 1PasswordADR-005

2. 1Password — Uso no Dia-a-Dia

Dois Modos

ContextoMecanismoQuando Usar
CI (GitHub Actions)Service Account TokenWorkflows, build, test, scripts de deploy
Servidores (EC2/Docker)1Password ConnectContainers produção/staging, op inject

Para Desenvolvedores: Como os secrets fluem

1. Secret vive no vault 1Password (ex.: CI-MYPROJECT)
2. Código referencia como op://CI-MYPROJECT/item-name/field-name
3. No CI: 1password/load-secrets-action resolve
4. No servidor: op inject resolve via Connect
5. Desenvolvedor nunca vê ou copia o valor do secret

Adicionando um secret a um projeto

  1. Crie o item no vault 1Password apropriado:

    VaultUse para
    CI-SHAREDCompartilhado entre projetos (GHCR, SES, Cloudflare global)
    CI-AWSCredenciais AWS (ECR, RDS, Lambda)
    CI-{PROJECT}Específico do projeto (deploy keys, API keys, certificados)
    PRIVATEChaves privadas, certificados
  2. Referencie no seu .env.*.tpl ou workflow:

    # Em .env.production.tpl
    MY_SECRET="op://CI-MYPROJECT/my-item/my-field"
    yaml
    # Em workflow do GitHub Actions
    - uses: 1password/load-secrets-action@v2
      env:
        OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SA_MYPROJECT }}
        MY_SECRET: op://CI-MYPROJECT/my-item/my-field
  3. Verifique se o vault é acessível pelo Service Account ou Connect server correto.

Mudanças de acesso a vaults

ContextoO que fazer
Connect (servidor)1Password web → Integrations → Connect server → adicionar vault. Sem redeploy.
Service Account (CI)Se vault já está no escopo do SA: nada. Se novo vault necessário: criar novo token SA, atualizar secret no GitHub.

Nomenclatura de Service Account

sa-github-ci-shared          → nível de org (CI-SHARED + CI-AWS)
sa-github-ci-myproject   → projeto my-project
sa-github-ci-{project}       → novo projeto

Posicionamento de secrets no GitHub

SecretNívelOnde
OP_SERVICE_ACCOUNT_TOKENOrgGitHub org settings → Secrets → Actions
OP_SA_{PROJECT}RepoGitHub repo settings → Secrets → Actions
OP_CONNECT_TOKENServerAmbiente EC2 (não no GitHub)

3. GitHub Actions — Workflows Reutilizáveis

Todos os workflows de CI/CD ficam em middag-io/.github-private e são chamados por repos individuais.

Workflows Disponíveis

WorkflowPropósitoUsado por
wp-plugin-ci.ymlPHP matrix + checks de UI opcionais (configurável via inputs)Repos wp-plugin-*
wp-theme-ci.ymlPHP matrix para temasRepos wp-theme-*
wp-plugin-post-release.ymlBuild ZIP dist, upload no GH Release, trigger privatesatisRepos wp-plugin-*
release-please.ymlVersion bump, changelog, GitHub ReleaseTodos os repos
docs-deploy.ymlBuild VitePress + deploy Cloudflare Pages (com gate de hash)Repos com docs
docker-build-push.ymlBuild multi-arch, push GHCR, cópia ECR opcionalRepos docker-* (planejado)
docker-deploy.ymlDeploy SSH para EC2Repos docker-* (planejado)
moodle-plugin-ci.ymlChecks moodle-plugin-ciRepos moodle-* (planejado)
composer-package-ci.ymlPHPUnit, PHPStan, PHPCSRepos de libs PHP (planejado)

Como usar no seu repo

yaml
# .github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  ci:
    uses: middag-io/.github-private/.github/workflows/wp-plugin-ci.yml@workflows-v1
    with:
      has-ui: true
      has-tests: true
      has-rector: true
    secrets: inherit
yaml
# .github/workflows/release.yml
name: Release
on:
  push:
    branches: [main]

jobs:
  release-please:
    uses: middag-io/.github-private/.github/workflows/release-please.yml@workflows-v1
    permissions:
      contents: write
      pull-requests: write
    secrets: inherit

  post-release:
    needs: release-please
    if: needs.release-please.outputs.release_created == 'true'
    uses: middag-io/.github-private/.github/workflows/wp-plugin-post-release.yml@workflows-v1
    with:
      tag-name: ${{ needs.release-please.outputs.tag_name }}
      has-ui: true
      has-strauss: true
      zip-name: my-plugin
    permissions:
      contents: write
    secrets: inherit

Inputs de workflow e variáveis

Configuração por repo é passada como inputs de workflow (não variáveis de repo). Secrets e variáveis no nível da org são compartilhados via secrets: inherit.

Inputs de workflow (por repo, passados em with:)

InputWorkflowPropósitoExemplo
zip-namewp-plugin-post-releaseNome da pasta do plugin para ZIPmy-project
has-uiwp-plugin-ci / post-releasePlugin tem diretório ui/true
has-strausswp-plugin-post-releasePrefixação de vendor com Strausstrue
has-testswp-plugin-ciExecutar composer testtrue
has-phpstanwp-plugin-ci / themeExecutar análise estática PHPStantrue
has-cs-fixerwp-plugin-ci / themeExecutar PHP CS Fixertrue
has-rectorwp-plugin-ci / themeExecutar Rectorfalse
needs-satis-authwp-plugin-post-releaseComposer precisa auth privatesatistrue
docs-pathdocs-deployDiretório de docs VitePressdocs/
project-namedocs-deployNome do projeto Cloudflare Pagesmy-project-docs

Secrets de org (compartilhados via secrets: inherit)

SecretVisibilidadePropósito
OP_SERVICE_ACCOUNT_TOKENTODOS1Password Service Account (org, CI-SHARED + CI-AWS)
PRIVATESATIS_PASSWORDTODOSSenha HTTP Basic para privatesatis.middag.com.br
PRIVATESATIS_DISPATCH_TOKENTODOSGitHub PAT com escopo repo — trigger repository_dispatch no my-satis-repo
CLOUDFLARE_API_TOKENTODOSToken API Cloudflare (deploy Pages)
DOCKERHUB_TOKENTODOSToken push Docker Hub
NPM_TOKENPRIVADOToken publish npm registry
SMTP_PASSWORDPRIVADOSenha SMTP SES
PRIVATESATIS_BOT_APP_IDTODOSGitHub App ID para privatesatis-bot (auth Composer cross-repo)
PRIVATESATIS_BOT_PRIVATE_KEYTODOSChave privada (PEM) do GitHub App para privatesatis-bot

Variáveis de org (compartilhadas via secrets: inherit)

VariávelVisibilidadeValorPropósito
PRIVATESATIS_USERNAMETODOSgithub-middag-ioUsername HTTP Basic para privatesatis
CLOUDFLARE_ACCOUNT_IDTODOS32ff8929...Conta Cloudflare
DOCKERHUB_USERNAMETODOSmiddagtecUsername push Docker Hub
SENTRY_ORGTODOSmiddagSlug da organização Sentry
SMTP_HOSTPRIVADOemail-smtp.us-east-1...Host SMTP SES
SMTP_PORTPRIVADO587Porta SMTP SES
SMTP_USERPRIVADOAKIA...Usuário IAM SMTP SES

Feature flags — variáveis de repo

Variáveis no nível do repo atuam como feature flags para habilitar steps opcionais do workflow sem alterar o código do workflow. Defina em GitHub repo Settings → Variables → Actions.

VariávelEscopoValorPropósito
PUSH_TO_ECRVar de repotrueHabilita crane copy do GHCR para ECR após build (veja ADR-007)
ECR_REPOVar de repomy-projectNome do repositório ECR (obrigatório quando PUSH_TO_ECR=true)

Padrão: qualquer step de workflow pode ser condicionado com if: vars.MY_FLAG == 'true'. Isso evita forks de workflow por repo — um workflow reutilizável atende todos os repos, variáveis de repo controlam o comportamento.


4. Docker Registry (GHCR / ECR)

Padrão: GHCR (GitHub Container Registry)

Todas as imagens Docker fazem push para GHCR por padrão:

ghcr.io/middag-io/{repo-name}/{service}:{tag}

Exemplo:

ghcr.io/middag-io/docker-wp-my-project/wordpress:a1b2c3d4e5f6
ghcr.io/middag-io/docker-wp-my-project/nginx:latest

Opcional: ECR (AWS Elastic Container Registry)

Habilitado por repo via variáveis de repositório:

PUSH_TO_ECR=true
ECR_REPO=my-project

Quando habilitado, docker-build-push.yml usa crane copy para replicar do GHCR para ECR após o build (sem rebuild):

GHCR (alvo do build) ──crane copy──> ECR (fonte do deploy)

Credenciais ECR do vault 1Password CI-AWS.

Quando usar ECR

  • Servidores de produção na AWS que se beneficiam de pull na mesma região
  • Atualmente: apenas docker-wp-my-project precisa de ECR
  • Padrão para novos projetos: apenas GHCR, a menos que haja necessidade específica

5. Deploy para Produção

Projetos Docker (EC2)

GitHub Actions                         EC2
     │                                  │
     ├── Build & push para GHCR         │
     ├── (opcional) crane copy para ECR  │
     ├── SCP: docker-compose.yml,       │
     │        .env.production.tpl,      │
     │        Makefile                  │
     ├── SSH: docker compose pull       │
     │        op inject .env.tpl → .env │  ← 1Password Connect (local)
     │        docker compose up -d      │
     └── Cleanup                        │

Release de plugin WordPress

1. Merge PR de develop → main
2. release-please detecta conventional commits em main
3. Cria/atualiza Release PR (version bump + CHANGELOG.md)
4. Merge da Release PR → dispara:
   a. GitHub Release criado (com tag)
   b. Workflow pós-release: build UI → Strauss → ZIP → upload no Release
   c. Trigger rebuild privatesatis (repository_dispatch)
   d. Deploy docs para Cloudflare Pages (se aplicável)

Release de tema WordPress

Mesmo que plugin, mas versão em style.css + functions.php.


6. Adicionando um Novo Projeto

Checklist

  1. Criar repo seguindo ADR-001:

    bash
    gh repo create middag-io/{name} --private --description "{desc}"
  2. Definir branch default como main, criar branch develop

  3. Adicionar topics (platform, type, client)

  4. Adicionar CI workflow chamando workflow reutilizável:

    yaml
    jobs:
      ci:
        uses: middag-io/.github-private/.github/workflows/wp-plugin-ci.yml@workflows-v1
        with:
          has-tests: true
        secrets: inherit
  5. Adicionar config release-please:

    • Criar release-please-config.json com release type simple e extra-files para header de versão
    • Criar .release-please-manifest.json com versão atual
    • Adicionar anotações // x-release-please-version nas linhas de versão do arquivo principal de plugin/theme
    • Adicionar workflow de release chamando release-please.yml + wp-plugin-post-release.yml
  6. Configurar secrets (se projeto precisa de SA próprio):

    • Criar Service Account no 1Password: sa-github-ci-{project}
    • Conceder acesso aos vaults apropriados
    • Adicionar token como repo secret: OP_SA_{PROJECT}
  7. Definir variáveis de repo (vars.*) para config de workflow

  8. Configurar proteção de branch (ou verificar se ruleset da org se aplica)

  9. Testar CI — push para develop, abrir PR, merge para main


7. Adicionando um Novo Secret

Árvore de decisão

É compartilhado entre projetos?
├── Sim → Adicionar ao vault CI-SHARED ou CI-AWS
│         (já acessível via SA da org)

└── Não → Existe vault do projeto (CI-{PROJECT})?
         ├── Sim → Adicionar ao CI-{PROJECT}
         │         (já acessível via SA do projeto)

         └── Não → Criar vault CI-{PROJECT}
                  Criar sa-github-ci-{project} SA
                  Adicionar OP_SA_{PROJECT} aos secrets do repo

Após adicionar o secret

  1. Referenciar no código: op://CI-{VAULT}/item-name/field-name
  2. Se usado no workflow CI: adicionar ao bloco env de 1password/load-secrets-action
  3. Se usado no servidor: adicionar ao .env.production.tpl (Connect resolve automaticamente)
  4. Testar no CI e/ou staging antes de produção

8. Migração Legada do Bitbucket

Wave 1 (WordPress) está COMPLETA. As tabelas abaixo permanecem como referência útil para futuras waves de migração.

Mapeamento de variáveis

BitbucketGitHub Actions
$BITBUCKET_COMMIT${{ github.sha }}
$BITBUCKET_REPO_SLUG${{ github.event.repository.name }}
$BITBUCKET_REPO_FULL_NAME${{ github.repository }}
$BITBUCKET_BRANCH${{ github.ref_name }}
$BITBUCKET_TAG${{ github.ref_name }} (em trigger de tag)
$BITBUCKET_SSH_KEY_FILEDeploy key ou action ssh-agent
$BITBUCKET_BOT_USERNAMEgithub-actions[bot]
$BITBUCKET_BOT_PASSWORD${{ secrets.GITHUB_TOKEN }}

Mapeamento Pipe → Action

Bitbucket PipeEquivalente GitHub
atlassian/bitbucket-upload-filegh release upload ou actions/upload-artifact
atlassian/trigger-pipelinerepository_dispatch ou workflow_dispatch

Etapas de migração Pipeline → Workflow

  1. Ler bitbucket-pipelines.yml
  2. Identificar steps, triggers, condições, services
  3. Mapear para workflow GitHub Actions usando workflows reutilizáveis
  4. Substituir variáveis BB por equivalentes GH (tabela acima)
  5. Substituir pipes BB por actions GH (tabela acima)
  6. Mover variáveis de pipeline BB para 1Password ou variáveis de repo GH
  7. Testar na branch develop antes de merge para main

O que NÃO é migrado

  • Bitbucket Issues e Pull Requests (Jira usado para tracking)
  • Bitbucket Downloads (mover backups para S3 ou GitHub Releases)
  • Histórico de builds do Pipeline (irrelevante)

Status da Migração

WaveStatusNotas
0 — InfraCOMPLETO.github, CI workflows, config da org
1 — WPCOMPLETO6 repos migrados, pipelines BB removidos, workflows reutilizáveis + release-please
2 — LibsPendenteRenomear + polimento de topics/properties
3 — AppsPendente
4 — Moodle corePendenteCI complexo
5 — Moodle sitesPendenteMigração por site

9. Troubleshooting

1Password no CI

ProblemaCausaSolução
could not find itemVault ou nome de item errado no op://Verificar nome do vault + título do item no 1Password
unauthorizedToken SA não tem acesso ao vaultVerificar permissões do SA no 1Password web
connect: connection refusedConnect não está rodando (apenas infra)docker compose up -d op-connect-api op-connect-sync

GitHub Actions

ProblemaCausaSolução
secrets: inherit não funcionaRepo não está na org, ou caminho erradoVerificar repo sob org middag-io
Workflow reutilizável não encontradoRef ou caminho erradomiddag-io/.github-private/.github/workflows/{name}.yml@workflows-v1
Permissão negada ao fazer push de tagProteção de branch bloqueia botAdicionar bot à lista de bypass no ruleset

Docker

ProblemaCausaSolução
Push GHCR negadoToken sem write:packagesVerificar escopo do GHCR_TOKEN no 1Password
Login ECR falhouCredenciais AWS expiradasVerificar credenciais no vault CI-AWS
crane copy falhaAuth no registry de destinoGarantir login em ambos os registries antes da cópia

MIDDAG Tecnologia