Here’s a basic GitLab CI/CD YAML (.gitlab-ci.yml) example that demonstrates how the workflow: configuration option is used, along with an explanation.
The workflow: keyword in GitLab CI/CD allows you to control if and when a pipeline is created and run for a given commit or event. This is different from job-level rules (or only/except), which control whether individual jobs within an already created pipeline will run. workflow:rules are evaluated before any jobs are even considered.
This is very useful for:
- Preventing duplicate pipelines (e.g., for a branch push that also has an open merge request).
- Specifying that pipelines should only run for merge requests, tags, or specific branches.
- Defining global conditions for pipeline execution to save CI/CD resources.
Example .gitlab-ci.yml with workflow:
YAML
# .gitlab-ci.yml
workflow:
name: 'Pipeline for Branch: ${CI_COMMIT_REF_NAME} | Source: ${CI_PIPELINE_SOURCE}' # Optional: Custom pipeline name
rules:
# Rule 1: Run pipelines for merge request events
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: always # Explicitly run the pipeline
# Rule 2: Run pipelines for commits to the default branch (e.g., main, master)
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
when: always
# Rule 3: Run pipelines for tags
- if: '$CI_COMMIT_TAG'
when: always
# Rule 4: Run pipelines for scheduled events
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: always
# Rule 5: (Optional but common) Prevent pipelines on other branches without an MR
# If none of the above rules match (e.g., a push to a feature branch without an MR),
# this rule effectively stops a pipeline from being created.
# If you want pipelines for all branches by default (and only use workflow to exclude specific cases),
# you would omit this or structure your rules differently.
- when: never # Default to never running if no other rule matches
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo "Building the project..."
- echo "Pipeline Source: $CI_PIPELINE_SOURCE"
- echo "Commit Branch: $CI_COMMIT_BRANCH"
- echo "Commit Tag: $CI_COMMIT_TAG"
- echo "Is Merge Request: $CI_MERGE_REQUEST_IID"
test_job:
stage: test
script:
- echo "Running tests..."
deploy_to_production:
stage: deploy
script:
- echo "Deploying to production..."
rules: # Job-level rules can further refine when this specific job runs
- if: '$CI_COMMIT_TAG || ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "push")'
when: on_success # Only deploy on success for tags or pushes to default branch
- when: never # Don't run this job in other cases (like merge requests to default branch)
Code language: PHP (php)
Explanation:
workflow:Section:- This top-level keyword controls the creation of the entire pipeline.
name: 'Pipeline for Branch: ...': (Optional) This allows you to define a custom name for your pipeline, which can be useful for quickly identifying pipelines in the GitLab UI. It can use CI/CD predefined variables.rules:: This is an array of rules that are evaluated in order from top to bottom. The first rule that matches determines the outcome for the pipeline.
- Understanding the
workflow:rules:- Rule 1:
if: '$CI_PIPELINE_SOURCE == "merge_request_event"'$CI_PIPELINE_SOURCE: A predefined CI/CD variable that indicates what triggered the pipeline.merge_request_event: This value means the pipeline was triggered by an event on a merge request (e.g., creating an MR, pushing commits to the MR’s source branch, an MR being merged – though merged results pipelines are a separate concept).when: always: If this condition is true, a pipeline will always be created.
- Rule 2:
if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'$CI_COMMIT_BRANCH: The name of the branch for which the pipeline is running.$CI_DEFAULT_BRANCH: The default branch for your project (e.g.,mainormaster).when: always: If the commit is on the default branch, a pipeline will be created. This is useful for running CI/CD on your main line of code. Note: This rule might create pipelines for both direct pushes to the default branch and for merge request events targeting the default branch if Rule 1 wasn’t present or specific enough. The order helps here; Rule 1 usually catches MRs first.
- Rule 3:
if: '$CI_COMMIT_TAG'$CI_COMMIT_TAG: This variable is set if the pipeline is for a Git tag.when: always: If a tag is pushed, a pipeline will be created. This is common for release pipelines.
- Rule 4:
if: '$CI_PIPELINE_SOURCE == "schedule"'schedule: This value indicates the pipeline was triggered by a scheduled event.when: always: If it’s a scheduled pipeline, it will run.
- Rule 5:
when: never- If none of the preceding
ifconditions are met (e.g., a commit is pushed to a regular feature branch that doesn’t have an open merge request, and it’s not a tag or the default branch), this rule will be evaluated. when: never: This explicitly prevents a pipeline from being created. This helps in avoiding running pipelines for every single commit on every feature branch unless it’s associated with a merge request or another explicit condition.
- If none of the preceding
- Rule 1:
stages:and Jobs (build_job,test_job,deploy_to_production):- These define the actual work to be done if the
workflow:rulesallow a pipeline to be created. - If the
workflow:rulesprevent a pipeline, none of these jobs will even be considered for execution. deploy_to_productionJob with Job-Levelrules:- This demonstrates that even if
workflow:rulesallow a pipeline to run (e.g., for a merge request), individual jobs can have their ownrulesto further refine whether they specifically should run. - In this case,
deploy_to_productionwill only run if it’s a tagged commit OR a direct push (not an MR event) to the default branch. It won’t run, for example, when a merge request pipeline runs for the default branch before it’s merged.
- This demonstrates that even if
- These define the actual work to be done if the
Key Concepts and Benefits:
- Pipeline Creation Control:
workflow:determines if a pipeline is created at all. Job-levelrulesdetermine if a specific job within that created pipeline runs. - Order of Evaluation:
workflow:rulesare evaluated from top to bottom. The first match dictates the outcome. - Preventing Redundant Pipelines: A common use case is to only run pipelines for merge requests and not for the underlying branch pushes if an MR is open.
- Example for preventing duplicate pipelines: YAML
workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' # If branch has an open MR when: never # Don't run a branch pipeline if an MR pipeline will run - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' # For default branch - if: '$CI_COMMIT_TAG' # For tags(This example shows a common pattern for more fine-grained control over duplicates.)
- Example for preventing duplicate pipelines: YAML
- Resource Optimization: By preventing unnecessary pipeline runs, you save CI/CD runner minutes and resources.
- Clarity: Makes it clear at a global level when pipelines are expected to run.
when: alwaysvs.when: never: These are the primary outcomes inworkflow:rules. If anifcondition in a rule is met and there’s nowhen:, it defaults towhen: always.
When designing your workflow:rules, think carefully about all the scenarios (pushes to feature branches, pushes to the default branch, merge request creations/updates, tag pushes, scheduled runs) and define rules to cover them explicitly. This helps avoid unexpected pipeline behavior.