View as MarkdownEdit on GitHub

CI/CD and production

Set up a CI/CD pipeline that tests on pull request, deploys to staging on merge, and then promotes the same commit to production behind an approval gate.

Prototype to Production


How the pipeline works#

  1. CI pipeline (triggered on pull request):

    • Runs unit and integration tests.
  2. Staging CD pipeline (triggered on merge to main):

    • Builds the container image. On Cloud Run and GKE, the pipeline pushes it to Artifact Registry. On Agent Runtime, Agent Runtime builds it from your Dockerfile.
    • Deploys to the staging environment.
    • Runs automated load testing.
  3. Production deployment (triggered after successful staging):

    • Waits for manual approval. Cloud Build always requires approval. On GitHub Actions, the job targets the production environment, and it waits only after you add a Required reviewers protection rule to that environment in your repository settings.
    • Builds and deploys the same commit that passed staging.

Set up the pipeline#

Before you start, sign in to the GitHub CLI with gh auth login, and make sure the project isn't in a gitignored folder. The infra cicd command supports GitHub repositories only, and it needs both a staging and a production project. For a single environment, use agents-cli infra single-project and agents-cli deploy instead.

The agents-cli infra cicd command runs terraform plan by default. Preview the changes first:

agents-cli infra cicd \
  --staging-project my-staging-project \
  --prod-project my-prod-project \
  --repository-name my-agent

Plan mode still does the setup that an accurate plan needs. It writes env.tfvars and backend.tf, creates the <cicd-project>-terraform-state bucket if it is missing (skipped with --local-state), and checks GitHub CLI auth and scopes. For Cloud Build, it also stores your GitHub token in Secret Manager or creates the GitHub connection.

When the plan looks right, re-run the same command with --apply:

agents-cli infra cicd \
  --staging-project my-staging-project \
  --prod-project my-prod-project \
  --repository-name my-agent \
  --apply

With --apply, the command sets up:

  • Infrastructure provisioning via Terraform for staging and production
  • CI/CD configuration with your chosen runner (Cloud Build or GitHub Actions)
  • Repository connection to GitHub

The command doesn't deploy anything. Push to main to start the first pipeline run. Staging CD runs only when relevant paths change (Python: the agent directory, tests/**, deployment/**, uv.lock; Go: the agent directory, e2e/**, deployment/**, go.mod, go.sum), so a push that touches none of them doesn't trigger it.

CI/CD runner detection#

Without --cicd-runner, the command picks the runner from the files in deployment/terraform/cicd/:

RunnerHow it's detected
GitHub Actionswif.tf and github.tf both exist. Uses Workload Identity Federation for keyless auth.
Google Cloud BuildAny other case. Sets up a Cloud Build connection to GitHub.

Options#

FlagDescription
--staging-projectGCP project ID for staging (required unless --interactive)
--prod-projectGCP project ID for production (required unless --interactive)
--cicd-projectSeparate project for CI/CD resources (defaults to prod)
--dev-projectDev project (optional, provisions dev infra too)
--regionGCP region (auto-detected from Terraform vars if not set, otherwise us-east1)
--repository-nameGitHub repository name (required unless --interactive)
--repository-ownerGitHub repository owner (defaults to the current GitHub user)
--host-connection-nameCloud Build host connection name (defaults to git-<repository-name>)
--github-patGitHub personal access token. Cloud Build needs it, with --github-app-installation-id, when you don't pass --interactive. Defaults to $GH_TOKEN or $GITHUB_TOKEN. With --interactive, Cloud Build connects to GitHub through a browser OAuth flow instead.
--github-app-installation-idGitHub App installation ID for programmatic auth. Cloud Build needs it without --interactive.
--local-stateUse local Terraform state instead of the remote GCS backend
--createCreate a new GitHub repository (omit to use an existing one)
--cicd-runnergoogle_cloud_build or github_actions (auto-detected if not set)
--applyApply the changes. Without it, only a plan is shown.
--force-bucket, --forceAllow a Terraform state bucket that exists in a different GCP project. Off by default to guard against bucket squatting.
-i, --interactivePrompt for missing values
--debugEnable debug logging

Terraform variables#

The pipeline uses Terraform variables defined in deployment/terraform/cicd/variables.tf:

VariableDescription
project_nameBase name for resource naming
prod_project_idGoogle Cloud project ID for production
staging_project_idGoogle Cloud project ID for staging
cicd_runner_project_idGoogle Cloud project ID where CI/CD pipelines execute
regionGoogle Cloud region (default: us-east1)
repository_nameGitHub repository name
repository_ownerGitHub username or organization
app_sa_rolesRoles for the application service account
cicd_rolesRoles for CI/CD runner service account

Once your agent is deployed, register it with the Gemini app using agents-cli publish gemini. Run with --help for all options.

Last updated