You are designing a .gitlab-ci.yml for a monorepo containing a Go backend, React frontend, and Terraform infrastructure code. The pipeline should only build/test changed components, support merge request pipelines, and deploy to multiple environments. Walk through your pipeline architecture.
Quick Answer
Use rules:changes to trigger jobs only for modified paths, merge_request pipelines for PR validation, extends/includes for DRY configuration, and environment-specific stages with manual approval gates for production deployment.
Detailed Answer
Pipeline Architecture for Monorepos
Monorepo CI/CD requires selective execution—you don't want to rebuild the frontend when only Terraform files change. GitLab's rules:changes directive triggers jobs based on file path patterns. Combined with include:local for per-component pipeline definitions and extends for shared job templates, you can build a maintainable pipeline that scales with the repository. The pipeline should use a DAG (Directed Acyclic Graph) structure with needs keywords to maximize parallelism.
Merge Request Pipelines vs Branch Pipelines
Merge request pipelines (rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event') run only when an MR is opened or updated. They see both source and target branch, enabling accurate changes detection against the merge target. Branch pipelines (rules: - if: $CI_COMMIT_BRANCH) run on every push. Best practice: use MR pipelines for validation (lint, test, security scan) and branch pipelines only for deployment after merge. Avoid duplicate pipelines with workflow:rules to ensure only one pipeline type runs.
Environment Strategy
Define environments (dev, staging, production) using GitLab environments with deployment tracking. Staging deploys automatically on merge to main. Production requires manual approval using when: manual with allow_failure: false (creates a blocking gate). Use environment:auto_stop_in for review apps that automatically destroy after 24 hours. Protected environments restrict who can trigger production deployments to maintainers.
Caching and Artifact Strategy
Cache dependencies per-component using component-specific cache keys (e.g., $CI_PROJECT_DIR/backend/go.sum for Go modules). Artifacts pass build outputs between stages. Use artifacts:expire_in to prevent storage bloat—test results expire in 1 week, release binaries in 30 days. dependencies keyword controls which artifacts a job downloads, preventing unnecessary data transfer.
Security and Compliance Integration
Include GitLab's SAST/DAST templates for automated security scanning. Compliance pipelines (available in Ultimate) enforce required jobs that project teams cannot bypass. Use protected variables for production secrets, masked variables for tokens, and file-type variables for certificates. The pipeline should fail-fast on security issues while allowing teams to acknowledge and track accepted risks.