Skip to content

Commit 369b0ea

Browse files
authored
merge: staging → main — Release v0.7.8 (Sprint 106)
Release v0.7.8. Sprint 106: imagem da plataforma em registry público (GHCR) — setup.sh puxa via minikube image load com fallback de build local. Programado em par com: Claude IA
2 parents dbed7c4 + e12c38e commit 369b0ea

15 files changed

Lines changed: 237 additions & 43 deletions
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Publish Image
2+
3+
# Publishes the platform image to GHCR on every release tag. setup.sh pulls this
4+
# image by default (see ADR 0019), turning the heavy local build into a fast pull.
5+
#
6+
# NOTE: the ghcr.io/greencapk8s/platform package must be made public once, manually,
7+
# after the first publish — GHCR creates packages as private by default. Public
8+
# visibility lets setup.sh pull with no imagePullSecret.
9+
10+
on:
11+
push:
12+
tags:
13+
- "v*"
14+
# workflow_dispatch is for re-publishing a release whose original run failed.
15+
# Run it against a tag ref (not a branch) — otherwise no image tag is derived and
16+
# the build fails cleanly instead of publishing a stray :latest from a branch.
17+
workflow_dispatch:
18+
19+
jobs:
20+
publish:
21+
# Fixed version, not ubuntu-latest — that tag moves to a newer image over time
22+
# without notice, risking non-reproducible CI runs
23+
runs-on: ubuntu-24.04
24+
timeout-minutes: 30
25+
permissions:
26+
contents: read
27+
packages: write
28+
steps:
29+
- uses: actions/checkout@v6
30+
31+
- name: Set up Docker Buildx
32+
uses: docker/setup-buildx-action@v3
33+
34+
- name: Log in to GHCR
35+
uses: docker/login-action@v3
36+
with:
37+
registry: ghcr.io
38+
username: ${{ github.actor }}
39+
password: ${{ secrets.GITHUB_TOKEN }}
40+
41+
- name: Derive image tags
42+
id: meta
43+
uses: docker/metadata-action@v5
44+
with:
45+
images: ghcr.io/greencapk8s/platform
46+
# latest is added explicitly below; disable metadata-action's auto-latest
47+
flavor: latest=false
48+
# {{version}} strips the leading v (v0.7.8 -> 0.7.8). latest is applied
49+
# only to stable release tags — never a pre-release (v0.7.8-rc.1 contains
50+
# a '-'), so an RC never hijacks the latest that end users pull.
51+
tags: |
52+
type=semver,pattern={{version}}
53+
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, '-') }}
54+
55+
- name: Build and push (linux/amd64)
56+
uses: docker/build-push-action@v6
57+
with:
58+
context: .
59+
file: docker/Dockerfile
60+
# amd64 only — arm64 would need QEMU emulation on the runner, slowing every
61+
# release to serve the macOS/Apple Silicon minority (setup.sh builds arm64
62+
# locally instead — see ADR 0019)
63+
platforms: linux/amd64
64+
push: true
65+
tags: ${{ steps.meta.outputs.tags }}
66+
labels: ${{ steps.meta.outputs.labels }}

.github/workflows/setup-script-validate.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ jobs:
4848
AUTO_INSTALL: "y"
4949
PROFILE_CHOICE: "1"
5050
INSTALL_ONLY: ${{ matrix.install_only }}
51+
# Force the local build path: setup.sh now defaults to pulling the published
52+
# image, but this workflow exists to validate that docker/Dockerfile and the
53+
# source still build. On PRs (pre-release) no image is published yet anyway.
54+
BUILD_LOCAL: "true"
5155
steps:
5256
- uses: actions/checkout@v6
5357

File renamed without changes.

.scratch/sprint-96/issues/02-migrar-execute-unico.md renamed to .scratch/archive/sprint-96/issues/02-migrar-execute-unico.md

File renamed without changes.

