56 results available
Sign in
Skills directory

gitlab-ci-patterns

wshobson

·

Last changed
Hardened

This skill has been hardened by Chainguard

Last changed

This version passed the hardening gate. Hardening applied 11 targeted changes and preserved all other text. Classified findings before hardening: 1. Classified findings closed: 1. Classified findings remaining: 0. Unclassified scanner findings remaining: 0. Review findings closed: 19. Review findings remaining: 2.


Open before hardening

1

Medium
Open after hardening

None


Expand
File
Group
Subtype
Severity
Status
SKILL.md

Excessive Agency & Rogue Autonomy

—

Medium
Closed

1 finding


SKILL.md

GitLab CI Patterns

Comprehensive GitLab CI/CD pipeline patterns for automated testing, building, and deployment.

Purpose

Create efficient GitLab CI pipelines with proper stage organization, caching, and deployment strategies.

When to Use

  • Automate GitLab-based CI/CD
  • Implement multi-stage pipelines
  • Configure GitLab Runners
  • Deploy to Kubernetes from GitLab
  • Implement GitOps workflows

Basic Pipeline Structure

stages:
  - build
  - test
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"

build:
  stage: build
  image: node:20
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 hour
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/

test:
  stage: test
  image: node:20
  script:
    - npm ci
    - npm run lint
    - npm test
  coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

deploy:
  stage: deploy
  image: bitnami/kubectl:1.31
  script:
    - kubectl apply -f k8s/
    - kubectl rollout status deployment/my-app
  when: manual
  only:
    - main
  environment:
    name: production
    url: https://app.example.com

The deploy job needs cluster credentials; configure them as shown in the Multi-Environment Deployment template, with scoped, protected variables. Validate manifests with a server side dry run and policy checks before applying, require code owner review for changes to the manifest directory and to access control rules, and use service accounts limited to one namespace that cannot create role bindings. Where possible, install dependencies with lifecycle scripts turned off, and limit protected variables to deploy jobs.

Docker Build and Push

Pass the registry password on standard input rather than as a command argument, and log out of the registry when the job ends. Docker in Docker needs privileged runners; prefer rootless image builders, or run this job only on dedicated, short lived, isolated runners selected by tags and used only for protected branches. Build the image once and tag the scanned commit image as latest rather than rebuilding it, push latest only from main after the scan passes, and deploy by commit SHA.

build-docker:
  stage: build
  image: docker:24
  services:
    - docker:24-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker build -t $CI_REGISTRY_IMAGE:latest .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
    - tags

Multi-Environment Deployment

Set KUBE_CA_CERT_FILE as a GitLab file variable containing the cluster CA certificate, and provide KUBE_TOKEN through a protected, masked CI variable. The file variable contains a path that kubectl uses to verify the API server certificate. Give KUBE_TOKEN a separate environment scoped value for staging and for production, each bound to a service account limited to its own namespace. Make the production value available only to the protected production environment on main, with required approvers, so develop receives only the staging value. Define the cluster server URL and KUBE_CA_CERT_FILE as protected, environment scoped variables too, and limit pipeline variable overrides to maintainers or turn them off.

.deploy_template: &deploy_template
  image: bitnami/kubectl:1.31
  before_script:
    - kubectl config set-cluster k8s --server="$KUBE_URL" --certificate-authority="$KUBE_CA_CERT_FILE" --embed-certs=true
    - kubectl config set-credentials admin --token="$KUBE_TOKEN"
    - kubectl config set-context default --cluster=k8s --user=admin
    - kubectl config use-context default

deploy:staging:
  <<: *deploy_template
  stage: deploy
  script:
    - kubectl apply -f k8s/ -n staging
    - kubectl rollout status deployment/my-app -n staging
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - develop

deploy:production:
  <<: *deploy_template
  stage: deploy
  script:
    - kubectl apply -f k8s/ -n production
    - kubectl rollout status deployment/my-app -n production
  environment:
    name: production
    url: https://app.example.com
  when: manual
  only:
    - main

Terraform Pipeline

Review the saved plan output before triggering apply, and run apply through a protected environment with required approvers. Store backend and provider credentials as protected variables, and keep write capable credentials for the apply step. Plan files can hold secret values in clear text, so treat them as secrets, restrict who can download job artifacts, and mark sensitive Terraform variables as sensitive. The Terraform image entrypoint must be overridden to empty so script steps run.

