From 02238ba21fb9645236bc4fb0b8edc55553b3d9e2 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Tue, 18 Aug 2026 08:25:46 -0300 Subject: [PATCH] =?UTF-8?q?ops(OPS54):=20allowlist=20onlyBuiltDependencies?= =?UTF-8?q?=20migra=20para=20pnpm-workspace.yaml=20=E2=80=94=20fim=20do=20?= =?UTF-8?q?warning=20do=20pnpm=2010=20sobre=20o=20campo=20pnpm=20ignorado?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 4 +- docs/CHANGELOG-AGENTS.md | 2 + docs/changelog/2026-08-18-ops54.md | 1 + ...pnpm-warning-onlybuiltdependencies-impl.md | 70 +++++++++++++++++++ package.json | 7 -- pnpm-workspace.yaml | 4 ++ 6 files changed, 79 insertions(+), 9 deletions(-) create mode 100644 docs/changelog/2026-08-18-ops54.md create mode 100644 docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md create mode 100644 pnpm-workspace.yaml diff --git a/Dockerfile b/Dockerfile index a0da5256..43175c74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,8 @@ RUN apk add --no-cache libc6-compat && corepack enable FROM base AS deps WORKDIR /app -# Install the pinned package manager and dependency graph. -COPY package.json pnpm-lock.yaml ./ +# pnpm-workspace.yaml carries onlyBuiltDependencies — pnpm 10 no longer reads the "pnpm" field. +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ RUN pnpm install --frozen-lockfile # One-shot image used to apply committed Payload migrations before a release. diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index f9cfe43d..4c6538da 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -12,6 +12,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente **Recently resolved (2026-08-18):** **OPS52** — a media saiu do Vercel Blob para o Garage S3 do homeserver (bucket `teqo-media` + key `teqo-key`, já existentes no Garage v2.1.0). O plugin `@payloadcms/storage-vercel-blob` (e o `@vercel/blob` do seed) foi trocado por `@payloadcms/storage-s3` na config, no padrão R2 da doc do Payload (`endpoint` + `forcePathStyle: true`, region `garage`). **Decisão de arquitetura (revisão A→B durante a execução, gate com o humano):** o Garage v2.1.0 **não suporta acesso anônimo na API S3** (sem `--public-read`; source: "when we support anonymous access in the future"; testado: `anonymous`/`anon` → `NoSuchAccessKey`), então o bucket fica **privado** e a app **serve a media por proxy** — sem `disablePayloadAccessControl`, o `staticHandler` do plugin streama o objeto do Garage em `/api/media/file/` (range + etag/304 nativos) — o **mesmo contrato de URL relativa que as 40 rows pré-existentes já têm** (a recuperação dos arquivos é a Issue #10/OPS52-media). **Fail-closed:** `resolveS3StorageEnv` (`src/utilities/mediaStorage.ts`, unit-testado) — envs `S3_*` parciais → throw no boot com a lista exata de faltantes; nenhuma → storage local (dev/test). Envs novas: `S3_BUCKET`, `S3_ENDPOINT`, `S3_REGION` (default `garage`), `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY` (`.env.example`, `.env.test`, worktree.mjs com cópia all-or-nothing). Seed de posts: pre-delete do Blob removido (o S3 sobrescreve o key determinístico nativamente — idempotência intacta). Prova de contrato real: PUT/GET/DELETE via SDK contra o Garage (tailnet `http://100.119.220.31:3900`), depois media criada pelo Payload local com S3 ativo → `media.url` relativa + `GET /api/media/file/...` → 200 image/png; bucket devolvido a 0 objects e `.env.local` do worktree limpo (sem credenciais de prod). Sem migration (config-only; importMap regenerado sem `VercelBlobClientUploadHandler`). Runbook de cutover registrado no impl plan: setar as 5 `S3_*` no `~/stack/teqo-1313.env` (endpoint tailnet) antes do próximo deploy (OPS53). Repo: `docs/plans/ops52-blob-vercel-para-garage-impl.md`; infra-solla `STATE.md` atualizado à parte. +**Recently resolved (2026-08-18):** **OPS54** — fim do warning do pnpm sobre o campo `pnpm` ignorado em `package.json`. A allowlist `onlyBuiltDependencies` (`sharp`, `esbuild`, `unrs-resolver`) migrou para o home canônico do pnpm 10: novo `pnpm-workspace.yaml` na raiz (sem `packages` — repo single-package, default `['.']`), e o bloco `"pnpm"` saiu do `package.json` (pnpm 10 não o lê mais; sem a remoção o warning persiste). `Dockerfile` estágio `deps` agora copia `pnpm-workspace.yaml` junto (`COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./`) — sem isso a allowlist não chegaria ao install da imagem e local×Docker divergiriam nos build scripts. Validado com a versão pinada (10.11.0) e no estágio deps real (`docker build --target deps`): nenhum `[WARN]` do campo `pnpm` em install/CI/Docker, `pnpm config get onlyBuiltDependencies` → os 3 pacotes, `--frozen-lockfile` limpo (lockfile e grafo intocados). Guardrails respeitados: `.npmrc` (`legacy-peer-deps`, `include`) e `packageManager` intocados; nada além do que o warning citava foi migrado (o aviso informativo de `msw`/`onnxruntime-node` é comportamento padrão do pnpm 10 — fora de escopo por anti-goal). Sem migration, sem UI, sem testes de domínio (config de ferramenta; gates completos verdes). Repo: `docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md`. + **Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte. **Recently resolved (2026-08-17):** **OPS53** — o deploy do site 1313 virou etapa do CI: mergir em `main` com a suite verde == site atualizado em `jorgesolla1313.com.br`, sem passo manual. O job `deploy` em `.forgejo/workflows/ci.yml` roda no host do runner (workstation, `runs-on: host`) e streama `scripts/deploy-homeserver.sh` para o homeserver via SSH (`ssh homeserver "bash -s -- "`); o script remoto (bash puro, homeserver sem dependências novas) faz: guard de HEAD (`git ls-remote` de `main` == SHA do job, senão skip "stale"), `flock` de serialização, workspace `~/teqo-deploy` (clone do Forgejo local), `docker login localhost:5000` (REGISTRY_USER/PASSWORD de `~/stack/.env`), build dos stages `runner`+`migrator` no homeserver (`--network stack_default` para a geração estática alcançar o `postgres:5432` do compose; secrets de `~/stack/teqo-1313.env` via BuildKit, sem camadas/logs), push no registry local, swap dos tags de imagem no compose (com backup `docker-compose.yml.pre-`), **migrate pelo serviço de maintenance `teqo-1313-migrate` antes do rollout**, `docker compose up -d`, espera do healthcheck e smoke (`/`, `/campanha/login`, `/admin`, barreira 307, WebAuthn `login-options` com body JSON, `POST /api/revalidate` com o secret real — rehearsal validado contra o site vivo). Falha pós-swap → rollback automático best-effort (restore do backup + `up -d` da imagem anterior) + job vermelho. **Divergência deliberada da intenção (fato novo):** a intenção assumia o runner no homeserver; verificado (`infra-solla/STATE.md` + ao vivo) que o runner é a `workstation-runner` na workstation e o stack de produção vive no homeserver — a abordagem A foi corrigida para "job `runs-on: host` na workstation + todo o deploy no homeserver via SSH", o que manteve **zero secrets novos no Forgejo** e zero mudança de infra no compose. Nenhum secret no repo; envs seguem chmod-600 no homeserver. Runbook (rollback manual, falhas conhecidas, decisão de segurança do host label): `docs/ops/teqo-1313-deploy.md` (novo). Guarda estrutural nova: `tests/unit/deployScript.unit.spec.ts` (bash -n + invariantes do pipeline: HEAD guard, BuildKit secrets, ordem migrate→rollout, smoke). AGENTS.md/AGENT-OPS.md atualizados. Primeiro deploy real = o merge deste próprio item (tracer bullet); infra-solla `STATE.md` anotado à parte. diff --git a/docs/changelog/2026-08-18-ops54.md b/docs/changelog/2026-08-18-ops54.md new file mode 100644 index 00000000..9f447fb6 --- /dev/null +++ b/docs/changelog/2026-08-18-ops54.md @@ -0,0 +1 @@ +**Recently resolved (2026-08-18):** **OPS54** — fim do warning do pnpm sobre o campo `pnpm` ignorado em `package.json`. A allowlist `onlyBuiltDependencies` (`sharp`, `esbuild`, `unrs-resolver`) migrou para o home canônico do pnpm 10: novo `pnpm-workspace.yaml` na raiz (sem `packages` — repo single-package, default `['.']`), e o bloco `"pnpm"` saiu do `package.json` (pnpm 10 não o lê mais; sem a remoção o warning persiste). `Dockerfile` estágio `deps` agora copia `pnpm-workspace.yaml` junto (`COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./`) — sem isso a allowlist não chegaria ao install da imagem e local×Docker divergiriam nos build scripts. Validado com a versão pinada (10.11.0) e no estágio deps real (`docker build --target deps`): nenhum `[WARN]` do campo `pnpm` em install/CI/Docker, `pnpm config get onlyBuiltDependencies` → os 3 pacotes, `--frozen-lockfile` limpo (lockfile e grafo intocados). Guardrails respeitados: `.npmrc` (`legacy-peer-deps`, `include`) e `packageManager` intocados; nada além do que o warning citava foi migrado (o aviso informativo de `msw`/`onnxruntime-node` é comportamento padrão do pnpm 10 — fora de escopo por anti-goal). Sem migration, sem UI, sem testes de domínio (config de ferramenta; gates completos verdes). Repo: `docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md`. diff --git a/docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md b/docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md new file mode 100644 index 00000000..d7e6aa48 --- /dev/null +++ b/docs/plans/ops54-pnpm-warning-onlybuiltdependencies-impl.md @@ -0,0 +1,70 @@ +# Impl: Acabar com o warning do pnpm sobre `pnpm.onlyBuiltDependencies` ignorado + +Status: aprovado +Atualizado em: 2026-08-18 +Issue: #12 +Intenção: docs/plans/ops54-pnpm-warning-onlybuiltdependencies.md +Appetite restante: herdado (~2–4 h; executável em bem menos — mudança de 3 arquivos) + +## Leitura da intenção + +- **Outcome:** nenhum comando pnpm (terminal, CI, Dockerfile) imprime o `[WARN]` do campo `pnpm`; a allowlist `onlyBuiltDependencies` (`sharp`, `esbuild`, `unrs-resolver`) volta a ser honrada no lugar canônico que o pnpm 10 aponta. +- **O que NÃO negociar:** `.npmrc` intocado (`legacy-peer-deps`, `include`); `packageManager` intocado; grafo de dependências inalterado (`pnpm install --frozen-lockfile` continua passando); nada além do que o warning cita é migrado. +- **O que reavaliar:** a hipótese da intenção já é a abordagem correta (pnpm-workspace.yaml). Ponto novo verificado empiricamente com a versão pinada (10.11.0): `pnpm-workspace.yaml` **sem** o campo `packages` funciona (single-package repo vira workspace de raiz, default `['.']`); o lockfile existente só tem um importer raiz — consistente. + +## Abordagem recomendada + +```mermaid +flowchart LR + A[package.json\nremove campo pnpm] --> C[pnpm-workspace.yaml\nonlyBuiltDependencies] + B[Dockerfile deps\n+ COPY pnpm-workspace.yaml] --> D[pnpm install --frozen-lockfile\nsem warning, allowlist honrada] + C --> D +``` + +**Opções consideradas:** A (pnpm-workspace.yaml — recomendado) | B (.npmrc) | C (manter campo + ignorar) +**Recomendação:** A — é o novo home canônico de settings do pnpm 10 e o destino que o próprio warning aponta; validado empiricamente com a versão pinada (10.11.0): sem warning, `pnpm config get onlyBuiltDependencies` → `sharp,esbuild,unrs-resolver`, `--frozen-lockfile` limpo. +**Rejeitadas:** + +- **B (.npmrc):** ainda suportado, mas caminho legado para este tipo de setting; o guardrail da intenção proíbe tocar `.npmrc` (e não há warning pedindo isso). +- **C (ignorar):** falha o aceite (warning persiste + allowlist continua ignorada). +- **(implicita) manter o campo `pnpm`:** o warning só some com a remoção do campo — a config no novo lugar não silencia o aviso. + +### Componentes / mudanças + +- **`pnpm-workspace.yaml`** (novo, raiz): `onlyBuiltDependencies: [sharp, esbuild, unrs-resolver]`. Sem campo `packages` (default `['.']` em pnpm 10.7+; repo single-package — validado). Sem comentários. +- **`package.json`**: remover o bloco `"pnpm": { "onlyBuiltDependencies": [...] }` (linhas 186–192). Nada mais no arquivo muda. +- **`Dockerfile`** (estágio `deps`, linha atual `COPY package.json pnpm-lock.yaml ./`): passar a copiar também `pnpm-workspace.yaml` — o `pnpm install --frozen-lockfile` da imagem precisa da config para honrar a allowlist (sem ela, local e Docker divergiriam: local rodaria os builds de `sharp`/`esbuild`/`unrs-resolver`, Docker não). Estágios `builder`/`migrator` usam `COPY . .` — o `.dockerignore` não exclui o YAML, já cobertos. +- **Changelog:** `docs/changelog/2026-08-18-ops54.md` + `pnpm changelog:build` (OPS44). +- **Migration:** sem migration (config de ferramenta). +- **Access / Consent / UI:** N/A. + +### Dados → forma (se aplicável) + +N/A — sem métricas/UI. + +## Fases verificáveis + +1. **Config + Dockerfile** — criar `pnpm-workspace.yaml`, remover campo `pnpm`, ajustar COPY do `deps`. +2. **Verificação local** — `pnpm install --frozen-lockfile` (sem warning, sem diff no lockfile — `git status` limpo depois); `pnpm config get onlyBuiltDependencies` → 3 pacotes; rodada de gates: `typecheck`, `lint`, `format:check`, `knip`, `check:cycles`, `test:unit`. +3. **Docker** — `docker build --target deps .` (não precisa de secrets) provando o estágio deps com a config nova. +4. **Gates** — `pnpm gate:fast`; push via `pnpm push`; CI Forgejo verde (o CI roda `pnpm install --frozen-lockfile` em todos os jobs — é a prova final do warning sumindo). + +## Rabbit holes / Não escopo (engenharia) + +- Mover `legacy-peer-deps` / `include` do `.npmrc` para o YAML — fora de escopo (nenhum warning pede). +- `dangerouslyAllowAllBuilds` ou re-approve de outros pacotes — fora de escopo. +- Atualizar pnpm / `packageManager` — fora de escopo. +- Comentários no YAML — não adicionar (convenção do repo); o plano + changelog documentam o porquê. + +## Riscos e mitigação + +- **`pnpm-workspace.yaml` sem `packages` muda semântica de workspace?** Baixo: pnpm 10.7+ defaulta `packages: ['.']`; lockfile atual tem um único importer raiz; validado no sandbox com a versão pinada (install + frozen-lockfile limpos). Se `pnpm install` local apontar drift, o passo 2 pega na hora. +- **Docker build sem a config no `deps`** silenciosamente bloquearia os builds dos 3 pacotes na imagem (regressão de comportamento local×Docker): mitigado pelo COPY explícito + verificação com `docker build --target deps`. +- **OPS53 compartilha o Dockerfile:** já resolvido — OPS53 está merged em `main` (6a3f61f9); sem concorrência em andamento. +- **CI com cache pnpm (pnpm/action-setup):** o cache é por lockfile — inalterado — então `cache: pnpm` segue batendo. Sem risco. + +## Aceite de engenharia + +- [x] Aceite de produto da intenção ainda coberto (warning some; allowlist honrada; `.npmrc`/`packageManager`/grafo intactos) +- [x] Invariantes AGENTS/engineering-standards (sem código runtime tocado; sem migration; sem DB) +- [x] Testes de domínio previstos: N/A (sem access/write paths); verificação empírica local + estágio Docker deps + CI como prova diff --git a/package.json b/package.json index 73504084..7e9eb300 100644 --- a/package.json +++ b/package.json @@ -183,13 +183,6 @@ "node": "24.x", "pnpm": "^9 || ^10" }, - "pnpm": { - "onlyBuiltDependencies": [ - "sharp", - "esbuild", - "unrs-resolver" - ] - }, "packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977", "private": true } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 00000000..a07f6284 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +onlyBuiltDependencies: + - sharp + - esbuild + - unrs-resolver