From 154d49869940479c0fc4c379affbeb1df8708f52 Mon Sep 17 00:00:00 2001 From: Dalton Alvarenga Date: Thu, 7 May 2026 10:57:50 -0300 Subject: [PATCH 1/3] =?UTF-8?q?fix(ci):=20seta=20quota=5Fproject=20pro=20g?= =?UTF-8?q?cloud=20builds=20submit=20n=C3=A3o=20500=20em=20service=20usage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gcloud builds submit` faz uma chamada interna à Service Usage API antes de uploadar o source pro bucket _cloudbuild. Quando autentica via SA key (google-github-actions/auth), o credentials file não tem quota_project, e gcloud cai num default que não é corepetro — a chamada à SU falha com "serviceusage.services.use forbidden" mesmo com roles/serviceusage.serviceUsageConsumer concedida no projeto. Fix: setar billing/quota_project explícito antes do builds submit + env var CLOUDSDK_BILLING_QUOTA_PROJECT como cinto-suspensório. --- .gitea/workflows/cd.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/cd.yml b/.gitea/workflows/cd.yml index e1307bb..43730bb 100644 --- a/.gitea/workflows/cd.yml +++ b/.gitea/workflows/cd.yml @@ -37,9 +37,18 @@ jobs: project_id: ${{ secrets.GCP_PROJECT }} - name: Build & push (Cloud Build, sem Docker local) + env: + # SA key auth não popula quota_project no credentials file → gcloud + # cai em "default" e a chamada à Service Usage API falha com + # "serviceusage.services.use forbidden" mesmo com a role concedida. + CLOUDSDK_BILLING_QUOTA_PROJECT: ${{ secrets.GCP_PROJECT }} run: | IMG="${IMAGE_BASE}/${{ gitea.event.repository.name }}:lab-${{ gitea.run_number }}" - gcloud builds submit --tag "$IMG" --project=${{ secrets.GCP_PROJECT }} --timeout=30m + gcloud config set billing/quota_project "${{ secrets.GCP_PROJECT }}" + gcloud builds submit \ + --tag "$IMG" \ + --project=${{ secrets.GCP_PROJECT }} \ + --timeout=30m echo "IMG=$IMG" >> $GITHUB_ENV - name: Deploy hml2 (apenas em push pra master/main) -- 2.45.2 From 18c22e85c42a24a849397f964ea79e3a0a535b13 Mon Sep 17 00:00:00 2001 From: Dalton Alvarenga Date: Thu, 7 May 2026 12:02:36 -0300 Subject: [PATCH 2/3] fix(ci): --gcs-source-staging-dir + --suppress-logs no builds submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quota_project sozinho não resolve. Reproduzi local com a SA gitea-cd e peguei a HTTP 403 raw da Storage API: dois calls bloqueados em sequência: 1. GET /storage/v1/b/_cloudbuild → resolvido com grant de roles/storage.admin no bucket (storage.buckets.get vinha do projectEditor legacy ACL, que gitea-cd não é). 2. GET /storage/v1/b?prefix=_cloudbuild&project=corepetro → 403 em storage.buckets.list. Esse permission é project-scoped (não dá pra grantar bucket-scoped). Em vez de inflar a SA com storage.admin no projeto, --gcs-source-staging-dir explícito faz gcloud pular o auto-detect inteiro e usar o bucket especificado. E --suppress-logs evita o stream que exige Viewer/Owner no projeto pra ler Cloud Logging — gitea-cd não tem; o build ainda é aguardado e o exit code propaga correto. Validado: build de teste alpine deu SUCCESS na primeira tentativa com esses dois flags. --- .gitea/workflows/cd.yml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/cd.yml b/.gitea/workflows/cd.yml index 43730bb..79b4ef8 100644 --- a/.gitea/workflows/cd.yml +++ b/.gitea/workflows/cd.yml @@ -37,18 +37,20 @@ jobs: project_id: ${{ secrets.GCP_PROJECT }} - name: Build & push (Cloud Build, sem Docker local) - env: - # SA key auth não popula quota_project no credentials file → gcloud - # cai em "default" e a chamada à Service Usage API falha com - # "serviceusage.services.use forbidden" mesmo com a role concedida. - CLOUDSDK_BILLING_QUOTA_PROJECT: ${{ secrets.GCP_PROJECT }} run: | IMG="${IMAGE_BASE}/${{ gitea.event.repository.name }}:lab-${{ gitea.run_number }}" - gcloud config set billing/quota_project "${{ secrets.GCP_PROJECT }}" + # --gcs-source-staging-dir: pula auto-detect que precisa de + # storage.buckets.list (project-scope) — gitea-cd só tem grant + # na bucket _cloudbuild, não no projeto inteiro. + # --suppress-logs: gcloud streama logs do Cloud Build pra stdout e + # isso exige Viewer/Owner no projeto. SA não tem; suprimir os + # logs ainda espera o build terminar e propaga o exit code. gcloud builds submit \ --tag "$IMG" \ --project=${{ secrets.GCP_PROJECT }} \ - --timeout=30m + --timeout=30m \ + --gcs-source-staging-dir="gs://${{ secrets.GCP_PROJECT }}_cloudbuild/source" \ + --suppress-logs echo "IMG=$IMG" >> $GITHUB_ENV - name: Deploy hml2 (apenas em push pra master/main) -- 2.45.2 From 037a3f4d93dc6a3a93359b3b28fb9bcd23c2d122 Mon Sep 17 00:00:00 2001 From: Dalton Alvarenga Date: Thu, 7 May 2026 13:20:27 -0300 Subject: [PATCH 3/3] fix(ci): troca --suppress-logs por --async + poll loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --suppress-logs nessa versão do gcloud CLI no runner ainda tenta ler logs no fim e dá exit != 0 quando a SA não tem Viewer no projeto. Build real do PR #8 deu SUCCESS (image lab-18 em AR), mas a step falhou na finalização do gcloud. Com --async, gcloud retorna o build ID logo após enfileirar e sai. Poll explícito via builds describe (que usa cloudbuild.builds.get, já em builds.editor). Mata o build no primeiro estado terminal != SUCCESS. --- .gitea/workflows/cd.yml | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/cd.yml b/.gitea/workflows/cd.yml index 79b4ef8..4574476 100644 --- a/.gitea/workflows/cd.yml +++ b/.gitea/workflows/cd.yml @@ -38,19 +38,40 @@ jobs: - name: Build & push (Cloud Build, sem Docker local) run: | + set -e IMG="${IMAGE_BASE}/${{ gitea.event.repository.name }}:lab-${{ gitea.run_number }}" - # --gcs-source-staging-dir: pula auto-detect que precisa de - # storage.buckets.list (project-scope) — gitea-cd só tem grant + # --gcs-source-staging-dir: pula o auto-detect que precisa de + # storage.buckets.list (project-scope). gitea-cd só tem grant # na bucket _cloudbuild, não no projeto inteiro. - # --suppress-logs: gcloud streama logs do Cloud Build pra stdout e - # isso exige Viewer/Owner no projeto. SA não tem; suprimir os - # logs ainda espera o build terminar e propaga o exit code. - gcloud builds submit \ + # --async: gcloud retorna assim que o build é enfileirado. Sem + # isso, gcloud tenta streamar Cloud Logging, exige Viewer/Owner + # no projeto, e --suppress-logs nessa versão do CLI ainda dá + # exit != 0 quando não consegue ler. Polling abaixo é o trade. + BUILD_ID=$(gcloud builds submit \ --tag "$IMG" \ --project=${{ secrets.GCP_PROJECT }} \ --timeout=30m \ --gcs-source-staging-dir="gs://${{ secrets.GCP_PROJECT }}_cloudbuild/source" \ - --suppress-logs + --async \ + --format="value(id)") + echo "Cloud Build kicked off: $BUILD_ID" + # Poll até terminar. gcloud builds describe usa cloudbuild.builds.get + # (já incluído em roles/cloudbuild.builds.editor). + while true; do + STATUS=$(gcloud builds describe "$BUILD_ID" \ + --project=${{ secrets.GCP_PROJECT }} \ + --region=global \ + --format="value(status)") + echo "[$(date -u +%H:%M:%S)] build $BUILD_ID: $STATUS" + case "$STATUS" in + SUCCESS) break ;; + FAILURE|INTERNAL_ERROR|TIMEOUT|CANCELLED|EXPIRED) + echo "::error::Cloud Build $STATUS — ver console: https://console.cloud.google.com/cloud-build/builds/$BUILD_ID?project=${{ secrets.GCP_PROJECT }}" + exit 1 + ;; + esac + sleep 15 + done echo "IMG=$IMG" >> $GITHUB_ENV - name: Deploy hml2 (apenas em push pra master/main) -- 2.45.2