Skip to content

Commit f1ddf68

Browse files
committed
feat: Build Context a partir de pasta local nos wizards de deploy
Deploy from Dockerfile e Deploy from Compose passam a aceitar uma pasta do computador como origem do Build Context, além do repositório Git — que continua sendo o padrão, por ser versionado e reproduzível. A seleção e o empacotamento acontecem no navegador: um módulo TypeScript próprio monta um tar.gz da árvore selecionada com CompressionStream nativa, sem dependência nova. Empacotar no cliente é o que preserva a estrutura de subpastas — o vaadin-upload descarta o webkitRelativePath, e static/style.css chegaria ao servidor como style.css, colidindo com homônimos de outras pastas. O contexto alcança o Kaniko pela API de exec do Kubernetes, não por objeto persistido: o Job ganha um initContainer que compartilha um emptyDir com o Kaniko e bloqueia numa sentinela até o archive ser injetado. Nem ConfigMap (teto de ~1MiB no etcd) nem PVC são necessários. Exige create em pods/exec no namespace de build. Exclusões compostas em duas camadas: lista embutida sempre exclui, e um .dockerignore na raiz acrescenta — uma negação não repõe o que a lista embutida removeu. O resumo pré-envio mostra as duas procedências separadas antes de qualquer byte sair do navegador. Teto de 50 MB comprimido. Decisões e alternativas descartadas em docs/adr/0020, que revisa a ADR 0007. Programado em par com: Claude IA
1 parent 5c9ab54 commit f1ddf68

28 files changed

