Предположим у нас есть некий репозиторий в gitlab-ci, давайте назовем его devops-ci. Там мы храним куски наших пайплайнов, которые в конечных проектах подключаем через опцию extends и используем либо в первозданном виде, либо с неким переопределением на уровне проекта, напомню, что extend делает именно «мердж» ямла.
Все это мы подключаем в конечном проекте в .gitlab-ci.yml например вот так:
include:
- project: "root_group/devops-ci"
ref: <version>
file:
- "path/to/file.yaml"
В поле ref можно указать версию нашего «общего» CI.
Для версионирования удобно использовать git tag, но здесь есть нюанс в том, что зарезирвированная переменная gitlab CI_COMMIT_REF_SLUG, которую часто используют в логики CI это и CI_GIT_TAG это одно и тоже. Говоря проще, когда мы комитим тег, для гита тег равняется имени ветки, так он по сути именем ветки и является.
Давайте попробуем реализовать версионирование, учитывая это.
Создаем в корне файл versions.yaml
version:
main: "v1.0.0"
other: "v0.0.1"
Пишем стейдж для создание релизного тега:
make_ci_tag:
stage: make_ci_tag
variables:
VERSION_FILE: "versions.yaml"
before_script:
- |
if [[ $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH ]];then
TAG_VERSION=$(cat $VERSION_FILE | grep main | awk '{print $2}' | tr -d '"')
else
TAG_VERSION=$(cat $VERSION_FILE | grep other | awk '{print $2}' | tr -d '"')
fi;
echo -e "\033[0;32mTag nubmer: $TAG_VERSION\e[0m"
script:
- git tag | xargs git tag -d || true
- git config --global user.name "${GITLAB_USER_NAME}"
- git config --global user.email "${GITLAB_USER_EMAIL}"
- git tag -a $TAG_VERSION -m "CI tag was created by ${GITLAB_USER_NAME}"
- git push https://tag_access_token:${TAG_ACCESS_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git $TAG_VERSION -o ci.skip
rules:
- if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
when: always
- if: '$CI_MERGE_REQUEST_IID && $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH'
when: always
- if: '$CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
- if: '$CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH'
when: always
Разберем логику работы:
В before_script мы «парсим» наш файл, если имя ветки комита равно дефолтной ветки, мы предполагаем, что это шаг будет после мердж комита, следовательно мы получаем версию из main файла versions.yaml. Если комит был в любую другую ветку, то берем other и присваиваем тег переменной TAG_VERSION. Так на этапе МР или еще до его создания мы всегда можем протестировать в конечном проекте наши изменения указав нужную версию.
В секции script основная логика того, как будет создан git tag. Вначале мы «очищаем» наше пространство от любых возможных тегов. Это нужно для активной параллельной разработки в нашем CI, учивая, что джоб может оказаться на том же раннере, что и у вашего коллеги.
Затем выставляем глобальныe переменные гиту из зарезервированных переменных gitlab-ci. Делаем комит используя команду git tag и делаем push в наш репозиторий, используя access token, зарезервированные переменные gitlab-ci и опцию -o ci.skip. Как раз она и нужна нам, чтобы избежать дублирования запуска джобов, потому что когда мы делаем git push вместе с тегами, создастся джоб для нашей ветки, например «dev» и для тега, например «v0.0.1», а мы хотим видеть запуск джобы только для комита тега.
В конце описываем правила:
- if: '$CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH'
when: always
Всегда запускать если имя ветки, куда мы комитим совпадает с именем дефолтной ветки.
- if: '$CI_MERGE_REQUEST_IID && $CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH'
when: always
Всегда запускаем, если есть идентификатор МР и ветка комита не равна дефолтной ветки.
- if: '$CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH && $CI_OPEN_MERGE_REQUESTS'
when: never
- if: '$CI_COMMIT_REF_NAME != $CI_DEFAULT_BRANCH'
when: always
Эти два правила нужны, чтобы избежать дублей при комите в ветку, при наличии МР.
Тем самым мы получаем «подконтрольное» версионирования нашего CI, без необходимости делать git tag локально.