GitLab CI/CD - Hands-On Lab: Rules and Merging Changes
Estimated time to complete: 15 minutes
Preface
After successfully implementing automated builds, your team at Tanuki Enterprises discovered a new problem. Every code commit triggers the entire pipeline—including the release job that creates production releases. This means:
- Premature Releases: Experimental features in development branches accidentally trigger production releases
- Wasted Resources: Build and test jobs run on every tiny commit, even on work-in-progress code
- No Quality Gates: Code merges directly to main without running through proper validation in Merge Requests
- Pipeline Chaos: Infinite loops occur when release jobs create tags that trigger more pipelines
- Lost Context: Developers can’t easily see which pipeline runs are associated with their Merge Requests
Objectives
In this lab, you’ll implement workflow rules and Merge Request pipelines to control when and how your CI/CD pipeline executes. You’ll learn to differentiate between development workflows and production releases, ensuring that the right jobs run at the right time based on the context of your code changes.
Task A. Workflow Rules
With our build process complete, we can now start making changes to our code. Most changes will take place in a Merge Request. When changes are made to code in a Merge Request, it is ideal to run a pipeline against the Merge Request. This will ensure that all code changes are production ready, making it easier to merge changes into production. Let’s take a look at a few ways to set up and configure a pipeline.
Workflow rules allow you to control when a pipeline runs. These rules give you control over the execution flow of your entire CI/CD pipeline. For example, consider our current .gitlab-ci.yml file:
default:
image: golang
stages:
- build
- run
build go:
stage: build
script:
- go build
artifacts:
paths:
- array
run go:
stage: run
script:
- ./array
Let’s introduce a new job that adds a release based on the current project code.
-
GitLab tracks releases of your project in the Deploy > Releases section. To create a new release, start by selecting your
.gitlab-ci.ymlfile and selecting Edit > Edit in pipeline editor. -
In the pipeline editor, add the
releasejob to yourstages:stages: - build - run - release -
Add a new job in the release stage below the
run gojob:release go: stage: release script: - echo "Generating the latest Tanuki App release!" -
The release job requires a special CLI tool. Add the image that contains the required tool to your release job as per the code below:
release go: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest script: - echo "Generating the latest Tanuki App release!" -
To create a release, you need to provide a
tag_nameand adescriptionfor the release. To produce a unique version number, we will use theCI_PIPELINE_IIDbuilt in variable. This variable contains a project level ID of the current pipeline. Add the release keyword below thescriptof yourrelease jobjob:image: registry.gitlab.com/gitlab-org/release-cli:latest script: - echo "Generating the latest Tanuki App release!" release: tag_name: 'v0.$CI_PIPELINE_IID' description: 'The latest release!'The job should now look like this:
release go: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest script: - echo "Generating the latest Tanuki App release!" release: tag_name: 'v0.$CI_PIPELINE_IID' description: 'The latest release!'When the release job runs, it will create a tag in your repository. The creation of a tag by default will trigger a pipeline to run. This creates an infinite loop, where each time the pipeline finishes, it creates a new tag, which then triggers a new pipeline.
To prevent the infinite loop, we can utilize a workflow to prevent a pipeline from triggering when a new commit tag is added.
-
Define the following workflow at the top of your
.gitlab-ci.ymlfile:workflow: rules: - if: $CI_COMMIT_TAG when: never - when: always -
Select Commit changes.
This rule applies to the whole pipeline. If a
CI_COMMIT_TAGis present, the if statement evaluates to true, resulting in the pipeline never running. If theCI_COMMIT_TAGis not present, then the pipeline will run.You can also search for specific
CI_COMMIT_TAGvalues if you want to only stop the run for releases. In this case, a tag in the formv0.*is a part of our release, so we can search for this specific pattern instead.
Task B. Merge Request Pipelines
Another consideration we have for our new release job is when the job to create a release runs. Currently, this job will run on every commit. However, we ideally only want it to run in commits to the main or default branch. To achieve this, we can implement a Merge Request pipeline.
A Merge Request pipeline will run every time you make a change to a branch with an open Merge Request. By controlling the flow for Merge Requests, we can prevent releases from running until they are fully merged.
To define a job that runs in a Merge Request, we will add a rules definition to the job. The rule we add will check the CI_PIPELINE_SOURCE to see if it is merge_request_event.
-
Open your
.gitlab-ci.ymlfile in the pipeline editor. -
Below the
scriptfor yourbuild goandrun gojobs, add the following rule:rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event'Make sure that this rule is indented by two spaces. It should be aligned with the
scriptkeyword.The build and run jobs should now look like this:
build go: stage: build script: - go build artifacts: paths: - array rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event' run go: stage: run script: - ./array rules: - if: $CI_PIPELINE_SOURCE == 'merge_request_event'Now, the build and run jobs will only run when the pipeline is part of a Merge Request.
-
Next, we can prevent the release job from running in Merge Requests by negating the rule. Below the
release jobscript, add the following rule:rules: - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCHMake sure this is indented to the same level as the
scriptkeyword, 2 spaces.The release job should now look like this:
release go: stage: release image: registry.gitlab.com/gitlab-org/release-cli:latest script: - echo "Generating the latest Tanuki App release!" release: tag_name: 'v0.$CI_PIPELINE_IID' description: 'The latest release!' rules: - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH -
With these changes made, select Commit changes to update your
.gitlab-ci.ymlfile. -
When you commit this to main, select Build > Pipelines to view your running jobs. You will notice that only the release job runs, because the commit was run on
main.Let’s get the other jobs to run by creating a Merge Request.
-
Navigate to Code > Branches.
-
Select New Branch.
-
Enter any branch name and select Create Branch.
-
Select Code > Merge requests.
-
Select New Merge Request.
-
Select the branch you created as the source branch.
-
Select Compare branches and continue.
-
Leave all options as default and select Create Merge Request.
To trigger the Merge Request pipeline, you need to make some change to the code.
-
Select Code > Open in Web IDE.
-
Select the
README.mdfile and change anything you want in the file. -
Once complete, select Source Control > Commit and push.
-
From the pop-up that appears on the bottom right of your screen, select Go to MR to return to your Merge Request.
-
Navigate to Build > Pipelines. You will see a new pipeline with the Merge Request label.
-
Select it to see its progress. You will see two jobs run, the build and run jobs specified in our
.gitlab-ci.yml file. -
Navigate back to the Merge Request by selecting Code > Merge Requests.
-
Select your Merge Request. Notice that there is now a section stating Merge Request pipeline. This section will show the progress of the Merge Request pipeline.
Postface
With workflow rules and Merge Request pipelines in place, you transformed Tanuki Enterprises’ CI/CD process from chaotic to controlled. The workflow rules eliminated the infinite loop problem by preventing pipelines from triggering on release tags, while Merge Request pipelines ensured that build and test jobs only run on active development work—not on every commit to main. Most importantly, the release job now executes exclusively on the main branch, preventing accidental production releases from feature branches.
Lab Guide Complete
You have completed this lab exercise. You can view the other lab guides for this course.
Suggestions?
If you wish to make a change to the lab, please submit your changes via Merge Request.
853f6f1b)
