GitLab CI (and CD and CD too if you dare)
This tutorial is a hands-on technically deep but succinct tutorial about how to use GitLab’s cloud-based CI-CD service to build programming code into packages, test it, deploy it on servers, and monitor apps as they run.
Use GitLab’s CI/CD capability instead of standing up another package such as Jenkins because it is integrated with GitLab’s Git repository and web portal.
Forrester cites “GitLab has a broad market reach, with over 80,000 active instances using the open source GitLab Community edition and over 500 enterprises paying for GitLab CI/CD.”
Among limitations Forrester quotes “Despite best-in-class extensibility, reference customers cited limitations in notifications and alerts, calling out difficulty integrating with Slack, among other platforms.”
Marketing landing pages:
- https://about.gitlab.com/auto-devops/
- https://about.gitlab.com/features/gitlab-ci-cd/
They say “you can use it for free on GitLab.com”. CI/CD capabilitis are included with both the open source GitLab Community Edition (CE) and the proprietary (licensed for money) GitLab Enterprise Edition. However, free accounts have a limit of 2,000 CI pipeline minutes per group per month.
AutoDevOps / Complete DevOps
The brand name was introduced with GitHub 11 to highlight all its capabilities.
GitLab’s Pricing page details the features offered for each level of pricing.
Servers
To avoid issues, GitLab runs CI/CD work on different machines than ones holding GitLab projects (repositories). you should too.
In GitLab Land, a “runner” is equivalent to a “node” worker in Jenkins. Runners run on the Digital Ocean cloud managed by GitLab. GitLab currently recommends standing up a server with at least 2 CPU cores and 4GB of RAM.
Source to build gitlab-runner is at https://gitlab.com/gitlab-org/gitlab-runner
GitLab Account
PROTIP: CI/CD is enabled on individual projects by an admin of that instance..
-
Login to https://gitlab.com
The landing page should be a list of your projects (repositories), as if you clicked Project, then Your projects.
Fork a repo
If you don’t already have one, fork one, such as this associated with the Demo: CI/CD with GitLab Mar 14, 2017 by Joshua Lambert, Product Manager at GitLab:
Scroll down to Settings in the left menu and select CI / CD.
The screen provides a short description of ther different types of GitLab Runners:
PROTIP: If you want a quick start, use
Shared Runners by clicking the green “Enable shared Runners”.
Shared Runners are available to any project, like a cafeteria.
For convenience, this is the default for new Runners created.
Shared Runners select jobs to run from a process queue using a “fair usage” algorithm. It selects where the lowest number of jobs are running.
A Shared Runner can be made into a Specific Runner (but not the reverse way).
PROTIP: If you have complex set of different services running on different Runners, use
Specific Runners which have been set to “Restrict projects for this Runner”.
From GitLab CI, go to Admin on the topbar which should take you to the Manage Projects page. Click the name of your project, then choose ‘Runners’ from the right hand menu. On that page should be a list of instructions, with the third instruction detailing the alternative token. For example:
PROTIP: If you are in an isolated team who don’t want to share, use
Group Runners to execute code all projects belonging to a designated group name.
Group Runners can be managed using the Runners API (below).
Shared Runner token
Tokens for shared Runners are like “9538b0ab”.
Tag “gce” stands for “Google Compute Engine”. In 2018 GitLab migrated from Azure to Google Cloud after Microsoft bought GitHub.
PROTIP: Alternately, automate this step by making a Registration API call.
Specific Runner token
To uniquely identify Runners on the public internet, Tokens (of random characters) are generated by the GitLab server so Runners can authenticate themselves.
Highlight the token displayed and copy to your Clipboard:
The type of jobs that a Runner is equipped to run, such as “Rails”, “Node”, etc. are specified by tags.
PROTIP: Tags should be defined when a Docker image is defined with components (such as Node, etc.)
gitlab-runner
In order for client machines to communicate CI / CD commands with GitLab, install the gitlab-runner client executable.
The GitLab Runner client is written in the Go language. So it can run on Linux, macOS, Windows, FreeBSD, Docker – any platform for which can build Go binaries.
Auto DevOps automatically build, test, and deploy apps based on a predefined Continuous Integration and Delivery configuration.
Click button “Reset runners registration token”.
NOTE: The Runner tokens in the list are the first 8 characters of that runner’s full token.
PROTIP: If you are concerned about compromise, put an entry on your calendar to reset periodically. TODO: Script to automatically reset and update scripts using the token.
Runner Installation
Until this issue to create a brew package is available to manage versions, follow https://docs.gitlab.com/runner/install/osx.html
Use my bash script to install gitlab-runner on your Mac at https://github.com/wilsonmar/DevSecOps/blob/master/gitlab/gitlab-runner-mac.sh
Highlight and copy this and paste it in your Terminal:
sh -c “$(curl -fsSL https://raw.githubusercontent.com/wilsonmar/DevSecOps/master/gitlab/gitlab-runner-mac.sh)”
There are comments in that script.
PROTIP: The folder /usr/local/bin/ is also where git modules are installed.
Verify version installed:
Run command on its own for sub-commands:
To verify its’s location:
/.gitlab-runner folder created by the installer to view config.toml. It contains:
TODO: Describe config.toml
.gitlab-ci.yml
Place a file named .gitlab-ci.yml in the root of any project. This is what the gitlab-runner looks for and runs every time a commit is pushed to the GitLab server.
QUESTION: Is there a hook file?
Another example is about CI for publishing NPM modules: https://www.exclamationlabs.com/blog/continuous-deployment-to-npm-using-gitlab-ci/
PROTIP: Programs thar read Yml files can be finicky. So run a linter by pasting into the online form at:
PROTIP: Better yet, specify a hook to run the linter automatically with every commit.
The .gitlab-ci.yml file contains a series of tasks.
Caches
All the files and modifications put in or do inside the .gitlab-ci.yml are reverted each time a commit is pushed to the server. This can be avoided by specifying caches.
Stages in pipeline
Stages defined in the file are defined to specify the order to be attempted:
PROTIP: Each column in the “pipeline” displayed on GitLab’s dashboard is named for each stage. So keep stage names short. Thus, many abbreviate “production” with “prod”.
Errors in any stage would stop progress to follow-on stages.
NOTE: “openMr” is a custom stage. Some use a stage named “review” instead of “test”, etc.
Actions are defined for each stage. For example:
Notice the sample action name “Build My App” can contain spaces, and is the action executed for the stage “build” defined in the list of stages above it.
In this example, the yarn utility is being used instead of npm. And it’s creating a cache folder that will contain all the yarn configurations that will not be recreated when each project runs (each time it’s pushed).
These and addtional steps are defined in the sample at
as described in blog https://hackernoon.com/configuring-gitlab-ci-yml-150a98e9765d
Another example is https://linuxhint.com/gitlab_runner_gitlab_ci/ which provides code.
Deploying to AWS?
See instructions from @autonix at https://stackoverflow.com/a/38672045/1057052
Get Token
Grab the shared-Runner token on the admin/runners page
Supply the URL, as there is no “press return for default”:
Enter a description for the Runner, you can change this later in GitLab’s UI:
Please enter the gitlab-ci description for this runner
Enter the tags associated with the Runner, you can change this later in GitLab’s UI:
Please enter the gitlab-ci tags for this runner (comma separated):
Enter the Runner executor:
Docker executor
If you chose Docker as your executor, you’ll be asked for the default image to be used for projects that do not define one in .gitlab-ci.yml:
Be sure Docker.app is installed on your mac
Variables
To hold passwords, secret keys, define variables which the runner applies to environments.
?? They can be protected by only exposing them to protected branches or tags.
Pipeline triggers
To force a specific branch or tag to get rebuilt with an API call, define triggers
to impersonate their associated user including their access to projects and their project permissions.
Docker Container Registry
Installing the Docker image enables auto-scaling of additional instances to automatically meet demand. See https://docs.gitlab.com/runner/executors/docker_machine.html
Login to a dedicated machine where the container registry proxy will be running Make sure that Docker Engine is installed on that machine
Optionally create a new container registry:
Configure Runners
Configure Runner to “Run untagged jobs option”.
Click Save changes for the changes to take effect
Set the maximum job timeout
Verify all registered Runners:
Configure Jobs
GitLab looks for a .gitlab-ci.yml file to specify what jobs do. Its format is described at:
A tutorial is at:
We don’t use a shared Runner because then you have to enable each project explicitly for the Runner to run its jobs.
Executor
To trigger your build script, the default is to use shell.
Your Runner should show in the Manager Runners page on the GitLab CI Admin Dashboard (topbar > Admin > Runners). Depending on your Runner type, it may be green, blue, or red.
git submodule update –init ls -la
Monitoring
As of Omnibus GitLab 9.0, Prometheus and its related exporters are enabled by default, to enable easy and in depth monitoring of GitLab. Approximately 200MB of memory will be consumed by these processes, with default settings.
If you would like to disable Prometheus and it’s exporters or read more information about it, check the Prometheus documentation. GitLab Ru
Do it
When a developer finishes a set of changes, they can open a merge request, which is equivalent to a “pull request” in GitHub. This is so other contributors to the repo can make comments.
A pipeline is a collection of jobs split in different stages. Every commit pushed to GitLab generates a pipeline attached to that commit. If multiple commits are pushed together the pipeline will be created for the last one only.
Основы CI/CD на gitlab
Gitlab предоставляет уникальную возможность получить одновременно бесплатный приватный репозиторий и бесплатный CI/CD из коробки в том же месте. Для сохранения баланса он конфигурируется не менее уникальным способом через конфиг-файл, который не так-то просто для понимания(я в первый раз вникала чересчур долго). В конце концов разобралась, и хочу поделиться.
Как это работает вообще все?
Есть файл .gitlab-ci.yml , который лежит в корне проекта, в нем конфигурируется вся магия сборки. Если гитлаб видит, что в ветке есть такой файл, он запускает сборку по алгоритму, описанному в этом файле.
По дефолту билды запускаются на каждый пуш в ветку.
То есть если у вас в одной ветке нет конфига, а в двух других он принципиально разный, если запушить что-то новое в три ветки сразу, запустится два билда с разными алгоритмами.
Настроить билды без этого файла, как, например, в тимсити или дженкинсе нельзя.
Ура, я хочу захостить свой фронтенд-проект на gitlab!
В отличие от простого gh-pages, где ты собираешь, что хочешь, и просто пушишь в репозиторий файл index.html, тут так поступить нельзя. Ну, то есть, в теории, можно, но автоматически ничего все равно работать не будет.
Как будет выглядеть в gitlab конфиг “типа как на gh-pages” (я предполагаю, что мы, как и с gh-pages уже собрали все в папку dist и запушили ее).
Главный секрет деплоя статики — положить все необходимое в папку public и отметить ее как артефакт.
И тут задумываемся — зачем собирать все локально, если можно делать это на машинах гитлаба, все равно же конфиг пишем. Это займет чуть больше времени, зато будет надежнее.
Все, мы разобрались с тем, как сделать, чтобы сайт собирался и деплоился, достаточно просто вносить изменения, пушить и он автоматически будет пересобираться и обновляться!
Теперь можно разобраться поподробнее в конфиге и придумать, какие еще возможности можно использовать
Этапы сборки
Конфиг “stage” — этап билда, по дефолту их три: build, test, deploy. Этапы всегда идут в четком порядке. Можно не указывать ничего, тогда запустится на этапе test.
Разберемся, что это такое и как использовать это во благо. Если подумать, этапов работы с кодом у нас и правда в целом три: собрать, протестировать(убедиться, что это можно деплоить), задеплоить. И если один из них упал, остальные запускать нет необходимости.
По этому принципу делает и гитлаб:
- jobs. Их может быть много разных. Каждая делает свое маленькое дело — собирает фронтенд с одним параметром, собирает с другим параметром, прогоняет тесты интерфейса, прогоняет линтер бекенда.
- stages: это этапы сборки. Внутри одного этапа все jobs выполняются без определенного порядка. Однако по умолчанию джобы следующего этапа не стартуют, пока все из предыдущего не завершились успешно
- pipelines — pipeline создается каждый раз при пуше или если запустить его руками. Один пайплайн — одна сборка с конкретной кофнигурацией stages и jobs.
Особенность конфига гитлаба в том, что мы не идем вроде как логично, объявляя этапы и наполняя их задачами, а, наоборот, каждой задаче указываем, на каком она этапе. Этапы, конечно же, можно создать любые, не ограничиваясь стандартными.
Например кусочек конфига для скриншота выше:
Стоит поменять порядок этапов в конфиге, и они начнут выполняться в другом порядке.
cache
Билды обычно идут дольше, чем хотелось бы. Первое, что приходит в голову — это что же, каждый раз скачивать зависимости? Это можно улучшить!
В конфиг задачи достаточно добавить вот такое:
$
Это будет работать следующим образом: каждый раз при запуске задачи гитлаб будет искать у себя сохраненный кеш с названием, совпадающим с cache-for-my-task-хеш_ветки . Если найдет — скачает его и добавит к кодовой базе, на которой будут совершаться дальнейшие действия.
В данном случае скачает все node_modules, которые сохранились с предыдущего запуска и после выполнения стандартного шага npm i установка зависимостей произойдет гораздо быстрее.
Гитлаб предоставляет много разных переменных, которые можно использовать здесь. Кешировать node_modules наиболее эффективно по веткам, хотя можно шерить кеш на весь репозиторий. Или вообще написать хитрое условие.
Артефакты
В конфиге выше написано:
Артефакты работают так: файлы, лежащие по указанным путям, в конце каждой джобы загружаются на сервер гитлаба и скачиваются в начале каждой джобы следующего этапа.
Погодите, звучит как кеш! В чем разница?
Кеш, исходя из названия, нужен для того, чтобы что-то временно хранить для ускорения сборки. Кстати, исходя из его предназначния, гитлаб не гарантирует то, что кеш найдется, он вполне себе может потеряться.
- У кеша мы можем указать ключ, а у артефактов — не можем. Это происходит потому, что кеш шарится между несколькими пайплайнами, можно запустить сборку ветки два раза, и они будут использовать один и тот же кеш (потому что название ветки, на которое мы ссылаемся в ключе — одинаковое).
А артефакт живет только внутри одного пайплайна и все, причем передается только из этапа в этап, не между джобами одного этапа. - Артефакт можно скачать из интерфейса гитлаба — все пакуется в zip и доступно для скачивания и анализа (удобно дебажить проблемы с node_modules). У кеша такой опции нет. Если смотреть чуть глубже, то кеш хранится на там, где установлен раннер, а артефакт — у самого гитлаба. Это как раз и обеспечивает гарантию наличия/отсутствия кеша и артефактов. Кстати, можно настроить и хранить артефакты до 30 дней.
Я описала два глобальных отличия, есть еще несколько, их можно найти здесь.
Но в итоге непонятно: как тут добавление артефактов позволяет деплоить pages, и когда вообще нужно их использовать?
- Деплой pages происходит просто потому, что гитлаб придумал такой алгоритм — если в артефактах папка public — она используется для раздачи статики. Вот и вся хитрость
- Артефакты же нужно использовать когда нужно передать какие-то данные между этапами сборки. Например, в одном этапе собрали бандл фронтовый, и в качестве артефакта передали его на этап деплоя.
Все, базовые основы CD на gitlab вы знаете, теперь можно хостить и деплоить код втайне от подписчиков на гитхабе (а если не хотите и гитлаб светить, то приватные репозитории + heroku в помощь!).
Name already in use
gitlabhq / doc / ci / quick_start / index.md
- Go to file T
- Go to line L
- Copy path
- Copy permalink
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Tutorial: Create and run your first GitLab CI/CD pipeline (FREE)
This tutorial shows you how to configure and run your first CI/CD pipeline in GitLab.
Before you start, make sure you have:
- A project in GitLab that you would like to use CI/CD for.
- The Maintainer or Owner role for the project.
If you don’t have a project, you can create a public project for free on https://gitlab.com.
To create and run your first pipeline:
If you’re using GitLab.com, you can skip this step. GitLab.com provides shared runners for you.
Create a .gitlab-ci.yml file at the root of your repository. This file is where you define the CI/CD jobs.
When you commit the file to your repository, the runner runs your jobs. The job results are displayed in a pipeline.
Ensure you have runners available
In GitLab, runners are agents that run your CI/CD jobs.
To view available runners:
- Go to Settings > CI/CD and expand Runners.
As long as you have at least one runner that’s active, with a green circle next to it, you have a runner available to process your jobs.
If you don’t have a runner
If you don’t have a runner:
-
on your local machine. for your project. Choose the shell executor.
When your CI/CD jobs run, in a later step, they will run on your local machine.
Create a .gitlab-ci.yml file
Now create a .gitlab-ci.yml file. It is a YAML file where you specify instructions for GitLab CI/CD.
In this file, you define:
- The structure and order of jobs that the runner should execute.
- The decisions the runner should make when specific conditions are encountered.
To create a .gitlab-ci.yml file:
On the left sidebar, select Repository > Files.
Above the file list, select the branch you want to commit to. If you’re not sure, leave master or main . Then select the plus icon ( ) and New file:

For the Filename, type .gitlab-ci.yml and in the larger window, paste this sample code:
This example shows four jobs: build-job , test-job1 , test-job2 , and deploy-prod . The comments listed in the echo commands are displayed in the UI when you view the jobs. The values for the predefined variables $GITLAB_USER_LOGIN and $CI_COMMIT_BRANCH are populated when the jobs run.
Select Commit changes.
The pipeline starts and runs the jobs you defined in the .gitlab-ci.yml file.
View the status of your pipeline and jobs
Now take a look at your pipeline and the jobs within.
Go to CI/CD > Pipelines. A pipeline with three stages should be displayed:

View a visual representation of your pipeline by selecting the pipeline ID:

View details of a job by selecting the job name. For example, deploy-prod :

You have successfully created your first CI/CD pipeline in GitLab. Congratulations!
Руководство по CI/CD в GitLab для (почти) абсолютного новичка
Наверное, у каждого разработчика, имеющего хотя бы один пет-проект, в определённый момент возникает зуд на тему красивых бейджиков со статусами, покрытием кода, версиями пакетов в nuget… И меня этот зуд привёл к написанию этой статьи. В процессе подготовки к её написанию я обзавёлся вот такой красотой в одном из своих проектов:

В статье будет рассмотрена базовая настройка непрерывной интеграции и поставки для проекта библиотеки классов на .Net Core в GitLab, с публикацией документации в GitLab Pages и отправкой собранных пакетов в приватный фид в Azure DevOps.
В качестве среды разработки использовалась VS Code c расширением GitLab Workflow (для валидации файла настроек прямо из среды разработки).
Краткое введение
Что такое CI/CD и зачем нужно — можно легко нагуглить. Полноценную документацию по настройке пайплайнов в GitLab найти также несложно. Здесь я кратко и по возможности без огрехов опишу процесс работы системы с высоты птичьего полёта:
- разработчик отпраляет коммит в репозиторий, создаёт merge request через сайт, или ещё каким-либо образом явно или неявно запускает пайплайн,
- из конфигурации выбираются все задачи, условия которых позволяют их запустить в данном контексте,
- задачи организуются в соответствии со своими этапами,
- этапы по очереди выполняются — т.е. параллельно выполняются все задачи этого этапа,
- если этап завершается неудачей (т.е. завершается неудачей хотя бы одна из задач этапа) — пайплайн останавливается (почти всегда),
- если все этапы завершены успешно, пайплайн считается успешно прошедшим.
Таким образом, имеем:
- пайплайн — набор задач, организованных в этапы, в котором можно собрать, протестировать, упаковать код, развернуть готовую сборку в облачный сервис, и пр.,
- этап (stage) — единица организации пайплайна, содержит 1+ задачу,
- задача (job) — единица работы в пайплайне. Состоит из скрипта (обязательно), условий запуска, настроек публикации/кеширования артефактов и много другого.
Соответственно, задача при настройке CI/CD сводится к тому, чтобы создать набор задач, реализующих все необходимые действия для сборки, тестирования и публикации кода и артефактов.
- Почему GitLab?
Потому, что когда появилась необходимость создать приватные репозитории под пет-проекты, на GitHub’e они были платными, а я — жадным. Репозитории стали бесплатными, но пока это не является для меня поводом достаточным переезжать на GitHub.
- Почему не Azure DevOps Pipelines?
Потому что там настройка элементарная — даже не требуются знания командной строки. Интеграция с внешними провайдерами git — в пару кликов, импорт SSH-ключей для отправки коммитов в репозиторий — тоже, пайплайн легко настраивается даже не из шаблона.
Исходная позиция: что имеется и чего хочется
- репозиторий в GitLab.
- автоматическую сборку и тестирование для каждого merge request,
- сборку пакетов для каждого merge request и пуша в мастер при условии наличия в сообщении коммита определённой строки,
- отправку собранных пакетов в приватный фид в Azure DevOps,
- сборку документации и публикацию в GitLab Pages,
- бейджики!11
Описанные требования органично ложатся на следующую модель пайплайна:
- Этап 1 — сборка
- Собираем код, выходные файлы публикуем как артефакты
- Получаем артефакты с этапа сборки, гоняем тесты, собраем данные покрытия кода
- Задача 1 — собираем nuget-пакет и отправляем в Azure DevOps
- Задача 2 — собираем сайт из xmldoc в исходном коде и публикуем в GitLab Pages
Собираем конфигурацию
Готовим аккаунты
Создаём новый проект
- Имя — любое
- Видимость — любая

При нажатии на кнопку Create проект будет создан, и будет совершён переход на его страницу. На этой странице можно отключить ненужные возможности, перейдя в настройки проекты (нижняя ссылка в списке слева -> Overview -> блок Azure DevOps Services)

Переходим в Atrifacts, жмём Create feed
- Вводим имя источника
- Выбираем видимость
- Снимаем галочку Include packages from common public sources, чтобы источник не превратился в
помойкуклон nuget
Жмём Connect to feed, выбираем Visual Studio, из блока Machine Setup копируем Source

Идём в настройки аккаунта, выбираем Personal Access Token

Создаём новый токен доступа
- Имя — произвольное
- Организация — текущая
- Срок действия — максимум 1 год
- Область действия (scope) — Packaging/Read & Write

Копируем созданный токен — после закрытия модального окна значение будет недоступно
Заходим в настройки репозитория в GitLab, выбираем настройки CI/CD

Раскрываем блок Variables, добавляем новую
- Имя — любое без пробелов (будет доступно в командной оболочке)
- Значение — токен доступа из п. 9
- Выбираем Mask variable

На этом предварительная настройка завершена.
Готовим каркас конфигурации
По умолчанию, для настройки CI/CD в GitLab используется файл .gitlab-ci.yml из корня репозитория. Можно настроить произвольный путь до этого файла в настройках репозитория, но в данном случае это не нужно.
Как видно из расширения, файл содержит конфигурацию в формате YAML . В документации подробно описано, какие ключи могут содержаться на верхнем уровне конфигурации, и в каждом из вложенных уровней.
Сначала добавим в файл конфигурации ссылку на docker-образ, в котором будет происходить выполнение задач. Для этого находим страницу образов .Net Core в Docker Hub. В GitHub есть подробное руководство, какой выбрать образ для разных задач. Нам для сборки подойдёт образ с .Net Core 3.1, поэтому смело добавляем первой строкой в конфигурацию
Теперь при запуске пайплайна с хранилища образов Microsoft будет скачан указанный образ, в котором и будут исполняться все задачи из конфигурации.
Следующий этап — добавить stage‘ы. По умолчанию GitLab определяет 5 этапов:
- .pre — выполняется до всех этапов,
- .post — выполняется после всех этапов,
- build — первый после .pre этап,
- test — второй этап,
- deploy — третий этап.
Ничего не мешает объявить их явно, впрочем. Порядок, в котором указаны этапы, влияет на порядок, в котором они выполняются. Для полноты изложения, добавим в конфигурацию:
Для отладки имеет смысл получить информацию об окружении, в котором исполняются задачи. Добавим глобальный набор команд, который будет выполняться перед каждой задачей, с помощью before_script :
Осталось добавить хотя бы одну задачу, чтобы при отправке коммитов пайплайн запустился. Пока что добавим пустую задачу для демонстрации:
Запускаем валидацию, получаем сообщение, что всё хорошо, коммитим, пушим, смотрим на сайте на результаты… И получаем ошибку скрипта — bash: .PSVersion: command not found . WTF?
Всё логично — по умолчанию runner’ы (отвечающие за исполнение скриптов задач, и предоставляемые GitLab’ом) используют bash для исполнения команд. Можно исправить это дело, явно указав в описании задачи, какие теги должны быть у исполняющего пайплайн раннера:
Отлично! Теперь пайплайн выполняется.
Внимательный читатель, повторив указанные шаги, заметит, что задача выполнилась в этапе test , хотя мы не указывали этап. Как можно догадаться, test является этапом по умолчанию.
Продолжим создание скелета конфигурации, добавив все задачи, описанные выше:
Получили не особенно функциональный, но тем не менее корректный пайплайн.
Настройка триггеров
Из-за того, что ни для одной из задач не указаны фильтры срабатывания, пайплайн будет полностью исполняться при каждой отправке коммитов в репозиторий. Так как это не является желаемым поведением в общем случае, мы настроим фильтры срабатывания для задач.
Фильтры могут настраиваться в двух форматах: only/except и rules. Вкратце, only/except позволяет настраивать фильтры по триггерам ( merge_request , например — настраивает задачу на выполнение при каждом создании запроса на слияние и при каждой отправке коммитов в ветку, являющуюся исходной в запросе на слияние) и именам веток (в т.ч. с использованием регулярных выражений); rules позволяет настраивать набор условий и, опционально, изменять условие выполнения задачи в зависимости от успеха предшествующих задач ( when в GitLab CI/CD).
Вспомним набор требований — сборка и тестирование только для merge request, упаковка и отправка в Azure DevOps — для merge request и пушей в мастер, генерация документации — для пушей в мастер.
Для начала настроим задачу сборки кода, добавив правило срабатывания только при merge request:
Теперь настроим задачу упаковки на срабатывания на merge request и добавление коммитов в мастер:
Как видно, всё просто и прямолинейно.
Также можно настроить задачу на срабатывание только если создан merge request с определённой целевой или исходной веткой:
В условиях можно использовать перечисленные здесь переменные; правила rules не совместимы с правилами only/except .
Настройка сохранения артефактов
Во время выполнения задачи build job у нас будут созданы артефакты сборки, которые можно переиспользовать в последующих задачах. Для этого нужно в конфигурацию задачи добавить пути, файлы по которым нужно будет сохранить и переиспользовать в следующих задачах, в ключ artifacts :
Пути поддерживают wildcards, что определённо упрощает их задание.
Если задача создаёт артефакты, то каждая последующая задача сможет их получить к ним доступ — они будут располагаться по тем же путям относительно корня репозитория, по которым были собраны из исходной задачи. Так же артефакты доступны для скачивания на сайте.
Теперь, когда у нас готов (и проверен) каркас конфигурации, можно переходить собственно к написанию скриптов для задач.
Пишем скрипты
Возможно, когда-то давно, в далёкой-далёкой галактике, собирать проекты (в том числе и на .net) из командной строки было болью. Сейчас же собрать, протестировать и опубликовать проект можно в 3 команды:
Естественно, есть некоторые нюансы, из-за которых мы несколько усложним команды.
- Мы хотим релизную, а не отладочную сборку, поэтому к каждой команде добавляем -c Release
- При тестировании мы хотим собирать данные о покрытии кода, поэтому потребуется подключить анализатор покрытия в тестовые библиотеки:
- Во все тестовые библиотеки следует добавить пакет coverlet.msbuild : dotnet add package coverlet.msbuild из папки проекта
- В команду запуска тестов добавим /p:CollectCoverage=true
- В конфигурацию задачи тестирования добавим ключ для получения результатов покрытия (см. ниже)
Собираем данные покрытия кода
Coverlet после запуска тестов выводит в консоль статистику по запуску:
GitLab позволяет указать регулярное выражение для получения статистики, которую потом можно получить в виде бейджа. Регулярное выражение указывается в настройках задачи с ключом coverage ; в выражении должна присутствовать capture-группа, значение которой и будет передано в бейдж:
Здесь мы получаем статистику из строки с общим покрытием по линиям.
Публикуем пакеты и документацию
Оба действия у нас назначены на последний этап пайплайна — раз уж сборка и тесты прошли, можно и поделиться с миром наработками.
Для начала рассмотрим публикацию в источник пакетов:
Если в проекте не присутствует файл конфигурации nuget ( nuget.config ), создадим новый: dotnet new nugetconfig
Зачем: в образе может быть запрещён доступ на запись к глобальным (пользовательской и машинной) конфигурациям. Чтобы не ловить ошибки, просто создадим новую локальную конфигурацию и будем работать с ней.
- name — локальное имя источника, не приниципиально
- url — URL источника из этапа «Готовим аккаунты», п. 6
- organization — название организации в Azure DevOps
- gitlab variable — имя переменной с токеном доступа, добавленной в GitLab («Готовим аккаунты», п. 11). Естественно, в формате $variableName
- -StorePasswordInClearText — хак для обхода ошибки отказа в доступе (не я первый на эти грабли наступил)
- На случай ошибок может быть полезным добавить -verbosity detailed
- Отправляем все пакеты из текущей директории, поэтому *.nupkg .
- name — из шага выше.
- key — любая строка. В Azure DevOps в окне Connect to feed всегда в качестве примера приводят строку az .
- -skipduplicate — при попытке отправить уже существующий пакет без этого ключа источник вернёт ошибку 409 Conflict ; с ключом отправка будет пропущена.
Теперь настроим создание документации:
- Для начала, в репозитории, в ветке master, инициализируем проект docfx. Для этого из корня надо выполнить команду docfx init и в интерактивном режиме зададим ключевые параметры для сборки документации. Подробное описание минимальной настройки проекта здесь.
- При настройке важно указать выходную директорию ..\public — GitLab по умолчанию берёт содержимое папки public в корне репозитория как источник для Pages. Т.к. проект будет располагаться во вложенной в репозиторий папке — добавляем в путь выход на уровень вверх.
- Скрипт:
- nuget install docfx.console -version 2.51.0 — установит docfx; версия указана для гарантии правильности путей установки пакета.
- .\docfx.console.2.51.0\tools\docfx.exe .\docfx_project\docfx.json — собираем документацию
Лирическое отступление про docfx
Раньше при настройке проекта я указывал источник кода для документации как файл решения. Основной минус — документация создаётся и для тестовых проектов. В случае, если это не нужно, можно задать такое значение узлу metadata.src :
- metadata.src.src: «../» — выходим на уровень вверх относительно расположения docfx.json , т.к. в паттернах не работает поиск вверх по дереву директорий.
- metadata.src.files: [«**/*.csproj»] — глобальный паттерн, собираем все проекты C# из всех директорий.
- metadata.src.exclude: [«*.tests*/**»] — глобальный паттерн, исключаем всё из папок с .tests в названии
Промежуточный итог
Вот такую простую конфигурацию можно составить буквально за полчаса и пару чашек кофе, которая позволит при каждом запросе слияния и отправке в мастер проверять, что код собирается и тесты проходят, собирать новый пакет, обновлять документацию и радовать глаз красивыми бейджиками в README проекта.
Кстати о бейджиках
Ради них ведь всё и затевалось!
Бейджи со статусами пайплайна и покрытием кода доступны в GitLab в настройках CI/CD в блоке Gtntral pipelines:

Бейдж со ссылкой на документацию я создавал на платформе Shields.io — там всё достаточно прямолинейно, можно создать свой бейдж и получать его с помощью запроса.
Azure DevOps Artifacts также позволяет создавать бейджи для пакетов с указанием актуальной версии. Для этого в источнике на сайте Azure DevOps нужно нажать на Create badge у выбранного пакета и скопировать markdown-разметку:


Добавляем красоты
Выделяем общие фрагменты конфигурации
Во время написания конфигурации и поисков по документации, я наткнулся на интересную возможность YAML — переиспользование фрагментов.
Как видно из настроек задач, все они требуют наличия тега windows у раннера, и срабатывают при отправке в мастер/создании запроса на слияние (кроме документации). Добавим это во фрагмент, который будем переиспользовать:
И теперь в описании задачи можем вставить объявленный ранее фрагмент:
Названия фрагментов должны начинаться с точки, чтобы не быть интерпретированными как задача.
Версионирование пакетов
При создании пакета компилятор проверяет ключи командной строки, и в их отсутствие — файлы проектов; найдя узел Version, он берёт его значение как версию собираемого пакета. Выходит, чтобы собрать пакет с новой версией, нужно либо обновить её в файле проекта, либо передать как аргумент командной строки.
Добавим ещё одну хотелку — пусть младшие два номера в версии будут годом и датой сборки пакета, и добавим пререлизные версии. Добавлять эти данные в файл проекта и проверять перед каждой отправкой можно, конечно — но можно ведь это делать и в пайплайне, собирая версию пакета из контекста и передавая через аргумент командной строки.
Условимся, что если в сообщении коммита есть строка вида release (v./ver./version) <version number> (rev./revision <revision>)? , то мы будем из этой строки брать версию пакета, дополнять её текущей датой и передавать как аргумент команде dotnet pack . В отсутствие строки — просто не будем собирать пакет.
Данную задачу решает следующий скрипт:
Добавляем скрипт в задачу pack and deploy job и наблюдаем сборку пакетов строго при наличии заданной строки в сообщении коммита.
Итого
Потратив примерно полчаса-час времени на написание конфигурации, отладку в локальном powershell и, возможно, пару неудачных запусков, мы получили несложную конфигурацию для автоматизации рутинных задач.
Конечно, GitLab CI/CD гораздо обширнее и многограннее, чем может показаться после прочтения этого руководства — это совершенно не так. Там даже Auto DevOps есть, позволяющий
Теперь в планах — сконфигурировать пайплайн для развёртывания приложений в Azure, с использованием Pulumi и автоматическим определением целевого окружения, что будет освещено в следующей статье.