You are done!¶
+Congratulations!¶
+You have finished the workshop
+ + + + + + +From e8415ef538657265e78d8bbf5ae9f3eff2294821 Mon Sep 17 00:00:00 2001
From: Nicholas Dille Task: Publish unit test results script:
- go vet .
-unit_tests:
- stage: check
- script:
- - go install gotest.tools/gotestsum@latest
+unit_tests:
+ stage: check
+ script:
+ - go install gotest.tools/gotestsum@latest
- gotestsum --junitfile report.xml
- artifacts:
- when: always
- reports:
- junit: report.xml
-
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
build:
stage: build
script:
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 d7fb25ef..9436f89d 100644
--- a/hands-on/2023-11-30/100_environments/exercise/index.html
+++ b/hands-on/2023-11-30/100_environments/exercise/index.html
@@ -1068,6 +1068,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Task 1: Add target environment stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1406,7 +1474,7 @@ Task 2: Add deployment
stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1439,7 +1507,7 @@ Task 2: Add deployment
- apt-get -y install curl ca-certificates
script:
- |
- curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
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 447c310e..c486d43e 100644
--- a/hands-on/2023-11-30/110_triggers/exercise/index.html
+++ b/hands-on/2023-11-30/110_triggers/exercise/index.html
@@ -1068,6 +1068,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TriggersGoal
Learn how to...
Triggering another pipeline requires a seconds project:
trigger
.gitlab-ci.yml
with the following content to the root of new project:
test:
script:
@@ -1287,7 +1359,7 @@ Task 1: Using a trigger token stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1314,13 +1386,13 @@ Task 1: Using a trigger tokendeploy:
stage: deploy
environment:
- name: dev
+ name: ${CI_COMMIT_REF_NAME}
before_script:
- apt-get update
- apt-get -y install curl ca-certificates
script:
- |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
+ curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
@@ -1328,13 +1400,13 @@ Task 1: Using a trigger token
trigger:
stage: trigger
- script:
- - |
- curl -X POST \
- --fail \
- -F token=$TOKEN \
- -F ref=main \
- https://gitlab.inmylab.de/api/v4/projects/9999/trigger/pipeline
+ script: |
+ curl https://gitlab.inmylab.de/api/v4/projects/seat${SEAT_INDEX}%2ftrigger/trigger/pipeline \
+ --request POST \
+ --silent \
+ --fail \
+ -F "token=${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 -- '*'
@@ -1440,7 +1512,7 @@ Task 2: Using a multi-project pip
stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1467,13 +1539,13 @@ Task 2: Using a multi-project pip
deploy:
stage: deploy
environment:
- name: dev
+ name: ${CI_COMMIT_REF_NAME}
before_script:
- apt-get update
- apt-get -y install curl ca-certificates
script:
- |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
+ curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
@@ -1486,7 +1558,7 @@ Task 2: Using a multi-project pip
This was just a demonstration. The changes will not be preserved in the following chapters.
Task 3: Using a parent-child pipeline¶
-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.
+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)
@@ -1595,7 +1667,7 @@ Task 3: Using a parent-child pipel
stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1622,13 +1694,13 @@ Task 3: Using a parent-child pipel
deploy:
stage: deploy
environment:
- name: dev
+ name: ${CI_COMMIT_REF_NAME}
before_script:
- apt-get update
- apt-get -y install curl ca-certificates
script:
- |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
+ curl https://${CI_COMMIT_REF_NAME}.seat${SEAT_INDEX}.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
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 f0f481f5..24ea416d 100644
--- a/hands-on/2023-11-30/120_templates/exercise/index.html
+++ b/hands-on/2023-11-30/120_templates/exercise/index.html
@@ -1059,6 +1059,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
include:
- local: go.yaml
stages:
@@ -1294,7 +1363,7 @@ Task 1: Prevent a job from running stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1317,13 +1386,13 @@ Task 1: Prevent a job from runningdeploy:
stage: deploy
environment:
- name: dev
+ name: ${CI_COMMIT_REF_NAME}
before_script:
- apt-get update
- apt-get -y install curl ca-certificates
script:
- |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
@@ -1333,16 +1402,17 @@ Task 1: Prevent a job from running
stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- script:
- - cp hello public/
- artifacts:
- paths:
- - public
-
-trigger:
- stage: trigger
- trigger:
- include: child.yaml
+
image: alpine
+ script:
+ - cp hello public
+ artifacts:
+ paths:
+ - public
+
+trigger:
+ stage: trigger
+ trigger:
+ include: child.yaml
If you want to jump to the solution, execute the following command:
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == 'push'
- if: $CI_PIPELINE_SOURCE == 'web'
@@ -1488,7 +1559,7 @@ Task 2: Prevent a pipeline from
stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1527,16 +1598,17 @@ Task 2: Prevent a pipeline from
stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- script:
- - cp hello public/
- artifacts:
- paths:
- - public
-
-trigger:
- stage: trigger
- trigger:
- include: child.yaml
+ image: alpine
+ script:
+ - cp hello public/
+ artifacts:
+ paths:
+ - public
+
+trigger:
+ stage: trigger
+ trigger:
+ include: child.yaml
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/130_rules_workflow -- '*'
@@ -1646,7 +1718,8 @@ Task 3: Use deploy freeze87
88
89
-90
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1687,7 +1760,7 @@ Task 3: Use deploy freeze stage: check
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1726,16 +1799,17 @@ Task 3: Use deploy freeze stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- script:
- - cp hello public/
- artifacts:
- paths:
- - public
-
-trigger:
- stage: trigger
- trigger:
- include: child.yaml
+ image: alpine
+ script:
+ - cp hello public/
+ artifacts:
+ paths:
+ - public
+
+trigger:
+ stage: trigger
+ trigger:
+ include: child.yaml
This was just a demonstration. The changes will not be preserved in the following chapters.
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 5c0561fe..6cb3b0e9 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 @@ -1068,6 +1068,74 @@ + + + + + + + + + + + + + + +workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1336,7 +1405,7 @@ Task 1: Use rules to r
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
script:
- go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1367,13 +1436,13 @@ Task 1: Use rules to r
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
environment:
- name: dev
+ name: ${CI_COMMIT_REF_NAME}
before_script:
- apt-get update
- apt-get -y install curl ca-certificates
script:
- |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
--fail \
--verbose \
--upload-file hello \
@@ -1383,18 +1452,19 @@ Task 1: Use rules to r
stage: deploy
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- script:
- - cp hello public/
- artifacts:
- paths:
- - public
-
-trigger:
- stage: trigger
- rules:
- - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- trigger:
- include: child.yaml
+ image: alpine
+ script:
+ - cp hello public/
+ artifacts:
+ paths:
+ - public
+
+trigger:
+ stage: trigger
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
+ trigger:
+ include: child.yaml
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/140_merge_requests -- '*'
@@ -1590,9 +1660,9 @@ Task 3: Avoid repetition u
stage: check
extends:
- .run-on-push-and-in-mr
- script:
- - go install gotest.tools/gotestsum@latest
- - gotestsum --junitfile report.xml --format testname
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
artifacts:
when: always
reports:
@@ -1602,42 +1672,42 @@ Task 3: Avoid repetition u
stage: build
extends:
- .run-on-push-and-in-mr
- extends:
- - .build-go
- artifacts:
- paths:
- - hello
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- image: alpine
- script:
- - ./hello
-
-deploy:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- environment:
- name: dev
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.dev.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- script:
+ - .build-go
+ artifacts:
+ paths:
+ - hello
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ image: alpine
+ script:
+ - ./hello
+
+deploy:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: alpine
+ script:
- cp hello public/
artifacts:
paths:
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 665ed2d8..d7257964 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
@@ -1068,6 +1068,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
.gitlab-ci.yml
:
1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20 -21 -22 -23 -24 -25 -26 -27 -28 -29 -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -40 -41 -42 -43 -44 -45 -46 -47 -48 -49 -50 -51 -52 -53 -54 -55 -56 -57 -58 -59 -60 -61 -62 -63 -64 -65 -66 -67 -68 -69 -70 -71 -72 -73 -74 -75 -76 -77 -78 -79 -80 -81 -82 -83 -84 -85 -86 -87 -88 -89 -90 -91 -92 -93 -94 -95 -96 |
|
|
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/220_services -- '*'
@@ -1516,9 +1608,9 @@ Task 2: Move the service into the
stage: check
extends:
- .run-on-push-and-in-mr
- script:
- - go vet .
-
+ script:
+ - go vet .
+
build:
stage: build
extends:
@@ -1535,9 +1627,9 @@ Task 2: Move the service into the
stage: test
extends:
- .run-on-push-to-default-branch
- services:
- - nginx:1.20.2
- script:
+ services:
+ - nginx:1.20.2
+ script:
- curl -s http://nginx
deploy:
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 04eada51..99694792 100644
--- a/hands-on/2023-11-30/230_docker/exercise/index.html
+++ b/hands-on/2023-11-30/230_docker/exercise/index.html
@@ -1050,6 +1050,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1314,89 +1384,91 @@ Task: Build a container imagedefault:
image: golang:1.19.2
-services:
-- nginx:1.20.2
-
-lint:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go fmt .
-
-audit:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go vet .
-
-build:
- stage: build
- extends:
- - .run-on-push-and-in-mr
- - .build-go
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- - .test-go
-
-test-service:
- stage: test
- extends:
- - .run-on-push-to-default-branch
- script:
- - curl -s http://nginx
-
-deploy:
- stage: deploy
- rules:
- - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
- environment:
- name: ${CI_COMMIT_REF_NAME}
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello-linux-amd64 \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- image: alpine
- script:
- - cp hello-linux-amd64 public/hello
- artifacts:
- paths:
- - public
-
-package:
- image: docker:20.10.18
- stage: package
- rules:
- - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
- variables:
- DOCKER_HOST: tcp://docker:2375
- services:
- - name: docker:20.10.18-dind
- command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
- script:
- - docker build --tag hello .
-
-trigger:
- stage: trigger
- extends:
- - .run-on-push-to-default-branch
- trigger:
- include: child.yaml
+lint:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go fmt .
+
+audit:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go vet .
+
+unit_tests:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
+build:
+ stage: build
+ extends:
+ - .run-on-push-and-in-mr
+ - .build-go
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ - .test-go
+
+deploy:
+ stage: deploy
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello-linux-amd64 \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: alpine
+ script:
+ - cp hello-linux-amd64 public/hello
+ artifacts:
+ paths:
+ - public
+
+package:
+ image: docker:20.10.18
+ stage: package
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ services:
+ - name: docker:20.10.18-dind
+ command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
+ script:
+ - docker build --tag hello .
+
+trigger:
+ stage: trigger
+ extends:
+ - .run-on-push-to-default-branch
+ trigger:
+ 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 9a6105cf..40afacdc 100644
--- a/hands-on/2023-11-30/240_registries/exercise/index.html
+++ b/hands-on/2023-11-30/240_registries/exercise/index.html
@@ -1041,6 +1041,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1289,94 +1359,96 @@ Task: Push a container imagedefault:
image: golang:1.19.2
-services:
-- nginx:1.20.2
-
-lint:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go fmt .
-
-audit:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go vet .
-
-build:
- stage: build
- extends:
- - .run-on-push-and-in-mr
- - .build-go
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- - .test-go
-
-test-service:
- stage: test
- extends:
- - .run-on-push-to-default-branch
- script:
- - curl -s http://nginx
-
-deploy:
- stage: deploy
- rules:
- - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
- environment:
- name: ${CI_COMMIT_REF_NAME}
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello-linux-amd64 \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- image: alpine
- script:
- - cp hello-linux-amd64 public/hello
- artifacts:
- paths:
- - public
-
-package:
- image: docker:20.10.18
- stage: package
- extends:
- - .run-on-push-to-default-branch
- services:
- - name: docker:20.10.18-dind
- command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
- variables:
- DOCKER_HOST: tcp://docker:2375
- before_script:
- - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
- script:
- - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
- - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
- after_script:
- - docker logout "${CI_REGISTRY}"
-
-trigger:
- stage: trigger
- extends:
- - .run-on-push-to-default-branch
- trigger:
- include: child.yaml
+lint:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go fmt .
+
+audit:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go vet .
+
+unit_tests:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
+build:
+ stage: build
+ extends:
+ - .run-on-push-and-in-mr
+ - .build-go
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ - .test-go
+
+deploy:
+ stage: deploy
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello-linux-amd64 \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: alpine
+ script:
+ - cp hello-linux-amd64 public/hello
+ artifacts:
+ paths:
+ - public
+
+package:
+ image: docker:20.10.18
+ stage: package
+ extends:
+ - .run-on-push-to-default-branch
+ services:
+ - name: docker:20.10.18-dind
+ command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ before_script:
+ - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
+ script:
+ - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
+ - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
+ after_script:
+ - docker logout "${CI_REGISTRY}"
+
+trigger:
+ stage: trigger
+ extends:
+ - .run-on-push-to-default-branch
+ trigger:
+ 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 d9539d5e..c0dba53f 100644
--- a/hands-on/2023-11-30/250_releases/exercise/index.html
+++ b/hands-on/2023-11-30/250_releases/exercise/index.html
@@ -1041,6 +1041,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1302,101 +1372,103 @@ Task: Create a releasedefault:
image: golang:1.19.2
-services:
-- nginx:1.20.2
-
-lint:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go fmt .
-
-audit:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go vet .
-
-build:
- stage: build
- extends:
- - .run-on-push-and-in-mr
- - .build-go
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- - .test-go
-
-test-service:
- stage: test
- extends:
- - .run-on-push-to-default-branch
- script:
- - curl -s http://nginx
-
-deploy:
- stage: deploy
- rules:
- - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
- environment:
- name: ${CI_COMMIT_REF_NAME}
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello-linux-amd64 \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
- release:
- tag_name: ${CI_PIPELINE_IID}
- name: Release ${CI_PIPELINE_IID}
- description: |
- Some multi
- line text
- ref: ${CI_COMMIT_SHA}
- script:
- - cp hello-linux-amd64 public/hello
- artifacts:
- paths:
- - public
-
-package:
- image: docker:20.10.18
- stage: package
- extends:
- - .run-on-push-to-default-branch
- services:
- - name: docker:20.10.18-dind
- command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
- variables:
- DOCKER_HOST: tcp://docker:2375
- before_script:
- - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
- script:
- - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
- - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
- after_script:
- - docker logout "${CI_REGISTRY}"
-
-trigger:
- stage: trigger
- extends:
- - .run-on-push-to-default-branch
- trigger:
- include: child.yaml
+lint:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go fmt .
+
+audit:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go vet .
+
+unit_tests:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
+build:
+ stage: build
+ extends:
+ - .run-on-push-and-in-mr
+ - .build-go
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ - .test-go
+
+deploy:
+ stage: deploy
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello-linux-amd64 \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
+ release:
+ tag_name: ${CI_PIPELINE_IID}
+ name: Release ${CI_PIPELINE_IID}
+ description: |
+ Some multi
+ line text
+ ref: ${CI_COMMIT_SHA}
+ script:
+ - cp hello-linux-amd64 public/hello
+ artifacts:
+ paths:
+ - public
+
+package:
+ image: docker:20.10.18
+ stage: package
+ extends:
+ - .run-on-push-to-default-branch
+ services:
+ - name: docker:20.10.18-dind
+ command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ before_script:
+ - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
+ script:
+ - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
+ - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
+ after_script:
+ - docker logout "${CI_REGISTRY}"
+
+trigger:
+ stage: trigger
+ extends:
+ - .run-on-push-to-default-branch
+ trigger:
+ 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 eef19281..107aaacc 100644
--- a/hands-on/2023-11-30/265_caches/exercise/index.html
+++ b/hands-on/2023-11-30/265_caches/exercise/index.html
@@ -1050,6 +1050,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1306,114 +1377,117 @@ Task: Add Renovate to your pipeline<
default:
image: golang:1.19.2
-services:
-- nginx:1.20.2
-
-renovate:
- stage: check
- rules:
- - if: '$CI_PIPELINE_SOURCE == "schedule" && $RENOVATE'
- image: renovate/renovate
- variables:
- LOG_LEVEL: debug
- script: |
- renovate --platform gitlab \
- --endpoint ${CI_API_V4_URL} \
- --token ${CI_JOB_TOKEN} \
- ${CI_PROJECT_PATH}
-
-lint:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go fmt .
-
-audit:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go vet .
-
-build:
- stage: build
- extends:
- - .run-on-push-and-in-mr
- - .build-go
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- - .test-go
-
-test-service:
- stage: test
- extends:
- - .run-on-push-to-default-branch
- script:
- - curl -s http://nginx
-
-deploy:
- stage: deploy
- rules:
- - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
- environment:
- name: ${CI_COMMIT_REF_NAME}
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello-linux-amd64 \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
- release:
- tag_name: ${CI_PIPELINE_IID}
- name: Release ${CI_PIPELINE_IID}
- description: |
- Some multi
- line text
- ref: ${CI_COMMIT_SHA}
- script:
- - cp hello-linux-amd64 public/hello
- artifacts:
- paths:
- - public
-
-package:
- image: docker:20.10.18
- stage: package
- extends:
- - .run-on-push-to-default-branch
- services:
- - name: docker:20.10.18-dind
- command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
- variables:
- DOCKER_HOST: tcp://docker:2375
- before_script:
- - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
- script:
- - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
- - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
- after_script:
- - docker logout "${CI_REGISTRY}"
-
-trigger:
- stage: trigger
- extends:
- - .run-on-push-to-default-branch
- trigger:
- include: child.yaml
+renovate:
+ stage: check
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "schedule" && $RENOVATE'
+ image: renovate/renovate
+ variables:
+ LOG_LEVEL: debug
+ script: |
+ renovate --platform gitlab \
+ --endpoint ${CI_API_V4_URL} \
+ --token ${CI_JOB_TOKEN} \
+ ${CI_PROJECT_PATH}
+
+lint:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go fmt .
+
+audit:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go vet .
+
+unit_tests:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ - .go-cache
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
+build:
+ stage: build
+ extends:
+ - .run-on-push-and-in-mr
+ - .build-go
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ - .test-go
+
+deploy:
+ stage: deploy
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello-linux-amd64 \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
+ release:
+ tag_name: ${CI_PIPELINE_IID}
+ name: Release ${CI_PIPELINE_IID}
+ description: |
+ Some multi
+ line text
+ ref: ${CI_COMMIT_SHA}
+ script:
+ - cp hello-linux-amd64 public/hello
+ artifacts:
+ paths:
+ - public
+
+package:
+ image: docker:20.10.18
+ stage: package
+ extends:
+ - .run-on-push-to-default-branch
+ services:
+ - name: docker:20.10.18-dind
+ command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ before_script:
+ - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
+ script:
+ - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
+ - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
+ after_script:
+ - docker logout "${CI_REGISTRY}"
+
+trigger:
+ stage: trigger
+ extends:
+ - .run-on-push-to-default-branch
+ trigger:
+ 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 9ce8220d..b88fa94c 100644
--- a/hands-on/2023-11-30/280_security/exercise/index.html
+++ b/hands-on/2023-11-30/280_security/exercise/index.html
@@ -16,6 +16,8 @@
+
+
@@ -1039,6 +1041,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
workflow:
rules:
- if: $CI_DEPLOY_FREEZE
when: never
@@ -1292,12 +1365,12 @@ Task: Add integrated security scans<
- local: go.yaml
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/SAST.gitlab-ci.yml
-- template: Security/Container-Scanning.gitlab-ci.yml
-
-container_scanning:
- variables:
- CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}
-
+- template: Security/Container-Scanning.gitlab-ci.yml
+
+container_scanning:
+ variables:
+ CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}
+
.run-on-push-to-default-branch:
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
@@ -1318,114 +1391,117 @@ Task: Add integrated security scans<
default:
image: golang:1.19.2
-services:
-- nginx:1.20.2
-
-renovate:
- stage: check
- rules:
- - if: '$CI_PIPELINE_SOURCE == "schedule" && $RENOVATE'
- image: renovate/renovate
- variables:
- LOG_LEVEL: debug
- script: |
- renovate --platform gitlab \
- --endpoint ${CI_API_V4_URL} \
- --token ${CI_JOB_TOKEN} \
- ${CI_PROJECT_PATH}
-
-lint:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go fmt .
-
-audit:
- stage: check
- extends:
- - .run-on-push-and-in-mr
- script:
- - go vet .
-
-build:
- stage: build
- extends:
- - .run-on-push-and-in-mr
- - .build-go
-
-test:
- stage: test
- extends:
- - .run-on-push-and-in-mr
- - .test-go
-
-test-service:
- stage: test
- extends:
- - .run-on-push-to-default-branch
- script:
- - curl -s http://nginx
-
-deploy:
- stage: deploy
- rules:
- - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
- environment:
- name: ${CI_COMMIT_REF_NAME}
- before_script:
- - apt-get update
- - apt-get -y install curl ca-certificates
- script:
- - |
- curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
- --fail \
- --verbose \
- --upload-file hello-linux-amd64 \
- --user seat${SEAT_INDEX}:${PASS}
-
-pages:
- stage: deploy
- extends:
- - .run-on-push-to-default-branch
- image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
- release:
- tag_name: ${CI_PIPELINE_IID}
- name: Release ${CI_PIPELINE_IID}
- description: |
- Some multi
- line text
- ref: ${CI_COMMIT_SHA}
- script:
- - cp hello-linux-amd64 public/hello
- artifacts:
- paths:
- - public
-
-package:
- image: docker:20.10.18
- stage: package
- extends:
- - .run-on-push-to-default-branch
- services:
- - name: docker:20.10.18-dind
- command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
- variables:
- DOCKER_HOST: tcp://docker:2375
- before_script:
- - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
- script:
- - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
- - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
- after_script:
- - docker logout "${CI_REGISTRY}"
-
-trigger:
- stage: trigger
- extends:
- - .run-on-push-to-default-branch
- trigger:
- include: child.yaml
+renovate:
+ stage: check
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "schedule" && $RENOVATE'
+ image: renovate/renovate
+ variables:
+ LOG_LEVEL: debug
+ script: |
+ renovate --platform gitlab \
+ --endpoint ${CI_API_V4_URL} \
+ --token ${CI_JOB_TOKEN} \
+ ${CI_PROJECT_PATH}
+
+lint:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go fmt .
+
+audit:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ script:
+ - go vet .
+
+unit_tests:
+ stage: check
+ extends:
+ - .run-on-push-and-in-mr
+ - .go-cache
+ script:
+ - go install gotest.tools/gotestsum@latest
+ - gotestsum --junitfile report.xml
+ artifacts:
+ when: always
+ reports:
+ junit: report.xml
+
+build:
+ stage: build
+ extends:
+ - .run-on-push-and-in-mr
+ - .build-go
+
+test:
+ stage: test
+ extends:
+ - .run-on-push-and-in-mr
+ - .test-go
+
+deploy:
+ stage: deploy
+ rules:
+ - if: '$CI_COMMIT_REF_NAME == "dev" || $CI_COMMIT_REF_NAME == "live"'
+ environment:
+ name: ${CI_COMMIT_REF_NAME}
+ before_script:
+ - apt-get update
+ - apt-get -y install curl ca-certificates
+ script:
+ - |
+ curl https://seat${SEAT_INDEX}.${CI_COMMIT_REF_NAME}.webdav.inmylab.de/ \
+ --fail \
+ --verbose \
+ --upload-file hello-linux-amd64 \
+ --user seat${SEAT_INDEX}:${PASS}
+
+pages:
+ stage: deploy
+ extends:
+ - .run-on-push-to-default-branch
+ image: registry.gitlab.com/gitlab-org/release-cli:v0.14.0
+ release:
+ tag_name: ${CI_PIPELINE_IID}
+ name: Release ${CI_PIPELINE_IID}
+ description: |
+ Some multi
+ line text
+ ref: ${CI_COMMIT_SHA}
+ script:
+ - cp hello-linux-amd64 public/hello
+ artifacts:
+ paths:
+ - public
+
+package:
+ image: docker:20.10.18
+ stage: package
+ extends:
+ - .run-on-push-to-default-branch
+ services:
+ - name: docker:20.10.18-dind
+ command: [ "dockerd", "--host", "tcp://0.0.0.0:2375" ]
+ variables:
+ DOCKER_HOST: tcp://docker:2375
+ before_script:
+ - docker login -u "${CI_REGISTRY_USER}" -p "${CI_REGISTRY_PASSWORD}" "${CI_REGISTRY}"
+ script:
+ - docker build --tag "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}" .
+ - docker push "${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}"
+ after_script:
+ - docker logout "${CI_REGISTRY}"
+
+trigger:
+ stage: trigger
+ extends:
+ - .run-on-push-to-default-branch
+ trigger:
+ include: child.yaml
If you want to jump to the solution, execute the following command:
git checkout origin/160_gitlab_ci/280_security -- '*'
@@ -1494,6 +1570,22 @@ Task: Add integrated security scans<
+
+
+
+
diff --git a/hands-on/2023-11-30/404.html b/hands-on/2023-11-30/404.html
index b582260c..9d9366b3 100644
--- a/hands-on/2023-11-30/404.html
+++ b/hands-on/2023-11-30/404.html
@@ -979,6 +979,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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_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:
seatN
(where N
is a number) and your code (looks like this ABCDEF
)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!
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.
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":"N
is a number.seatN
(where N
is a number) and your passwordThe 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.
demo
project under /home/seat
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:
demo
project directorygit remote -v
git pull upstream
Goal
Learn how to...
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).gitlab-ci.yml
in the root of the projectbuild
.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)stages
check
in the stage check
.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
.
.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...
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)variable
keyword to define a variable inside the job called build
.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)variable
keyword from the job called build
${version}
with the predefined variable ${CI_COMMIT_REF_NAME}
.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)Settings
> CI/CD
> Variables
AUTHOR
with your nameThe -ldflags
option needs to be extended with -X 'main.Author=${AUTHOR}'
.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...
before_script
and after_script
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
.
.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.
after_script
is executed","text":"Add commands to all three script block before_script
, script
and after_script
. Test two scenarios:
What happens to the code in after_script
?
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.
Goal
Learn how to...
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)before_script
image: golang:1.19.2
instead.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:
python:3
and test running python --version
node
and test running node --version
Goal
Learn how to...
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)image
from all build jobsdefault
with the image
directive at the top.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:
image
directory to the new jobSee the official documentation for default
as well as variables
and check how they are related.
Global variables are not located under default
but under the global variables
keyword.
Goal
Learn how to...
Artifacts are useful for splitting a task in separate job. Refer to the official documentation.
Improve the pipeline by using artifacts:
hello
binarytest
with a job called test
hello
binary as a smoke testAfterwards 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
.
.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...
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...
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...
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...
Create CI variables for use in the following exercises:
PASS
twice with scope dev
and live
SEAT_INDEX
with your seat numberAdd 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
.
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
.
.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 --format testname\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 --format testname\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
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.
Goal
Learn how to...
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
Triggering another pipeline requires a seconds project:
.gitlab-ci.yml
with the following content to the root of new project: test:\n script:\n - printenv\n
The trigger token allows pipelines to be triggered using the API. Let's give this a try!
In the web UI:
curl
snippetdemo
projectTOKEN
In your pipeline:
trigger
as well as a job trigger
curl
snippet in script
blockTOKEN
with the variable $TOKEN
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 --format testname\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\ntrigger:\n stage: trigger\n script:\n - |\n curl -X POST \\\n --fail \\\n -F token=$TOKEN \\\n -F ref=main \\\n https://gitlab.inmylab.de/api/v4/projects/9999/trigger/pipeline\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.
.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 --format testname\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\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.
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.
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 --format testname\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\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 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 --format testname\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: 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
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 --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
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...
In this exercise we will publish a static web page to download the hello
binary.
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...
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.
See complex rules for combining conditions using and (&&
) and or (||
).
.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 --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\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\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 --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\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\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.
.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 --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\npages:\n stage: deploy\n rules:\n - if: '$CI_PIPELINE_SOURCE == \"push\" && $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'\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...
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:
lint
, audit
, unit_tests
, build
and test
, add rules so that the jobs are executed when...trigger
only when pushing to the default branchpages
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.
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 --format testname\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: 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 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:
main
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.
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.
.run-on-push-to-default
with the corresponding rule(s).run-on-push-and-mr
with the corresponding rule(s)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.
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 --format testname\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 extends:\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: 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 extends:\n - .run-on-push-to-default-branch\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...
script
for different inputsSwitch back to the branch main
.
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
:
parallel
to create a matrix jobGOOS=linux
and GOARCH=amd64
GOOS=linux
and GOARCH=arm64
hello-${GOOS}-${GOARCH}
hello
binarieshello
binary for linux/amd64
deploy
to upload the hello
binary for linux/amd64
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 --format testname\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 extends:\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: 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-linux-amd64 \\\n --user seat${SEAT_INDEX}:${PASS}\n\npages:\n stage: deploy\n extends:\n - .run-on-push-to-default-branch\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:
.test-go
to go.yaml
defining a matrix job using the same inputs as for .build-go
file hello-${GOOS}-${GOARCH}
to display the target platformtest
to use the new templateAfterwards 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 -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
.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\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...
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:
nginx:1.20.2
test-service
to the stage test
with the following code: curl -s http://nginx\n
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\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...
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...
package
to the pipelinepackage
to the pipelinedocker:20.10.18
for the jobdocker:20.10.18-dind
command
set to [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]
DOCKER_HOST
with value tcp://docker:2375
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\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\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\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...
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 pushModify the job package
:
docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .
docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"
docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"
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.
.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\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\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\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...
release-cli
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.
release
keywordpages
to create a release in addition to the script block$CI_COMMIT_SHA
)$CI_PIPELINE_IID
) as the tag nameFor the release
keyword to work, the release-cli
binary must be present in the execution environment of the job:
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\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\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: 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...
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:
test_cache
to the first stagecurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.mod\ncurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.sum\n
go test
run go mod download
to download dependencies onlyAfterwards 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.
.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...
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:
renovate
to the stage check
$RENOVATE
is setrenovate/renovate
LOG_LEVEL
to debug
renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${CI_JOB_TOKEN} \\\n ${CI_PROJECT_PATH}\n
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\nservices:\n- nginx:1.20.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 ${CI_PROJECT_PATH}\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 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: 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...
GitLab offers multiple security scanners in the community edition:
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:
Security/Secret-Detection.gitlab-ci.yml
Security/SAST.gitlab-ci.yml
Security/Container-Scanning.gitlab-ci.yml
.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 variables:\n CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${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\nservices:\n- nginx:1.20.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 ${CI_PROJECT_PATH}\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 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: 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:
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:
seatN
(where N
is a number) and your code (looks like this ABCDEF
)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!
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.
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":"N
is a number.seatN
(where N
is a number) and your passwordThe 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.
demo
project under /home/seat
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:
demo
project directorygit remote -v
git pull upstream
Goal
Learn how to...
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).gitlab-ci.yml
in the root of the projectbuild
.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)stages
check
in the stage check
.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
.
.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...
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)variable
keyword to define a variable inside the job called build
.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)variable
keyword from the job called build
${version}
with the predefined variable ${CI_COMMIT_REF_NAME}
.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)Settings
> CI/CD
> Variables
AUTHOR
with your nameThe -ldflags
option needs to be extended with -X 'main.Author=${AUTHOR}'
.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...
before_script
and after_script
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
.
.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.
after_script
is executed","text":"Add commands to all three script block before_script
, script
and after_script
. Test two scenarios:
What happens to the code in after_script
?
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.
Goal
Learn how to...
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)before_script
image: golang:1.19.2
instead.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:
python:3
and test running python --version
node
and test running node --version
Goal
Learn how to...
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)image
from all build jobsdefault
with the image
directive at the top.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:
image
directory to the new jobSee the official documentation for default
as well as variables
and check how they are related.
Global variables are not located under default
but under the global variables
keyword.
Goal
Learn how to...
Artifacts are useful for splitting a task in separate job. Refer to the official documentation.
Improve the pipeline by using artifacts:
hello
binarytest
with a job called test
hello
binary as a smoke testAfterwards 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
.
.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...
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...
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...
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...
Create CI variables for use in the following exercises:
PASS
twice with scope dev
and live
SEAT_INDEX
with your seat numberAdd 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
.
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
.
.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.
Goal
Learn how to...
Heads up
Checkout the branch main
to make sure that the following exercises are based on the correct code base.
Triggering another pipeline requires a seconds project:
trigger
.gitlab-ci.yml
with the following content to the root of new project: test:\n script:\n - printenv\n
The trigger token allows pipelines to be triggered using the API. Let's give this a try!
In the web UI:
curl
snippetdemo
projectTOKEN
In your pipeline:
trigger
as well as a job trigger
curl
snippet in script
blockTOKEN
with the variable $TOKEN
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.
.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.
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 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...
In this exercise we will publish a static web page to download the hello
binary.
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...
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.
See complex rules for combining conditions using and (&&
) and or (||
).
.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.
.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...
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:
lint
, audit
, unit_tests
, build
and test
, add rules so that the jobs are executed when...trigger
only when pushing to the default branchpages
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.
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:
main
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.
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.
.run-on-push-to-default
with the corresponding rule(s).run-on-push-and-mr
with the corresponding rule(s)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.
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...
script
for different inputsSwitch back to the branch main
.
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
:
parallel
to create a matrix jobGOOS=linux
and GOARCH=amd64
GOOS=linux
and GOARCH=arm64
hello-${GOOS}-${GOARCH}
hello
binarieshello
binary for linux/amd64
deploy
to upload the hello
binary for linux/amd64
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:
.test-go
to go.yaml
defining a matrix job using the same inputs as for .build-go
file hello-${GOOS}-${GOARCH}
to display the target platformtest
to use the new templateAfterwards 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...
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:
nginx:1.20.2
test-service
to the stage test
with the following code: curl -s http://nginx\n
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...
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...
package
to the pipelinepackage
to the pipelinedocker:20.10.18
for the jobdocker:20.10.18-dind
command
set to [ \"dockerd\", \"--host\", \"tcp://0.0.0.0:2375\" ]
DOCKER_HOST
with value tcp://docker:2375
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...
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 pushModify the job package
:
docker build --tag \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\" .
docker push \"${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_NAME}\"
docker login -u \"${CI_REGISTRY_USER}\" -p \"${CI_REGISTRY_PASSWORD}\" \"${CI_REGISTRY}\"
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.
.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...
release-cli
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.
release
keywordpages
to create a release in addition to the script block$CI_COMMIT_SHA
)$CI_PIPELINE_IID
) as the tag nameFor the release
keyword to work, the release-cli
binary must be present in the execution environment of the job:
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...
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:
test_cache
to the first stagecurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.mod\ncurl -sSLfO https://github.com/uniget-org/cli/raw/main/go.sum\n
go test
run go mod download
to download dependencies onlyAfterwards 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.
.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...
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:
renovate
to the stage check
$RENOVATE
is setrenovate/renovate
LOG_LEVEL
to debug
renovate --platform gitlab \\\n --endpoint ${CI_API_V4_URL} \\\n --token ${CI_JOB_TOKEN} \\\n ${CI_PROJECT_PATH}\n
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 ${CI_PROJECT_PATH}\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 - .go-cache\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...
GitLab offers multiple security scanners in the community edition:
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:
Security/Secret-Detection.gitlab-ci.yml
Security/SAST.gitlab-ci.yml
Security/Container-Scanning.gitlab-ci.yml
.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 variables:\n CS_DEFAULT_BRANCH_IMAGE: ${CI_REGISTRY_IMAGE}:${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 ${CI_JOB_TOKEN} \\\n ${CI_PROJECT_PATH}\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 - .go-cache\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: