# Gitea Actions — diagnóstico e ativação do runner Repositório `cferraz/adaptador-planilhas` em `gitea.fatorxtech.com`, Gitea **1.26.2**. ## Causa confirmada da ausência de execuções **Os runs existem. O que não existe é runner para executá-los.** A falha está na **atribuição**, não no enfileiramento. | Evidência | Resultado | | --- | --- | | `ci.yml` detectado por Gitea | sim, listado em Workflows | | Gatilho de `ci.yml` | `on: push` sem filtro de branch — contempla `homologacao` | | Actions no repositório | habilitado (`has_actions: true`) | | Actions na instância | habilitado (a página `/actions` responde e lista workflows) | | Run criado pelo push | **sim** — `ci.yml #2`, run `666`, commit `5b11496cd9`, branch `homologacao`, `2026-10-16 00:05:18 +00:00` | | Estado do run | **Waiting**, duração `0s` | | Mensagem do Gitea | **`No matching online runner with label: ubuntu-latest`** | | Run anterior | `ci.yml #1`, run `584`, commit `419325db12`, branch `main` — **Canceled** | Links: - https://gitea.fatorxtech.com/cferraz/adaptador-planilhas/actions - https://gitea.fatorxtech.com/cferraz/adaptador-planilhas/actions/runs/666 (Waiting) - https://gitea.fatorxtech.com/cferraz/adaptador-planilhas/actions/runs/584 (Canceled) ### Por que a API parecia dizer "zero execuções" `GET /api/v1/repos/cferraz/adaptador-planilhas/actions/tasks` devolve `{"workflow_runs":[],"total_count":0}`. Esse endpoint lista **tasks**, isto é, jobs que um runner aceitou. Nenhum runner aceitou nada, então a lista é vazia — mas os runs estão criados e esperando. `GET /actions/runs` responde `token is required`, e foi por isso que a primeira leitura passou a impressão errada. **Consequência prática: nada precisa ser republicado.** O run `666` está em `Waiting` e começa sozinho no momento em que um runner com a label compatível ficar online. ## Runners encontrados e labels | | | | --- | --- | | Runners com label `ubuntu-latest` **online** | **nenhum** (é o que o Gitea reporta) | | Outros runners na instância | **não determinado** | Listar runners exige token de owner ou admin: `GET /api/v1/repos/{owner}/{repo}/actions/runners`, ou a interface em **Settings → Actions → Runners** (nível repositório, organização ou admin). Os números de run (`584`, `666`) sugerem que a instância já teve atividade de Actions em outros repositórios, então **é possível que existam runners com outra label**. Confira antes de instalar um novo: - se já houver runner Linux com Docker e outra label (`linux_amd64`, `ubuntu-22.04`, `docker`), **não instale nada**: basta ajustar o `runs-on` dos workflows. Diga qual é a label e a troca é de uma linha em três arquivos; - se não houver, siga a instalação abaixo. ## Configuração recomendada do runner Máquina Linux **dedicada a homologação**, sem nenhuma relação com o Docker de produção do Elo Pro. ### Por que Docker-in-Docker, e não o socket do host O repositório é **público**. Montar `/var/run/docker.sock` do host dá ao workflow controle do Docker da máquina — e, num repositório público, código de terceiro poderia chegar lá por pull request de fork. A imagem `-dind-rootless` sobe um Docker próprio dentro do contêiner do runner: o job `image` constrói e executa contêineres sem alcançar o Docker do host. ### Instalação Versão fixada, compatível com o Gitea 1.26.2 (`0.6.1` é a mais recente publicada; as alternativas semver são `0.1.4` a `0.6.1`): ```bash # 1. Token de registro: Settings → Actions → Runners → Create new runner, # no NÍVEL DO REPOSITÓRIO (escopo restrito, não use token de admin). # Grave o token num arquivo com permissão 600 — nunca em commit, nunca em log. umask 077 printf '%s' 'COLE_O_TOKEN_AQUI' > /etc/gitea-runner.token # 2. Suba o runner com Docker-in-Docker rootless. docker run -d \ --name gitea-runner-hml \ --restart always \ --privileged \ -e GITEA_INSTANCE_URL=https://gitea.fatorxtech.com \ -e GITEA_RUNNER_REGISTRATION_TOKEN_FILE=/run/secrets/token \ -e GITEA_RUNNER_NAME=hml-adaptador \ -e GITEA_RUNNER_LABELS='ubuntu-latest:docker://node:24-bookworm' \ -v /etc/gitea-runner.token:/run/secrets/token:ro \ -v gitea-runner-hml-data:/data \ docker.io/gitea/act_runner:0.6.1-dind-rootless ``` Notas que importam: - `GITEA_RUNNER_REGISTRATION_TOKEN_FILE` em vez da variável com o valor: o token não aparece em `docker inspect` nem no histórico do shell. - `--privileged` é exigência do Docker-in-Docker. É justamente por isso que a máquina precisa ser dedicada. - A label **tem de ser `ubuntu-latest`**, que é o `runs-on` dos três jobs. O mapeamento `docker://node:24-bookworm` já entrega `node` e `npm` na versão certa. - `psql` e `python3` não vêm nessa imagem. O job `services` usa `psql` e a jornada assíncrona usa `python3`. Duas saídas: trocar o mapeamento para uma imagem que os tenha, ou deixar que o `runner-check` reporte a ausência e então instalá-los num passo do workflow. Rode o `runner-check` primeiro: ele faz esse inventário. - Rede de saída necessária: `gitea.fatorxtech.com` (fila e logs), `github.com` (o Gitea resolve `actions/checkout@v4`, `actions/setup-node@v4` e `docker/*` via proxy), `registry.npmjs.org` e `docker.io`. ### Conferir que subiu ```bash docker logs -f gitea-runner-hml # espere "runner registered successfully" ``` Depois, em **Settings → Actions → Runners**, o runner aparece online com a label `ubuntu-latest`. O run `666`, que está em `Waiting`, começa sozinho. ## Ordem de validação Nenhum destes foi executado ainda. Não marque nada como aprovado sem o log. 1. **`runner-check`** — dispare manualmente. Não faz checkout, não instala nada, não usa segredo. Confirma a ligação e inventaria `node`, `npm`, `docker`, `psql`, `python3`. 2. **`verify`** — lint, typecheck, 102 testes, build. 3. **`services`** — Postgres e Redis efêmeros: as quatro migrations, idempotência, `--dry-run` sem efeito, shim recusado com `ADAPTER_APPLY_IDENTITY_SHIM=0`, `app_user` sem `BYPASSRLS`, `FORCE RLS`, isolamento entre workspaces, auditoria não alterável. 4. **`image`** — build Linux, conteúdo da imagem, usuário não-root, ausência de `.env`/docs/testes, os oito scripts operacionais transformando e executando, fail-fast com código diferente de zero na web e no worker, liveness com cabeçalhos, compose até `ready`, heartbeat do worker, jornada assíncrona entre processos e desligamento gracioso exigindo `SIGTERM recebido` em menos de 25 s. ## Segurança revisada | Risco | Estado | | --- | --- | | Repositório **público** com Actions | `ci.yml`, `runner-check` e os três jobs têm guarda: PR de fork **não executa** (`head.repo.full_name == github.repository`). Push continua rodando | | Código não confiável com acesso privilegiado | O runner usa Docker-in-Docker rootless, sem o socket do host. A máquina é dedicada e não alcança o Docker de produção | | Deploy automático | `deploy-staging.yml` tem gatilho único `workflow_dispatch`. Nenhum push, tag ou merge dispara deploy | | Aprovação manual de deploy | Os ambientes `hml-migracao`, `hml-deploy` e `hml-flag` são conceito do GitHub. **O Gitea não os implementa da mesma forma**: se o deploy for rodar aqui, as aprovações deixam de existir e isso precisa ser resolvido antes de apontar o workflow ao servidor | | Permissão do token de Actions | `permissions: contents: read` nos dois workflows | | Fila de runs esperando runner | `concurrency` por ref, com cancelamento do anterior | | SSH de produção | **não configurado**. Nenhum segredo de produção foi adicionado | | Branch `main` | **preservada** em `419325d` | | Token de registro do runner | por arquivo com permissão 600, fora de commit e de log | Vale decidir também se este repositório deve seguir público. Auditei os commits e não há segredo algum — só modelos com campos vazios — mas um repositório privado remove de uma vez o risco de PR de fork. ## Pendências **Para o CI rodar:** um runner Linux online com a label `ubuntu-latest`, Docker-in-Docker e saída de rede. Nada além disso. **Para homologação, depois do CI verde:** projeto Supabase de homologação com contas de teste e bucket privado; `DATABASE_URL` e a decisão do `ADAPTER_APPLY_IDENTITY_SHIM`; Redis com senha; DSN do Sentry; DNS, TLS e Nginx de `hml-ada.elopro.app`; smoke real e ensaio de rollback. O procedimento está em [`20-homologacao-runbook.md`](20-homologacao-runbook.md) e o estado item a item em [`21-checklist-validacao.md`](21-checklist-validacao.md).