.scratch/sprint-96/issues/03-migrar-polling-recorrente.md renamed to .scratch/archive/sprint-96/issues/03-migrar-polling-recorrente.md

File renamed without changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# 01 — Workflow de publicação da imagem da plataforma no GHCR
2+
3+
Status: done
4+
5+
Hoje nenhum pipeline publica a imagem da plataforma — ela só existe quando alguém roda `docker build` localmente (no `setup.sh` ou no `docker compose`). Esta entrega cria o lado **produtor**: um workflow do GitHub Actions que constrói a imagem da plataforma e a publica em `ghcr.io/greencapk8s/platform` a cada release, deixando-a pronta para o `setup.sh` puxar (issue 02).
6+
7+
Gatilho do workflow: push de git tag `v*` — a mesma tag que já é a fonte de verdade da versão em `main` (ver `build.gradle.kts``lastGitTag`), garantindo uma imagem por release e o versionamento da imagem 1:1 com o do binário. Além disso, `workflow_dispatch` para permitir republicar manualmente uma tag caso a release falhe no meio, sem custo e como rede de segurança.
8+
9+
A imagem é construída a partir de `docker/Dockerfile` para a plataforma **`linux/amd64`** apenas — publicar arm64 exigiria emulação QEMU no runner, encarecendo cada release para beneficiar a minoria macOS/Apple Silicon (decisão registrada na ADR 0019; o `setup.sh` cobre arm64 com build local). O login no GHCR usa o `GITHUB_TOKEN` do próprio Actions com permissão `packages: write` — sem cadastrar secret externo.
10+
11+
Tags aplicadas à imagem publicada, derivadas do nome da git tag (`v0.7.8``0.7.8`): a versão exata `X.Y.Z` sempre, e `latest` **apenas** quando a tag é um release estável (`vX.Y.Z`), nunca um pré-release (`vX.Y.Z-rc.N`) — assim um RC tagueado não sequestra o `latest` que os usuários finais puxam. A tag `X.Y.Z` é o ponto imutável e reproduzível; `latest` é o ponteiro móvel que o `setup.sh` consome por padrão.
12+
13+
O pacote no GHCR precisa ficar com **visibilidade pública** (sem `imagePullSecret` no consumo) e vinculado ao repositório. A primeira publicação cria o pacote como privado por padrão — ajustar a visibilidade para público (via configuração do pacote no GitHub ou `gh`) faz parte desta entrega; documentar no corpo da issue/PR que esse passo é manual e único, caso o Actions não consiga defini-lo por si.
14+
15+
Cobertura de teste: não há código Java envolvido — a validação é a própria execução do workflow. Como a publicação só dispara em tag `v*`, o caminho pode ser exercitado antes do primeiro release real via `workflow_dispatch`, confirmando que a imagem aparece pública em `ghcr.io/greencapk8s/platform` com as tags esperadas. O gate de testes Karibu/integração do fluxo de sprint não se aplica a esta entrega.
16+
17+
Fora de escopo: multi-arch (só amd64 nesta sprint) e a troca do `docker-compose.yml` para a imagem publicada (registrada no backlog como follow-up).
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# 02 — Step 5 do setup.sh puxa a imagem publicada e carrega via `minikube image load`
2+
3+
Status: done
4+
5+
Esta é a entrega central da sprint: o lado **consumidor** da imagem publicada (issue 01). Hoje o Step 5 do `setup.sh` sempre roda um `docker build` completo (resolução de dependências Gradle + `bootJar` com frontend Vaadin de produção) e faz `docker push` para o registry interno do minikube via port-forward — o passo mais pesado da instalação, mesmo quando o código não mudou. Esta entrega reescreve o Step 5 para, por padrão, **puxar** a imagem já publicada e carregá-la no cluster, mantendo o build local como caminho alternativo.
6+
7+
Decisão de puxar vs buildar, com esta precedência: (1) se `BUILD_LOCAL=true`, builda — via deliberada para o desenvolvedor testar o próprio código-fonte não-lançado; (2) senão, se a arquitetura (`uname -m`) **não** for `amd64` (arm64 em Apple Silicon ou Linux ARM), builda com aviso — não há imagem publicada para essa arquitetura; (3) senão (amd64, sem override), puxa a imagem publicada. Se o pull falhar (rede fora, GHCR indisponível), o script **cai automaticamente no build local com um aviso visível** — nunca trava a instalação, coerente com o posicionamento plug-and-play para iniciantes (ADR 0019).
8+
9+
Tag puxada: `latest` por padrão, sobrescrevível por `PLATFORM_IMAGE_TAG` (ex. `PLATFORM_IMAGE_TAG=0.7.7`). O `latest` mantém o script sem manutenção acoplada ao release; o override preserva reprodutibilidade quando alguém precisa de uma versão exata.
10+
11+
Ambos os caminhos convergem para o mesmo nome de imagem local `ghcr.io/greencapk8s/platform:latest`: o pull já traz esse nome (normalizado para a tag local `:latest` quando `PLATFORM_IMAGE_TAG` aponta para outra versão, via `docker tag`); o build local produz a imagem com esse mesmo nome. Daí em diante, um único mecanismo: `minikube image load ghcr.io/greencapk8s/platform:latest -p $PROFILE`, que injeta a imagem direto no runtime dos nós — eliminando o port-forward e o `docker push` para o registry interno que o Step 5 fazia. O registry interno **permanece** no cluster, usado só pelo Kaniko dos Templates e pela view de Registry; o que sai é apenas a imagem da plataforma passar por ele.
12+
13+
O manifest `setup/manifests/05-greencap-deployment.yaml` passa a referenciar `ghcr.io/greencapk8s/platform:latest` com `imagePullPolicy: IfNotPresent` (hoje é `localhost:5000/greencap-platform/platform:latest` com `Always`). O `IfNotPresent` é essencial: usa a imagem carregada pelo `image load` sem tentar sair para a internet — necessário no caminho build-local, onde essa tag nem existe na GHCR. Como a tag `:latest` não muda entre releases, um `kubectl apply` de rerun não detectaria imagem nova sozinho; por isso, após o `image load`, o Step 5 executa `kubectl rollout restart deployment/greencap` para o rerun pegar a imagem recarregada. (O deployment só é criado no Step 7 — o rollout restart deve ser tolerante à primeira execução, quando o deployment ainda não existe.)
14+
15+
Ajuste de CI incluído nesta mesma entrega, para não deixar a validação quebrada: o workflow `setup-script-validate.yml` passa a rodar o `setup.sh` com `BUILD_LOCAL=true`, mantendo a validação do caminho de build do Dockerfile/código-fonte (o propósito do workflow) mesmo depois de o padrão do script virar pull. Sem isso, o CI passaria a exercitar o pull de uma imagem que, em PRs pré-release, ainda não existe.
16+
17+
Cobertura de teste: não há código Java — a validação é o CI (`setup-script-validate` continua verde com `BUILD_LOCAL=true`) mais o aceite manual no browser (`http://greencap.local`, login admin/admin, plataforma sobe). O caminho de pull só é plenamente exercitável depois que a issue 01 publicar uma imagem; até lá o `setup.sh` exercita o fallback de build, e o pull pode ser fumaça-testado publicando uma imagem via `workflow_dispatch` (issue 01) e rodando `setup.sh` em amd64. O gate Karibu/integração do fluxo de sprint não se aplica.
18+
19+
Fora de escopo: garbage collection do registry interno (item vizinho no backlog) e a troca do `docker-compose.yml` (follow-up no backlog).
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# 03 — README: documentar a imagem publicada e os overrides do setup
2+
3+
Status: done
4+
5+
Com o `setup.sh` passando a puxar uma imagem publicada por padrão (issue 02), o README precisa refletir o novo comportamento — hoje ele descreve (ou pressupõe) que a instalação constrói a imagem localmente. Esta entrega atualiza a documentação para explicar que a instalação agora baixa uma imagem pronta de `ghcr.io/greencapk8s/platform`, tornando o setup mais rápido, e expõe as chaves de override para quem precisa de outro comportamento.
6+
7+
O que documentar, de forma enxuta e sem prometer detalhes de implementação: que o `setup.sh` puxa a imagem publicada em `amd64` e constrói localmente em arm64 ou quando `BUILD_LOCAL=true`; que `PLATFORM_IMAGE_TAG` fixa uma versão específica (ex. `PLATFORM_IMAGE_TAG=0.7.7`) em vez do `latest` padrão; e que, se o pull falhar, a instalação constrói localmente de forma automática — não trava. Mencionar que a imagem é pública (nenhuma autenticação é necessária para puxá-la).
8+
9+
Manter o tom e o posicionamento já estabelecidos no README (setup-first, plug-and-play): a mensagem principal é "a instalação agora é mais rápida porque baixa uma imagem pronta", com os overrides como nota para desenvolvedores, não como fluxo principal.
10+
11+
Cobertura de teste: entrega puramente documental — sem código, sem CI novo. Validação por revisão do texto no aceite manual.
12+
13+
Fora de escopo: documentar a troca do `docker-compose.yml` para a imagem publicada (essa mudança está no backlog; o README só será ajustado nesse ponto quando o follow-up for implementado).

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,13 @@ cd greencap-k8s
9797
./setup/setup.sh
9898
```
9999

100-
The wizard provisions a real Kubernetes cluster (minikube), builds and deploys GreenCap into it, and wires up local access. When it finishes:
100+
The wizard provisions a real Kubernetes cluster (minikube), pulls the published GreenCap image and deploys it, and wires up local access. When it finishes:
101101

102102
- **URL:** http://greencap.local
103103
- **Login:** `admin` / `admin`  (change it after your first login)
104104

105+
> **How the image is provided.** On `amd64` the wizard pulls the prebuilt public image from `ghcr.io/greencapk8s/platform` — no build, no authentication. On `arm64` (Apple Silicon), or when you set `BUILD_LOCAL=true`, it builds from source locally instead; if a pull ever fails, it falls back to a local build automatically, so setup never stalls. Pin a specific release with `PLATFORM_IMAGE_TAG=X.Y.Z ./setup/setup.sh` (defaults to `latest`).
106+
105107
To tear everything down:
106108

107109
```bash
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Imagem da plataforma publicada em GHCR e carregada via `minikube image load`
2+
3+
O passo mais pesado da instalação era o Step 5 do `setup.sh`: um `docker build` completo (resolução de dependências Gradle + `bootJar` com frontend Vaadin de produção) rodado do zero em toda execução, mesmo sem mudança no código. A plataforma passa a publicar sua imagem em `ghcr.io/greencapk8s/platform` (pacote **público**, só `linux/amd64`) a cada release, disparado por push de git tag `v*` — a mesma tag que já é a fonte de verdade da versão em `main`. O `setup.sh` passa a **puxar** a imagem publicada quando a arquitetura é `amd64` e a **buildar** localmente caso contrário (arm64) ou quando o desenvolvedor pede explicitamente (`BUILD_LOCAL=true`); se o pull falhar, cai automaticamente no build local com aviso, para nunca travar a instalação. Em ambos os caminhos a imagem recebe o mesmo nome local (`ghcr.io/greencapk8s/platform:latest`) e entra no cluster via `minikube image load` — não mais via push para o registry interno do minikube. O registry interno **permanece**, usado só pelo Kaniko dos Templates (ADR 0007) e pela view de Registry; o que muda é que a imagem da plataforma deixa de passar por ele.
4+
5+
## Considered Options
6+
7+
- **Docker Hub** em vez de GHCR — descartado: a org do GitHub já é `greencapk8s` e o `GITHUB_TOKEN` do Actions tem push nativo para GHCR (`packages: write`), sem cadastrar secret; Docker Hub exigiria conta/token externos e uma vantagem de descoberta que não pesa num tool cuja porta de entrada é o `setup.sh`.
8+
- **Imagem multi-arch (`amd64` + `arm64`)** — descartado nesta sprint: publicar arm64 exigiria emulação QEMU no runner amd64 do CI, deixando cada release muito mais lento para beneficiar a minoria macOS/Apple Silicon (plataforma secundária, Docker já é pré-requisito manual — ver ADR 0016). O mismatch de arquitetura vira o gatilho natural do fallback de build local no `setup.sh`.
9+
- **Re-push da imagem puxada para o registry interno** (mantendo o manifest em `localhost:5000/...`) — descartado: faz duas cópias (host→registry→kubelet) contra uma do `image load` (host→nó), e manteria o port-forward do Step 5. O `image load` é mais rápido e com menos partes móveis.
10+
- **Kubelet puxando direto da GHCR** (manifest apontando para `ghcr.io`, sem `image load`) — descartado: seria o mais leve só no caminho pull, mas o caminho build-local não tem como (imagem buildada só existe no docker do host), forçando referência/política diferentes por caminho. O `image load` mantém um único manifest e um único mecanismo downstream; só a *obtenção* da imagem (pull vs build) difere.
11+
- **Tag fixada no `setup.sh`** (`PLATFORM_VERSION=X.Y.Z` embutido) em vez de `latest` — descartado: obrigaria bumpar a constante a cada release (fácil esquecer) e ficaria defasada no `develop`. `latest` é zero-manutenção; a reprodutibilidade fica preservada pela tag imutável `X.Y.Z` via override `PLATFORM_IMAGE_TAG`.
12+
- **Falha explícita quando o pull cai**, exigindo `BUILD_LOCAL=true` manual — descartado em favor do fallback automático: o produto é plug-and-play voltado a iniciantes, para quem parar e pedir uma variável de ambiente é fricção; o build automático (com aviso visível) sempre entrega um cluster no final, e o caso normal (amd64 + rede ok) continua rápido.
13+
14+
## Consequences
15+
16+
- A imagem publicada é só `amd64`. Em Apple Silicon e Linux ARM o `setup.sh` builda localmente — esses usuários não colhem o ganho da imagem pronta enquanto a decisão de multi-arch não for revisitada (registrado como possível follow-up).
17+
- `latest` é ponteiro móvel: reruns do `setup.sh` em momentos diferentes podem trazer imagens diferentes, silenciosamente. É desejável (o usuário ganha correções), e há a tag `X.Y.Z` para quem precisa de versão exata.
18+
- O manifest passa a fixar `ghcr.io/greencapk8s/platform:latest` com `imagePullPolicy: IfNotPresent`. Como a tag `:latest` não muda entre releases, o `kubectl apply` sozinho não detecta imagem nova num rerun — o `setup.sh` faz um `kubectl rollout restart deployment/greencap` após o `image load` para garantir a atualização.
19+
- O CI de validação (`setup-script-validate`, `docker-compose-validate`) continua **buildando do código-fonte** (via `BUILD_LOCAL=true` / override `.dev.yml`) — a nova via de pull não reduz a cobertura que valida que o Dockerfile e o código ainda sobem.
20+
- Um novo caminho de release passa a existir e precisa ser respeitado: publicar a imagem depende de tagear `v*`. Uma release que só crie a tag no GitHub sem o push (ou com o workflow falho) fica sem imagem `latest` atualizada — mitigado pelo `workflow_dispatch` para republicar manualmente.
21+
- A imagem da plataforma e as imagens de Template (Kaniko) passam a viver em registries diferentes (GHCR público vs registry interno do cluster), quebrando a uniformidade anterior de "tudo no registry interno" — assumido conscientemente, já que os dois têm ciclos de vida distintos (release versionado vs build efêmero por Cluster).

0 commit comments

Comments
 (0)