From 5a1b285d9a44c7c8a48c148dcc5bd0d98718d1b7 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Wed, 19 Aug 2026 21:52:45 -0300 Subject: [PATCH] =?UTF-8?q?plan(OPS71-INFRA):=20impl=20plan=20=E2=80=94=20?= =?UTF-8?q?runner=20self-hosted=20do=20GitHub=20no=20homeserver=20(Issue?= =?UTF-8?q?=20#113)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...nner-self-hosted-github-homeserver-impl.md | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 docs/plans/ops71-runner-self-hosted-github-homeserver-impl.md diff --git a/docs/plans/ops71-runner-self-hosted-github-homeserver-impl.md b/docs/plans/ops71-runner-self-hosted-github-homeserver-impl.md new file mode 100644 index 00000000..22005327 --- /dev/null +++ b/docs/plans/ops71-runner-self-hosted-github-homeserver-impl.md @@ -0,0 +1,135 @@ +# Impl: Runner self-hosted do GitHub no homeserver (deploy do dispatch manual) + CURSOR_API_KEY + +Status: aguardando aprovação +Atualizado em: 2026-08-19 +Issue: #113 +Intenção: o body da Issue #113 (OPS71-INFRA) é a spec — sem plano de intenção separado +Appetite: horas de operação (não é Issue de código — nada de schema/UI/access) + +## Leitura da intenção + +- **Outcome:** o job `deploy` do `.github/workflows/deploy.yml` (dispatch manual) + roda no **runner self-hosted do GitHub instalado no homeserver** (labels + `self-hosted` + `homeserver`), executando `scripts/deploy-homeserver.sh ` + localmente (sem SSH). O hosted (`verify`) nunca toca o homeserver. +- **O que NÃO negociar:** runner só no `deploy.yml` (nunca no `ci-pr.yml`); + usuário com docker group + leitura de `~/stack/`; outbound-only (funciona + atrás do Cloudflare tunnel); registro do runner em infra-solla `STATE.md`. +- **Já validado na sessão (pre-flight):** + - SSH ao homeserver OK (LAN `192.168.15.142`, user `fsolla`). + - `fsolla` tem `docker` no groups + `sudo`; lê `~/stack/teqo-1313.env` e + `~/stack/.env` (modo 600, dono = fsolla). Arch `x86_64` → tarball linux-x64. + - **Runner ainda NÃO existe** no homeserver (sem `~/actions-runner`, sem unit + systemd do runner; o `runner-watch.*` é outra coisa — WoL/shutdown da + workstation). + - **PAT do OPS71 funciona** (local, `~/.bashrc`): `GET /repos/fsolla/teqo` 200 + e `POST …/actions/runners/registration-token` 201 → admin no repo OK. + - Versão estável atual do runner: **v2.336.0** linux-x64, 226 MB, + sha256 `04cf0be1aff4c3ec3554466c39124ca250e3effd8873bb7e8d68535aa9505d5d` + (digest da API de releases do GitHub). + - **Linger do homeserver já ativo** (`loginctl show-user fsolla` → + `Linger=yes`) → systemd **user** funciona sem sudo (o `runner-watch` já é + user service persistente lá — prova do padrão). + - Workspace `~/teqo-deploy` existe mas ainda aponta origin → Forgejo + (`http://localhost:3000/…`): o `set-url` idempotente do + `deploy-homeserver.sh` re-aponta para `TEQO_REPO_URL` (GitHub) no primeiro + deploy — nada a fazer, mas é o que o log do primeiro deploy deve mostrar. + +## Abordagem recomendada + +Sequência **operacional no homeserver** (nenhum commit no repo teqo — o código +do OPS71 já foi mergeado; o único artefato commitado desta Issue é o registro no +`STATE.md` do infra-solla, repo separado): + +1. **Download** (homeserver, `~/actions-runner`): tarball v2.336.0 linux-x64 da + release oficial, **sha256 conferido** contra o digest da API de releases. +2. **Registration token** (workstation, PAT local): `POST +/repos/fsolla/teqo/actions/runners/registration-token` (TTL 1 h — gerar e + configar na sequência, um único passo encadeado). +3. **config.sh** (homeserver, user fsolla): + `./config.sh --url https://github.com/fsolla/teqo --token --labels +self-hosted,homeserver --name teqo-1313-runner --work _work`. +4. **svc.sh como systemd USER** (`./svc.sh install` sem root → unit + `~/.config/systemd/user/actions.runner.fsolla-teqo-1313-runner.service`) + + `systemctl --user enable --now …` (start). Linger já ativo → persiste sem + login; roda como fsolla (docker + `~/stack` envs). +5. **Verificação do runner**: `systemctl --user status …` ativo; runner listado + em `GET /repos/fsolla/teqo/actions/runners` como online. +6. **Registro em infra-solla** (`~/Code/infra-solla/STATE.md`): entrada na + seção do Teqo + nota na tabela de topologia (runner GitHub no homeserver, + labels, dir, systemd user) — e no runbook `docs/ops/teqo-1313-deploy.md` do + teqo (a tabela "Onde roda cada coisa" já o menciona prospectivamente; só + confirmar o texto com o estado real). Changelog `docs/changelog/2026-08-20-ops71-infra.md` + `pnpm changelog:build`. +7. **Aceite (verificação ao vivo)**: dispatch manual do `deploy.yml` (ref main) + → `verify` full verde → job `deploy` roda no runner do homeserver → log com + o `set-url` do workspace (Forgejo → GitHub) + smoke ok → **segundo dispatch + do mesmo SHA** → guard "already deployed". + +**Decisões:** + +- **systemd user + linger (já ativo) vs. `svc.sh install` via root:** user — sem + senha sudo interativa, mesmo padrão do `runner-watch` (persistência provada no + próprio homeserver); o unit system-level não adicionaria nada (o unit user + roda com `User=fsolla` de qualquer forma). +- **`~/actions-runner` + workdir `_work`:** convenção oficial; `~/teqo-deploy` + permanece como workspace do deploy (o script seta `TEQO_REPO_URL` nele). +- **Sem mudanças em código/workflows:** `runs-on: [self-hosted, homeserver]` + já está no `deploy.yml`; o `deploy-homeserver.sh` já tem o `set-url` + idempotente. O runner vem do GitHub com o token de registro — sem secrets + novos no repo (o repo é público; as envs de produção ficam no homeserver). + +**Rejeitadas:** + +- **`svc.sh install` como root** — exigiria sudo interativo; nada ganho sobre o + unit user (mesmo usuário efetivo, linger já garante auto-start no boot). +- **Rodar o runner em container/docker** — complexidade extra; o padrão oficial + do GitHub é o binário com svc; o runner-watch local prova o user service. +- **Instalar agora o helper `archive-cursor-agent` / criar o secret + `CURSOR_API_KEY` no GitHub** — item secundário da Issue, dormente (pool morto, + OPS65): a key não existe no ambiente; fica **registrado como pendência** no + fechamento (pedir a key ao humano quando quiser reativar o helper), sem ação + agora. + +## Fases verificáveis + +1. Runner baixado + sha256 conferido. +2. Runner configurado (`.runner` + `.credentials` no `~/actions-runner`), + labels corretas, nome `teqo-1313-runner`. +3. Unit systemd user ativo e **persistente** (`systemctl --user is-enabled …`), + runner online na API do GitHub. +4. `STATE.md` (infra-solla) + runbook + changelog registrados. +5. Dispatch manual de validação (verify → deploy → already-deployed no segundo + dispatch) — **aceite final, exige o humano** (dispara no GitHub UI). + +## Rabbit holes / Não escopo (engenharia) + +- Não re-engenharia do `deploy-homeserver.sh` (fluxo compose/migrate intacto). +- Não mexer no stack/registry/env do homeserver. +- Não criar secret `CURSOR_API_KEY` (dormente — só com a key do usuário). +- Não editar o `ci-pr.yml` nem a branch protection. + +## Riscos e mitigação + +- **Token de registro expira em 1 h** → o `config.sh` roda na mesma sequência + do POST (script encadeado da workstation → homeserver). +- **`svc.sh install` gerar unit system em vez de user** (se detectar root): + rodar sem sudo garante o user unit; se algo criar system unit, desfazer com + `svc.sh uninstall` e repetir como user. +- **Runner offline no dispatch** → falha conhecida já documentada no runbook + ("job deploy nunca roda / runner offline"): checar `systemctl --user status`, + o runner-watch local não cuida deste runner (não é o forgejo-runner). +- **verify full ~50 min hosted antes do deploy** → aceite da Issue é um ciclo + completo; nada do homeserver é tocado até `verify` verde. +- **Rollback**: `~/actions-runner/./svc.sh uninstall` + `rm -rf ~/actions-runner` + (desregistra no GitHub); o deploy volta a ficar indisponível até o runner ser + religado — comportamento conhecido, documentado no runbook. + +## Aceite de engenharia + +- [ ] Aceite de produto da intenção coberto (deploy self-hosted no homeserver, + verify hosted nunca toca o homeserver) +- [ ] Runner instalado como fsolla (docker + `~/stack`), systemd user com + linger, outbound-only +- [ ] Registrado em infra-solla `STATE.md` + runbook + changelog +- [ ] Validação ao vivo: dispatch manual → deploy no runner → "already + deployed" no segundo dispatch