name: Release # Publica a imagem no registro de containers do Gitea depois de um merge em main, # e de novo com tag semver quando uma tag v* é enviada. O servidor só faz pull. # Precisa do secret de usuário REGISTRY_TOKEN (token do Gitea com write:package). on: push: branches: [main] tags: ['v*'] workflow_dispatch: concurrency: group: release cancel-in-progress: false env: REGISTRY: git.lucasrguerra.dev.br 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. 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|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" # 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: [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 - uses: docker/setup-buildx-action@v4 - name: Autentica no registro uses: docker/login-action@v4 with: registry: ${{ env.REGISTRY }} username: ${{ gitea.actor }} password: ${{ secrets.REGISTRY_TOKEN }} - name: Metadados id: meta uses: docker/metadata-action@v6 with: images: ${{ needs.plano.outputs.image }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=raw,value=latest 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 }} ${{ needs.plano.outputs.image }}:inputs-${{ needs.plano.outputs.inputs }} labels: ${{ steps.meta.outputs.labels }} 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 }}" # Cria a entrada em "Releases" quando a execução é de uma tag v*. Roda depois # de publicar a imagem, com as notas da seção do CHANGELOG.md. release: name: Cria a release no Gitea needs: [plano, publicar] # Explícito, como o publicar: o ci acima é pulado quando a imagem é # reaproveitada, e o success() implícito pularia este job também. if: ${{ !cancelled() && needs.publicar.result == 'success' && startsWith(gitea.ref, 'refs/tags/v') }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Release env: TAG: ${{ gitea.ref_name }} TOKEN: ${{ gitea.token }} API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }} IMAGE: ${{ needs.plano.outputs.image }} run: | scripts/release-notes.sh "$TAG" "$IMAGE" > notes.md pre=false; case "$TAG" in *-*) pre=true ;; esac jq -n --arg tag "$TAG" --arg name "ESPDocs ${TAG#v}" --rawfile body notes.md --argjson pre "$pre" \ '{tag_name:$tag, name:$name, body:$body, prerelease:$pre}' > release.json curl -fsS -X POST "$API/releases" -H "Authorization: token $TOKEN" \ -H 'Content-Type: application/json' -d @release.json