diff --git a/.gitea/scripts/digest-latest.sh b/.gitea/scripts/digest-latest.sh new file mode 100755 index 0000000..f8140c0 --- /dev/null +++ b/.gitea/scripts/digest-latest.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# Uso: .gitea/scripts/digest-latest.sh +# Imprime o digest do manifesto de :latest, ou nada se a tag ainda +# não existe. Fica em .gitea/ para não entrar no hash da imagem (plano-imagem.sh). +# O release.yml compara o valor antes e depois de publicar para +# decidir se há o que implantar. Com o pacote privado, passe REGISTRY_USER e +# REGISTRY_PASSWORD (token com read:package). +set -euo pipefail +registry=$1; ns=${2,,} + +auth=(); [ -n "${REGISTRY_PASSWORD:-}" ] && auth=(-u "${REGISTRY_USER:?}:$REGISTRY_PASSWORD") +token=$(curl -fsS "${auth[@]}" "https://$registry/v2/token?service=container_registry&scope=repository:$ns:pull" | jq -r .token) +curl -sSI \ + -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,application/vnd.docker.distribution.manifest.v2+json' \ + "https://$registry/v2/$ns/manifests/latest" \ + | tr -d '\r' | awk -F': ' 'tolower($1) == "docker-content-digest" { print $2 }' diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index bd143e8..baebe6f 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -1,7 +1,9 @@ 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. +# e de novo com tag semver quando uma tag v* é enviada. Quando o latest passa a +# apontar para outra imagem, o job deploy pede ao Coolify para implantá-la. +# Precisa também dos secrets COOLIFY_URL e COOLIFY_TOKEN no repositório. # Precisa do secret de usuário REGISTRY_TOKEN (token do Gitea com write:package). on: push: @@ -52,6 +54,8 @@ jobs: # 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 + outputs: + mudou: ${{ steps.depois.outputs.mudou }} steps: - uses: actions/checkout@v4 @@ -69,6 +73,13 @@ jobs: username: ${{ gitea.actor }} password: ${{ secrets.REGISTRY_TOKEN }} + # O deploy só acontece se o latest passar a apontar para outra imagem. + # Um merge só de docs ou uma tag reaponta o latest para o mesmo manifesto, + # e reiniciar o container nesse caso seria trabalho repetido. + - name: Digest do latest antes + id: antes + run: echo "digest=$(.gitea/scripts/digest-latest.sh "$REGISTRY" "${{ gitea.repository }}")" >> "$GITHUB_OUTPUT" + - name: Metadados id: meta uses: docker/metadata-action@v6 @@ -101,6 +112,15 @@ jobs: 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 }}" + - name: Digest do latest depois + id: depois + run: | + antes="${{ steps.antes.outputs.digest }}" + depois=$(.gitea/scripts/digest-latest.sh "$REGISTRY" "${{ gitea.repository }}") + echo "latest: ${antes:-nenhum} -> $depois" + [ -n "$depois" ] && [ "$depois" != "$antes" ] && mudou=true || mudou=false + echo "mudou=$mudou" >> "$GITHUB_OUTPUT" + # 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: @@ -127,3 +147,38 @@ jobs: '{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 + + # Avisa o Coolify para puxar o latest novo e espera o deploy terminar, para + # que uma falha apareça aqui, e não só no painel do Coolify. Roda só quando o + # latest mudou, ou num disparo manual (que força o deploy). + # Precisa dos secrets COOLIFY_URL e COOLIFY_TOKEN no repositório. + deploy: + name: Deploy no Coolify + needs: publicar + if: ${{ !cancelled() && needs.publicar.result == 'success' && (needs.publicar.outputs.mudou == 'true' || gitea.event_name == 'workflow_dispatch') }} + runs-on: ubuntu-latest + steps: + - name: Deploy de ESPDocs + env: + COOLIFY_URL: ${{ secrets.COOLIFY_URL }} + COOLIFY_TOKEN: ${{ secrets.COOLIFY_TOKEN }} + APP: 62wmq51cgbcz9nyfwizmytku + run: | + if [ -z "$COOLIFY_URL" ] || [ -z "$COOLIFY_TOKEN" ]; then + echo "faltam os secrets COOLIFY_URL e COOLIFY_TOKEN no repositório"; exit 1 + fi + auth="Authorization: Bearer $COOLIFY_TOKEN" + dep=$(curl -fsS -H "$auth" "$COOLIFY_URL/api/v1/deploy?uuid=$APP&force=false" | jq -r '.deployments[0].deployment_uuid') + [ -n "$dep" ] && [ "$dep" != null ] || { echo "o Coolify não devolveu um deployment"; exit 1; } + echo "deployment $dep" + # Até 15 minutos: o pull da imagem arm64 e o healthcheck cabem com folga. + for _ in $(seq 1 90); do + st=$(curl -fsS -H "$auth" "$COOLIFY_URL/api/v1/deployments/$dep" | jq -r '.status') + echo "status: $st" + case "$st" in + finished) exit 0 ;; + failed|cancelled*) exit 1 ;; + esac + sleep 10 + done + echo "o deploy não terminou em 15 minutos"; exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 90a1111..591c095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ Tudo o que muda de relevante no ESPDocs está aqui. O formato segue o ### Adicionado +- Deploy automático no Coolify: depois de publicar a imagem, o `release.yml` pede o deploy e + espera o resultado. Só implanta quando o `latest` muda, então merge só de documentação ou + tag não reiniciam o container. Um disparo manual do workflow força o deploy. - Formação 2 da LASER em `/formacoes/2`: estação de temperatura e umidade com DHT22, LCD 16x2 I2C e ESP32-S3, simulada no Wokwi com o framework Arduino. Slides, apostila, projeto do Wokwi e programa de scanner I2C, tudo validado no simulador.