Repository navigation
Ambiente de desenvolvimento por slot (LUI-184) - #18
Closed
argentinaluiz wants to merge 2 commits into
Closed
argentinaluiz wants to merge 2 commits into
argentinaluiz wants to merge 2 commits into
Conversation
Acceptance file docs/tickets/LUI-182/LUI-184/aceite.md (CA1-CA6). No route, contract or schema changes: the ticket changes the development Compose file, harness scripts and documents. The names of the slot variables and the port formula, which this ticket owns, are written in docs/lld.md, section 6, "Ambiente local por slot".
Decision: the slot reaches the Compose file only through a root .env that Compose reads by itself, with the same file and command. The .env carries the four ports already computed (WEB_PORT, API_PORT, STORAGE_PORT, MAILPIT_PORT), because Compose interpolation cannot do arithmetic; the formula (today's port plus slot x 10) lives in the LLD and in whoever writes the file. SLOT is in the file only to be read by the front script (LUI-186), and Compose ignores it. Every port has today's value as default, so no .env means slot 0.
Decision: the three addresses the API and the browser need (WEB_ORIGIN, STORAGE_PUBLIC_URL, STORAGE_EMULATOR_HOST) are passed through without a default (an environment entry with no value) to the api service, and STORAGE_PUBLIC_URL also to the playwright service. A default in the Compose file would make the variables exist at slot 0 and the resolved configuration would stop being the one of today (CA2); passed through, they are absent without the .env, and the API keeps using apps/api/.env. The process environment wins over apps/api/.env, so a slot value overrides the file. Verified with docker compose config: a pass-through variable reads from the .env and is null when the .env lacks it.
Decision: the storage emulator listens inside its container on the slot port (-port and -public-host both from STORAGE_PORT, and the healthcheck too), as the spec says, so the browser rule of the tests (resolve localhost to the storage service, keeping the port) stays valid. The consequence is that the storage address between containers also follows the slot (STORAGE_EMULATOR_HOST, http://storage:4453 at slot 1). It is the only inter-container address that changes; web, API, mailpit and database addresses stay.
Decision: the .git mask of the api container uses the long volume syntax with type and source from variables: type ${GIT_MASK_TYPE:-tmpfs} and source ${GIT_MASK_SOURCE:-}. Without the .env it resolves to the same read-only tmpfs of today; the worktree .env sets GIT_MASK_TYPE=bind and GIT_MASK_SOURCE=/dev/null, which covers the .git file with an empty one. Verified: Compose accepts a variable type and an empty source for tmpfs, and in a real worktree /dev/null bound over .git is an empty character device while the tmpfs does not mount over a file.
Decision: the secret scan in a worktree sets GIT_DIR (the absolute git dir of the worktree) and GIT_WORK_TREE=/repo and mounts the common .git read-only at the same absolute path, with --mount type=bind,...,readonly. This corrects a premise of the spec: the .git file of a worktree can hold a relative path (worktree.useRelativePaths is on in the maintainer's Git config), and for a sibling worktree the relative path resolved from /repo points nowhere, so mounting the common dir alone is not enough (verified: gitleaks reads 0 commits and errors; with GIT_DIR it reads the commit and finds a planted key). Git picks the dir from GIT_DIR, so it works whatever the .git file says. The short -v src:dst:ro form of the same mount failed in this Docker Desktop, the --mount form works. Outside a worktree (.git is a directory) the command is the one of today.
Decision: the argument building of the scan goes in a new scripts/git-mount.sh, sourced by scripts/verify.sh as full-lock.sh is, so a shell test can check the arguments for a repository and for a worktree without Docker. The scan itself (CA5) stays a human review with evidence in the PR.
Decision: the tests of CA1 to CA3 are shell tests in scripts/test/dev-env-slot.test.sh, in the report with the @LUI-184-CA<n> tag. They resolve the configuration with docker compose --env-file <file> config --format json and always pass the file, so a .env in the checkout of whoever runs them never changes the result; no container starts. CA2 checks today's values (ports, storage entrypoint, healthcheck, tmpfs mask, no slot variable in the api and playwright), and at implementation the configuration resolved without a .env is also compared once with the one of this plan commit's Compose file. CA3 checks the mask in real Docker, with the postgres image the environment already pulled: the same read-only bind of the folder plus the mask the resolved configuration declares, over a .git directory and over a .git file, plus the default mask over a file as a control that fails to start.
Decision: the other suites follow the slot, so the full suite passes at slot 1 (CA4). The API end-to-end test of the password recovery reads the web origin from the injected app configuration instead of the literal http://localhost:3000, and the two web end-to-end tests that expect the storage host localhost:4443 (the emulator proof and the download one) read it from STORAGE_PUBLIC_URL, with today's value as default. The mail links need no change: the web tests keep only the path of a link.
Decision: CA4 and CA5 are human review, as the ticket says, with the commands and outputs in the PR. There is no front script yet, so the worktree and its .env are made by hand, following the LLD table. Each environment weighs about 2 GB of memory at rest.
Files and interfaces the implementation will touch:
- compose.dev.yaml: ports of web, api, storage and mailpit from WEB_PORT, API_PORT, STORAGE_PORT and MAILPIT_PORT; storage entrypoint and healthcheck from STORAGE_PORT; pass-through of WEB_ORIGIN, STORAGE_PUBLIC_URL and STORAGE_EMULATOR_HOST to api and of STORAGE_PUBLIC_URL to playwright; the .git mask from GIT_MASK_TYPE and GIT_MASK_SOURCE.
- .gitignore: the root .env.
- scripts/git-mount.sh (new): a function that prints the docker run arguments the secret scan needs for the current checkout, nothing extra for a .git directory, the common .git mount, GIT_DIR and GIT_WORK_TREE for a .git file.
- scripts/verify.sh: secret_scan builds its docker run from that function.
- scripts/test/dev-env-slot.test.sh (new): the tests of CA1 to CA3.
- scripts/test/git-mount.test.sh (new): the arguments for a repository and for a worktree, without tag.
- apps/api/test/auth-password-recovery.e2e-spec.ts: the web origin from the injected configuration.
- apps/web/e2e/storage-emulator.spec.ts, apps/web/e2e/download.spec.ts and apps/web/e2e/support/storage.ts: the storage public host from STORAGE_PUBLIC_URL, default http://localhost:4443.
- apps/api/.env.example: a comment saying that in a slot the Compose file overrides WEB_ORIGIN, STORAGE_PUBLIC_URL and STORAGE_EMULATOR_HOST.
- docs/lld.md: the section of the storage emulator and the variable table point to the slot (the slot contract of section 6 is already in the plan).
- AGENTS.md (root), apps/api/AGENTS.md, apps/web/AGENTS.md and README.md: the address table of the root is the one of slot 0, and the others point to the slot address where they cite localhost with a fixed port.
Existing tests whose behavior changes: the password recovery end-to-end test of the API (origin from the configuration), and the emulator and download tests of the web (host from the variable); all keep their result at slot 0.
Data changes: none.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ANcDtWf5vRMzv1r42Ax9ce
A root .env sets the slot: the published ports of web, API, storage and mailpit, the storage emulator port and public host, and the web origin and public storage address that the API reads. Without the .env the resolved configuration is the one of before. The .git mask of the API container takes its type and source from the .env, so a worktree, where .git is a file, is covered by /dev/null instead of a tmpfs. The secret scan mounts the common .git and sets GIT_DIR in a worktree (scripts/git-mount.sh). The test of the mask uses the storage image, not the postgres one the plan names: it is the smallest of the environment and has sh and grep. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ANcDtWf5vRMzv1r42Ax9ce
Contributor
Author
Revisão de conformidade: LUI-184
Relatório do revisor, na íntegraAchados
O que não consegui conferir
|
There was a problem hiding this comment.
🟡 Changes recommended
CA3’s acceptance criterion and test implementation disagree about using postgres versus storage.
1 open issue
What changed in this PR
Adds slot-based development environments, worktree-aware secret scanning, and updates to tests and documentation.
Changes:
- Parameterizes Compose ports and service URLs by slot.
- Supports Git mounts for worktrees.
- Updates E2E tests, documentation, and environment examples.
| File | Summary |
|---|---|
scripts/verify.sh |
Mounts shared Git metadata during secret scanning. |
scripts/test/git-mount.test.sh |
Tests Git mounting for worktrees. |
scripts/test/dev-env-slot.test.sh |
Tests slot configuration; CA3 uses storage while its criterion specifies postgres. |
scripts/git-mount.sh |
Provides Docker arguments for worktree Git mounts. |
README.md |
Documents default slot endpoints. |
docs/tickets/LUI-182/LUI-184/aceite.md |
Defines acceptance criteria; CA3 references postgres instead of the implemented storage image. |
docs/lld.md |
Documents the slot environment contract. |
compose.dev.yaml |
Configures slot-based ports, storage, and Git masking. |
apps/web/playwright.config.ts |
Supports variable storage ports. |
apps/web/e2e/support/storage.ts |
Resolves the slot-specific storage host. |
apps/web/e2e/storage-emulator.spec.ts |
Adapts storage tests to slots. |
apps/web/e2e/download.spec.ts |
Adapts download tests to slots. |
apps/web/AGENTS.md |
Documents slot-aware web testing. |
apps/api/test/auth-password-recovery.e2e-spec.ts |
Uses configurable web origins in email links. |
apps/api/AGENTS.md |
Documents the API port. |
apps/api/.env.example |
Documents environment-variable precedence. |
AGENTS.md |
Documents the slot-based development environment. |
.gitignore |
Ignores the root .env. |
Review effort: Lite
|
|
||
| **Marca:** teste de unidade | ||
|
|
||
| **Cenário:** Dado um repositório temporário com o `.git` como pasta e um worktree temporário com o `.git` como arquivo, quando o contêiner sobe com a máscara que a configuração resolvida declara para o contêiner da API (a do `.env` de um worktree para o arquivo, a padrão para a pasta), então o `.git` visto dentro do contêiner fica vazio nos dois casos, sem o histórico, e o contêiner sobe. Como controle, a máscara padrão num `.git` que é arquivo não sobe, que é o defeito que o ticket corrige. O contêiner do teste é o do `postgres`, que o ambiente já baixou, com o mesmo `.:/repo` somente leitura do contêiner da API. |
Contributor
Author
|
Fechado: os PRs agora são publicados no repositório argentinaluiz/spec-pullrequest-ia. Este PR continua lá em argentinaluiz/spec-pullrequest-ia#22, com o mesmo corpo e o mesmo comentário da revisão de conformidade. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Aceite
docs/tickets/LUI-182/LUI-184/aceite.md
O que muda
O ambiente de desenvolvimento passa a seguir um slot (0 a 3), lido de um
.envna raiz do checkout, que o Git ignora. O contrato do.env(variáveis, fórmula das portas e máscara do.git) está na LLD, seção 6, "Ambiente local por slot". Sem o.env, a configuração resolvida é a de antes.compose.dev.yaml: portas da web, da API, do storage e dos e-mails por variável; o storage escuta e se anuncia na porta do slot; a API e oplaywrightrecebem os endereços do slot por variável sem valor; a máscara do.gitda API por tipo e origem (tmpfspor padrão,/dev/nullnum worktree).scripts/git-mount.sh(novo) escripts/verify.sh: a varredura de segredos monta o.gitcomum e aponta oGIT_DIRnum worktree.scripts/test/dev-env-slot.test.sh(CA1 a CA3) escripts/test/git-mount.test.sh(novos).localhost:3000elocalhost:4443.AGENTS.mdda raiz, da API e da web, README e.env.example.Desvios do plano
.gitde um worktree pode trazer caminho relativo (worktree.useRelativePaths), e a spec supunha absoluto. A varredura usaGIT_DIReGIT_WORK_TREE, como o plano já decidiu depois de testar.storage(84 MB), e não a dopostgres(645 MB) que o plano cita: é a menor do ambiente, oupjá a baixa, e ela temshegrep. Veio da/simplify(achado 3 da revisão de conformidade).STORAGE_EMULATOR_HOST), consequência de o emulador escutar na porta do slot.Harness
O PR altera
compose.dev.yaml,scripts/eAGENTS.md, então a Guarda do harness vai reprová-lo e ele só entra por bypass do humano.Evidência do CA4 e do CA5 (revisão humana)
Worktree irmão
techweekia16-live-slot1, com o.gitde caminho relativo (gitdir: ../techweekia16-live/.git/worktrees/...) e o.envdo slot 1 feito à mão, como no contrato da LLD (seção 6, "Ambiente local por slot").CA4
healthy.scripts/verify.sh fullaprovou nos dois (96 critérios com teste cobertos, entre eles LUI-184-CA1 a CA3).http://localhost:3010/verificar-email?token=..., lido na tela do slot 1 (porta 8035). O mesmo destinatário tem 0 e-mails no Mailpit do slot 0.uploadUrledownload-urlcom o hosthttp://localhost:4453; o PUT respondeu 200, a conclusão 200 e o GET da URL de download 200, com o conteúdoola!!.docker compose downno slot 1: as portas 3010, 3011, 4453 e 8035 deixaram de responder, e o slot 0 seguiu de pé (web 307, API respondendo, Mailpit 200, todoshealthy).CA5
fullpassou (1 commit lido). Num worktree irmão com uma chave plantada num commit, oscripts/verify.sh prepushsaiu com 1 e acusou 1 vazamento. Antes da mudança, o mesmo passo lia 0 commits e dava erro (not a git repository: /repo/../techweekia16-live/.git/worktrees/...)./repo/.gitcom 0 bytes e semgitdir; variáveisWEB_ORIGIN,STORAGE_PUBLIC_URLeSTORAGE_EMULATOR_HOSTdo slot.🤖 Generated with Claude Code
https://claude.ai/code/session_01ANcDtWf5vRMzv1r42Ax9ce