-
Notifications
You must be signed in to change notification settings - Fork 0
CI CD Integration
Inky Quill edited this page Feb 1, 2026
·
1 revision
This guide covers integrating GitLab CI Lint into your CI/CD pipelines.
Validate your .gitlab-ci.yml before running jobs:
# .gitlab-ci.yml
stages:
- lint
- build
- test
- deploy
# Lint stage first
lint:ci:
stage: lint
image: alpine:latest
script:
- apk add --no-cache curl
- curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
- chmod +x gitlab-ci-lint
- ./gitlab-ci-lint --skip-api .gitlab-ci.yml
only:
changes:
- .gitlab-ci.yml
# Your actual jobs
build:
stage: build
script:
- echo "Building..."
needs:
- lint:ciGitLab provides $CI_JOB_TOKEN automatically for authentication:
lint:ci:
stage: lint
image: alpine:latest
script:
- apk add --no-cache curl
- curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
- chmod +x gitlab-ci-lint
- ./gitlab-ci-lint --token $CI_JOB_TOKEN .gitlab-ci.yml
only:
changes:
- .gitlab-ci.ymlIf you have a Docker image with gitlab-ci-lint:
lint:ci:
stage: lint
image: yourregistry/gitlab-ci-lint:latest
script:
- gitlab-ci-lint --token $CI_JOB_TOKEN .gitlab-ci.yml
only:
changes:
- .gitlab-ci.ymllint:ci:
stage: lint
script:
- gitlab-ci-lint --token $CI_JOB_TOKEN --project $CI_PROJECT_ID .gitlab-ci.yml
only:
changes:
- .gitlab-ci.ymllint:ci:
stage: lint
script:
# Validate main CI file
- gitlab-ci-lint --token $CI_JOB_TOKEN .gitlab-ci.yml
# Validate other CI files
- gitlab-ci-lint --token $CI_JOB_TOKEN ci/docker.gitlab-ci.yml
- gitlab-ci-lint --token $CI_JOB_TOKEN ci/deploy.gitlab-ci.yml
only:
changes:
- .gitlab-ci.yml
- ci/**/*.gitlab-ci.ymllint:ci:
stage: lint
script:
- gitlab-ci-lint --token $CI_JOB_TOKEN .gitlab-ci.yml
allow_failure: false # Pipeline fails if lint fails
only:
changes:
- .gitlab-ci.ymllint:ci:
stage: lint
script:
- if [ -f ".gitlab-ci.yml" ]; then gitlab-ci-lint --token $CI_JOB_TOKEN .gitlab-ci.yml; fi
allow_failure: true# .github/workflows/ci-lint.yml
name: CI Lint
on:
push:
branches: [main, develop]
paths:
- '.gitlab-ci.yml'
pull_request:
paths:
- '.gitlab-ci.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download gitlab-ci-lint
run: |
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
chmod +x gitlab-ci-lint
- name: Lint CI configuration
env:
GCL_TOKEN: ${{ secrets.GITLAB_TOKEN }}
run: ./gitlab-ci-lint .gitlab-ci.ymlname: CI Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
strategy:
matrix:
file:
- .gitlab-ci.yml
- ci/docker.gitlab-ci.yml
- ci/deploy.gitlab-ci.yml
steps:
- uses: actions/checkout@v4
- name: Download gitlab-ci-lint
run: |
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
chmod +x gitlab-ci-lint
- name: Lint ${{ matrix.file }}
env:
GCL_TOKEN: ${{ secrets.GITLAB_TOKEN }}
run: ./gitlab-ci-lint "${{ matrix.file }}"name: CI Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download gitlab-ci-lint
run: |
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
chmod +x gitlab-ci-lint
- name: Lint and parse output
id: lint
env:
GCL_TOKEN: ${{ secrets.GITLAB_TOKEN }}
run: |
result=$(./gitlab-ci-lint --output json .gitlab-ci.yml)
echo "result=$result" >> $GITHUB_OUTPUT
valid=$(echo "$result" | jq -r '.valid')
echo "valid=$valid" >> $GITHUB_OUTPUT
- name: Check result
if: steps.lint.outputs.valid != 'true'
run: exit 1pipeline {
agent any
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Lint CI') {
steps {
sh '''
if [ ! -f "gitlab-ci-lint" ]; then
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
chmod +x gitlab-ci-lint
fi
./gitlab-ci-lint --token ${GITLAB_TOKEN} .gitlab-ci.yml
'''
}
}
}
environment {
GITLAB_TOKEN = credentials('gitlab-token')
}
}# .circleci/config.yml
version: 2.1
orbs:
gitlab-ci-lint: your-organization/gitlab-ci-lint@1.0.0
workflows:
lint-workflow:
jobs:
- gitlab-ci-lint/lint:
file: .gitlab-ci.yml// package.json
{
"name": "my-project",
"scripts": {
"prepare": "husky install"
},
"devDependencies": {
"husky": "^8.0.0"
}
}# .husky/pre-commit
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
gitlab-ci-lint --skip-api .gitlab-ci.yml# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: gitlab-ci-lint
name: GitLab CI Lint
entry: gitlab-ci-lint --skip-api
language: system
files: '\.gitlab-ci\.ya?ml$'FROM alpine:latest AS lint
RUN apk add --no-cache curl
RUN curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o /usr/local/bin/gitlab-ci-lint && \
chmod +x /usr/local/bin/gitlab-ci-lint
# Your actual image
FROM python:3.11
COPY --from=lint /usr/local/bin/gitlab-ci-lint /usr/local/bin/gitlab-ci-lint
WORKDIR /app
COPY . .
RUN gitlab-ci-lint --skip-api .gitlab-ci.yml
# Continue with your build...version: '3.8'
services:
lint:
image: alpine:latest
command:
- sh
- -c
- |
apk add --no-cache curl &&
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o /tmp/gitlab-ci-lint &&
chmod +x /tmp/gitlab-ci-lint &&
/tmp/gitlab-ci-lint --skip-api /app/.gitlab-ci.yml
volumes:
- .:/app# terraform/main.tf
resource "gitlab_project" "example" {
# ... project configuration
}
resource "null_resource" "lint_ci" {
triggers = {
ci_hash = filesha256(".gitlab-ci.yml")
}
provisioner "local-exec" {
command = "gitlab-ci-lint .gitlab-ci.yml"
}
depends_on = [gitlab_project.example]
}Validate GitLab CI files before committing to GitOps repository:
# webhook-validation.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: gitlab-ci-lint
webhooks:
- name: validate-gitlab-ci
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["configmaps"]
clientConfig:
service:
name: gitlab-ci-lint-webhook
namespace: default
path: /validate
admissionReviewVersions: ["v1"]
sideEffects: None# Script that runs periodically and exports metrics
#!/bin/bash
gitlab-ci-lint --output json .gitlab-ci.yml > /tmp/result.json
valid=$(jq -r '.valid' /tmp/result.json)
errors=$(jq '[.results[].errors] | add | length' /tmp/result.json)
warnings=$(jq '[.results[].warnings] | add | length' /tmp/result.json)
# Export to Prometheus node exporter
echo "# HELP gitlab_ci_lint_valid CI configuration validity"
echo "# TYPE gitlab_ci_lint_valid gauge"
echo "gitlab_ci_lint_valid ${valid}"
echo "# HELP gitlab_ci_lint_errors Number of CI errors"
echo "# TYPE gitlab_ci_lint_errors gauge"
echo "gitlab_ci_lint_errors ${errors}"
echo "# HELP gitlab_ci_lint_warnings Number of CI warnings"
echo "# TYPE gitlab_ci_lint_warnings gauge"
echo "gitlab_ci_lint_warnings ${warnings}"# ✅ Good: Lint stage is first
stages:
- lint
- build
- test
- deploy
lint:ci:
stage: lint
script:
- gitlab-ci-lint .gitlab-ci.yml# ✅ Good: Fail immediately on invalid config
lint:ci:
stage: lint
script:
- gitlab-ci-lint .gitlab-ci.yml
allow_failure: false# ✅ Good: Cache gitlab-ci-lint binary
.cache:
paths:
- gitlab-ci-lint
lint:ci:
stage: lint
cache:
policy: pull
script:
- |
if [ ! -f "gitlab-ci-lint" ]; then
curl -sSL https://github.com/InkyQuill/gitlab-ci-lint/releases/download/v1.0.0/gitlab-ci-lint-linux-amd64 -o gitlab-ci-lint
chmod +x gitlab-ci-lint
fi
./gitlab-ci-lint .gitlab-ci.yml# ✅ Good: Validate with project context
lint:ci:
stage: lint
script:
- gitlab-ci-lint --project $CI_PROJECT_ID .gitlab-ci.yml# ✅ Good: Only run when CI files change
lint:ci:
stage: lint
script:
- gitlab-ci-lint .gitlab-ci.yml
only:
changes:
- .gitlab-ci.yml
- ci/**/*.yml# ✅ Good: Fast feedback during development
lint:ci-syntax:
stage: lint
script:
- gitlab-ci-lint --skip-api .gitlab-ci.yml
lint:ci-full:
stage: lint
script:
- gitlab-ci-lint .gitlab-ci.yml
only:
- main
- developSolution: Ensure read_api scope is enabled (default for CI job tokens in GitLab 14+)
Solution: Increase timeout
gitlab-ci-lint --timeout 120s .gitlab-ci.ymlPossible causes:
- Different GitLab instance
- Different token permissions
- Missing environment variables
Solution: Use --verbose to debug
gitlab-ci-lint --verbose .gitlab-ci.yml