From ac-tools
Interactively sets up GCP Cloud Build + Cloud Run infrastructure. Provisions APIs, Artifact Registry, service accounts, IAM, secrets, and triggers with security best practices.
npx claudepluginhub waterplanai/agentic-config --plugin ac-toolsThis skill is limited to using the following tools:
Interactive skill for bootstrapping GCP infrastructure for Cloud Build + Cloud Run projects. Handles everything from `gcloud auth login` to verified production deploy.
agents/provisioner.mdagents/security-auditor.mdassets/agents.template.mdassets/cloudbuild-template.yamlassets/dockerfile-go.templateassets/dockerfile-node.templateassets/dockerfile-python.templateassets/dockerignore-templateassets/env-exampleassets/gcp-setup-example.ymlassets/managing-secrets-and-envs.template.mdassets/pr-template.mdassets/readme.template.mdcookbook/auth-proxy.mdcookbook/authjs-setup.mdcookbook/build-setup.mdcookbook/environments.mdcookbook/iap-setup.mdcookbook/oauth-setup.mdcookbook/prerequisites.mdGuides secure deployments to Google Cloud Storage (static apps), Cloud Run, or GKE (dynamic/container apps) after analyzing app type via package.json and enforcing secret scanning.
Guides finding GCP Cloud Run services for Your Project via Cloud Console, gcloud CLI, and repo scripts; covers dev/staging/prod deployments and URLs.
Guides deploying containers to Google Cloud Run with gcloud commands, service.yaml editing, scaling, concurrency, traffic splitting, cold starts, jobs, GPU setup, networking, and secrets mounting.
Share bugs, ideas, or general feedback.
Interactive skill for bootstrapping GCP infrastructure for Cloud Build + Cloud Run projects. Handles everything from gcloud auth login to verified production deploy.
Every phase validates security posture. The skill enforces:
All tools are relative to this skill directory:
SKILL_DIR="${CLAUDE_PLUGIN_ROOT}/skills/gcp-setup"
Detect and load configuration before any infrastructure operations.
Check ~/.agents/customization/gcp-setup/index.md for saved preferences.
If missing, use AskUserQuestion to collect:
example.com)Example Corp)ExampleOrg)us-central1)Save to ~/.agents/customization/gcp-setup/index.md:
# GCP Setup Preferences
- domain: example.com
- company: Example Corp
- github_org: ExampleOrg
- region: us-central1
If values already exist, present them as defaults. If user provides different values, update index.md.
Detect .gcp-setup.yml in the project root.
If exists: Load and present current config to user. AskUserQuestion: "Config found. Proceed with these settings, or modify?"
If missing: Auto-detect everything possible, then confirm. Typeform-style: one question at a time, smart defaults, minimal friction.
Step 1: Silent auto-detection (no user interaction — run all probes first)
# GitHub repo from git remote
GITHUB_SLUG=$(git remote get-url origin 2>/dev/null | sed -E 's#.*[:/]([^/]+/[^/.]+)(\.git)?$#\1#')
DIR_NAME=$(basename "$(pwd)")
# Runtime + framework
RUNTIME=""
[[ -f package.json ]] && RUNTIME="nodejs"
[[ -f requirements.txt || -f pyproject.toml ]] && RUNTIME="python"
[[ -f go.mod ]] && RUNTIME="go"
FRAMEWORK=""
grep -q '"express"' package.json 2>/dev/null && FRAMEWORK="express"
grep -q 'fastapi' requirements.txt pyproject.toml 2>/dev/null && FRAMEWORK="fastapi"
# Feature detection from code/dependencies
HAS_OAUTH=$(grep -rl -m 1 "passport\|GoogleStrategy\|google-auth-library\|authlib" \
--include="*.ts" --include="*.js" --include="*.py" \
--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=vendor . 2>/dev/null | head -1)
HAS_FIRESTORE=$(grep -rl -m 1 "@google-cloud/firestore\|google.cloud.firestore" \
--include="*.ts" --include="*.js" --include="*.py" --include="*.go" \
--exclude-dir=node_modules --exclude-dir=.git --exclude-dir=vendor . 2>/dev/null | head -1)
# Build configs, Dockerfile, health endpoint
CLOUDBUILD_FILES=$(ls cloudbuild*.yaml cloudbuild*.yml 2>/dev/null || true)
DOCKERFILE=$(ls Dockerfile */Dockerfile 2>/dev/null | head -1)
HEALTH_EP=$(grep -roh '"/health[z]*"\|"/api/health"' \
--include="*.ts" --include="*.js" --include="*.py" . 2>/dev/null | head -1 | tr -d '"')
# Secret manifest from .env.example
ENV_EXAMPLE=$(ls .env.example .env.sample env.example 2>/dev/null | head -1)
# GCP projects (for matching)
GCP_PROJECTS=$(gcloud projects list --format="value(projectId)" 2>/dev/null)
Load user preferences from Phase 0a (~/.agents/customization/gcp-setup/index.md): domain, company, org, region.
Step 2: Typeform questionnaire — one AskUserQuestion per step
Rules:
Q1: GCP Projects (required — cannot auto-detect)
Filter $GCP_PROJECTS for pairs matching *-stage/*-dev + *-prod patterns, or containing $DIR_NAME. Present best-match pair first:
detected-app-dev + detected-app-prod (if pair found)Multi-app pattern: Multiple apps can share the same GCP projects. Each app gets its own
.gcp-setup.ymlwith uniquesa_prefix,service_name, and secret names. Shared resources (AR repo, KMS keyring, GitHub connection) are created idempotently — the second app's provisioning simply finds them already in place. Seecookbook/environments.mdfor naming conventions and examples.
Q2: Service Names (skip if $DIR_NAME is clean)
${DIR_NAME}-stage, ${DIR_NAME} — convention: stage suffixed, prod is base nameIf $DIR_NAME produces clean service names (lowercase, no special chars, <30 chars), skip this question and use option 1 automatically — show in review.
Q3: Detected Features (confirmation only)
Present auto-detected features as a summary:
$RUNTIME / $FRAMEWORK (or "not detected")$HAS_OAUTH)$HAS_FIRESTORE)$HEALTH_EP or /api/health (default)If nothing ambiguous was detected, skip this question too.
Q3b: OAuth Mode (conditional — only when OAuth = Yes from Q3)
How should this app handle authentication?
Standalone — This app authenticates users directly via Google OAuth. Each app needs its own redirect URIs registered in the GCP Console. Standard setup for single apps or small teams.
Auth-proxy — This app IS a centralized authentication service. Register its OAuth redirect URIs once. Future apps delegate auth to this proxy — no additional Console configuration needed per app. See
cookbook/auth-proxy.mdfor full architecture.IAP (recommended for internal tools) — Google Cloud Identity-Aware Proxy. Google manages the entire auth flow. Enable per service with one CLI flag (
--iap). No redirect URIs, no OAuth clients to manage per app, no extra infrastructure. Access controlled via IAM policies. Trade-off: no custom login UI (Google's IAP login page). Seecookbook/iap-setup.mdfor setup guide.
Cannot be auto-detected — always ask when OAuth is enabled.
Set oauth.mode to standalone (option 1), auth-proxy (option 2), or iap (option 3).
Q4: Review & Generate
Build the COMPLETE config YAML from auto-detected values + user answers + conventions. Present it formatted.
Convention-derived values (NEVER ask the user for these):
artifact_registry.repo_name: docker-imagessa_prefix.runtime: app-runtimesa_prefix.cloudbuild: app-cloudbuildkms.keyring: cloudbuild-keyskms.key: connection-keygithub.connection_name: repo name from sluggithub.linked_repo: slug with / replaced by -oauth.callback_path: /auth/google/cb (Express + Passport) or /api/auth/callback/google (Auth.js / NextAuth) — register BOTH in OAuth client redirect URIsoauth.domain_restriction: company domain from user preferencesoauth.consent_type: Internaloauth.mode: from Q3b answer (default: standalone). When iap is selected, oauth.callback_path and oauth.consent_type are not used (IAP manages these).firestore.collection: express-sessionsfirestore.ttl_field: expiresAtcloudbuild-stage.yaml / cloudbuild-prod.yamlsession-secret with auto_generate: true.gcp-setup.ymlGenerate .gcp-setup.yml using assets/gcp-setup-example.yml as template reference. Write to project root.
.gcp-setup.ymlcontains no secrets and should be committed to version control.
After saving config, ensure .env files are gitignored:
# Ensure .env files are gitignored (secret values must never be committed)
if [[ -f .gitignore ]]; then
grep -qxF '.env' .gitignore || echo '.env' >> .gitignore
grep -qxF '.env.*' .gitignore || echo '.env.*' >> .gitignore
else
printf '.env\n.env.*\n' > .gitignore
fi
After config is saved, check for missing build files and generate from templates. See cookbook/build-setup.md for the full non-tech guide.
Detection (uses variables from Phase 0b Step 1):
HAS_DOCKERFILE=$([[ -n "$DOCKERFILE" ]] && echo true || echo false)
HAS_DOCKERIGNORE=$([[ -f .dockerignore ]] && echo true || echo false)
HAS_CLOUDBUILD=$([[ -n "$CLOUDBUILD_FILES" ]] && echo true || echo false)
1. .dockerignore (always generate if missing):
cp "$SKILL_DIR/assets/dockerignore-template" .dockerignore
2. Dockerfile (generate if missing, based on detected $RUNTIME):
| Runtime | Template | Default CMD |
|---|---|---|
nodejs | $SKILL_DIR/assets/dockerfile-node.template | CMD ["node", "server.js"] |
python | $SKILL_DIR/assets/dockerfile-python.template | CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"] |
go | $SKILL_DIR/assets/dockerfile-go.template | ENTRYPOINT ["/app/server"] |
After copying the template, adjust the CMD/ENTRYPOINT based on detected framework and entry point:
main field in package.json or server.js/index.js/app.jsCMD ["npm", "start"]main:app or app.main:app from importsapp:app or project.wsgi:applicationIf runtime is not detected, use AskUserQuestion to ask which runtime to use.
3. CloudBuild YAMLs (generate if missing):
Read $SKILL_DIR/assets/cloudbuild-template.yaml and substitute tokens from config. Generate TWO files:
cloudbuild-stage.yaml:
CONFIG=".gcp-setup.yml"
PROJECT_ID=$(yq -r '.projects.stage.id' "$CONFIG")
SERVICE_NAME=$(yq -r '.projects.stage.service_name' "$CONFIG")
REGION=$(yq -r '.region' "$CONFIG")
REPO_NAME=$(yq -r '.artifact_registry.repo_name' "$CONFIG")
IMAGE_NAME="$SERVICE_NAME"
MAX_INSTANCES=$(yq -r '.app.max_instances_stage // 5' "$CONFIG")
SA_PREFIX=$(yq -r '.sa_prefix.runtime' "$CONFIG")
RUNTIME_SA="${SA_PREFIX}@${PROJECT_ID}.iam.gserviceaccount.com"
APP_DIR=$(dirname "$(yq -r '.app.dockerfile_path // "Dockerfile"' "$CONFIG")")
[[ "$APP_DIR" == "." || -z "$APP_DIR" ]] && APP_DIR="."
# Resolve auth flag and IAP settings based on oauth.mode
OAUTH_MODE=$(yq -r '.oauth.mode // "standalone"' "$CONFIG")
if [[ "$OAUTH_MODE" == "iap" ]]; then
_AUTH_FLAG="--no-allow-unauthenticated"
_IAP_FLAG="--iap"
_ENTRYPOINT="gcloud beta"
else
_AUTH_FLAG="--allow-unauthenticated"
_IAP_FLAG=""
_ENTRYPOINT="gcloud"
fi
# Generate secrets mapping from manifest
SECRETS_MAPPING=""
SECRET_COUNT=$(yq -r '.secrets | length' "$CONFIG")
for ((i=0; i<SECRET_COUNT; i++)); do
NAME=$(yq -r ".secrets[$i].name" "$CONFIG")
ENV_VAR=$(yq -r ".secrets[$i].env_var" "$CONFIG")
[[ -n "$SECRETS_MAPPING" ]] && SECRETS_MAPPING="${SECRETS_MAPPING},"
SECRETS_MAPPING="${SECRETS_MAPPING}${ENV_VAR}=${NAME}:latest"
done
Substitute all __TOKEN__ placeholders in the template and write to cloudbuild-stage.yaml:
__ENTRYPOINT__ with $_ENTRYPOINT (i.e., gcloud beta for IAP, gcloud otherwise)__AUTH_FLAG__ with $_AUTH_FLAG$_IAP_FLAG is non-empty, replace __IAP_FLAG__ with --iap$_IAP_FLAG is empty, remove the entire - '__IAP_FLAG__' line from the generated YAML (do not leave an empty string arg)$SECRETS_MAPPING is empty, remove the entire --set-secrets=__SECRETS_MAPPING__ line (an empty --set-secrets= causes deploy failure)$ENV_VARS is empty, remove the entire --set-env-vars=__ENV_VARS__ line (an empty --set-env-vars= causes deploy failure)cloudbuild-prod.yaml: Same process with prod values and max_instances_prod (default: 100).
AskUserQuestion gate: Present a summary of generated files:
Generated build files:
- .dockerignore (universal exclusions)
- Dockerfile (Node.js multi-stage, non-root)
- cloudbuild-stage.yaml (stage project, max 5 instances)
- cloudbuild-prod.yaml (prod project, max 100 instances)
Review and confirm, or modify specific files.
All generated files should be committed to version control.
After build files are in place, generate project documentation if missing. These docs help onboard new team members and guide AI assistants.
Detection:
HAS_SECRETS_GUIDE=$([[ -f docs/managing-secrets-and-envs.md ]] && echo true || echo false)
HAS_README=$([[ -f README.md ]] && echo true || echo false)
HAS_AGENTS=$([[ -f AGENTS.md || -f CLAUDE.md ]] && echo true || echo false)
Compute deterministic URLs (needed for templates):
CONFIG=".gcp-setup.yml"
STAGE_PROJECT=$(yq -r '.projects.stage.id' "$CONFIG")
PROD_PROJECT=$(yq -r '.projects.prod.id' "$CONFIG")
STAGE_SERVICE=$(yq -r '.projects.stage.service_name' "$CONFIG")
PROD_SERVICE=$(yq -r '.projects.prod.service_name' "$CONFIG")
REGION=$(yq -r '.region' "$CONFIG")
STAGE_NUM=$(gcloud projects describe "$STAGE_PROJECT" --format="value(projectNumber)")
PROD_NUM=$(gcloud projects describe "$PROD_PROJECT" --format="value(projectNumber)")
STAGE_URL="https://${STAGE_SERVICE}-${STAGE_NUM}.${REGION}.run.app"
PROD_URL="https://${PROD_SERVICE}-${PROD_NUM}.${REGION}.run.app"
CB_CONSOLE_STAGE="https://console.cloud.google.com/cloud-build/builds;region=${REGION}?project=${STAGE_PROJECT}"
CB_CONSOLE_PROD="https://console.cloud.google.com/cloud-build/builds;region=${REGION}?project=${PROD_PROJECT}"
For each missing doc, generate from template:
docs/managing-secrets-and-envs.md — Read $SKILL_DIR/assets/managing-secrets-and-envs.template.md, substitute tokens from config. Generate ${SECRETS_TABLE} from secrets manifest:
| Secret Name | Env Var | Environments |
|-------------|---------|--------------|
| oauth-client-id | GOOGLE_CLIENT_ID | stage, prod |
...
Generate ${ENV_VARS_TABLE} for non-secret env vars (AUTH_URL, AUTH_TRUST_HOST, etc.).
Create docs/ directory if needed.
README.md — Read $SKILL_DIR/assets/readme.template.md, substitute tokens. Set ${PROJECT_NAME} from directory name (title-cased). Set ${PROJECT_DESCRIPTION} from a brief AskUserQuestion or auto-detect from package.json description. Compute ${HEALTH_ENDPOINT} and ${HEALTH_RESPONSE} from config.
AGENTS.md — Read $SKILL_DIR/assets/agents.template.md, substitute tokens. Framework-specific values:
| Token | Node.js/Express | Node.js/Next.js | Python/FastAPI | Go |
|---|---|---|---|---|
${RUNTIME_DISPLAY} | Node.js 22 (Alpine in Docker) | Node.js 22 (Alpine in Docker) | Python 3.12 (slim in Docker) | Go 1.23 (distroless in Docker) |
${LANGUAGE} | JavaScript/TypeScript | TypeScript (strict mode) | Python | Go |
${PACKAGE_MANAGER} | npm | npm | pip | go modules |
${BUILD_CMD} | npm run build | npm run build | N/A | go build |
${DEV_CMD} | npm run dev | npm run dev | uvicorn main:app --reload | go run . |
${LINT_CMD} | npm run lint | npm run lint | ruff check | golangci-lint run |
${TYPECHECK_CMD} | npx tsc --noEmit | npx tsc --noEmit | pyright | go vet ./... |
${STYLE_CONVENTIONS} — framework-specific conventions (App Router for Next.js, route structure for Express, etc.).
${AUTH_DESCRIPTION} — derived from oauth config (e.g., "Google OAuth with @example.com domain restriction (Auth.js v5)").
AskUserQuestion gate: Present generated docs summary:
Generated project documentation:
- docs/managing-secrets-and-envs.md (secrets guide with current inventory)
- README.md (environments, local dev, deploy workflow)
- AGENTS.md (AI assistant project guidelines)
Review and confirm, or modify specific files.
All generated docs should be committed to version control.
Probe existing infrastructure state (unchanged logic, reads from config):
CONFIG=".gcp-setup.yml"
STAGE=$(yq -r '.projects.stage.id' "$CONFIG")
PROD=$(yq -r '.projects.prod.id' "$CONFIG")
# Quick check — do APIs exist?
gcloud services list --enabled --filter="name:run.googleapis.com" --project="$STAGE" --format="value(name)" 2>/dev/null
Present state table to user and use AskUserQuestion:
bash "$SKILL_DIR/tools/preflight.sh" --config .gcp-setup.yml
AskUserQuestion gate: If any required check fails, present install instructions and wait for confirmation before proceeding.
MANDATORY — CMEK Encryption Key Setup:
Before creating the GitHub connection, provision a Cloud KMS encryption key. Connection data (including GitHub OAuth tokens stored in Secret Manager) MUST be encrypted with a customer-managed key.
bash "$SKILL_DIR/tools/provision.sh" --config .gcp-setup.yml --cmek-only --env all
This enables the Cloud KMS API, creates the keyring + key, provisions the Secret Manager service agent, and grants it cryptoKeyEncrypterDecrypter on the key.
MANUAL GATE — 2nd-gen GitHub Connection:
After CMEK setup, the user must create GitHub connections in both GCP projects. This CANNOT be automated (requires browser OAuth).
Use AskUserQuestion: "CMEK keys are provisioned. Have you created the 2nd-gen GitHub connection in Cloud Build for both projects? You MUST select the encryption key during connection creation."
If NO, read and present the guide:
cat "$SKILL_DIR/cookbook/oauth-setup.md"
Present the Console URLs (read project IDs from config):
STAGE=$(yq -r '.projects.stage.id' .gcp-setup.yml)
PROD=$(yq -r '.projects.prod.id' .gcp-setup.yml)
echo "Stage: https://console.cloud.google.com/cloud-build/repositories/2nd-gen?project=$STAGE"
echo "Prod: https://console.cloud.google.com/cloud-build/repositories/2nd-gen?project=$PROD"
Steps:
github.connection_nameSECURITY WARNING: Do NOT skip the encryption key selection. Google-managed encryption is NOT acceptable. The connection stores GitHub OAuth tokens in Secret Manager — these must be encrypted with the customer-managed key for key rotation control and compliance.
Wait for user confirmation.
Run provision:
bash "$SKILL_DIR/tools/provision.sh" --config .gcp-setup.yml --env all
Note: The temporary
secretmanager.admingrant auto-expires in 20 minutes. No manual revocation needed.
Create secret shells:
bash "$SKILL_DIR/tools/secrets.sh" --config .gcp-setup.yml --action create --env all
AskUserQuestion: "How do you want to populate secrets?"
--action populate --env-file /path/to/.envPopulate secrets:
bash "$SKILL_DIR/tools/secrets.sh" --config .gcp-setup.yml \
--action populate --env "$TARGET_ENV" --env-file "$ENV_FILE"
SECURITY WARNING: If populating both envs from the same .env, warn:
"OAuth credentials SHOULD differ per environment. Consider running
--env stagefirst, then--env prod --env-file /path/to/prod.env."
Skip entirely when oauth.enabled is false in config.
Read oauth.mode from config (default: standalone).
When oauth.mode is iap, authentication is handled entirely by Google Cloud Identity-Aware Proxy. No OAuth clients, no redirect URIs, no custom auth code needed.
One-time project setup (first time enabling IAP in this GCP project):
Enable required APIs:
for PROJECT in "$STAGE_PROJECT" "$PROD_PROJECT"; do
gcloud services enable iap.googleapis.com cloudresourcemanager.googleapis.com --project="$PROJECT"
done
Configure Google Auth Platform (Console — one time per project):
openid, email, profileAskUserQuestion gate:
Per-service setup (automated — repeat for each app):
Enable IAP on Cloud Run services:
for PROJECT in "$STAGE_PROJECT" "$PROD_PROJECT"; do
ENV_KEY=$( [ "$PROJECT" = "$STAGE_PROJECT" ] && echo stage || echo prod )
SERVICE_NAME=$(yq -r ".projects.${ENV_KEY}.service_name" "$CONFIG")
# For existing services:
gcloud beta run services update "$SERVICE_NAME" \
--region="$REGION" \
--iap \
--project="$PROJECT"
# For new deploys, use: gcloud beta run deploy ... --iap --no-allow-unauthenticated
done
Note:
services update --iapworks on already-deployed services. For first deploys, usegcloud beta run deploy ... --iap --no-allow-unauthenticatedor add these flags to the CloudBuild deploy step.
Grant IAP service agent invoker permission:
for PROJECT in "$STAGE_PROJECT" "$PROD_PROJECT"; do
ENV_KEY=$( [ "$PROJECT" = "$STAGE_PROJECT" ] && echo stage || echo prod )
SERVICE_NAME=$(yq -r ".projects.${ENV_KEY}.service_name" "$CONFIG")
PROJECT_NUMBER=$(gcloud projects describe "$PROJECT" --format='value(projectNumber)')
gcloud run services add-iam-policy-binding "$SERVICE_NAME" \
--region="$REGION" \
--member="serviceAccount:service-${PROJECT_NUMBER}@gcp-sa-iap.iam.gserviceaccount.com" \
--role="roles/run.invoker" \
--project="$PROJECT"
done
Remove public access (if the service was previously public):
for PROJECT in "$STAGE_PROJECT" "$PROD_PROJECT"; do
ENV_KEY=$( [ "$PROJECT" = "$STAGE_PROJECT" ] && echo stage || echo prod )
SERVICE_NAME=$(yq -r ".projects.${ENV_KEY}.service_name" "$CONFIG")
gcloud run services remove-iam-policy-binding "$SERVICE_NAME" \
--region="$REGION" \
--member="allUsers" \
--role="roles/run.invoker" \
--project="$PROJECT" 2>/dev/null || true
done
Warning: If
allUsershasroles/run.invoker, anyone can bypass IAP by calling the Cloud Run URL directly. Always remove public access when enabling IAP.
Grant domain access:
DOMAIN=$(yq -r '.oauth.domain_restriction' "$CONFIG")
for PROJECT in "$STAGE_PROJECT" "$PROD_PROJECT"; do
ENV_KEY=$( [ "$PROJECT" = "$STAGE_PROJECT" ] && echo stage || echo prod )
SERVICE_NAME=$(yq -r ".projects.${ENV_KEY}.service_name" "$CONFIG")
gcloud beta iap web add-iam-policy-binding \
--resource-type=cloud-run \
--service="$SERVICE_NAME" \
--region="$REGION" \
--member="domain:${DOMAIN}" \
--role="roles/iap.httpsResourceAccessor" \
--project="$PROJECT"
done
Read the IAP cookbook:
cat "$SKILL_DIR/cookbook/iap-setup.md"
Present IAP guidance to the user:
X-Goog-Authenticated-User-Email and X-Goog-IAP-JWT-Assertion.--iap --no-allow-unauthenticated to the deploy command and run the IAM bindings above. No Console changes needed.cookbook/iap-setup.md for header verification, service-to-service auth, and local development.Note: When
oauth.modeisiap, OAuth secrets (oauth-client-id,oauth-client-secret) are NOT needed in the secrets manifest. Google manages the OAuth client internally. Onlysession-secretis needed if the app manages its own sessions.
When oauth.mode is standalone (or not set):
SECURITY WARNING — Display this to the user before proceeding:
The OAuth consent screen MUST be set to the configured consent type (default: "Internal"). If set to "Internal", this restricts authentication to the configured domain only. Do NOT select "External" unless required — doing so would allow ANY Google account to reach the OAuth flow. If you are unsure, stop and consult the team.
This sub-phase is entirely manual. The skill provides exact step-by-step guidance.
Compute deterministic Cloud Run URLs:
CONFIG=".gcp-setup.yml"
STAGE=$(yq -r '.projects.stage.id' "$CONFIG")
PROD=$(yq -r '.projects.prod.id' "$CONFIG")
STAGE_SVC=$(yq -r '.projects.stage.service_name' "$CONFIG")
PROD_SVC=$(yq -r '.projects.prod.service_name' "$CONFIG")
REGION=$(yq -r '.region' "$CONFIG")
STAGE_NUM=$(gcloud projects describe "$STAGE" --format="value(projectNumber)")
PROD_NUM=$(gcloud projects describe "$PROD" --format="value(projectNumber)")
echo "Stage URL: https://${STAGE_SVC}-${STAGE_NUM}.${REGION}.run.app"
echo "Prod URL: https://${PROD_SVC}-${PROD_NUM}.${REGION}.run.app"
Warning: Do NOT use
status.urlto get the service URL for OAuth configuration. It returns a legacy hash-based URL that differs from the deterministic format. Always compute deterministic URLs.
Read the full OAuth guide:
cat "$SKILL_DIR/cookbook/oauth-setup.md"
Present step-by-step with pre-computed URLs:
https://<cloud-run-url><callback_path from config>AskUserQuestion gates:
When oauth.mode is auth-proxy, first complete ALL standalone steps above (the proxy itself needs OAuth registration), then:
Read the auth-proxy cookbook:
cat "$SKILL_DIR/cookbook/auth-proxy.md"
Present auth-proxy guidance to the user:
return_url allowlist validation, domain restriction.https://<proxy-service>-<project-num>.<region>.run.appcookbook/auth-proxy.md for the complete architecture, implementation checklist, and client integration guide.AskUserQuestion gate:
cookbook/auth-proxy.md? This app will serve as the centralized auth service for future apps."Run full verification:
bash "$SKILL_DIR/tools/verify.sh" --config .gcp-setup.yml --env all
Interpret results:
After all phases pass, guide the user through the first deploy:
/gcbrun on the PR to trigger stage buildIf issues arise post-deploy, run diagnostics:
bash "$SKILL_DIR/tools/diagnose.sh" --config .gcp-setup.yml --env "$TARGET_ENV"
For detailed troubleshooting reference:
cat "$SKILL_DIR/cookbook/troubleshooting.md"
To add the deploy checklist to PRs:
cat "$SKILL_DIR/assets/pr-template.md"
Copy this to .github/PULL_REQUEST_TEMPLATE.md, replacing ${STAGE_URL} and ${CB_CONSOLE_URL} with actual values.
| Resource | Location |
|---|---|
| Build setup guide | cookbook/build-setup.md |
| Prerequisites | cookbook/prerequisites.md |
| Security model | cookbook/security-model.md |
| OAuth setup guide | cookbook/oauth-setup.md |
| Auth.js on Cloud Run | cookbook/authjs-setup.md |
| Auth-proxy guide | cookbook/auth-proxy.md |
| IAP setup guide | cookbook/iap-setup.md |
| Troubleshooting | cookbook/troubleshooting.md |
| Environment configs | cookbook/environments.md |
| Cloud Build template | assets/cloudbuild-template.yaml |
| PR template | assets/pr-template.md |
| .env template | assets/env-example |
| Secrets guide template | assets/managing-secrets-and-envs.template.md |
| README template | assets/readme.template.md |
| AGENTS.md template | assets/agents.template.md |
| Config example | assets/gcp-setup-example.yml |