ci: build, test and publish the image once, then let the release reuse it
Drop the pull_request trigger (the branch push already validates the commit), drop the second npm run build from the lint job (the image build compiles it), and build the image on arm64 after the cheap checks pass. The CI publishes it as inputs-<hash>, so the merge to main only re-tags it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7d0edf13c4
commit
4ad130a334
3 files changed
+84
-44
No files matched your search
+51
-21
@@ -3,9 +3,13 @@ name: CI
|
||||
# Documentação e configuração de ferramenta não mudam nada que a suíte teste.
|
||||
# Na main não roda por push: o release.yml é quem a chama, e só quando a imagem
|
||||
# precisa ser reconstruída; rodar nos dois repetiria a mesma suíte no mesmo commit.
|
||||
# Sem gatilho de pull_request: o push da branch já valida o mesmo commit, e os
|
||||
# dois juntos rodariam tudo duas vezes (o repositório não recebe PR de fork).
|
||||
#
|
||||
# A imagem é construída aqui uma única vez, em arm64 (a arquitetura do servidor),
|
||||
# testada e publicada como inputs-<hash>. Ao fazer o merge, o release encontra
|
||||
# essa tag e só aplica os nomes novos, sem rodar nem construir nada de novo.
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore: ['**.md', 'LICENSE', '.claude/**', '.superset/**', '.wolf/**', 'scripts/release-notes.sh']
|
||||
push:
|
||||
branches-ignore: [main]
|
||||
paths-ignore: ['**.md', 'LICENSE', '.claude/**', '.superset/**', '.wolf/**', 'scripts/release-notes.sh']
|
||||
@@ -20,7 +24,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
verificar:
|
||||
name: Lint, design e build
|
||||
name: Lint e auditorias
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
@@ -35,24 +39,11 @@ jobs:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
|
||||
# Next.js guarda aqui o resultado da compilação anterior; sem isso, cada
|
||||
# execução recompila todas as páginas do zero.
|
||||
- name: Cache de build do Next
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: .next/cache
|
||||
key: next-${{ runner.arch }}-${{ hashFiles('package-lock.json') }}-${{ hashFiles('app/**', 'components/**', 'lib/**') }}
|
||||
restore-keys: |
|
||||
next-${{ runner.arch }}-${{ hashFiles('package-lock.json') }}-
|
||||
next-${{ runner.arch }}-
|
||||
|
||||
- run: npm ci
|
||||
|
||||
# eslint + auditoria do design system (tokens, contraste AAA) + auditoria de páginas
|
||||
- run: npm run check
|
||||
|
||||
- run: npm run build
|
||||
|
||||
# Trivy roda pela imagem oficial: o trivy-action clona o repositório do Trivy
|
||||
# do mesmo servidor do workflow, que aqui é o Gitea, e não o GitHub.
|
||||
# O workspace do job é um volume que o daemon não enxerga, então os arquivos
|
||||
@@ -72,20 +63,51 @@ jobs:
|
||||
docker rm "$cid" >/dev/null
|
||||
exit "$status"
|
||||
|
||||
# Só começa depois das verificações baratas: um lint quebrado não gasta um
|
||||
# build de imagem. Roda no runner arm64, o mesmo tipo do servidor, então o
|
||||
# que é escaneado e testado é a imagem que vai ao ar.
|
||||
imagem:
|
||||
name: Imagem Docker
|
||||
runs-on: ubuntu-latest
|
||||
name: Imagem Docker (linux/arm64)
|
||||
needs: [verificar, seguranca]
|
||||
runs-on: ubuntu-24.04-arm
|
||||
env:
|
||||
REGISTRY: git.lucasrguerra.dev.br
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# `docker build` simples: o cache de camadas fica no volume persistente do
|
||||
# daemon do runner, e o buildx criaria um BuildKit novo a cada job.
|
||||
|
||||
# Conteúdo já publicado (re-push, tag num commit já validado): nada a fazer.
|
||||
- id: plano
|
||||
run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ gitea.actor }}
|
||||
password: ${{ secrets.REGISTRY_TOKEN }}
|
||||
|
||||
- name: Build
|
||||
run: docker build -t espdocs:ci .
|
||||
if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
load: true
|
||||
tags: espdocs:ci
|
||||
cache-from: type=registry,ref=${{ steps.plano.outputs.image }}:buildcache
|
||||
cache-to: type=registry,ref=${{ steps.plano.outputs.image }}:buildcache,mode=max
|
||||
|
||||
- name: Trivy na imagem
|
||||
if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
run: |
|
||||
docker run --rm --network host -v trivy-cache:/root/.cache/ -e DOCKER_HOST=tcp://127.0.0.1:2375 aquasec/trivy:0.70.0 \
|
||||
image --severity HIGH,CRITICAL --exit-code 1 espdocs:ci
|
||||
|
||||
- name: A imagem sobe e responde
|
||||
if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
run: |
|
||||
docker run -d --name espdocs-ci --network host -e PORT=3999 espdocs:ci
|
||||
for i in $(seq 1 30); do
|
||||
@@ -95,3 +117,11 @@ jobs:
|
||||
docker logs espdocs-ci | tail -5
|
||||
docker rm -f espdocs-ci >/dev/null
|
||||
test "${ok:-0}" = 1
|
||||
|
||||
# Passou em tudo: publica a mesma imagem que foi testada, sem reconstruir.
|
||||
- name: Publica inputs-<hash>
|
||||
if: ${{ steps.plano.outputs.reuse != 'true' }}
|
||||
run: |
|
||||
tag="${{ steps.plano.outputs.image }}:inputs-${{ steps.plano.outputs.inputs }}"
|
||||
docker tag espdocs:ci "$tag"
|
||||
docker push "$tag"
|
||||
@@ -18,10 +18,10 @@ env:
|
||||
|
||||
jobs:
|
||||
# A imagem só depende dos arquivos que entram no build, não da documentação nem
|
||||
# da configuração de ferramentas. Quando já existe no registro uma imagem para
|
||||
# o mesmo conteúdo (um merge só de docs, uma mudança de workflow, uma tag num
|
||||
# commit que acabou de ser publicado), não há o que construir: as tags novas
|
||||
# apontam para o manifesto existente.
|
||||
# da configuração de ferramentas. O push da branch já a construiu, testou e
|
||||
# publicou como inputs-<hash>; quando essa tag existe (o caso normal de um
|
||||
# merge, uma tag num commit validado, um merge só de docs), não há o que
|
||||
# construir: as tags novas apontam para o manifesto existente.
|
||||
plano:
|
||||
name: Plano (reaproveitar a imagem?)
|
||||
runs-on: ubuntu-latest
|
||||
@@ -32,23 +32,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: plano
|
||||
env:
|
||||
REPO: ${{ gitea.repository }}
|
||||
run: |
|
||||
inputs=$(git ls-tree -r HEAD \
|
||||
| awk -F'\t' '$2 !~ /^(\.gitea|\.github|\.claude|\.superset|docs)\// && $2 !~ /^(README.*|CLAUDE\.md|DESIGN\.md|LICENSE|\.gitignore|CHANGELOG\.md|scripts\/release-notes\.sh)$/' \
|
||||
| sha256sum | cut -c1-16)
|
||||
ns="${REPO,,}"
|
||||
token=$(curl -fsS "https://$REGISTRY/v2/token?service=container_registry&scope=repository:$ns:pull" | jq -r .token)
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -I \
|
||||
-H "Authorization: Bearer $token" \
|
||||
-H 'Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.oci.image.manifest.v1+json' \
|
||||
"https://$REGISTRY/v2/$ns/manifests/inputs-$inputs")
|
||||
echo "inputs-$inputs -> HTTP $code"
|
||||
reuse=false; [ "$code" = 200 ] && reuse=true
|
||||
echo "inputs=$inputs" >> "$GITHUB_OUTPUT"
|
||||
echo "reuse=$reuse" >> "$GITHUB_OUTPUT"
|
||||
echo "image=$REGISTRY/$ns" >> "$GITHUB_OUTPUT"
|
||||
run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
# Nada é publicado sem a suíte completa passar antes. Pulada quando a imagem é
|
||||
# reaproveitada: o mesmo conteúdo já passou por ela.
|
||||
@@ -68,6 +52,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Se o ci chamado acima acabou de publicar a imagem (push direto na main),
|
||||
# o plano lá de cima está desatualizado: confere de novo antes de construir.
|
||||
- id: replano
|
||||
run: scripts/plano-imagem.sh "$REGISTRY" "${{ gitea.repository }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Autentica no registro
|
||||
@@ -89,7 +78,7 @@ jobs:
|
||||
type=sha,format=long
|
||||
|
||||
- name: Build e push
|
||||
if: ${{ needs.plano.outputs.reuse != 'true' }}
|
||||
if: ${{ steps.replano.outputs.reuse != 'true' }}
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
@@ -104,7 +93,7 @@ jobs:
|
||||
|
||||
# Mesmo manifesto, nomes novos: nenhuma camada é lida nem escrita.
|
||||
- name: Aplica as tags à imagem existente
|
||||
if: ${{ needs.plano.outputs.reuse == 'true' }}
|
||||
if: ${{ steps.replano.outputs.reuse == 'true' }}
|
||||
run: |
|
||||
tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}')
|
||||
docker buildx imagetools create $tags "${{ needs.plano.outputs.image }}:inputs-${{ needs.plano.outputs.inputs }}"
|
||||
|
||||
Reference in new issue
Block a user