From 111a81776c2e2acc04feeca9b05031bbb827843b Mon Sep 17 00:00:00 2001 From: Lucas Rayan Date: Fri, 30 Oct 2026 12:22:46 -0300 Subject: [PATCH] ci: reuse the published image when its sources are unchanged The release workflow hashes everything that goes into the image build and skips CI and the build when the registry already has an image for that hash, so a docs or tooling change, or a version tag, only re-points the tags. ci.yml no longer runs on main by push: release.yml calls it when a build is needed, and running both repeated the same suite on the same commit. Co-Authored-By: Claude Sonnet 5.5 --- .gitea/workflows/ci.yml | 8 +++-- .gitea/workflows/release.yml | 65 ++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 21d9a1e..7520acf 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -1,10 +1,14 @@ 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. on: pull_request: + paths-ignore: ['**.md', 'LICENSE', '.claude/**', '.superset/**'] push: - branches: [main] - # O release.yml roda esta mesma suíte antes de publicar a imagem. + branches-ignore: [main] + paths-ignore: ['**.md', 'LICENSE', '.claude/**', '.superset/**'] workflow_call: permissions: diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index d9e8718..d2e99b8 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -17,24 +17,57 @@ env: REGISTRY: git.lucasrguerra.dev.br jobs: - # Nada é publicado sem a suíte completa passar antes. + # 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. + plano: + name: Plano (reaproveitar a imagem?) + runs-on: ubuntu-latest + outputs: + inputs: ${{ steps.plano.outputs.inputs }} + reuse: ${{ steps.plano.outputs.reuse }} + image: ${{ steps.plano.outputs.image }} + 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)$/' \ + | 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" + + # Nada é publicado sem a suíte completa passar antes. Pulada quando a imagem é + # reaproveitada: o mesmo conteúdo já passou por ela. ci: + needs: plano + if: ${{ needs.plano.outputs.reuse != 'true' }} uses: ./.gitea/workflows/ci.yml publicar: name: Publica a imagem (linux/arm64) - needs: ci + needs: [plano, ci] + # ci é pulado, e não bem-sucedido, quando a imagem é reaproveitada. + if: ${{ !cancelled() && needs.plano.result == 'success' && (needs.ci.result == 'success' || needs.plano.outputs.reuse == 'true') }} # O único runner é arm64 e o servidor de destino (Oracle) também, então a # imagem é construída nativamente, sem emulação. runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - - id: nome - run: | - repo='${{ gitea.repository }}' - echo "image=${{ env.REGISTRY }}/${repo,,}" >> "$GITHUB_OUTPUT" - - uses: docker/setup-buildx-action@v4 - name: Autentica no registro @@ -48,7 +81,7 @@ jobs: id: meta uses: docker/metadata-action@v6 with: - images: ${{ steps.nome.outputs.image }} + images: ${{ needs.plano.outputs.image }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} @@ -56,12 +89,22 @@ jobs: type=sha,format=long - name: Build e push + if: ${{ needs.plano.outputs.reuse != 'true' }} uses: docker/build-push-action@v7 with: context: . platforms: linux/arm64 push: true - tags: ${{ steps.meta.outputs.tags }} + tags: | + ${{ steps.meta.outputs.tags }} + ${{ needs.plano.outputs.image }}:inputs-${{ needs.plano.outputs.inputs }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ steps.nome.outputs.image }}:buildcache - cache-to: type=registry,ref=${{ steps.nome.outputs.image }}:buildcache,mode=max + cache-from: type=registry,ref=${{ needs.plano.outputs.image }}:buildcache + cache-to: type=registry,ref=${{ needs.plano.outputs.image }}:buildcache,mode=max + + # Mesmo manifesto, nomes novos: nenhuma camada é lida nem escrita. + - name: Aplica as tags à imagem existente + if: ${{ needs.plano.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 }}"