GitLab CI config validation stage

GitLab pipelines run stages in order, which makes a validate stage before deploy the natural gate for configuration. This page wires it up, extending CI/CD Config Validation.

Problem 1: deploy stage runs regardless

# ANTI-PATTERN: no validate stage; deploy runs even with broken config
stages: [build, deploy]
deploy:
  stage: deploy
  script: ./deploy.sh

Nothing checks the configuration before the deploy script boots the app.

Problem 2: secrets in plain CI variables

# ANTI-PATTERN: unmasked, unprotected variable visible in job logs
variables:
  API_KEY: "sk_live_xxxx"     # in the repo, in logs, in every fork

A secret defined inline in .gitlab-ci.yml lives in the repository and the logs.

Secure implementation

# .gitlab-ci.yml
stages: [validate, deploy]

validate-config:
  stage: validate
  image: python:3.12
  script:
    - pip install -r requirements.txt
    - gitleaks detect --no-banner
    - python -m ci.validate_config        # constructs Settings(); fails on error
  # DATABASE_URL and API_KEY come from masked, protected CI/CD variables.

deploy:
  stage: deploy
  script: ./deploy.sh
  needs: ["validate-config"]              # blocked until validation passes
  environment: production

Masked, protected CI/CD variables supply the secrets; the validate stage constructs the model and runs gitleaks; deploy declares needs so it cannot start until validation succeeds.

Gotchas & version-specific behaviour

  • Mark secret CI/CD variables Masked and Protected so they only appear on protected branches and never print.
  • needs: enforces the ordering even when stages would otherwise parallelize.
  • Use rules: to run validation on merge requests so config errors are caught pre-merge.
  • Scope environment: production so deploy-time variables match the target.

Production parity checklist

  • A validate stage precedes deploy, joined by needs.
  • Secrets are Masked + Protected CI/CD variables, never inline.
  • gitleaks runs in the validate stage.
  • Validation runs on the production Python version.
  • extra="forbid" catches stray injected variables.

Conclusion

A validate stage with needs makes correct configuration a precondition for deploy in GitLab. For the GitHub Actions version, see Validate Config in GitHub Actions Before Deploy.