Lines changed: 2055 additions & 181 deletions
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# 01 — Seleção e empacotamento do Build Context no navegador
2+
3+
Status: done
4+
5+
Esta é a fundação da sprint: o componente que transforma uma pasta do computador do usuário num artefato único, pronto para virar contexto de build. Nada mais da sprint funciona sem ele, e ele não depende de nenhuma outra entrega.
6+
7+
Hoje não existe caminho nenhum entre um arquivo local e um Build — as duas telas de deploy só aceitam URL de repositório Git. Esta entrega cria um componente de UI reutilizável, no mesmo padrão de módulo TypeScript já usado por `topology-graph.ts` e `code-mirror-editor.ts`, exposto ao lado Java como um componente Vaadin.
8+
9+
O comportamento visível: um botão que abre o seletor nativo de pastas do sistema operacional. O usuário navega até a pasta do projeto e confirma; nenhum caminho é digitado, porque navegador nenhum entrega o conteúdo de um path escrito à mão. Depois da seleção, e **antes** de qualquer envio, o componente mostra um resumo do que vai subir: quantos arquivos entraram, o tamanho total já comprimido, e a lista do que foi excluído com a contagem por entrada. O usuário confirma o que está prestando antes de comprometer-se com o build.
10+
11+
As exclusões desta entrega são a lista embutida — `.git`, `node_modules`, `venv`, `__pycache__`, `target`, `dist` e `build` — aplicadas sempre, antes do empacotamento. São as mesmas pastas que qualquer `.dockerignore` bem escrito exclui, e sem elas um projeto Node.js comum tentaria empacotar centenas de megabytes e travar a aba. A camada do `.dockerignore` do próprio projeto vem na issue 05 e compõe-se com esta; o desenho da lista embutida deve prever essa composição desde já.
12+
13+
O empacotamento produz um `tar.gz` montado no próprio navegador, usando a API de compressão nativa — sem biblioteca nova no `package.json`. Isso é deliberado e não é detalhe de implementação: é o que preserva a estrutura de subpastas. O `vaadin-upload` envia apenas o nome do arquivo e descarta o caminho relativo, então uma transferência arquivo a arquivo faria `static/style.css` chegar ao servidor como `style.css`, colidindo com homônimos de outras pastas. Empacotado, o contexto sobe numa requisição só e já no formato que o Kaniko consome. O raciocínio completo está na ADR 0020.
14+
15+
O contexto é limitado a 50 MB depois de comprimido e de aplicadas as exclusões. Acima disso, o componente recusa com uma mensagem que diz o tamanho encontrado, o limite, e aponta as maiores pastas incluídas — o usuário precisa saber o que pesou, não só que estourou.
16+
17+
No lado servidor, o upload é bufferizado em disco temporário, não em memória. O `ClustersView` usa buffer em memória para kubeconfig, que é um arquivo de poucos KB; segurar dezenas de MB em heap por upload simultâneo é escolha diferente e errada aqui. O arquivo temporário é descartado ao sair da view.
18+
19+
Cobertura de teste: o empacotamento e as regras de exclusão vivem no TypeScript, fora do alcance do Karibu, e serão validados no aceite manual (pasta com `node_modules`, pasta com subpastas aninhadas, pasta acima do teto). O que é testável em Karibu e deve ser coberto: o componente recusar o prosseguimento enquanto nenhuma pasta foi selecionada, e a validação de tamanho refletir-se em estado inválido na view que o hospeda.
20+
21+
Fora de escopo: interpretação do `.dockerignore` (issue 05), e qualquer uso do artefato produzido — as issues 02 a 04 consomem o que esta entrega gera.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 02 — Transporte do Build Context local até o Job Kaniko
2+
3+
Status: done
4+
5+
A contraparte da issue 01 do lado do cluster: fazer o contexto empacotado chegar ao Kaniko. Hoje `RegistryService.startBuild` monta o Job com contexto Git e o próprio Pod clona o repositório — o GreenCap nunca transfere byte nenhum. Esta entrega abre um segundo caminho, mantendo o primeiro intacto.
6+
7+
`BuildRequest` hoje descreve uma origem só, por campos de Git (URL, branch, context path, dockerfile path). Precisa passar a expressar **duas** origens de Build Context sem que os quatro call sites existentes precisem mudar de comportamento — três deles (Registry, Deploy Template e os wizards no estado atual) continuam usando Git. O desenho deve tornar impossível construir um request ambíguo, com Git e pasta local ao mesmo tempo.
8+
9+
O mecanismo de transporte, decidido na ADR 0020: o Job Kaniko ganha um `initContainer` que compartilha um volume efêmero com o container do Kaniko e fica bloqueado esperando um arquivo sentinela aparecer. Com o Pod já em execução, o GreenCap injeta o contexto empacotado nesse initContainer pela API de exec do Kubernetes — o mesmo mecanismo do `kubectl cp`, já exposto pelo Fabric8 — e em seguida grava a sentinela. O initContainer então termina, e o container do Kaniko sobe apontando para o arquivo local em vez de para uma URL Git.
10+
11+
Nem ConfigMap nem PersistentVolumeClaim entram nisso, e a razão importa para quem for mexer depois: ConfigMap tem teto rígido de cerca de 1 MiB no etcd, e um PVC exigiria a mesma API de exec de qualquer forma, somando uma StorageClass e um volume a limpar para guardar um artefato que vive segundos.
12+
13+
O ponto delicado é a coreografia: a injeção só pode acontecer depois que o Pod está em execução, e o build só pode começar depois que a injeção terminou. A espera pelo Pod precisa de um limite de tempo com erro legível — um Pod que nunca agenda (nó sem recursos, imagem não baixável) não pode deixar a tela pendurada indefinidamente. Falha em qualquer etapa deve encerrar o Job em vez de deixá-lo bloqueado no initContainer até o TTL expirar.
14+
15+
Esta entrega traz uma exigência de RBAC nova: criar `pods/exec` no namespace de build. Contas sem essa permissão recebem 403, que o `KubernetesOperationException` já traduz para uma mensagem legível sobre permissão. Não haverá verificação prévia — o projeto não tem esse padrão desde que a ADR 0013 removeu as checagens antecipadas, e introduzi-lo para um caso só seria abstrair antes da segunda ocorrência. O que esta entrega deve garantir é que a falha apareça de forma compreensível e no momento em que ocorre, não como erro genérico de build.
16+
17+
Cobertura de teste: testes de integração do `RegistryService` cobrindo a montagem do Job com origem local (presença do initContainer, do volume compartilhado e dos argumentos do Kaniko apontando para o arquivo em vez de URL Git), a montagem inalterada com origem Git, e a rejeição de um `BuildRequest` malformado. O caminho de exec real depende de um Pod em execução e será exercitado no aceite manual, via issue 03.
18+
19+
Fora de escopo: as telas que acionam este caminho (issues 03 e 04) e o dialog de Build da view de Registry, que continua só com Git — follow-up registrado no backlog.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# 03 — Deploy from Dockerfile aceita pasta local como origem
2+
3+
Status: done
4+
5+
Primeira entrega ponta a ponta da sprint, e a que prova o mecanismo inteiro: selecionar uma pasta no computador, buildar e ver a aplicação rodando no cluster, sem publicar nada num host Git. Consome as issues 01 e 02.
6+
7+
A etapa 1 do wizard (`Source & Name`) passa a oferecer uma escolha de origem entre Repositório Git e Pasta Local, com **Git como padrão** — o fluxo existente não muda para quem já o usa, e a origem Git continua sendo a mais forte das duas por ser versionada e reproduzível.
8+
9+
Ao escolher Pasta Local, os campos da etapa mudam de sentido. `Branch` deixa de existir, porque não há ref a resolver. O `Dockerfile path` continua, agora relativo à raiz da pasta selecionada em vez da raiz do repositório, mantendo o padrão `Dockerfile` quando em branco. O `Context path` continua, ainda designando um subdiretório dentro do contexto — o significado é o mesmo, só a raiz muda. O nome da aplicação, hoje sugerido a partir do nome do repositório, passa a ser sugerido a partir do nome da pasta selecionada, seguindo as mesmas regras de saneamento para nome de Namespace.
10+
11+
Trocar de origem não pode deixar resíduo: alternar entre Git e Pasta Local precisa limpar o estado da origem abandonada, para que não seja possível disparar um build com uma pasta selecionada e uma URL Git preenchida ao mesmo tempo.
12+
13+
A detecção da porta exposta pelo Dockerfile, que hoje acontece ao avançar da etapa 1 buscando o arquivo cru pela HTTP do host Git, precisa de um caminho equivalente para a origem local, lendo o Dockerfile a partir do que foi selecionado. O comportamento visível deve ser o mesmo nas duas origens: a porta sugerida na etapa 2 quando encontrada, e o campo em branco quando não. Falha na detecção nunca bloqueia o avanço — o usuário informa a porta manualmente.
14+
15+
A etapa de Review deve descrever a origem de forma honesta em vez de reaproveitar o texto de Git: nome da pasta, quantidade de arquivos e tamanho do contexto, no lugar de URL e branch. Quem confirma precisa reconhecer o que está prestes a buildar.
16+
17+
A partir da confirmação, nada mais muda: build com log ao vivo, provisionamento de Namespace, Deployment e opcionalmente Service, PVC e Ingress, navegação para a Topologia em caso de sucesso, erro inline sem rollback em caso de falha. Toda a diferença entre as origens termina no momento em que o Job Kaniko começa.
18+
19+
Cobertura de teste: testes Karibu cobrindo a alternância de origem (campos de Git escondidos ao escolher Pasta Local e vice-versa, sem estado residual), a validação da etapa 1 recusando o avanço com Pasta Local escolhida e nenhuma pasta selecionada, e a sugestão do nome da aplicação a partir do nome da pasta. O build real, o exec e a subida da aplicação são aceite manual.
20+
21+
Fora de escopo: o fluxo de Compose (issue 04) e o `.dockerignore` (issue 05), que se aplicará a esta tela automaticamente quando entregue.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 04 — Deploy from Docker Compose aceita pasta local como origem
2+
3+
Status: done
4+
5+
A segunda superfície da sprint, com o mesmo desenho do fluxo de Dockerfile: uma escolha de origem na etapa 1, um botão que abre o seletor nativo de pastas, e o caminho do arquivo continuando como campo de texto. Origem Git permanece o padrão e inalterada.
6+
7+
O campo de caminho do Compose vale nas duas origens, agora relativo à raiz da pasta selecionada em vez da raiz do repositório. É por ele que o usuário aponta um arquivo cujo nome ou localização fujam do padrão — `compose.yaml` em vez de `docker-compose.yml`, ou um arquivo dentro de um subdiretório. O default cobre o caso comum sem ninguém digitar nada.
8+
9+
Com a pasta selecionada, os serviços que buildam resolvem-se todos a partir do mesmo contexto: cada `build:` aponta para um subdiretório dentro da árvore que subiu. Um upload serve todos os serviços que precisam buildar, sem mecanismo adicional — a resolução de caminho já existente, que compõe o diretório do compose com o contexto declarado no serviço, passa a valer sobre a raiz da pasta em vez da raiz do repositório.
10+
11+
Trocar de origem não pode deixar resíduo: alternar entre Git e Pasta Local precisa limpar o estado da origem abandonada, para que não seja possível disparar um deploy com uma pasta selecionada e uma URL Git preenchida ao mesmo tempo.
12+
13+
O restante do fluxo permanece: review com PVCs e imagens editáveis, avisos de diretivas ignoradas, builds sequenciais com log ao vivo, provisionamento e navegação para a Topologia.
14+
15+
**Selecionar o arquivo Compose isolado ficou fora, por decisão tomada durante o aceite manual.** O desenho original oferecia essa segunda forma pensando em quem copia um compose de tutorial que só referencia imagens públicas: não há nada para buildar, e exigir uma pasta inteira para um arquivo de quinze linhas parecia atrito gratuito. Na prática ela custava mais do que economizava — era o único ponto da plataforma onde um Build Context era opcional, e produzia um estado de tela em que serviços com `build:` não tinham como ser buildados, estado que só se resolvia com um aviso acionável e um bloqueio de deploy existindo unicamente por causa dessa forma. Alinhar as duas telas eliminou o estado inteiro. O custo aceito é que quem tem um compose solto numa pasta grande empacota a pasta inteira; o resumo pré-envio e o teto de 50 MB tornam isso visível antes de qualquer envio.
16+
17+
Cobertura de teste: testes Karibu cobrindo a alternância entre Git e Pasta Local sem estado residual, a permanência do campo de caminho na origem local, a recusa de avanço com Pasta Local escolhida e nenhuma pasta selecionada, e a sugestão do namespace a partir do nome da pasta. Um teste de unidade do parsing cobre que um compose lido de conteúdo local produz o mesmo resultado que o mesmo conteúdo vindo de Git, já que o parser antes só era alcançado pelo caminho de busca HTTP. Builds reais são aceite manual.
18+
19+
Fora de escopo: Ingress por serviço no Compose (item já no backlog desde a Sprint 83) e o `.dockerignore` (issue 05).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 05 — `.dockerignore` do projeto compondo com a lista embutida
2+
3+
Status: done
4+
5+
Refinamento da issue 01. Com a lista embutida entregue, o contexto já está protegido do caso que quebra a aba do navegador. Falta respeitar o que o próprio projeto declara: um `.dockerignore` na raiz da pasta selecionada existe justamente para dizer o que não pertence à imagem, e ignorá-lo faria o GreenCap subir arquivos que o `docker build` do usuário nunca subiria.
6+
7+
A ordem de precedência foi decidida e é o coração desta entrega: a lista embutida exclui sempre, e o `.dockerignore` **acrescenta** exclusões por cima. Uma negação no `.dockerignore` — uma linha tentando reincluir `node_modules`, por exemplo — não devolve ao contexto o que a lista embutida removeu. O `.dockerignore` só consegue tirar mais, nunca repor. Isso é assimétrico em relação ao Docker de propósito, e a consequência está registrada na ADR 0020: um projeto que legitimamente precise de uma dessas pastas no contexto não tem como forçá-la.
8+
9+
A sintaxe suportada é um subconjunto pragmático, não a implementação exata do Go usada pelo Docker: linhas de comentário iniciadas por `#`, linhas em branco ignoradas, curingas de um nível e de múltiplos níveis, curinga de caractere único, barra final designando diretório, e negação aplicada na ordem em que as linhas aparecem. O que ficar fora do subconjunto não deve falhar silenciosamente nem abortar o empacotamento — um padrão não compreendido é ignorado, e o resumo mostrado ao usuário deve deixar isso visível em vez de esconder.
10+
11+
O resumo pré-envio da issue 01 passa a distinguir as duas procedências ao listar o que foi excluído: o que saiu pela lista embutida e o que saiu pelo `.dockerignore` do projeto. Sem essa distinção, um usuário que veja um arquivo faltando no contexto não tem como saber se a causa foi uma regra do GreenCap ou uma regra que ele mesmo escreveu.
12+
13+
Quando não há `.dockerignore` na pasta, vale apenas a lista embutida e nada muda em relação à issue 01. O arquivo `.dockerignore` em si, como qualquer `.dockerignore`, não precisa entrar no contexto empacotado.
14+
15+
Cobertura de teste: a interpretação vive no TypeScript, fora do Karibu. Os casos que precisam ser exercitados no aceite manual, com o resumo pré-envio como evidência: pasta sem `.dockerignore` (só a lista embutida atua), pasta com `.dockerignore` excluindo um arquivo que a lista embutida não pega (exclusão adicional aparece), e pasta com `.dockerignore` tentando reincluir `node_modules` (a lista embutida vence e a pasta continua fora).
16+
17+
Fora de escopo: paridade completa com a semântica do Docker, e `.dockerignore` em subdiretórios — o Docker só considera o da raiz do contexto, e esta entrega faz o mesmo.

0 commit comments

Comments
 (0)