stages:
  - validate
  - plan
  - apply

variables:
  TF_ROOT: ${CI_PROJECT_DIR}/terraform
  TF_VERSION: "1.6.0"

before_script:
  - cd ${TF_ROOT}
  - terraform --version

validate:
  stage: validate
  image: hashicorp/terraform:${TF_VERSION}
  script:
    - terraform init -backend=false
    - terraform validate
    - terraform fmt -check

plan:
  stage: plan
  image: hashicorp/terraform:${TF_VERSION}
  script:
    - terraform init
    - terraform plan -out=tfplan
  only:
    - main
  artifacts:
    paths:
      - ${TF_ROOT}/tfplan
    expire_in: 1 hour

apply:
  stage: apply
  image: hashicorp/terraform:${TF_VERSION}
  script:
    - terraform init
    - terraform apply tfplan
  dependencies:
    - plan
  when: manual
  only:
    - main

Security Scanning

Run the scan after the image build, give it registry credentials from the existing registry user and password variables, and make deploy jobs wait on it. Record documented exceptions in an ignore file so the gate stays blocking.

include:
  - template: Security/SAST.gitlab-ci.yml
  - template: Security/Dependency-Scanning.gitlab-ci.yml
  - template: Security/Container-Scanning.gitlab-ci.yml

trivy-scan:
  stage: test
  image: aquasec/trivy:0.58.0
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - main
    - tags

Caching Strategies

Keep separate caches for protected branches, key dependency caches on the lockfile, and pass build outputs as artifacts rather than cache.

# Cache node_modules
build:
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
    policy: pull-push

# Global cache
cache:
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - .cache/
    - vendor/

# Separate cache per job
job1:
  cache:
    key: job1-cache
    paths:
      - build/

job2:
  cache:
    key: job2-cache
    paths:
      - dist/

Dynamic Child Pipelines

Keep the generator from templating untrusted values such as branch names, merge request titles or external data into the YAML, lint the output before triggering it, and restrict child pipelines that use protected variables to protected branches.

generate-pipeline:
  stage: build
  script:
    - python generate_pipeline.py > child-pipeline.yml
  artifacts:
    paths:
      - child-pipeline.yml

trigger-child:
  stage: deploy
  trigger:
    include:
      - artifact: child-pipeline.yml
        job: generate-pipeline
    strategy: depend

Best Practices

  1. Use specific image tags (node:20, not node:latest), pin images by digest kept current with a dependency update tool, and use a maintained kubectl image
  2. Cache dependencies appropriately
  3. Use artifacts for build outputs
  4. Implement manual gates for production
  5. Use environments for deployment tracking
  6. Enable merge request pipelines
  7. Use pipeline schedules for recurring jobs
  8. Implement security scanning
  9. Use CI/CD variables for secrets
  10. Monitor pipeline performance
  • github-actions-templates - For GitHub Actions
  • deployment-pipeline-design - For architecture
  • secrets-management - For secrets handling

Install

Install with chainctl
chainctl skill install public/wshobson/gitlab-ci-patterns:latest
Get started with chainctl
View source

Harden your own skills

Bring Chainguard's hardening pipeline to your first-party skills — custom rulesets, automated remediation, full audit trail.

Provenance

Upstream sourcehttps://github.com/wshobson/agents
Upstream commit46891e7e60da0e52baf1050b7b6391b64e84c6d9
OCI reference

skills.cgr.dev/public/github.com/wshobson/plugins/cicd-automation/skills/gitlab-ci-patterns:latest

License

MIT

Files

HARDENING.md

LICENSE

SKILL.md


The trusted source for open source

Talk to an expert
PrivacyTerms

Product

Chainguard ContainersChainguard LibrariesChainguard VMsChainguard OS PackagesChainguard ActionsChainguard Agent SkillsIntegrationsPricing
© 2026 Chainguard, Inc. All Rights Reserved.
Chainguard® and the Chainguard logo are registered trademarks of Chainguard, Inc. in the United States and/or other countries.
The other respective trademarks mentioned on this page are owned by the respective companies and use of them does not imply any affiliation or endorsement.