Skip to content

Execution Policy

Control when pipeline jobs run. Jobs without rules: can execute on every push, tag, or schedule — including deploy stages that should be gated to protected branches or merge requests.

Bad

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  # no rules — runs on every pipeline source
release:
  stage: publish
  rules:
    - when: always

Good

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
    - when: never

scan:
  stage: test
  script:
    - ./scan.sh
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Policy

From security/execution-policy.feature:

Feature: Pipeline execution controls

  Scenario: Deploy jobs must define rules
    Given I have any job defined
    When its stage is deploy
    Then it must contain rules

  Scenario: Jobs with rules must include conditional guards
    Given I have any job defined
    When it has rules
    Then its rules must match "if:"

  Scenario: Non-template jobs must define rules
    Given I have any job defined
    When its name does not start with "."
    Then it must contain rules

Legacy file rules.feature remains for backward compatibility.

Consume in GitLab CI (project level)

Pair the execution policy with a compliance job in the same project:

stages:
  - test
  - build
  - deploy

include:
  - local: example-ci/compliance-jobs.yml

compliance:
  extends: .compliance:offline

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual
    - when: never

Warn-only rollout:

compliance:
  extends: .compliance:offline
  allow_failure: true

Remove allow_failure once every job has proper rules:.

See also example-ci/.gitlab-ci.consumer.yml for a minimal consumer project.

Org-wide rollout (GitLab Pipeline Execution Policy)

For GitLab tiers with Pipeline execution policies, inject the compliance job into every member project from a security policy project.

1. Security policy project.gitlab/security-policies/policy.yml:

pipeline_execution_policy:
  - name: Enforce GitLab CI compliance
    description: Injects gitlab-compliance job into member project pipelines
    enabled: true
    pipeline_config_strategy: inject_policy
    content:
      include:
        - project: my-group/gitlab-compliance-policies
          file: policy-ci.yml
          ref: "1.0.0"
    policy_scope:
      projects:
        including:
          - full_path: my-group/*

2. Injected CI configpolicy-ci.yml in the same repo:

stages:
  - compliance
  - test
  - build

policy::gitlab-compliance:
  stage: compliance
  image: python:3.12
  script:
    - pip install --quiet gitlab-compliance
    - gitlab-compliance check -f policies/security/ -p .gitlab-ci.yml
        --project "$CI_PROJECT_PATH" --strict
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

The injected job runs the same Gherkin pack (policies/security/execution-policy.feature) as project-level consumption. Vendor examples/example-policies/security/ into policies/security/ in the security policy repo.

Full walkthrough: Pipeline Execution Policy.

Run locally

cp -r examples/example-policies/security/ policies/security/
gitlab-compliance check -f policies/security/ -p .gitlab-ci.yml

Back to Examples.