diff --git a/hands-on/2023-11-30/010_jobs_and_stages/exercise/index.html b/hands-on/2023-11-30/010_jobs_and_stages/exercise/index.html index f7ca60dd..ee713b7a 100644 --- a/hands-on/2023-11-30/010_jobs_and_stages/exercise/index.html +++ b/hands-on/2023-11-30/010_jobs_and_stages/exercise/index.html @@ -1230,7 +1230,7 @@
This workshop is based on an example hello world application written in Go. Get the code using the following command:
-git checkout origin/160_gitlab_ci/example_app -- '*'
+
Task 1: Create a single job¶
Add a pipeline to build the code using the following commands:
@@ -1264,7 +1264,7 @@ Task 1: Create a single job - ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/010_jobs_and_stages/build -- '*'
+
Task 2: Add a stage¶
@@ -1325,7 +1325,7 @@ Task 2: Add a stage - ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/010_jobs_and_stages/lint -- '*'
+
Task 3: Add parallel jobs¶
@@ -1389,7 +1389,7 @@ Task 3: Add parallel jobs - ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/010_jobs_and_stages/parallel -- '*'
+
diff --git a/hands-on/2023-11-30/020_variables/exercise/index.html b/hands-on/2023-11-30/020_variables/exercise/index.html
index 3190df6c..8a1b1195 100644
--- a/hands-on/2023-11-30/020_variables/exercise/index.html
+++ b/hands-on/2023-11-30/020_variables/exercise/index.html
@@ -1212,7 +1212,7 @@ VariablesTask 1: Create a job variable¶
This exercise requires an updates version of our hello world program:
-git checkout origin/160_gitlab_ci/020_variables/inline -- main.go
+
Add a variable called version
to the job called build
and modify the build command as follows:
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/020_variables/inline -- '*'
+git checkout upstream/160_gitlab_ci/020_variables/inline -- '*'
Task 2: Use a predefined variable¶
Read the official documentation about predefined variables and replace the job variable with the predefined variable CI_COMMIT_REF_NAME
.
@@ -1366,11 +1366,11 @@ Task 2: Use a predefined variable
- ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/020_variables/predefined -- '*'
+git checkout upstream/160_gitlab_ci/020_variables/predefined -- '*'
This exercise requires an updates version of our hello world application:
-git checkout origin/160_gitlab_ci/020_variables/ci -- main.go
+
The application now also prints the name of the author which must be supplied during compilation as well.
Read the official documentation about CI variables and extend the build command to provide main.Author
through a CI variable called AUTHOR
.
@@ -1448,7 +1448,7 @@ Task 3: Add a CI variable in the UI<
- ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/020_variables/ci -- '*'
+
diff --git a/hands-on/2023-11-30/030_script_blocks/exercise/index.html b/hands-on/2023-11-30/030_script_blocks/exercise/index.html
index c8f60133..32346731 100644
--- a/hands-on/2023-11-30/030_script_blocks/exercise/index.html
+++ b/hands-on/2023-11-30/030_script_blocks/exercise/index.html
@@ -1285,7 +1285,7 @@ Task: Separa
- ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/030_script_blocks -- '*'
+
Cleanup commands can be move to after_script
(official documentation) but we have no use for this in the current example.
diff --git a/hands-on/2023-11-30/040_image/exercise/index.html b/hands-on/2023-11-30/040_image/exercise/index.html
index 9ad25568..66289acf 100644
--- a/hands-on/2023-11-30/040_image/exercise/index.html
+++ b/hands-on/2023-11-30/040_image/exercise/index.html
@@ -1258,7 +1258,7 @@ Task: Simplify using container ima
- ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/040_image -- '*'
+
Bonus: Test different images¶
diff --git a/hands-on/2023-11-30/050_defaults/exercise/index.html b/hands-on/2023-11-30/050_defaults/exercise/index.html
index 625d88ed..5c169a88 100644
--- a/hands-on/2023-11-30/050_defaults/exercise/index.html
+++ b/hands-on/2023-11-30/050_defaults/exercise/index.html
@@ -1276,7 +1276,7 @@ Task: Don't repeat yourself - ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/050_default -- '*'
+
Bonus 1: Override defaults¶
diff --git a/hands-on/2023-11-30/060_artifacts/exercise/index.html b/hands-on/2023-11-30/060_artifacts/exercise/index.html
index ce242ab2..42b16e59 100644
--- a/hands-on/2023-11-30/060_artifacts/exercise/index.html
+++ b/hands-on/2023-11-30/060_artifacts/exercise/index.html
@@ -1303,7 +1303,7 @@ Task 1: Pass an artifact to t
- ./hello
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/060_artifact -- '*'
+
Bonus 1: Define from which jobs to receive artifacts¶
diff --git a/hands-on/2023-11-30/090_unit_tests/exercise/index.html b/hands-on/2023-11-30/090_unit_tests/exercise/index.html
index 637be04b..705c7bd2 100644
--- a/hands-on/2023-11-30/090_unit_tests/exercise/index.html
+++ b/hands-on/2023-11-30/090_unit_tests/exercise/index.html
@@ -1194,7 +1194,7 @@ Unit testsPreparation¶
Let's update the code:
-
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/090_unit_tests -- '*'
+
diff --git a/hands-on/2023-11-30/100_environments/exercise/index.html b/hands-on/2023-11-30/100_environments/exercise/index.html
index 9436f89d..0d0abcd0 100644
--- a/hands-on/2023-11-30/100_environments/exercise/index.html
+++ b/hands-on/2023-11-30/100_environments/exercise/index.html
@@ -1383,7 +1383,7 @@ Task 1: Add target environment --user seat${SEAT_INDEX}:${PASS}
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/100_environments/demo1 -- '*'
+
Task 2: Add deployment to development environment¶
@@ -1514,7 +1514,7 @@ Task 2: Add deployment
--user seat${SEAT_INDEX}:${PASS}
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/100_environments/demo2 -- '*'
+
This was just a demonstration. The changes will not be preseved in the following chapters.
diff --git a/hands-on/2023-11-30/110_triggers/exercise/index.html b/hands-on/2023-11-30/110_triggers/exercise/index.html
index c486d43e..a8dea32b 100644
--- a/hands-on/2023-11-30/110_triggers/exercise/index.html
+++ b/hands-on/2023-11-30/110_triggers/exercise/index.html
@@ -1409,7 +1409,7 @@ Task 1: Using a trigger token -F "ref=main"
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/110_triggers/curl -- '*'
+
This was just a demonstration. The changes will not be preserved in the following chapters.
@@ -1712,7 +1712,7 @@ Task 3: Using a parent-child pipel
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/110_triggers/parent-child -- '*'
+
diff --git a/hands-on/2023-11-30/120_templates/exercise/index.html b/hands-on/2023-11-30/120_templates/exercise/index.html
index 24ea416d..a8a3d5c9 100644
--- a/hands-on/2023-11-30/120_templates/exercise/index.html
+++ b/hands-on/2023-11-30/120_templates/exercise/index.html
@@ -1371,7 +1371,7 @@ Task 1: Create a template inline
You decide whether artifacts
is part of the template or not!
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/120_templates/inline -- '*'
+
Task 2: Loading templates from a local file¶
@@ -1534,7 +1534,7 @@ Task 2: Loading templates fr
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/120_templates/local -- '*'
+
Task 3: Loading templates from another project¶
diff --git a/hands-on/2023-11-30/130_rules/exercise/index.html b/hands-on/2023-11-30/130_rules/exercise/index.html
index 4675a9a7..1bf7f4f2 100644
--- a/hands-on/2023-11-30/130_rules/exercise/index.html
+++ b/hands-on/2023-11-30/130_rules/exercise/index.html
@@ -1231,7 +1231,7 @@ Rules&
In this exercise we will publish a static web page to download the hello
binary.
Preparation¶
Add a file public/index.html
to your project using the following command:
-git checkout origin/160_gitlab_ci/130_rules -- 'public/index.html'
+
Task 1: Prevent a job from running¶
Add a job pages
to the stage deploy
with the following content:
@@ -1415,7 +1415,7 @@ Task 1: Prevent a job from running include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/130_templates -- '*'
+
Task 2: Prevent a pipeline from running¶
@@ -1611,7 +1611,7 @@ Task 2: Prevent a pipeline from
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/130_rules_workflow -- '*'
+
Task 3: Use deploy freeze¶
diff --git a/hands-on/2023-11-30/140_merge_requests/exercise/index.html b/hands-on/2023-11-30/140_merge_requests/exercise/index.html
index 6cb3b0e9..0ea5eef3 100644
--- a/hands-on/2023-11-30/140_merge_requests/exercise/index.html
+++ b/hands-on/2023-11-30/140_merge_requests/exercise/index.html
@@ -1467,7 +1467,7 @@ Task 1: Use rules to r
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/140_merge_requests -- '*'
+
Task 2: Create a merge request¶
@@ -1721,7 +1721,7 @@ Task 3: Avoid repetition u
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/140_merge_requests_rule_templates -- '*'
+
diff --git a/hands-on/2023-11-30/150_matrix_jobs/exercise/index.html b/hands-on/2023-11-30/150_matrix_jobs/exercise/index.html
index d7257964..ee59e2b2 100644
--- a/hands-on/2023-11-30/150_matrix_jobs/exercise/index.html
+++ b/hands-on/2023-11-30/150_matrix_jobs/exercise/index.html
@@ -1499,7 +1499,7 @@ Task 1: Build binary for mul
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/150_matrix_jobs_demo1 -- '*'
+
Task 2: Test an alternative to specify the same inputs¶
@@ -1799,7 +1799,7 @@ Bonus: Check binaries for cor
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/150_matrix_jobs_demo2 -- '*'
+
diff --git a/hands-on/2023-11-30/220_services/exercise/index.html b/hands-on/2023-11-30/220_services/exercise/index.html
index 63b86937..c32ab87e 100644
--- a/hands-on/2023-11-30/220_services/exercise/index.html
+++ b/hands-on/2023-11-30/220_services/exercise/index.html
@@ -1448,7 +1448,7 @@ Task 1: Create and use a service include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/220_services -- '*'
+
Task 2: Move the service into the job¶
diff --git a/hands-on/2023-11-30/230_docker/exercise/index.html b/hands-on/2023-11-30/230_docker/exercise/index.html
index 99694792..4c659c56 100644
--- a/hands-on/2023-11-30/230_docker/exercise/index.html
+++ b/hands-on/2023-11-30/230_docker/exercise/index.html
@@ -1193,7 +1193,7 @@ DockerPreparation¶
Building a container image requires a Dockerfile
which can be fetched with the following command:
-git checkout origin/160_gitlab_ci/230_docker -- Dockerfile
+
Task: Build a container image¶
For building a container image, you will need to...
@@ -1471,7 +1471,7 @@ Task: Build a container image include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/230_docker -- '*'
+
diff --git a/hands-on/2023-11-30/240_registries/exercise/index.html b/hands-on/2023-11-30/240_registries/exercise/index.html
index 40afacdc..d41b5f66 100644
--- a/hands-on/2023-11-30/240_registries/exercise/index.html
+++ b/hands-on/2023-11-30/240_registries/exercise/index.html
@@ -1451,7 +1451,7 @@ Task: Push a container image include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/240_registries -- '*'
+
diff --git a/hands-on/2023-11-30/250_releases/exercise/index.html b/hands-on/2023-11-30/250_releases/exercise/index.html
index c0dba53f..6f820085 100644
--- a/hands-on/2023-11-30/250_releases/exercise/index.html
+++ b/hands-on/2023-11-30/250_releases/exercise/index.html
@@ -1471,7 +1471,7 @@ Task: Create a release include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/250_releases -- '*'
+
diff --git a/hands-on/2023-11-30/265_caches/exercise/index.html b/hands-on/2023-11-30/265_caches/exercise/index.html
index 107aaacc..a0612e71 100644
--- a/hands-on/2023-11-30/265_caches/exercise/index.html
+++ b/hands-on/2023-11-30/265_caches/exercise/index.html
@@ -1327,7 +1327,7 @@ Task 2: Add caching file hello-${GOOS}-${GOARCH}
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/265_caches -- '*'
+
diff --git a/hands-on/2023-11-30/270_renovate/exercise/index.html b/hands-on/2023-11-30/270_renovate/exercise/index.html
index a3a18ea8..c6d6b2e8 100644
--- a/hands-on/2023-11-30/270_renovate/exercise/index.html
+++ b/hands-on/2023-11-30/270_renovate/exercise/index.html
@@ -1490,7 +1490,7 @@ Task: Add Renovate to your pipeline<
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/270_renovate -- '*'
+
diff --git a/hands-on/2023-11-30/280_security/exercise/index.html b/hands-on/2023-11-30/280_security/exercise/index.html
index 063ef397..b95ada6f 100644
--- a/hands-on/2023-11-30/280_security/exercise/index.html
+++ b/hands-on/2023-11-30/280_security/exercise/index.html
@@ -1508,7 +1508,7 @@ Task: Add integrated security scans<
include: child.yaml
If you want to jump to the solution, execute the following command:
-git checkout origin/160_gitlab_ci/280_security -- '*'
+
diff --git a/hands-on/2023-11-30/search/search_index.json b/hands-on/2023-11-30/search/search_index.json
index b442bf10..94784594 100644
--- a/hands-on/2023-11-30/search/search_index.json
+++ b/hands-on/2023-11-30/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Workshop: GitLab CI","text":"This site contains the exercises to learn about GitLab CI.
"},{"location":"#audience","title":"Audience","text":"Software developers and system administrators who want to learn how to use GitLab CI.
"},{"location":"#duration","title":"Duration","text":"Two days
"},{"location":"#expected-knowledge","title":"Expected knowledge","text":"Basic under standing of another CI/CD server, e.g. GitHub and Jenkins
"},{"location":"#goals","title":"Goals","text":"Learn about the features of GitLab CI and how to use then in a software development project.
"},{"location":"#style","title":"Style","text":"20% theoretical introduction
80% practical exercises
The instructor will provide an introduction to each topic. Participants will then work on exercises. The instructor will be available to answer questions and provide hints. The instructor will also demonstrate solutions to the exercises.
"},{"location":"navigation/","title":"Navigation","text":"All pages have the same navigation structure.
"},{"location":"navigation/#section-1-menu-on-the-left","title":"Section 1: Menu on the left","text":"The left-hand menu contains the available chapters and displays the structure of the workshop.
On smaller screens or windows, the left-hand menu can be shown by clicking on the burger menu .
"},{"location":"navigation/#section-2-page-contents-on-the-right","title":"Section 2: Page contents on the right","text":"The right-hand menu contains the contents of the current chapter and lists all exercises contained in the current chapter.
On smaller screens or windows, the right-hand menu can be shown by clicking on the burger menu and then clicking on the icon next to the current chapter.
"},{"location":"you_are_done/","title":"You are done!","text":""},{"location":"you_are_done/#congratulations","title":"Congratulations!","text":"You have finished the workshop
"},{"location":"you_are_ready/","title":"You are ready!","text":"A few last words...
"},{"location":"you_are_ready/#exercises-tell-a-story","title":"Exercises tell a story","text":"Each chapter focuses on a single feature
Each exercise improves the previous state
All exercises will have leave some questions unanswered
Following exercises will again improve
"},{"location":"you_are_ready/#work-at-your-own-pace","title":"Work at your own pace","text":"Either follow topics and exercises at the instructor's pace
Or work ahead if you finish early
Please be mindful of other participants
Please do not confuse participants with your questions when racing ahead
"},{"location":"000_rollout/exercise_gitlab/","title":"GitLab","text":"This workshop is performed on a shared GitLab instance. You have been assigned a user and password for this instance in an email.
"},{"location":"000_rollout/exercise_gitlab/#task-1-retrieve-your-credentials-digitally","title":"Task 1: Retrieve your credentials digitally","text":"You have received your credentials in an email but the password is not fun to type in. Let's retrieve it digitally:
- Go to https://code.inmylab.de
- Select your username from the list
- Login using your personal user
seatN
(where N
is a number) and your code (looks like this ABCDEF
) - The web page displays your credentials
Keep the page open to refer to your credentials anytime throughout the workshop.
"},{"location":"000_rollout/exercise_gitlab/#task-2-login-to-gitlab","title":"Task 2: Login to GitLab","text":"Go to https://gitlab.inmylab.de and login with your credentials.
Hint (Click if you are stuck) Your username looks like seatN
where N
is a number.
Your password is a long, random string which is displayed on the web pages access in the previous task.
"},{"location":"000_rollout/exercise_gitlab/#task-3-access-the-demo-project","title":"Task 3: Access the demo project","text":"A personal project has been provisioned for you to follow this workshop. It is called demo
. Let's find it!
Hint (Click if you are stuck) Personal projects are access by clicking on your avatar in the top left corner.
Solution (Click if you are stuck) The deep link is https://gitlab.inmylab.de/seatN/demo where N
is a number.
"},{"location":"000_rollout/exercise_ide/","title":"IDE","text":"You can follow this workshop in the IDE (Integrated Development Environment) of your choice but...
A web-based instance of Visual Studio Code has been provisioned for you to follow this workshop.
"},{"location":"000_rollout/exercise_ide/#task-access-your-instance-of-visual-studio-code","title":"Task: Access your instance of Visual Studio Code","text":" - Go to https://seatN.vscode.inmylab.de where
N
is a number. - Login using your personal user
seatN
(where N
is a number) and your password
The password is the same as your GitLab password.
Hint (Click if you are stuck) Your username looks like seatN
where N
is a number.
Your password is a long, random string which is displayed on the web pages access in the previous chapter.
"},{"location":"000_rollout/exercise_project/","title":"Project","text":""},{"location":"000_rollout/exercise_project/#task-1-view-the-demo-project","title":"Task 1: View the demo project","text":"Your instance of Visual Studio Code is provisioned with your credentials to allow Git-over-HTTP to the GitLab instance as well as a clone of the demo
project.
- In the directory tree
- Find the
demo
project under /home/seat
"},{"location":"000_rollout/exercise_project/#task-2-pulling-from-the-upstream-repository","title":"Task 2: Pulling from the upstream repository","text":"The local clone of the demo
project is configured with a remote pointing to the location of the original project on GitHub. This is prepared in case the instructor needs to fix a demo. Let's test this safeguard:
- Open a terminal
- Change to the
demo
project directory - List remotes:
git remote -v
- Pull from the upstream repository:
git pull upstream
"},{"location":"010_jobs_and_stages/exercise/","title":"Jobs and stages","text":"Goal
Learn how to...
- create jobs
- organize them in stages
- understand when jobs in different stages are executed
"},{"location":"010_jobs_and_stages/exercise/#preparation","title":"Preparation","text":"This workshop is based on an example hello world application written in Go. Get the code using the following command:
git checkout origin/160_gitlab_ci/example_app -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-1-create-a-single-job","title":"Task 1: Create a single job","text":"Add a pipeline to build the code using the following commands:
apk update\napk add go\ngo build -o hello .\n./hello\n
See the official documentation about jobs.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Add a file called
.gitlab-ci.yml
in the root of the project - Add a job called
build
Solution (Click if you are stuck) .gitlab-ci.yml
:
build:\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/010_jobs_and_stages/build -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-2-add-a-stage","title":"Task 2: Add a stage","text":"Modify the pipeline to consist of two stages called check
and build
where the check
stage contains the following commands:
apk update\napk add go\ngo fmt .\ngo vet .\n
See the official documentation about stages.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Define two stages using
stages
- Add a job called
check
in the stage check
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/010_jobs_and_stages/lint -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-3-add-parallel-jobs","title":"Task 3: Add parallel jobs","text":"Split the job check
so that one job called lint
executes go fmt .
and another job called audit
executes go vet .
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Both jobs lint
and audit
must be in the stage check
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/010_jobs_and_stages/parallel -- '*'\n
"},{"location":"020_variables/exercise/","title":"Variables","text":"Goal
Learn how to...
- add local variable to your pipeline
- consume pre-defined variables
- add secrets in the UI
"},{"location":"020_variables/exercise/#task-1-create-a-job-variable","title":"Task 1: Create a job variable","text":"This exercise requires an updates version of our hello world program:
git checkout origin/160_gitlab_ci/020_variables/inline -- main.go\n
Add a variable called version
to the job called build
and modify the build command as follows:
go build -o hello -ldflags \"-X main.Version=${version}\" .\n
See the official documentation about variables.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Use the
variable
keyword to define a variable inside the job called build
- Replace the build command with the one provided above
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n variables:\n version: dev\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${version}\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/020_variables/inline -- '*'
"},{"location":"020_variables/exercise/#task-2-use-a-predefined-variable","title":"Task 2: Use a predefined variable","text":"Read the official documentation about predefined variables and replace the job variable with the predefined variable CI_COMMIT_REF_NAME
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove the
variable
keyword from the job called build
- Replace the variable
${version}
with the predefined variable ${CI_COMMIT_REF_NAME}
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME}\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/020_variables/predefined -- '*'
"},{"location":"020_variables/exercise/#task-3-add-a-ci-variable-in-the-ui","title":"Task 3: Add a CI variable in the UI","text":"This exercise requires an updates version of our hello world application:
git checkout origin/160_gitlab_ci/020_variables/ci -- main.go\n
The application now also prints the name of the author which must be supplied during compilation as well.
Read the official documentation about CI variables and extend the build command to provide main.Author
through a CI variable called AUTHOR
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) - Go to
Settings
> CI/CD
> Variables
- Add a variable called
AUTHOR
with your name
Hint 2 (Click if you are stuck) The -ldflags
option needs to be extended with -X 'main.Author=${AUTHOR}'
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/020_variables/ci -- '*'\n
"},{"location":"030_script_blocks/exercise/","title":"Scriptblocks","text":"Goal
Learn how to...
- Use
before_script
and after_script
- Separate preparation and cleanup commands from core functionality
"},{"location":"030_script_blocks/exercise/#task-separate-script-blocks-into-preparation-and-main-task","title":"Task: Separate script blocks into preparation and main task","text":"Commands are currently specified using the script
directive. These commands consist of preparation, core functionality and (possibly) cleanup.
To improve readability, move the preparation of the execution environment to a before_script
. See the official documentation.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Move calls to apk
to the before_script
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n before_script:\n - apk update\n - apk add go\n script:\n - go fmt .\n\naudit:\n stage: check\n before_script:\n - apk update\n - apk add go\n script:\n - go vet .\n\nbuild:\n stage: build\n before_script:\n - apk update\n - apk add go\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/030_script_blocks -- '*'\n
Cleanup commands can be move to after_script
(official documentation) but we have no use for this in the current example.
"},{"location":"030_script_blocks/exercise/#bonus-1-when-after_script-is-executed","title":"Bonus 1: When after_script
is executed","text":"Add commands to all three script block before_script
, script
and after_script
. Test two scenarios:
- The pipeline succeeds
- The pipeline failes
What happens to the code in after_script
?
Solution (Click to reveal) Command in after_script
are always executed even if the job fails.
This can be very useful for cleaning up.
"},{"location":"030_script_blocks/exercise/#bonus-2-what-happens-to-environment-variables-in-script-blocks","title":"Bonus 2: What happens to environment variables in script blocks?","text":"Define environment variables in all three script blocks and display them in the same and in the following script block.
When are environment variables available?
Solution (Click to reveal) Commands in before_script
and script
share a shell session. Environment variables are available throughout these script blocks.
Commands in after_script
are executed in a new shell session. Environment variables defined in before_script
and script
are gone.
"},{"location":"040_image/exercise/","title":"Images","text":"Goal
Learn how to...
- specify which container image to use for a job
- tailor the execution environment to your needs
"},{"location":"040_image/exercise/#task-simplify-using-container-images","title":"Task: Simplify using container images","text":"In the previous exampes, we called apk
at the beginning of every job to install Go. This had to be repeated for every job because Go was not present. Choosing an image for a job using the image
directive, time is saved by avoiding commands to install required tools. See the official documentation.
Replace the calls to apk
with the container image golang:1.19.2
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove
before_script
- Add
image: golang:1.19.2
instead
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n image: golang:1.19.2\n script:\n - go fmt .\n\naudit:\n stage: check\n image: golang:1.19.2\n script:\n - go vet .\n\nbuild:\n stage: build\n image: golang:1.19.2\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/040_image -- '*'\n
"},{"location":"040_image/exercise/#bonus-test-different-images","title":"Bonus: Test different images","text":"Add a job to your pipeline to test different container images. Check how different images offer specialized execution environments:
- Use
python:3
and test running python --version
- Use
node
and test running node --version
"},{"location":"050_defaults/exercise/","title":"Defaults","text":"Goal
Learn how to...
- avoid repetition in jobs
- specify defaults are the top of your pipeline
"},{"location":"050_defaults/exercise/#task-dont-repeat-yourself","title":"Task: Don't repeat yourself","text":"All jobs currently have a dedicated image
directive. Using defaults, this repetition can be avoided. See the official documentation.
Replace job specific image
directives with the default
directive.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove
image
from all build jobs - Add
default
with the image
directive at the top
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/050_default -- '*'\n
"},{"location":"050_defaults/exercise/#bonus-1-override-defaults","title":"Bonus 1: Override defaults","text":"Jobs can still choose to use an image different from the default:
- Add a new job
- Add an
image
directory to the new job - Specify a different image
- Check out how the executation environment changes
"},{"location":"050_defaults/exercise/#bonus-2-default-values-for-variables","title":"Bonus 2: Default values for variables","text":"See the official documentation for default
as well as variables
and check how they are related.
Solution (Click if you are stuck) Global variables are not located under default
but under the global variables
keyword.
"},{"location":"060_artifacts/exercise/","title":"Artifacts","text":"Goal
Learn how to...
- define artifacts
- consume artifacts
"},{"location":"060_artifacts/exercise/#task-1-pass-an-artifact-to-the-next-stage","title":"Task 1: Pass an artifact to the next stage","text":"Artifacts are useful for splitting a task in separate job. Refer to the official documentation.
Improve the pipeline by using artifacts:
- Create an artifact from the
hello
binary - Create a new stage called
test
with a job called test
- Call the
hello
binary as a smoke test
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Example for creating an artifacts:
job_name:\n script:\n - echo foo >file.txt\n artifacts:\n paths:\n - file.txt\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/060_artifact -- '*'\n
"},{"location":"060_artifacts/exercise/#bonus-1-define-from-which-jobs-to-receive-artifacts","title":"Bonus 1: Define from which jobs to receive artifacts","text":"Usually, artifacts are received from all jobs in the previous stages. Decide from which jobs to receive artifacts using the dependencies
keyword. See the official documentation.
Modify the job test
to consume artifacts only from the job build
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n dependencies:\n - build\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"060_artifacts/exercise/#bonus-2-passing-environment-variables","title":"Bonus 2: Passing environment variables","text":"In some situations, artifacts are to heavy-weight and passing a variable would be enough. Read the documentation for passing environment variables and implement this between two jobs of your choice.
The following hint and solution are a working example.
Hint (Click if you are stuck) Example for creating an artifact for environment variables:
job_name:\n script:\n - echo \"foo=bar\" >build.env\n artifacts:\n reports:\n dotenv: build.env\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n variables:\n BINARY_NAME: hello\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o \"${BINARY_NAME}\" \\\n .\n - echo \"${BINARY_NAME}\" >build.env\n artifacts:\n paths:\n - hello\n reports:\n dotenv: build.env\n\ntest:\n stage: test\n image: alpine\n script:\n - ./${BINARY_NAME}\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"065_job_dependencies/exercise/","title":"Job dependencies","text":"Goal
Learn how to...
- ignore stages
- start jobs as soon as dependencies are met
"},{"location":"065_job_dependencies/exercise/#task-start-a-job-early","title":"Task: Start a job early","text":"Start the job build
as soon as the job audit
completes without waiting for other job of the stage check
to finish. Check out the official documentation of needs
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n needs:\n - audit\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"065_job_dependencies/exercise/#bonus-start-a-job-late","title":"Bonus: Start a job late","text":"If two jobs in the same stage should not be executed at the same time, the needs
keyword can also delay a job until the dependencies are met. Modify the job lint
so that it waits for the job audit
to finish.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n needs:\n - audit\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n needs:\n - audit\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"070_schedules/exercise/","title":"Schedules","text":"Goal
Learn how to...
- schedule a pipeline run
- use a schedule to execute a preconfigured pipeline
"},{"location":"070_schedules/exercise/#task-create-a-schedule","title":"Task: Create a schedule","text":"Create a schedule to run a 5 minutes on the branch main
in the correct timezone. See the official documentation for schedules.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"070_schedules/exercise/#bonus-start-a-schedule-manually","title":"Bonus: Start a schedule manually","text":"Run the previously created schedule manually by clicking the play button . This come in handy if you need to run a pipeline with pre-configured variables.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"090_unit_tests/exercise/","title":"Unit tests","text":"Goal
Learn how to...
- execute unit tests
- publish results in GitLab
This exercise adds a unit test to the hello world application.
"},{"location":"090_unit_tests/exercise/#preparation","title":"Preparation","text":"Let's update the code:
git checkout origin/160_gitlab_ci/090_unit_tests -- main_test.go go.mod go.sum\n
"},{"location":"090_unit_tests/exercise/#task-publish-unit-test-results","title":"Task: Publish unit test results","text":"The following commands execute unit tests and automatically convert the results to JUnit using gotestsum:
go install gotest.tools/gotestsum@latest\ngotestsum --junitfile report.xml\n
See the official documentation for special artifacts and specifically reports.
Add a job unit_test
to the stage check
containing the above commands. The job needs to define a special artifact from the file report.xml
so that GitLab recognizes it as as JUnit XML report.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run which shows the unit test results on the tab in the overview.
Hint (Click if you are stuck) GitLab has published an example. The unit test report is published using a special type of artifact:
build:\n stage: test\n script: echo\n artifacts:\n when: always\n reports:\n junit: report.xml\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/090_unit_tests -- '*'\n
"},{"location":"100_environments/exercise/","title":"Environments","text":"Goal
Learn how to...
- use environments to specify deployment targets
- select environments dynamically
"},{"location":"100_environments/exercise/#preparation","title":"Preparation","text":"Create CI variables for use in the following exercises:
- Retrieve passwords for dev and live environments from the info page
- Create unprotected but masked CI variable
PASS
twice with scope dev
and live
- Create unprotected CI variable
SEAT_INDEX
with your seat number
"},{"location":"100_environments/exercise/#task-1-add-target-environment","title":"Task 1: Add target environment","text":"Add a new stage deploy
with a job called deploy
and use the following commands to upload the binary to the dev environment:
curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
Mind that curl
is not available in the default image golang:1.19.2
but must be installed using the following commands. Apply what you learned about script blocks as well as separating commands into preparation, core steps and cleanup.
apt-get update\napt-get -y install curl ca-certificates\n
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to download the hello
binary from https://seatN.dev.webdav.inmylab.de/hello
.
Hint (Click if you are stuck) Install curl
in a before_script
to separate the preparation from the core steps:
job_name:\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n
Now place the curl
command under script
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/100_environments/demo1 -- '*'\n
"},{"location":"100_environments/exercise/#task-2-add-deployment-to-development-environment","title":"Task 2: Add deployment to development environment","text":"Create a new branch dev
from the branch main
and modify the job deploy
to use the environment from the pre-defined variable $CI_COMMIT_REF_NAME
. Mind that the upload URL is also using a hard-coded environment name.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) stages:\n- check\n- build\n- test\n- deploy\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/100_environments/demo2 -- '*'\n
This was just a demonstration. The changes will not be preseved in the following chapters.
"},{"location":"100_environments/exercise/#task-3-add-deployment-to-production-environment","title":"Task 3: Add deployment to production environment","text":"Create the branch live
from the branch dev
and push it without further changes.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to download the hello
binary from https://seatN.live.inmylab.de/hello
.
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
"},{"location":"110_triggers/exercise/","title":"Triggers","text":"Goal
Learn how to...
- trigger pipelines in other projects
- learn about upstream and downstream pipelines
- use trigger tokens
- use multi-project pipelines
- use parent-child pipelines
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
"},{"location":"110_triggers/exercise/#preparation","title":"Preparation","text":"Triggering another pipeline requires a seconds project:
- Create a new project, e.g. a private project called
trigger
- Add
.gitlab-ci.yml
with the following content to the root of new project: test:\n script:\n - printenv\n
"},{"location":"110_triggers/exercise/#task-1-using-a-trigger-token","title":"Task 1: Using a trigger token","text":"The trigger token allows pipelines to be triggered using the API. Let's give this a try!
In the web UI:
- In the second project, go to Settings > CI/CD and unfold Pipeline trigger tokens
- Create a trigger token and copy the token as well as the
curl
snippet - Go back to
demo
project - Create an unprotected but masked CI variable called
TOKEN
In your pipeline:
- Add new stage
trigger
as well as a job trigger
- Add
curl
snippet in script
block - Replace
TOKEN
with the variable $TOKEN
- Replace
REF_NAME
with branch name (main
)
Afterwards check the pipeline in both projects in the GitLab UI. You should see successful pipeline runs.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n script: |\n curl https://gitlab.inmylab.de/api/v4/projects/seat${SEAT_INDEX}%2ftrigger/trigger/pipeline \\\n --request POST \\\n --silent \\\n --fail \\\n -F \"token=${TOKEN}\" \\\n -F \"ref=main\"\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/110_triggers/curl -- '*'\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"110_triggers/exercise/#task-2-using-a-multi-project-pipeline","title":"Task 2: Using a multi-project pipeline","text":"The second option for triggering a pipeline in another project, are multi-project pipelines. They come with a handy syntax in gitlab-ci.yaml
by using the trigger
keyword.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to expand the downstream pipeline to see the jobs and their status.
Hint (Click if you are stuck) Replace the script
keyword with the trigger
keyword.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger: <path-to-project>\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"110_triggers/exercise/#task-3-using-a-parent-child-pipeline","title":"Task 3: Using a parent-child pipeline","text":"A parent-child pipeline executes a downstream pipeline from a YAML file. Modify the contents of the trigger
keyword to use include
to execute a pipeline with the same content as in the first task but - this time - from a local file child.yaml
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to expand the downstream pipeline to see the jobs and their status.
Hint (Click if you are stuck) Create the file child.yaml
with the following pipeline:
test:\n script:\n - printenv\n
Use trigger
> include
to call the pipeline from this file.
Solution (Click if you are stuck) child.yaml
:
test:\n script:\n - printenv\n
.gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/110_triggers/parent-child -- '*'\n
"},{"location":"120_templates/exercise/","title":"Templates","text":"Goal
Learn how to...
- create templates
- make jobs reusable
- load templates from different locations
"},{"location":"120_templates/exercise/#task-1-create-a-template-inline","title":"Task 1: Create a template inline","text":"Create a template for compiling a go binary from the job build
and use it in the job build
. See the official documentation for templates for guidance.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) XXX
.build-go:\n script:\n #...\n\nbuild:\n extends: #...\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\n.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
You decide whether artifacts
is part of the template or not!
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/120_templates/inline -- '*'\n
"},{"location":"120_templates/exercise/#task-2-loading-templates-from-a-local-file","title":"Task 2: Loading templates from a local file","text":"Move the template into a separate file go.yaml
and use the include
keyword to import the template.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) go.yaml
:
.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n
Solution (Click if you are stuck) go.yaml
:
.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n
.gitlab-ci.yml
:
include:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/120_templates/local -- '*'\n
"},{"location":"120_templates/exercise/#task-3-loading-templates-from-another-project","title":"Task 3: Loading templates from another project","text":"Create a new project anywhere (!), move go.yaml
there and fix the include
keyword. See the extended syntax of the include
keyword to import templates from another project.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
include:\n- project: seat/template-go\n ref: main\n file: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml --format testname\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"130_rules/exercise/","title":"Rules","text":"Goal
Learn how to...
- define when to run jobs (and when not)
- how (workflow) rules can apply to whole pipelines
- how to use GitLab Pages to publish static web pages
In this exercise we will publish a static web page to download the hello
binary.
"},{"location":"130_rules/exercise/#preparation","title":"Preparation","text":"Add a file public/index.html
to your project using the following command:
git checkout origin/160_gitlab_ci/130_rules -- 'public/index.html'\n
"},{"location":"130_rules/exercise/#task-1-prevent-a-job-from-running","title":"Task 1: Prevent a job from running","text":"Add a job pages
to the stage deploy
with the following content:
pages:\n stage: deploy\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n
Review the official documentation for the rules
keyword to limit the job pages
to run when...
- the pipeline was triggered by a push event
- the change applied to the default branch
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) In pre-defined variables see $CI_PIPELINE_SOURCE
for trigger events, $CI_COMMIT_REF_NAME
for the current Git reference and $CI_DEFAULT_BRANCH
for the default branch.
Hint 2 (Click if you are stuck) See complex rules for combining conditions using and (&&
) and or (||
).
Solution (Click if you are stuck) .gitlab-ci.yml
:
include:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/130_templates -- '*'\n
"},{"location":"130_rules/exercise/#task-2-prevent-a-pipeline-from-running","title":"Task 2: Prevent a pipeline from running","text":"Rules can also be placed under the global workflow
keyword to apply to the whole pipeline instead of individual jobs.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The syntax of workflow
looks like this:
workflow:\n rules:\n #...\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/130_rules_workflow -- '*'\n
"},{"location":"130_rules/exercise/#task-3-use-deploy-freeze","title":"Task 3: Use deploy freeze","text":"Projects can define a deploy freeze to prevent pipelines to run but the settings only results in an environment varialbe $CI_DEPLOY_FREEZE
. Rules as well as workflow rules can be used to enforce deploy freezes.
Modify the pipeline to prevent the execution when $CI_DEPLOY_FREEZE
is not empty.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) Simply enter the variable into a rule to check if it is not empty.
Hint 2 (Click if you are stuck) Checkout the when
keyword under if
to control whether to start a pipeline/job or not.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"140_merge_requests/exercise/","title":"Merge requests","text":"Goal
Learn how to...
- run pipelines in the context of a merge request using rules
- use template to avoid repetition when using rules
"},{"location":"140_merge_requests/exercise/#task-1-use-rules-to-run-in-merge-request-context","title":"Task 1: Use rules to run in merge request context","text":"In the last chapter about rules
, you learned how to use $CI_PIPELINE_SOURCE
to restrict execution to specific events. You will need this now.
On the branch main
, add rules to the jobs to specify when to run them:
- For the jobs
lint
, audit
, unit_tests
, build
and test
, add rules so that the jobs are executed when... - pushing to the default branch
- running in merge request context
- Run the job
trigger
only when pushing to the default branch - Do not modify the existing rules for the job
pages
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) $CI_PIPELINE_SOURCE
can take the values push
and merge_request_event
in this context. $CI_COMMIT_REF_NAME
contains the name of the Git reference (e.g. branch) the pipeline is running on. $CI_DEFAULT_BRANCH
contains the name of the default branch of the repository in the current project. You can use the logical operator &&
to combine multiple conditions.
Solution (Click if you are stuck) workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go fmt .\n\naudit:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go vet .\n\nunit_tests:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/140_merge_requests -- '*'\n
"},{"location":"140_merge_requests/exercise/#task-2-create-a-merge-request","title":"Task 2: Create a merge request","text":"Now we want to check which jobs are executed in the context of a merge request:
- Create a new branch based on
main
- Create a merge request into
main
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"140_merge_requests/exercise/#bonus-explore-additional-predefined-variables","title":"Bonus: Explore additional predefined variables","text":"On the branch of the merge request, add a job and run printenv
to get a list of variables available to the pipeline. Check out additional variables specific to merge request pipelines. See also the official documentation.
"},{"location":"140_merge_requests/exercise/#task-3-avoid-repetition-using-rule-templates","title":"Task 3: Avoid repetition using rule templates","text":"In the first task we have implemented the same set of rules for multiple jobs. By combining rules with templates, this repetition can be avoided.
- Create an inline template called
.run-on-push-to-default
with the corresponding rule(s) - Create a second inline template called
.run-on-push-and-mr
with the corresponding rule(s) - Modify the jobs to use the rule templates
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Use extends
to use the rule template in a job.
Remember that only variables
are kumulative. All other keywords overwrite each other in the order of appearance.
Solution (Click if you are stuck) workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/140_merge_requests_rule_templates -- '*'\n
"},{"location":"150_matrix_jobs/exercise/","title":"Matrix Jobs","text":"Goal
Learn how to...
- run the same
script
for different inputs
"},{"location":"150_matrix_jobs/exercise/#preparation","title":"Preparation","text":"Switch back to the branch main
.
"},{"location":"150_matrix_jobs/exercise/#task-1-build-binary-for-multiple-platforms","title":"Task 1: Build binary for multiple platforms","text":"Build the hello
binary for multiple target platform. Matrix jobs enable you to reuse the existing code and parameterize it using different inputs.
Improve the template .build-go
in go.yaml
to build for linux/amd64
and linux/arm64
:
- Check the official documentation for
parallel
to create a matrix job - Add one input for
GOOS=linux
and GOARCH=amd64
- Add another input for
GOOS=linux
and GOARCH=arm64
- Modify the build command to write to separate files using
hello-${GOOS}-${GOARCH}
- Move the artifact definition into the template and include all
hello
binaries - Fix the smoke test to execute the
hello
binary for linux/amd64
- Fix the job
deploy
to upload the hello
binary for linux/amd64
- Fix the job
pages
to copy the hello
binary for linux/amd64
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.build-go:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello-${GOOS}-${GOARCH} \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n
.gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n image: alpine\n script:\n - ./hello-linux-amd64\n\ndeploy:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/150_matrix_jobs_demo1 -- '*'\n
"},{"location":"150_matrix_jobs/exercise/#task-2-test-an-alternative-to-specify-the-same-inputs","title":"Task 2: Test an alternative to specify the same inputs","text":"Test whether the following syntax for the inputs produces the same results in a pipeline:
.build-go:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: [ amd64, arm64 ]\n
"},{"location":"150_matrix_jobs/exercise/#bonus-check-binaries-for-correct-platform","title":"Bonus: Check binaries for correct platform","text":"Add another matrix job to check the target platform of the hello
binaries:
- Add another template called
.test-go
to go.yaml
defining a matrix job using the same inputs as for .build-go
- In the new template run
file hello-${GOOS}-${GOARCH}
to display the target platform - Modify the job
test
to use the new template
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.go-targets:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n\n.build-go:\n extends:\n - .go-targets\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello-${GOOS}-${GOARCH} \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n\n.test-go:\n extends:\n - .go-targets\n before_script:\n - apt-get update\n - apt-get -y install file\n script:\n - |\n file hello-${GOOS}-${GOARCH}\n
.gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/150_matrix_jobs_demo2 -- '*'\n
"},{"location":"220_services/exercise/","title":"Services","text":"Goal
Learn how to...
- define a service
- access the service
"},{"location":"220_services/exercise/#task-1-create-and-use-a-service","title":"Task 1: Create and use a service","text":"Service are launched in parallel to the regular job to add missing functionality, e.g. a database backend to execute integration tests. See the official documentation and modify the pipeline:
- Create service for the whole pipeline based on the container image
nginx:1.20.2
- Add a new job
test-service
to the stage test
with the following code: curl -s http://nginx\n
- Make sure the new job only executes when pushing to
main
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The service is defined globally in the gitlab-ci.yml
using the keyword services
:
services:\n- nginx:1.20.2\n
Solution (Click if you are stuck) gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nservices:\n- nginx:1.20.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ntest-service:\n stage: test\n extends:\n - .run-on-push-to-default-branch\n script:\n - curl -s http://nginx\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/220_services -- '*'\n
"},{"location":"220_services/exercise/#task-2-move-the-service-into-the-job","title":"Task 2: Move the service into the job","text":"The above task forced a second container to be created for every job although only one job has used the service. Optimize resource usage by moving the service into the job test-service
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ntest-service:\n stage: test\n extends:\n - .run-on-push-to-default-branch\n services:\n - nginx:1.20.2\n script:\n - curl -s http://nginx\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"230_docker/exercise/","title":"Docker","text":"Goal
Learn how to...
- build a container image...
- ...using a service
"},{"location":"230_docker/exercise/#preparation","title":"Preparation","text":"Building a container image requires a Dockerfile
which can be fetched with the following command:
git checkout origin/160_gitlab_ci/230_docker -- Dockerfile\n
"},{"location":"230_docker/exercise/#task-build-a-container-image","title":"Task: Build a container image","text":"For building a container image, you will need to...
- Add a new stage
package
to the pipeline - Add a new job
package
to the pipeline - Use the image
docker:20.10.18
for the job - Add a rule to limit execution to pushes to the default branch
- Add a service to the job:
- using the image
docker:20.10.18-dind
- using the
command
set to [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]
- Add a job variable
DOCKER_HOST
with value tcp://docker:2375
- Execute the command
docker build --tag hello .
Heads-Up
The GitLab runner must be configured to run services in privileged mode so that the Docker daemon is able to start.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The service should be set to:
services:\n- name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n variables:\n DOCKER_HOST: tcp://docker:2375\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n script:\n - docker build --tag hello .\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/230_docker -- '*'\n
"},{"location":"240_registries/exercise/","title":"Registries","text":"Goal
Learn how to...
- authenticate to the GitLab container registry
- push a container image to the registry
"},{"location":"240_registries/exercise/#task-push-a-container-image","title":"Task: Push a container image","text":"GitLab include a container registry. In this task you will push a container image to the registry. If the container registry is enabled, GitLab automatically provides predefined variables to access and authenticate to the registry:
$CI_REGISTRY
: The registry URL $CI_REGISTRY_IMAGE
: The registry URL with the project name $CI_REGISTRY_USER
: The username to use to push $CI_REGISTRY_PASSWORD
: The password to use to push
Modify the job package
:
- Update the build command to use the variables above:
docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .
- Add the push command directly after the build command:
docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"
- Login to the registry before building:
docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"
- Logout from the registry after pushing:
docker logout \"${CI_REGISTRY}\"
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Use before_script
for logging in and after_script
after logging out.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/240_registries -- '*'\n
"},{"location":"250_releases/exercise/","title":"Releases","text":"Goal
Learn how to...
- create a release on GitLab
- use the GitLab
release-cli
"},{"location":"250_releases/exercise/#task-create-a-release","title":"Task: Create a release","text":"GitLab can create releases based on a Git tag. The release can contain a description and links to assets. The assets must be stored elsewhere.
- Check out the official documentation about the
release
keyword - Modify the job
pages
to create a release in addition to the script block - The release should be based on the current commit hash (
$CI_COMMIT_SHA
) - Use the unique pipeline ID (
$CI_PIPELINE_IID
) as the tag name - Set an arbitrary name and description
For the release
keyword to work, the release-cli
binary must be present in the execution environment of the job:
- Set
image
to registry.gitlab.com/gitlab-org/release-cli:v0.14.0
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Your release should look similar to this:
release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/250_releases -- '*'\n
"},{"location":"265_caches/exercise/","title":"Caches","text":"Goal
Learn how to...
- define caches
- store data in a cache
- restore data from a cache
- avoid relying on the cache
"},{"location":"265_caches/exercise/#task-1-test-caching","title":"Task 1: Test caching","text":"The cache is used by adding the keyword cache
in a pipeline job, a job template or in the default
section. Let's give it a try:
- Add a job
test_cache
to the first stage - Download dependency information from the uniget project:
curl -sSLfO https://github.com/uniget-org/cli/raw/main/go.mod\ncurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.sum\n
- Use the official example for Go to enable caching
- Instead of
go test
run go mod download
to download dependencies only
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run. Retry the job to see the effect of the cache.
Hint (Click if you are stuck) Use the .go-cache
job template from the official example for Go.
Solution (Click if you are stuck) .gitlab-ci-yml
:
#...\n\n.go-cache:\n variables:\n GOPATH: $CI_PROJECT_DIR/.go\n before_script:\n - mkdir -p .go\n cache:\n key: ${CI_PROJECT_PATH_SLUG}\n policy: pull-push\n paths:\n - .go/pkg/mod/\n\ntest_cache:\n stage: check\n extends:\n - .go-cache\n script:\n - go mod download \n\n#...\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"265_caches/exercise/#task-2-add-caching","title":"Task 2: Add caching","text":"Now, integrate the job template .go-cache
into the pipeline and use it for the jobs build
and unit_test
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.go-targets:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n\n.go-cache:\n variables:\n GOPATH: $CI_PROJECT_DIR/.go\n before_script:\n - mkdir -p .go\n cache:\n key: ${CI_PROJECT_PATH_SLUG}\n policy: pull-push\n paths:\n - .go/pkg/mod/\n\n.build-go:\n extends:\n - .go-targets\n - .go-cache\n script:\n - |\n go build \\\n -o hello-${GOOS}-${GOARCH} \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n\n.test-go:\n extends:\n - .go-targets\n before_script:\n - apt-get update\n - apt-get -y install file\n script:\n - |\n file hello-${GOOS}-${GOARCH}\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/265_caches -- '*'\n
"},{"location":"270_renovate/exercise/","title":"Renovate","text":"Goal
Learn how to...
- discover dependencies used in your code and pipeline
- get update proposals for outdated dependencies
We will be using Renovate to discover and update dependencies.
"},{"location":"270_renovate/exercise/#task-add-renovate-to-your-pipeline","title":"Task: Add Renovate to your pipeline","text":"The easiest way to get Renovate on GitLab is to integrated it into your pipeline:
- Create a new project access token
renovate
with role Developer
and scopes api
, read_repository
, read_registry
- Add project access token
renovate
to CI variable RENOVATE_TOKEN
- Add a job
renovate
to the stage check
- Limit execution to a) scheduled pipelines and b) if the variable
$RENOVATE
is set - Use the image
renovate/renovate
- Set the variable
LOG_LEVEL
to debug
- Use the following script to execute Renovate:
renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${RENOVATE_TOKEN} \\\n ${CI_PROJECT_PATH}\n
- Create a scheduled pipeline and define a variable
RENOVATE
with the value true
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nrenovate:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"schedule\" && $RENOVATE'\n image: renovate/renovate\n variables:\n LOG_LEVEL: debug\n script: |\n renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${CI_JOB_TOKEN} \\\n --autodiscover true\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/270_renovate -- '*'\n
"},{"location":"280_security/exercise/","title":"Security","text":"Goal
Learn how to...
- detect secrets in your code
- detect vulnerabilities in your code
"},{"location":"280_security/exercise/#task-add-integrated-security-scans","title":"Task: Add integrated security scans","text":"GitLab offers multiple security scanners in the community edition:
- Checkout the official documentation for secret detection and integrate it into your pipeline
- Checkout the official documentation for static application security testing and integrate it into your pipeline
- Checkout the official documentation for container scanning and integrate it into your pipeline
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The following templates are available for the above features:
- Secret detection:
Security/Secret-Detection.gitlab-ci.yml
- Static application security testing:
Security/SAST.gitlab-ci.yml
- Container scanning:
Security/Container-Scanning.gitlab-ci.yml
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n- template: Security/Secret-Detection.gitlab-ci.yml\n- template: Security/SAST.gitlab-ci.yml\n- template: Security/Container-Scanning.gitlab-ci.yml\n\ncontainer_scanning:\n stage: trigger\n variables:\n CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\n CI_APPLICATION_REPOSITORY: ${CI_REGISTRY_IMAGE}\n CI_APPLICATION_TAG: ${CI_COMMIT_REF_NAME}\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nrenovate:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"schedule\" && $RENOVATE'\n image: renovate/renovate\n variables:\n LOG_LEVEL: debug\n script: |\n renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${RENOVATE_TOKEN} \\\n --autodiscover true\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/280_security -- '*'\n
Heads-Up
You can also select a different scanner for container scanning using the variable $CS_ANALYZER_IMAGE
. The following values are available:
Scanner Image Default (trivy) registry.gitlab.com/security-products/container-scanning:6 Grype registry.gitlab.com/security-products/container-scanning/grype:6 Trivy registry.gitlab.com/security-products/container-scanning/trivy:6"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Workshop: GitLab CI","text":"This site contains the exercises to learn about GitLab CI.
"},{"location":"#audience","title":"Audience","text":"Software developers and system administrators who want to learn how to use GitLab CI.
"},{"location":"#duration","title":"Duration","text":"Two days
"},{"location":"#expected-knowledge","title":"Expected knowledge","text":"Basic under standing of another CI/CD server, e.g. GitHub and Jenkins
"},{"location":"#goals","title":"Goals","text":"Learn about the features of GitLab CI and how to use then in a software development project.
"},{"location":"#style","title":"Style","text":"20% theoretical introduction
80% practical exercises
The instructor will provide an introduction to each topic. Participants will then work on exercises. The instructor will be available to answer questions and provide hints. The instructor will also demonstrate solutions to the exercises.
"},{"location":"navigation/","title":"Navigation","text":"All pages have the same navigation structure.
"},{"location":"navigation/#section-1-menu-on-the-left","title":"Section 1: Menu on the left","text":"The left-hand menu contains the available chapters and displays the structure of the workshop.
On smaller screens or windows, the left-hand menu can be shown by clicking on the burger menu .
"},{"location":"navigation/#section-2-page-contents-on-the-right","title":"Section 2: Page contents on the right","text":"The right-hand menu contains the contents of the current chapter and lists all exercises contained in the current chapter.
On smaller screens or windows, the right-hand menu can be shown by clicking on the burger menu and then clicking on the icon next to the current chapter.
"},{"location":"you_are_done/","title":"You are done!","text":""},{"location":"you_are_done/#congratulations","title":"Congratulations!","text":"You have finished the workshop
"},{"location":"you_are_ready/","title":"You are ready!","text":"A few last words...
"},{"location":"you_are_ready/#exercises-tell-a-story","title":"Exercises tell a story","text":"Each chapter focuses on a single feature
Each exercise improves the previous state
All exercises will have leave some questions unanswered
Following exercises will again improve
"},{"location":"you_are_ready/#work-at-your-own-pace","title":"Work at your own pace","text":"Either follow topics and exercises at the instructor's pace
Or work ahead if you finish early
Please be mindful of other participants
Please do not confuse participants with your questions when racing ahead
"},{"location":"000_rollout/exercise_gitlab/","title":"GitLab","text":"This workshop is performed on a shared GitLab instance. You have been assigned a user and password for this instance in an email.
"},{"location":"000_rollout/exercise_gitlab/#task-1-retrieve-your-credentials-digitally","title":"Task 1: Retrieve your credentials digitally","text":"You have received your credentials in an email but the password is not fun to type in. Let's retrieve it digitally:
- Go to https://code.inmylab.de
- Select your username from the list
- Login using your personal user
seatN
(where N
is a number) and your code (looks like this ABCDEF
) - The web page displays your credentials
Keep the page open to refer to your credentials anytime throughout the workshop.
"},{"location":"000_rollout/exercise_gitlab/#task-2-login-to-gitlab","title":"Task 2: Login to GitLab","text":"Go to https://gitlab.inmylab.de and login with your credentials.
Hint (Click if you are stuck) Your username looks like seatN
where N
is a number.
Your password is a long, random string which is displayed on the web pages access in the previous task.
"},{"location":"000_rollout/exercise_gitlab/#task-3-access-the-demo-project","title":"Task 3: Access the demo project","text":"A personal project has been provisioned for you to follow this workshop. It is called demo
. Let's find it!
Hint (Click if you are stuck) Personal projects are access by clicking on your avatar in the top left corner.
Solution (Click if you are stuck) The deep link is https://gitlab.inmylab.de/seatN/demo where N
is a number.
"},{"location":"000_rollout/exercise_ide/","title":"IDE","text":"You can follow this workshop in the IDE (Integrated Development Environment) of your choice but...
A web-based instance of Visual Studio Code has been provisioned for you to follow this workshop.
"},{"location":"000_rollout/exercise_ide/#task-access-your-instance-of-visual-studio-code","title":"Task: Access your instance of Visual Studio Code","text":" - Go to https://seatN.vscode.inmylab.de where
N
is a number. - Login using your personal user
seatN
(where N
is a number) and your password
The password is the same as your GitLab password.
Hint (Click if you are stuck) Your username looks like seatN
where N
is a number.
Your password is a long, random string which is displayed on the web pages access in the previous chapter.
"},{"location":"000_rollout/exercise_project/","title":"Project","text":""},{"location":"000_rollout/exercise_project/#task-1-view-the-demo-project","title":"Task 1: View the demo project","text":"Your instance of Visual Studio Code is provisioned with your credentials to allow Git-over-HTTP to the GitLab instance as well as a clone of the demo
project.
- In the directory tree
- Find the
demo
project under /home/seat
"},{"location":"000_rollout/exercise_project/#task-2-pulling-from-the-upstream-repository","title":"Task 2: Pulling from the upstream repository","text":"The local clone of the demo
project is configured with a remote pointing to the location of the original project on GitHub. This is prepared in case the instructor needs to fix a demo. Let's test this safeguard:
- Open a terminal
- Change to the
demo
project directory - List remotes:
git remote -v
- Pull from the upstream repository:
git pull upstream
"},{"location":"010_jobs_and_stages/exercise/","title":"Jobs and stages","text":"Goal
Learn how to...
- create jobs
- organize them in stages
- understand when jobs in different stages are executed
"},{"location":"010_jobs_and_stages/exercise/#preparation","title":"Preparation","text":"This workshop is based on an example hello world application written in Go. Get the code using the following command:
git checkout upstream/160_gitlab_ci/example_app -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-1-create-a-single-job","title":"Task 1: Create a single job","text":"Add a pipeline to build the code using the following commands:
apk update\napk add go\ngo build -o hello .\n./hello\n
See the official documentation about jobs.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Add a file called
.gitlab-ci.yml
in the root of the project - Add a job called
build
Solution (Click if you are stuck) .gitlab-ci.yml
:
build:\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/010_jobs_and_stages/build -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-2-add-a-stage","title":"Task 2: Add a stage","text":"Modify the pipeline to consist of two stages called check
and build
where the check
stage contains the following commands:
apk update\napk add go\ngo fmt .\ngo vet .\n
See the official documentation about stages.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Define two stages using
stages
- Add a job called
check
in the stage check
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/010_jobs_and_stages/lint -- '*'\n
"},{"location":"010_jobs_and_stages/exercise/#task-3-add-parallel-jobs","title":"Task 3: Add parallel jobs","text":"Split the job check
so that one job called lint
executes go fmt .
and another job called audit
executes go vet .
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Both jobs lint
and audit
must be in the stage check
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - go build -o hello .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/010_jobs_and_stages/parallel -- '*'\n
"},{"location":"020_variables/exercise/","title":"Variables","text":"Goal
Learn how to...
- add local variable to your pipeline
- consume pre-defined variables
- add secrets in the UI
"},{"location":"020_variables/exercise/#task-1-create-a-job-variable","title":"Task 1: Create a job variable","text":"This exercise requires an updates version of our hello world program:
git checkout upstream/160_gitlab_ci/020_variables/inline -- main.go\n
Add a variable called version
to the job called build
and modify the build command as follows:
go build -o hello -ldflags \"-X main.Version=${version}\" .\n
See the official documentation about variables.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Use the
variable
keyword to define a variable inside the job called build
- Replace the build command with the one provided above
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n variables:\n version: dev\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${version}\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/020_variables/inline -- '*'
"},{"location":"020_variables/exercise/#task-2-use-a-predefined-variable","title":"Task 2: Use a predefined variable","text":"Read the official documentation about predefined variables and replace the job variable with the predefined variable CI_COMMIT_REF_NAME
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove the
variable
keyword from the job called build
- Replace the variable
${version}
with the predefined variable ${CI_COMMIT_REF_NAME}
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME}\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/020_variables/predefined -- '*'
"},{"location":"020_variables/exercise/#task-3-add-a-ci-variable-in-the-ui","title":"Task 3: Add a CI variable in the UI","text":"This exercise requires an updates version of our hello world application:
git checkout upstream/160_gitlab_ci/020_variables/ci -- main.go\n
The application now also prints the name of the author which must be supplied during compilation as well.
Read the official documentation about CI variables and extend the build command to provide main.Author
through a CI variable called AUTHOR
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) - Go to
Settings
> CI/CD
> Variables
- Add a variable called
AUTHOR
with your name
Hint 2 (Click if you are stuck) The -ldflags
option needs to be extended with -X 'main.Author=${AUTHOR}'
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n script:\n - apk update\n - apk add go\n - go fmt .\n\naudit:\n stage: check\n script:\n - apk update\n - apk add go\n - go vet .\n\nbuild:\n stage: build\n script:\n - apk update\n - apk add go\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/020_variables/ci -- '*'\n
"},{"location":"030_script_blocks/exercise/","title":"Scriptblocks","text":"Goal
Learn how to...
- Use
before_script
and after_script
- Separate preparation and cleanup commands from core functionality
"},{"location":"030_script_blocks/exercise/#task-separate-script-blocks-into-preparation-and-main-task","title":"Task: Separate script blocks into preparation and main task","text":"Commands are currently specified using the script
directive. These commands consist of preparation, core functionality and (possibly) cleanup.
To improve readability, move the preparation of the execution environment to a before_script
. See the official documentation.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Move calls to apk
to the before_script
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n before_script:\n - apk update\n - apk add go\n script:\n - go fmt .\n\naudit:\n stage: check\n before_script:\n - apk update\n - apk add go\n script:\n - go vet .\n\nbuild:\n stage: build\n before_script:\n - apk update\n - apk add go\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/030_script_blocks -- '*'\n
Cleanup commands can be move to after_script
(official documentation) but we have no use for this in the current example.
"},{"location":"030_script_blocks/exercise/#bonus-1-when-after_script-is-executed","title":"Bonus 1: When after_script
is executed","text":"Add commands to all three script block before_script
, script
and after_script
. Test two scenarios:
- The pipeline succeeds
- The pipeline failes
What happens to the code in after_script
?
Solution (Click to reveal) Command in after_script
are always executed even if the job fails.
This can be very useful for cleaning up.
"},{"location":"030_script_blocks/exercise/#bonus-2-what-happens-to-environment-variables-in-script-blocks","title":"Bonus 2: What happens to environment variables in script blocks?","text":"Define environment variables in all three script blocks and display them in the same and in the following script block.
When are environment variables available?
Solution (Click to reveal) Commands in before_script
and script
share a shell session. Environment variables are available throughout these script blocks.
Commands in after_script
are executed in a new shell session. Environment variables defined in before_script
and script
are gone.
"},{"location":"040_image/exercise/","title":"Images","text":"Goal
Learn how to...
- specify which container image to use for a job
- tailor the execution environment to your needs
"},{"location":"040_image/exercise/#task-simplify-using-container-images","title":"Task: Simplify using container images","text":"In the previous exampes, we called apk
at the beginning of every job to install Go. This had to be repeated for every job because Go was not present. Choosing an image for a job using the image
directive, time is saved by avoiding commands to install required tools. See the official documentation.
Replace the calls to apk
with the container image golang:1.19.2
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove
before_script
- Add
image: golang:1.19.2
instead
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\nlint:\n stage: check\n image: golang:1.19.2\n script:\n - go fmt .\n\naudit:\n stage: check\n image: golang:1.19.2\n script:\n - go vet .\n\nbuild:\n stage: build\n image: golang:1.19.2\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/040_image -- '*'\n
"},{"location":"040_image/exercise/#bonus-test-different-images","title":"Bonus: Test different images","text":"Add a job to your pipeline to test different container images. Check how different images offer specialized execution environments:
- Use
python:3
and test running python --version
- Use
node
and test running node --version
"},{"location":"050_defaults/exercise/","title":"Defaults","text":"Goal
Learn how to...
- avoid repetition in jobs
- specify defaults are the top of your pipeline
"},{"location":"050_defaults/exercise/#task-dont-repeat-yourself","title":"Task: Don't repeat yourself","text":"All jobs currently have a dedicated image
directive. Using defaults, this repetition can be avoided. See the official documentation.
Replace job specific image
directives with the default
directive.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) - Remove
image
from all build jobs - Add
default
with the image
directive at the top
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/050_default -- '*'\n
"},{"location":"050_defaults/exercise/#bonus-1-override-defaults","title":"Bonus 1: Override defaults","text":"Jobs can still choose to use an image different from the default:
- Add a new job
- Add an
image
directory to the new job - Specify a different image
- Check out how the executation environment changes
"},{"location":"050_defaults/exercise/#bonus-2-default-values-for-variables","title":"Bonus 2: Default values for variables","text":"See the official documentation for default
as well as variables
and check how they are related.
Solution (Click if you are stuck) Global variables are not located under default
but under the global variables
keyword.
"},{"location":"060_artifacts/exercise/","title":"Artifacts","text":"Goal
Learn how to...
- define artifacts
- consume artifacts
"},{"location":"060_artifacts/exercise/#task-1-pass-an-artifact-to-the-next-stage","title":"Task 1: Pass an artifact to the next stage","text":"Artifacts are useful for splitting a task in separate job. Refer to the official documentation.
Improve the pipeline by using artifacts:
- Create an artifact from the
hello
binary - Create a new stage called
test
with a job called test
- Call the
hello
binary as a smoke test
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Example for creating an artifacts:
job_name:\n script:\n - echo foo >file.txt\n artifacts:\n paths:\n - file.txt\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/060_artifact -- '*'\n
"},{"location":"060_artifacts/exercise/#bonus-1-define-from-which-jobs-to-receive-artifacts","title":"Bonus 1: Define from which jobs to receive artifacts","text":"Usually, artifacts are received from all jobs in the previous stages. Decide from which jobs to receive artifacts using the dependencies
keyword. See the official documentation.
Modify the job test
to consume artifacts only from the job build
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n dependencies:\n - build\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"060_artifacts/exercise/#bonus-2-passing-environment-variables","title":"Bonus 2: Passing environment variables","text":"In some situations, artifacts are to heavy-weight and passing a variable would be enough. Read the documentation for passing environment variables and implement this between two jobs of your choice.
The following hint and solution are a working example.
Hint (Click if you are stuck) Example for creating an artifact for environment variables:
job_name:\n script:\n - echo \"foo=bar\" >build.env\n artifacts:\n reports:\n dotenv: build.env\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n variables:\n BINARY_NAME: hello\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o \"${BINARY_NAME}\" \\\n .\n - echo \"${BINARY_NAME}\" >build.env\n artifacts:\n paths:\n - hello\n reports:\n dotenv: build.env\n\ntest:\n stage: test\n image: alpine\n script:\n - ./${BINARY_NAME}\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"065_job_dependencies/exercise/","title":"Job dependencies","text":"Goal
Learn how to...
- ignore stages
- start jobs as soon as dependencies are met
"},{"location":"065_job_dependencies/exercise/#task-start-a-job-early","title":"Task: Start a job early","text":"Start the job build
as soon as the job audit
completes without waiting for other job of the stage check
to finish. Check out the official documentation of needs
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n needs:\n - audit\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"065_job_dependencies/exercise/#bonus-start-a-job-late","title":"Bonus: Start a job late","text":"If two jobs in the same stage should not be executed at the same time, the needs
keyword can also delay a job until the dependencies are met. Modify the job lint
so that it waits for the job audit
to finish.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n needs:\n - audit\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nbuild:\n stage: build\n needs:\n - audit\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"070_schedules/exercise/","title":"Schedules","text":"Goal
Learn how to...
- schedule a pipeline run
- use a schedule to execute a preconfigured pipeline
"},{"location":"070_schedules/exercise/#task-create-a-schedule","title":"Task: Create a schedule","text":"Create a schedule to run a 5 minutes on the branch main
in the correct timezone. See the official documentation for schedules.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"070_schedules/exercise/#bonus-start-a-schedule-manually","title":"Bonus: Start a schedule manually","text":"Run the previously created schedule manually by clicking the play button . This come in handy if you need to run a pipeline with pre-configured variables.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"090_unit_tests/exercise/","title":"Unit tests","text":"Goal
Learn how to...
- execute unit tests
- publish results in GitLab
This exercise adds a unit test to the hello world application.
"},{"location":"090_unit_tests/exercise/#preparation","title":"Preparation","text":"Let's update the code:
git checkout upstream/160_gitlab_ci/090_unit_tests -- main_test.go go.mod go.sum\n
"},{"location":"090_unit_tests/exercise/#task-publish-unit-test-results","title":"Task: Publish unit test results","text":"The following commands execute unit tests and automatically convert the results to JUnit using gotestsum:
go install gotest.tools/gotestsum@latest\ngotestsum --junitfile report.xml\n
See the official documentation for special artifacts and specifically reports.
Add a job unit_test
to the stage check
containing the above commands. The job needs to define a special artifact from the file report.xml
so that GitLab recognizes it as as JUnit XML report.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run which shows the unit test results on the tab in the overview.
Hint (Click if you are stuck) GitLab has published an example. The unit test report is published using a special type of artifact:
build:\n stage: test\n script: echo\n artifacts:\n when: always\n reports:\n junit: report.xml\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/090_unit_tests -- '*'\n
"},{"location":"100_environments/exercise/","title":"Environments","text":"Goal
Learn how to...
- use environments to specify deployment targets
- select environments dynamically
"},{"location":"100_environments/exercise/#preparation","title":"Preparation","text":"Create CI variables for use in the following exercises:
- Retrieve passwords for dev and live environments from the info page
- Create unprotected but masked CI variable
PASS
twice with scope dev
and live
- Create unprotected CI variable
SEAT_INDEX
with your seat number
"},{"location":"100_environments/exercise/#task-1-add-target-environment","title":"Task 1: Add target environment","text":"Add a new stage deploy
with a job called deploy
and use the following commands to upload the binary to the dev environment:
curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
Mind that curl
is not available in the default image golang:1.19.2
but must be installed using the following commands. Apply what you learned about script blocks as well as separating commands into preparation, core steps and cleanup.
apt-get update\napt-get -y install curl ca-certificates\n
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to download the hello
binary from https://seatN.dev.webdav.inmylab.de/hello
.
Hint (Click if you are stuck) Install curl
in a before_script
to separate the preparation from the core steps:
job_name:\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n
Now place the curl
command under script
.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/100_environments/demo1 -- '*'\n
"},{"location":"100_environments/exercise/#task-2-add-deployment-to-development-environment","title":"Task 2: Add deployment to development environment","text":"Create a new branch dev
from the branch main
and modify the job deploy
to use the environment from the pre-defined variable $CI_COMMIT_REF_NAME
. Mind that the upload URL is also using a hard-coded environment name.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) stages:\n- check\n- build\n- test\n- deploy\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/100_environments/demo2 -- '*'\n
This was just a demonstration. The changes will not be preseved in the following chapters.
"},{"location":"100_environments/exercise/#task-3-add-deployment-to-production-environment","title":"Task 3: Add deployment to production environment","text":"Create the branch live
from the branch dev
and push it without further changes.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to download the hello
binary from https://seatN.live.inmylab.de/hello
.
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
"},{"location":"110_triggers/exercise/","title":"Triggers","text":"Goal
Learn how to...
- trigger pipelines in other projects
- learn about upstream and downstream pipelines
- use trigger tokens
- use multi-project pipelines
- use parent-child pipelines
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
"},{"location":"110_triggers/exercise/#preparation","title":"Preparation","text":"Triggering another pipeline requires a seconds project:
- Create a new project, e.g. a private project called
trigger
- Add
.gitlab-ci.yml
with the following content to the root of new project: test:\n script:\n - printenv\n
"},{"location":"110_triggers/exercise/#task-1-using-a-trigger-token","title":"Task 1: Using a trigger token","text":"The trigger token allows pipelines to be triggered using the API. Let's give this a try!
In the web UI:
- In the second project, go to Settings > CI/CD and unfold Pipeline trigger tokens
- Create a trigger token and copy the token as well as the
curl
snippet - Go back to
demo
project - Create an unprotected but masked CI variable called
TOKEN
In your pipeline:
- Add new stage
trigger
as well as a job trigger
- Add
curl
snippet in script
block - Replace
TOKEN
with the variable $TOKEN
- Replace
REF_NAME
with branch name (main
)
Afterwards check the pipeline in both projects in the GitLab UI. You should see successful pipeline runs.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n script: |\n curl https://gitlab.inmylab.de/api/v4/projects/seat${SEAT_INDEX}%2ftrigger/trigger/pipeline \\\n --request POST \\\n --silent \\\n --fail \\\n -F \"token=${TOKEN}\" \\\n -F \"ref=main\"\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/110_triggers/curl -- '*'\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"110_triggers/exercise/#task-2-using-a-multi-project-pipeline","title":"Task 2: Using a multi-project pipeline","text":"The second option for triggering a pipeline in another project, are multi-project pipelines. They come with a handy syntax in gitlab-ci.yaml
by using the trigger
keyword.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to expand the downstream pipeline to see the jobs and their status.
Hint (Click if you are stuck) Replace the script
keyword with the trigger
keyword.
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger: <path-to-project>\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"110_triggers/exercise/#task-3-using-a-parent-child-pipeline","title":"Task 3: Using a parent-child pipeline","text":"A parent-child pipeline executes a downstream pipeline from a YAML file. Modify the contents of the trigger
keyword to use include
to execute a pipeline with the same content as in the first task but - this time - from a local file child.yaml
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run and be able to expand the downstream pipeline to see the jobs and their status.
Hint (Click if you are stuck) Create the file child.yaml
with the following pipeline:
test:\n script:\n - printenv\n
Use trigger
> include
to call the pipeline from this file.
Solution (Click if you are stuck) child.yaml
:
test:\n script:\n - printenv\n
.gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/110_triggers/parent-child -- '*'\n
"},{"location":"120_templates/exercise/","title":"Templates","text":"Goal
Learn how to...
- create templates
- make jobs reusable
- load templates from different locations
"},{"location":"120_templates/exercise/#task-1-create-a-template-inline","title":"Task 1: Create a template inline","text":"Create a template for compiling a go binary from the job build
and use it in the job build
. See the official documentation for templates for guidance.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) XXX
.build-go:\n script:\n #...\n\nbuild:\n extends: #...\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
stages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\n.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
You decide whether artifacts
is part of the template or not!
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/120_templates/inline -- '*'\n
"},{"location":"120_templates/exercise/#task-2-loading-templates-from-a-local-file","title":"Task 2: Loading templates from a local file","text":"Move the template into a separate file go.yaml
and use the include
keyword to import the template.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) go.yaml
:
.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n
Solution (Click if you are stuck) go.yaml
:
.build-go:\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello \\\n .\n
.gitlab-ci.yml
:
include:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/120_templates/local -- '*'\n
"},{"location":"120_templates/exercise/#task-3-loading-templates-from-another-project","title":"Task 3: Loading templates from another project","text":"Create a new project anywhere (!), move go.yaml
there and fix the include
keyword. See the extended syntax of the include
keyword to import templates from another project.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
include:\n- project: seat/template-go\n ref: main\n file: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml --format testname\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"130_rules/exercise/","title":"Rules","text":"Goal
Learn how to...
- define when to run jobs (and when not)
- how (workflow) rules can apply to whole pipelines
- how to use GitLab Pages to publish static web pages
In this exercise we will publish a static web page to download the hello
binary.
"},{"location":"130_rules/exercise/#preparation","title":"Preparation","text":"Add a file public/index.html
to your project using the following command:
git checkout upstream/160_gitlab_ci/130_rules -- 'public/index.html'\n
"},{"location":"130_rules/exercise/#task-1-prevent-a-job-from-running","title":"Task 1: Prevent a job from running","text":"Add a job pages
to the stage deploy
with the following content:
pages:\n stage: deploy\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n
Review the official documentation for the rules
keyword to limit the job pages
to run when...
- the pipeline was triggered by a push event
- the change applied to the default branch
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) In pre-defined variables see $CI_PIPELINE_SOURCE
for trigger events, $CI_COMMIT_REF_NAME
for the current Git reference and $CI_DEFAULT_BRANCH
for the default branch.
Hint 2 (Click if you are stuck) See complex rules for combining conditions using and (&&
) and or (||
).
Solution (Click if you are stuck) .gitlab-ci.yml
:
include:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/130_templates -- '*'\n
"},{"location":"130_rules/exercise/#task-2-prevent-a-pipeline-from-running","title":"Task 2: Prevent a pipeline from running","text":"Rules can also be placed under the global workflow
keyword to apply to the whole pipeline instead of individual jobs.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The syntax of workflow
looks like this:
workflow:\n rules:\n #...\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/130_rules_workflow -- '*'\n
"},{"location":"130_rules/exercise/#task-3-use-deploy-freeze","title":"Task 3: Use deploy freeze","text":"Projects can define a deploy freeze to prevent pipelines to run but the settings only results in an environment varialbe $CI_DEPLOY_FREEZE
. Rules as well as workflow rules can be used to enforce deploy freezes.
Modify the pipeline to prevent the execution when $CI_DEPLOY_FREEZE
is not empty.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint 1 (Click if you are stuck) Simply enter the variable into a rule to check if it is not empty.
Hint 2 (Click if you are stuck) Checkout the when
keyword under if
to control whether to start a pipeline/job or not.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n script:\n - go fmt .\n\naudit:\n stage: check\n script:\n - go vet .\n\nunit_tests:\n stage: check\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n environment:\n name: dev\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"140_merge_requests/exercise/","title":"Merge requests","text":"Goal
Learn how to...
- run pipelines in the context of a merge request using rules
- use template to avoid repetition when using rules
"},{"location":"140_merge_requests/exercise/#task-1-use-rules-to-run-in-merge-request-context","title":"Task 1: Use rules to run in merge request context","text":"In the last chapter about rules
, you learned how to use $CI_PIPELINE_SOURCE
to restrict execution to specific events. You will need this now.
On the branch main
, add rules to the jobs to specify when to run them:
- For the jobs
lint
, audit
, unit_tests
, build
and test
, add rules so that the jobs are executed when... - pushing to the default branch
- running in merge request context
- Run the job
trigger
only when pushing to the default branch - Do not modify the existing rules for the job
pages
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) $CI_PIPELINE_SOURCE
can take the values push
and merge_request_event
in this context. $CI_COMMIT_REF_NAME
contains the name of the Git reference (e.g. branch) the pipeline is running on. $CI_DEFAULT_BRANCH
contains the name of the default branch of the repository in the current project. You can use the logical operator &&
to combine multiple conditions.
Solution (Click if you are stuck) workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go fmt .\n\naudit:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go vet .\n\nunit_tests:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n extends:\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/140_merge_requests -- '*'\n
"},{"location":"140_merge_requests/exercise/#task-2-create-a-merge-request","title":"Task 2: Create a merge request","text":"Now we want to check which jobs are executed in the context of a merge request:
- Create a new branch based on
main
- Create a merge request into
main
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
"},{"location":"140_merge_requests/exercise/#bonus-explore-additional-predefined-variables","title":"Bonus: Explore additional predefined variables","text":"On the branch of the merge request, add a job and run printenv
to get a list of variables available to the pipeline. Check out additional variables specific to merge request pipelines. See also the official documentation.
"},{"location":"140_merge_requests/exercise/#task-3-avoid-repetition-using-rule-templates","title":"Task 3: Avoid repetition using rule templates","text":"In the first task we have implemented the same set of rules for multiple jobs. By combining rules with templates, this repetition can be avoided.
- Create an inline template called
.run-on-push-to-default
with the corresponding rule(s) - Create a second inline template called
.run-on-push-and-mr
with the corresponding rule(s) - Modify the jobs to use the rule templates
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Use extends
to use the rule template in a job.
Remember that only variables
are kumulative. All other keywords overwrite each other in the order of appearance.
Solution (Click if you are stuck) workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n artifacts:\n paths:\n - hello\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n image: alpine\n script:\n - ./hello\n\ndeploy:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello public/\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/140_merge_requests_rule_templates -- '*'\n
"},{"location":"150_matrix_jobs/exercise/","title":"Matrix Jobs","text":"Goal
Learn how to...
- run the same
script
for different inputs
"},{"location":"150_matrix_jobs/exercise/#preparation","title":"Preparation","text":"Switch back to the branch main
.
"},{"location":"150_matrix_jobs/exercise/#task-1-build-binary-for-multiple-platforms","title":"Task 1: Build binary for multiple platforms","text":"Build the hello
binary for multiple target platform. Matrix jobs enable you to reuse the existing code and parameterize it using different inputs.
Improve the template .build-go
in go.yaml
to build for linux/amd64
and linux/arm64
:
- Check the official documentation for
parallel
to create a matrix job - Add one input for
GOOS=linux
and GOARCH=amd64
- Add another input for
GOOS=linux
and GOARCH=arm64
- Modify the build command to write to separate files using
hello-${GOOS}-${GOARCH}
- Move the artifact definition into the template and include all
hello
binaries - Fix the smoke test to execute the
hello
binary for linux/amd64
- Fix the job
deploy
to upload the hello
binary for linux/amd64
- Fix the job
pages
to copy the hello
binary for linux/amd64
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.build-go:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello-${GOOS}-${GOARCH} \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n
.gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n image: alpine\n script:\n - ./hello-linux-amd64\n\ndeploy:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/150_matrix_jobs_demo1 -- '*'\n
"},{"location":"150_matrix_jobs/exercise/#task-2-test-an-alternative-to-specify-the-same-inputs","title":"Task 2: Test an alternative to specify the same inputs","text":"Test whether the following syntax for the inputs produces the same results in a pipeline:
.build-go:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: [ amd64, arm64 ]\n
"},{"location":"150_matrix_jobs/exercise/#bonus-check-binaries-for-correct-platform","title":"Bonus: Check binaries for correct platform","text":"Add another matrix job to check the target platform of the hello
binaries:
- Add another template called
.test-go
to go.yaml
defining a matrix job using the same inputs as for .build-go
- In the new template run
file hello-${GOOS}-${GOARCH}
to display the target platform - Modify the job
test
to use the new template
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.go-targets:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n\n.build-go:\n extends:\n - .go-targets\n script:\n - |\n go build \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n -o hello-${GOOS}-${GOARCH} \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n\n.test-go:\n extends:\n - .go-targets\n before_script:\n - apt-get update\n - apt-get -y install file\n script:\n - |\n file hello-${GOOS}-${GOARCH}\n
.gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/150_matrix_jobs_demo2 -- '*'\n
"},{"location":"220_services/exercise/","title":"Services","text":"Goal
Learn how to...
- define a service
- access the service
"},{"location":"220_services/exercise/#task-1-create-and-use-a-service","title":"Task 1: Create and use a service","text":"Service are launched in parallel to the regular job to add missing functionality, e.g. a database backend to execute integration tests. See the official documentation and modify the pipeline:
- Create service for the whole pipeline based on the container image
nginx:1.20.2
- Add a new job
test-service
to the stage test
with the following code: curl -s http://nginx\n
- Make sure the new job only executes when pushing to
main
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The service is defined globally in the gitlab-ci.yml
using the keyword services
:
services:\n- nginx:1.20.2\n
Solution (Click if you are stuck) gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nservices:\n- nginx:1.20.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ntest-service:\n stage: test\n extends:\n - .run-on-push-to-default-branch\n script:\n - curl -s http://nginx\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/220_services -- '*'\n
"},{"location":"220_services/exercise/#task-2-move-the-service-into-the-job","title":"Task 2: Move the service into the job","text":"The above task forced a second container to be created for every job although only one job has used the service. Optimize resource usage by moving the service into the job test-service
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ntest-service:\n stage: test\n extends:\n - .run-on-push-to-default-branch\n services:\n - nginx:1.20.2\n script:\n - curl -s http://nginx\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"230_docker/exercise/","title":"Docker","text":"Goal
Learn how to...
- build a container image...
- ...using a service
"},{"location":"230_docker/exercise/#preparation","title":"Preparation","text":"Building a container image requires a Dockerfile
which can be fetched with the following command:
git checkout upstream/160_gitlab_ci/230_docker -- Dockerfile\n
"},{"location":"230_docker/exercise/#task-build-a-container-image","title":"Task: Build a container image","text":"For building a container image, you will need to...
- Add a new stage
package
to the pipeline - Add a new job
package
to the pipeline - Use the image
docker:20.10.18
for the job - Add a rule to limit execution to pushes to the default branch
- Add a service to the job:
- using the image
docker:20.10.18-dind
- using the
command
set to [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]
- Add a job variable
DOCKER_HOST
with value tcp://docker:2375
- Execute the command
docker build --tag hello .
Heads-Up
The GitLab runner must be configured to run services in privileged mode so that the Docker daemon is able to start.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The service should be set to:
services:\n- name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n variables:\n DOCKER_HOST: tcp://docker:2375\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n script:\n - docker build --tag hello .\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/230_docker -- '*'\n
"},{"location":"240_registries/exercise/","title":"Registries","text":"Goal
Learn how to...
- authenticate to the GitLab container registry
- push a container image to the registry
"},{"location":"240_registries/exercise/#task-push-a-container-image","title":"Task: Push a container image","text":"GitLab include a container registry. In this task you will push a container image to the registry. If the container registry is enabled, GitLab automatically provides predefined variables to access and authenticate to the registry:
$CI_REGISTRY
: The registry URL $CI_REGISTRY_IMAGE
: The registry URL with the project name $CI_REGISTRY_USER
: The username to use to push $CI_REGISTRY_PASSWORD
: The password to use to push
Modify the job package
:
- Update the build command to use the variables above:
docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .
- Add the push command directly after the build command:
docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"
- Login to the registry before building:
docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"
- Logout from the registry after pushing:
docker logout \"${CI_REGISTRY}\"
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Use before_script
for logging in and after_script
after logging out.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: alpine\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/240_registries -- '*'\n
"},{"location":"250_releases/exercise/","title":"Releases","text":"Goal
Learn how to...
- create a release on GitLab
- use the GitLab
release-cli
"},{"location":"250_releases/exercise/#task-create-a-release","title":"Task: Create a release","text":"GitLab can create releases based on a Git tag. The release can contain a description and links to assets. The assets must be stored elsewhere.
- Check out the official documentation about the
release
keyword - Modify the job
pages
to create a release in addition to the script block - The release should be based on the current commit hash (
$CI_COMMIT_SHA
) - Use the unique pipeline ID (
$CI_PIPELINE_IID
) as the tag name - Set an arbitrary name and description
For the release
keyword to work, the release-cli
binary must be present in the execution environment of the job:
- Set
image
to registry.gitlab.com/gitlab-org/release-cli:v0.14.0
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) Your release should look similar to this:
release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/250_releases -- '*'\n
"},{"location":"265_caches/exercise/","title":"Caches","text":"Goal
Learn how to...
- define caches
- store data in a cache
- restore data from a cache
- avoid relying on the cache
"},{"location":"265_caches/exercise/#task-1-test-caching","title":"Task 1: Test caching","text":"The cache is used by adding the keyword cache
in a pipeline job, a job template or in the default
section. Let's give it a try:
- Add a job
test_cache
to the first stage - Download dependency information from the uniget project:
curl -sSLfO https://github.com/uniget-org/cli/raw/main/go.mod\ncurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.sum\n
- Use the official example for Go to enable caching
- Instead of
go test
run go mod download
to download dependencies only
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run. Retry the job to see the effect of the cache.
Hint (Click if you are stuck) Use the .go-cache
job template from the official example for Go.
Solution (Click if you are stuck) .gitlab-ci-yml
:
#...\n\n.go-cache:\n variables:\n GOPATH: $CI_PROJECT_DIR/.go\n before_script:\n - mkdir -p .go\n cache:\n key: ${CI_PROJECT_PATH_SLUG}\n policy: pull-push\n paths:\n - .go/pkg/mod/\n\ntest_cache:\n stage: check\n extends:\n - .go-cache\n script:\n - go mod download \n\n#...\n
This was just a demonstration. The changes will not be preserved in the following chapters.
"},{"location":"265_caches/exercise/#task-2-add-caching","title":"Task 2: Add caching","text":"Now, integrate the job template .go-cache
into the pipeline and use it for the jobs build
and unit_test
.
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) go.yaml
:
.go-targets:\n parallel:\n matrix:\n - GOOS: linux\n GOARCH: amd64\n - GOOS: linux\n GOARCH: arm64\n\n.go-cache:\n variables:\n GOPATH: $CI_PROJECT_DIR/.go\n before_script:\n - mkdir -p .go\n cache:\n key: ${CI_PROJECT_PATH_SLUG}\n policy: pull-push\n paths:\n - .go/pkg/mod/\n\n.build-go:\n extends:\n - .go-targets\n - .go-cache\n script:\n - |\n go build \\\n -o hello-${GOOS}-${GOARCH} \\\n -ldflags \"-X main.Version=${CI_COMMIT_REF_NAME} -X 'main.Author=${AUTHOR}'\" \\\n .\n artifacts:\n paths:\n - hello-${GOOS}-${GOARCH}\n\n.test-go:\n extends:\n - .go-targets\n before_script:\n - apt-get update\n - apt-get -y install file\n script:\n - |\n file hello-${GOOS}-${GOARCH}\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/265_caches -- '*'\n
"},{"location":"270_renovate/exercise/","title":"Renovate","text":"Goal
Learn how to...
- discover dependencies used in your code and pipeline
- get update proposals for outdated dependencies
We will be using Renovate to discover and update dependencies.
"},{"location":"270_renovate/exercise/#task-add-renovate-to-your-pipeline","title":"Task: Add Renovate to your pipeline","text":"The easiest way to get Renovate on GitLab is to integrated it into your pipeline:
- Create a new project access token
renovate
with role Developer
and scopes api
, read_repository
, read_registry
- Add project access token
renovate
to CI variable RENOVATE_TOKEN
- Add a job
renovate
to the stage check
- Limit execution to a) scheduled pipelines and b) if the variable
$RENOVATE
is set - Use the image
renovate/renovate
- Set the variable
LOG_LEVEL
to debug
- Use the following script to execute Renovate:
renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${RENOVATE_TOKEN} \\\n ${CI_PROJECT_PATH}\n
- Create a scheduled pipeline and define a variable
RENOVATE
with the value true
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nrenovate:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"schedule\" && $RENOVATE'\n image: renovate/renovate\n variables:\n LOG_LEVEL: debug\n script: |\n renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${CI_JOB_TOKEN} \\\n --autodiscover true\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/270_renovate -- '*'\n
"},{"location":"280_security/exercise/","title":"Security","text":"Goal
Learn how to...
- detect secrets in your code
- detect vulnerabilities in your code
"},{"location":"280_security/exercise/#task-add-integrated-security-scans","title":"Task: Add integrated security scans","text":"GitLab offers multiple security scanners in the community edition:
- Checkout the official documentation for secret detection and integrate it into your pipeline
- Checkout the official documentation for static application security testing and integrate it into your pipeline
- Checkout the official documentation for container scanning and integrate it into your pipeline
Afterwards check the pipeline in the GitLab UI. You should see a successful pipeline run.
Hint (Click if you are stuck) The following templates are available for the above features:
- Secret detection:
Security/Secret-Detection.gitlab-ci.yml
- Static application security testing:
Security/SAST.gitlab-ci.yml
- Container scanning:
Security/Container-Scanning.gitlab-ci.yml
Solution (Click if you are stuck) .gitlab-ci.yml
:
workflow:\n rules:\n - if: $CI_DEPLOY_FREEZE\n when: never\n - if: $CI_PIPELINE_SOURCE == 'push'\n - if: $CI_PIPELINE_SOURCE == 'web'\n - if: $CI_PIPELINE_SOURCE == 'schedule'\n - if: $CI_PIPELINE_SOURCE == 'merge_request_event'\n - if: $CI_PIPELINE_SOURCE == 'pipeline'\n - if: $CI_PIPELINE_SOURCE == 'api'\n when: never\n - if: $CI_PIPELINE_SOURCE == 'trigger'\n when: never\n\ninclude:\n- local: go.yaml\n- template: Security/Secret-Detection.gitlab-ci.yml\n- template: Security/SAST.gitlab-ci.yml\n- template: Security/Container-Scanning.gitlab-ci.yml\n\ncontainer_scanning:\n stage: trigger\n variables:\n CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\n CI_APPLICATION_REPOSITORY: ${CI_REGISTRY_IMAGE}\n CI_APPLICATION_TAG: ${CI_COMMIT_REF_NAME}\n\n.run-on-push-to-default-branch:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n\n.run-on-push-and-in-mr:\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\n - if: '$CI_PIPELINE_SOURCE == \"merge_request_event\"'\n\nstages:\n- check\n- build\n- test\n- deploy\n- package\n- trigger\n\ndefault:\n image: golang:1.19.2\n\nrenovate:\n stage: check\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"schedule\" && $RENOVATE'\n image: renovate/renovate\n variables:\n LOG_LEVEL: debug\n script: |\n renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${RENOVATE_TOKEN} \\\n --autodiscover true\n\nlint:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go fmt .\n\naudit:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go vet .\n\nunit_tests:\n stage: check\n extends:\n - .run-on-push-and-in-mr\n script:\n - go install gotest.tools/gotestsum@latest\n - gotestsum --junitfile report.xml\n artifacts:\n when: always\n reports:\n junit: report.xml\n\nbuild:\n stage: build\n extends:\n - .run-on-push-and-in-mr\n - .build-go\n\ntest:\n stage: test\n extends:\n - .run-on-push-and-in-mr\n - .test-go\n\ndeploy:\n stage: deploy\n rules:\n - if: '$CI_COMMIT_REF_NAME == \"dev\" || $CI_COMMIT_REF_NAME == \"live\"'\n environment:\n name: ${CI_COMMIT_REF_NAME}\n before_script:\n - apt-get update\n - apt-get -y install curl ca-certificates\n script:\n - |\n curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \\\n --fail \\\n --verbose \\\n --upload-file hello-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\n image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0\n release:\n tag_name: ${CI_PIPELINE_IID}\n name: Release ${CI_PIPELINE_IID}\n description: |\n Some multi\n line text\n ref: ${CI_COMMIT_SHA}\n script:\n - cp hello-linux-amd64 public/hello\n artifacts:\n paths:\n - public\n\npackage:\n image: docker:20.10.18\n stage: package\n extends:\n - .run-on-push-to-default-branch\n services:\n - name: docker:20.10.18-dind\n command: [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]\n variables:\n DOCKER_HOST: tcp://docker:2375\n before_script:\n - docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"\n script:\n - docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .\n - docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"\n after_script:\n - docker logout \"${CI_REGISTRY}\"\n\ntrigger:\n stage: trigger\n extends:\n - .run-on-push-to-default-branch\n trigger:\n include: child.yaml\n
If you want to jump to the solution, execute the following command:
git checkout upstream/160_gitlab_ci/280_security -- '*'\n
Heads-Up
You can also select a different scanner for container scanning using the variable $CS_ANALYZER_IMAGE
. The following values are available:
Scanner Image Default (trivy) registry.gitlab.com/security-products/container-scanning:6 Grype registry.gitlab.com/security-products/container-scanning/grype:6 Trivy registry.gitlab.com/security-products/container-scanning/trivy:6"}]}
\ No newline at end of file
diff --git a/hands-on/2023-11-30/sitemap.xml.gz b/hands-on/2023-11-30/sitemap.xml.gz
index 6eb71400..f55c378f 100644
Binary files a/hands-on/2023-11-30/sitemap.xml.gz and b/hands-on/2023-11-30/sitemap.xml.gz differ