GitHub Actions Deployment Setup¶
This guide will help you set up automated deployment to Cloud Run using GitHub Actions.
📋 Overview¶
The GitHub Actions workflow (deploy-production.yml) runs automatically when:
- The
mainbranch passes the CI workflow successfully (workflow_run) - You run it manually from the GitHub UI
The workflow does:
- ✅ Builds the Docker image
- ✅ Pushes to Artifact Registry
- ✅ Deploys to Cloud Run
- ✅ Maps custom domain
- ✅ Verifies deployment
- ✅ Automatic rollback if it fails
Important (Nov 2025): workflow authentication no longer uses JSON keys. It's now required to configure Workload Identity Federation and expose two secrets in GitHub:
GCP_WORKLOAD_IDENTITY_PROVIDER: provider resource (projects/<project-number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>)GCP_SERVICE_ACCOUNT_EMAIL: service account that the workflow will assume (github-actions@<project-id>.iam.gserviceaccount.com)The
GCP_SA_KEYsecret is legacy and should be removed once migrated.
🔐 Step 1: Create Workload Identity Federation + Service Account¶
1. Create the Workload Identity Pool and Provider (CLI)¶
PROJECT_ID="norse-breaker-474323-n8"
POOL_ID="github-actions"
PROVIDER_ID="github-oidc"
gcloud iam workload-identity-pools create "$POOL_ID" \
--project="$PROJECT_ID" \
--location="global" \
--display-name="GitHub Actions"
gcloud iam workload-identity-pools providers create-oidc "$PROVIDER_ID" \
--project="$PROJECT_ID" \
--location="global" \
--workload-identity-pool="$POOL_ID" \
--display-name="GitHub OIDC" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.ref=assertion.ref"
gcloud iam service-accounts create github-actions \
--project="$PROJECT_ID" \
--description="Service account for GitHub deployments" \
--display-name="GitHub Actions"
gcloud iam service-accounts add-iam-policy-binding \
github-actions@$PROJECT_ID.iam.gserviceaccount.com \
--project="$PROJECT_ID" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')/locations/global/workloadIdentityPools/$POOL_ID/attribute.repository/roofsonfire/chat"
2. Assign Permissions to the Service Account¶
Use the same bindings described in the previous section (roles/run.admin, roles/storage.admin, etc.) on github-actions@$PROJECT_ID.iam.gserviceaccount.com.
3. Get the Values to Upload as Secrets¶
PROJECT_NUMBER="$(gcloud projects describe "$PROJECT_ID" --format='value(projectNumber)')"
echo "Provider: projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL_ID/providers/$PROVIDER_ID"
echo "Service Account: github-actions@$PROJECT_ID.iam.gserviceaccount.com"
Save both values; we'll use them in Step 2.
⚠️ Still need JSON keys? Only keep them temporarily during migration. The new workflow will fail if
GCP_WORKLOAD_IDENTITY_PROVIDERorGCP_SERVICE_ACCOUNT_EMAILare not configured.
(Legacy) Create Service Account with JSON Keys¶
Option A: Use gcloud CLI (Recommended)¶
# Configurar variables
PROJECT_ID="norse-breaker-474323-n8"
SA_NAME="github-actions"
SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"
# Create service account
gcloud iam service-accounts create $SA_NAME \
--description="Service account for GitHub Actions deployments" \
--display-name="GitHub Actions"
# Give necessary permissions
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/run.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/storage.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/iam.serviceAccountUser"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/artifactregistry.admin"
# Create and download the key
gcloud iam service-accounts keys create github-actions-key.json \
--iam-account=$SA_EMAIL
# Display the content (you'll need this for GitHub)
cat github-actions-key.json
# ⚠️ IMPORTANT: Save this file securely
# After adding it to GitHub, delete it:
# rm github-actions-key.json
Option B: Use Google Cloud Console¶
- Ve a: https://console.cloud.google.com/iam-admin/serviceaccounts?project=norse-breaker-474323-n8
- Click "Create Service Account"
- Nombre:
github-actions - Description:
Service account for GitHub Actions deployments - Click "Create and Continue"
- Agregar roles:
- Cloud Run Admin
- Storage Admin
- Service Account User
- Artifact Registry Administrator
- Click "Continue" → "Done"
- Click en el service account creado
- Tab "Keys" → "Add Key" → "Create new key"
- Tipo: JSON
- Click "Create" (descargará el archivo)
🔑 Paso 2: Agregar Secret a GitHub¶
Secrets requeridos (Workload Identity Federation)¶
gh secret set GCP_WORKLOAD_IDENTITY_PROVIDER --body "projects/<project-number>/locations/global/workloadIdentityPools/<pool>/providers/<provider>"
gh secret set GCP_SERVICE_ACCOUNT_EMAIL --body "github-actions@<project-id>.iam.gserviceaccount.com"
💡 Recomendado: mantener
PROJECT_ID,REGIONy otros parámetros estáticos como Repository Variables (Settings → Environments → Variables).
Opción Legacy (solo mientras migras): usar GitHub CLI¶
# Instalar GitHub CLI si no lo tienes
# Ubuntu/Debian: sudo apt install gh
# macOS: brew install gh
# Autenticarte
gh auth login
# Agregar el secret (usa el contenido del JSON)
gh secret set GCP_SA_KEY < github-actions-key.json
# Verificar
gh secret list
Opción Legacy B: Usando GitHub UI¶
- Ve a tu repositorio: https://github.com/roofsonfire/chat
- Click "Settings" → "Secrets and variables" → "Actions"
- Click "New repository secret"
- Name:
GCP_SA_KEY - Value: Pega el contenido del archivo
github-actions-key.json - Click "Add secret"
✅ Paso 3: Verificar el Workflow¶
Verificar que existe el workflow¶
Trigger manual (primera vez)¶
- Ve a: https://github.com/roofsonfire/chat/actions
- Click en "Deploy to Cloud Run (Production)"
- Click "Run workflow" → "Run workflow"
- Espera y monitorea el progreso
Push automático¶
# Cualquier push a main triggereará el deployment
git add .
git commit -m "feat: enable GitHub Actions deployment"
git push origin main
# Monitorea en:
# https://github.com/roofsonfire/chat/actions
📊 Paso 4: Monitorear el Deployment¶
En GitHub¶
- Ve a: https://github.com/roofsonfire/chat/actions
- Click en el workflow que se está ejecutando
- Click en el job "Deploy to Cloud Run"
- Ve los logs en tiempo real
En Google Cloud Console¶
# Ver logs del servicio
gcloud run logs tail chat-production --region=us-central1
# Ver detalles del servicio
gcloud run services describe chat-production --region=us-central1
# Ver revisiones (deployments)
gcloud run revisions list --service=chat-production --region=us-central1
🔧 Configuración del Workflow¶
El workflow está en .github/workflows/deploy-production.yml y tiene esta configuración:
env:
PROJECT_ID: norse-breaker-474323-n8
REGION: us-central1
SERVICE_NAME: chat-production
DOMAIN: chat.daza.ar
Modificar recursos del servicio¶
Edita estas líneas en el workflow:
flags: |
--memory=1Gi # Cambiar memoria
--cpu=1 # Cambiar CPU
--max-instances=10 # Máximo de instancias
--min-instances=0 # Mínimo de instancias
Agregar variables de entorno¶
🎯 Testing del Setup¶
Test 1: Verificar Service Account¶
# Listar service accounts
gcloud iam service-accounts list
# Verificar permisos
gcloud projects get-iam-policy norse-breaker-474323-n8 \
--flatten="bindings[].members" \
--format="table(bindings.role)" \
--filter="bindings.members:github-actions@norse-breaker-474323-n8.iam.gserviceaccount.com"
Test 2: Verificar Secret en GitHub¶
# Usando GitHub CLI
gh secret list
# Deberías ver: GCP_WORKLOAD_IDENTITY_PROVIDER, GCP_SERVICE_ACCOUNT_EMAIL
Test 3: Dry Run del Workflow¶
Haz un commit pequeño para probar:
# Crear un cambio pequeño
echo "# GitHub Actions Enabled" >> README.md
git add README.md
git commit -m "test: verify GitHub Actions deployment"
git push origin main
# Monitorea en:
# https://github.com/roofsonfire/chat/actions
🚨 Troubleshooting¶
Error: "Permission denied" o "403 Forbidden"¶
Problema: Service account no tiene permisos suficientes
Solución:
PROJECT_ID="norse-breaker-474323-n8"
SA_EMAIL="github-actions@${PROJECT_ID}.iam.gserviceaccount.com"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/run.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/storage.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/iam.serviceAccountUser"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/artifactregistry.admin"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/cloudbuild.builds.builder"
Error: "Secret GCP_WORKLOAD_IDENTITY_PROVIDER not found"¶
Problema: Falta el secreto requerido para Workload Identity Federation.
Solución:
- Ve a: https://github.com/roofsonfire/chat/settings/secrets/actions
- Confirma que existen
GCP_WORKLOAD_IDENTITY_PROVIDERyGCP_SERVICE_ACCOUNT_EMAIL - Copia los valores con los comandos del Paso 1 y vuelve a crear los secretos si están vacíos
Error: "Failed to push image"¶
Problema: Artifact Registry no está habilitado o no existe el repository
Solución:
# Habilitar API
gcloud services enable artifactregistry.googleapis.com
# Crear repository si no existe
gcloud artifacts repositories create cloud-run-source-deploy \
--repository-format=docker \
--location=us-central1 \
--description="Docker images for Cloud Run deployments"
Workflow no se ejecuta automáticamente¶
Problema: El workflow no se triggerea en push a main
Solución:
- Verifica que el archivo existe:
.github/workflows/deploy-production.yml - Verifica que la sintaxis YAML es correcta
- Ve a GitHub Actions y verifica si hay errores
- Asegúrate de hacer push a
main(no otra rama)
🔐 Seguridad¶
Mejores Prácticas¶
- ✅ Service Account con permisos mínimos: Solo dar los roles necesarios
- ✅ Sin llaves permanentes: Usa Workload Identity Federation y elimina
GCP_SA_KEY - ✅ No commitear credenciales: Mantén las configuraciones en Secrets/Variables
- ✅ Workload Identity Federation habilitada:
# En deploy-production.yml, reemplazar:
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
workload_identity_provider: "projects/123456789/locations/global/workloadIdentityPools/github/providers/github"
service_account: "github-actions@norse-breaker-474323-n8.iam.gserviceaccount.com"
Eliminar Service Account (si necesitas reset)¶
PROJECT_ID="norse-breaker-474323-n8"
SA_EMAIL="github-actions@${PROJECT_ID}.iam.gserviceaccount.com"
# Eliminar service account
gcloud iam service-accounts delete $SA_EMAIL
📈 Mejoras Futuras¶
1. Agregar Environment en GitHub¶
Ve a: https://github.com/roofsonfire/chat/settings/environments
- Crear environment "production" (o el que prefieras proteger)
- Agregar "Required reviewers" si quieres aprobación manual
- Agregar "Wait timer" para delays antes de deployment
Luego en el workflow:
2. Agregar Notificaciones¶
Agregar step para notificar en Slack/Discord:
- name: Notify Slack
if: always()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
3. Deployment Preview para PRs¶
Crear entornos temporales por cada PR (más avanzado)
📚 Recursos¶
- GitHub Actions - Google Cloud
- Cloud Run Documentation
- Workload Identity Federation
- Service Account Best Practices
✅ Checklist Completo¶
- Service account creado en Google Cloud
- Permisos agregados al service account
- JSON key descargado
- Secretos
GCP_WORKLOAD_IDENTITY_PROVIDERyGCP_SERVICE_ACCOUNT_EMAILagregados en GitHub - (Legacy) Secret
GCP_SA_KEYeliminado una vez terminada la migración - Workflow file existe:
.github/workflows/deploy-production.yml - Test deployment ejecutado
- Deployment exitoso verificado
- Service accesible en: https://chat.daza.ar
- JSON key eliminado localmente (seguridad)
¿Listo para empezar? Ejecuta los comandos del Paso 1! 🚀