GitLab CIでブランチをオートマージ(自動追従)させる
はじめに
こんにちは。フォルシア株式会社エンジニアの谷井です。
フォルシアでは、ソースコード管理にGitLabを使用してチーム開発を行っていますが、その中で「あるブランチに入る変更を、自動で別のブランチに取り込みたい」という状況が時折発生します。
例えば、
- 「大型新機能開発のためのブランチを切って開発を行うが、その間も本流のmasterブランチには通常開発の変更が入る」
- 「検証環境反映用のブランチにだけ入れておきたい機能があるが、それ以外は本番環境用のブランチと同じ状態にしておきたい」
というようなケースです。
あとでまとめて取り込もうとすると大量のコンフリクトと格闘することになるが、一方でこまめに手動で取り込むには労力がかかる...ということで、GitLab CIを使ってブランチの自動追従(オートマージ)を行うスクリプトを作成して使っているのでご紹介します。
(GitLab CI/CDについては以前フォルシアのアドベントカレンダーでも扱っているので、詳細はぜひそちらの記事をご覧ください。)
前準備
GitLab CI/CD実行時のdocker内からの接続はreadonlyになっているため、単に.gitlab-ci.yml内でgit merge
, git push
と書くだけでは変更をリポジトリ側に反映できません。
今回は特定のGitLabユーザー(こうした用途に利用するためのbotアカウントを想定)に対してssh公開鍵を登録し、その鍵を使って接続します。
ssh鍵を生成
- 参考 GitLab Docs
マージを行うbotユーザーに公開鍵を登録
- botユーザーでログイン
- 画面右上のアイコン > Preferences > SSH Keys を選択
- Key欄に公開鍵を貼り付け
プロジェクトの環境変数に秘密鍵を登録
- 秘密鍵の内容はリポジトリ内に含めるのは望ましくないため、
.gitlab-ci.yml
内には定義せずプロジェクトの環境変数として登録 - CIを設定したいプロジェクトの
Settings
>CI/CD
>Variables
>Expand
-
Add Variable
を押してKeyにAUTO_MERGE_SSH_KEY
, Valueに生成した秘密鍵を貼り付け
これで事前準備は完了です。
.gitlab-ci.ymlの設定
リポジトリ直下に.gitlab-ci.yml
, auto_merge.sh
を作成し、以下の通り記述します。
yml内のvariablesに定義された変数や、sh内の変数のデフォルト値については適宜実際の値に書き換えてください。
auto_merge:
stage: auto_merge
variables:
# どのブランチへのpush時にマージを実行するかの設定
SOURCE_BRANCH: develop
# push時にどのブランチにマージをするのかの設定
TARGET_BRANCH: develop-feature
# submoduleの設定
GIT_SUBMODULE_STRATEGY: recursive
# マージを行うbotユーザーの設定
GIT_USER: yourBotUserName
GIT_USER_EMAIL: bot@email.com
before_script:
- echo "merge ${SOURCE_BRANCH} into ${TARGET_BRANCH} branch"
script:
- bash auto_merge.sh
rules:
- if: $CI_COMMIT_BRANCH == $SOURCE_BRANCH
#!/bin/bash
# ブランチをpushした時に自動で別の特定のブランチにマージさせるCI
set -eux
ROOT_DIR=$(git rev-parse --show-toplevel)
GITLAB_DOMAIN="yourOwnDomainName"
TARGET_BRANCH=${TARGET_BRANCH:?}
SOURCE_BRANCH=${SOURCE_BRANCH:?}
GIT_USER=${GIT_USER:-"yourBotUserName"}
GIT_USER_EMAIL=${GIT_USER_EMAIL:-"bot@email.com"}
set +xu
# SOURCEとTARGETに同一ブランチが指定されていればスキップ
if [ ${SOURCE_BRANCH} = ${TARGET_BRANCH} ]; then
echo "#### SOURCE_BRANCH(${SOURCE_BRANCH}) AND TARGET_BRANCH(${TARGET_BRANCH}) IS SAME. SKIP..."
exit 0
fi
which ssh-agent || ( apk add --update openssh )
eval "$(ssh-agent -s)"
ssh-add <(echo "$AUTO_MERGE_SSH_KEY")
mkdir -p ~/.ssh
chmod 700 ~/.ssh
ssh-keyscan -H "${GITLAB_DOMAIN}" >> ~/.ssh/known_hosts
echo "Clone Repository.(git@${GITLAB_DOMAIN}:${CI_PROJECT_PATH}.git)"
git clone git@${GITLAB_DOMAIN}:${CI_PROJECT_PATH}.git
cd ${CI_PROJECT_NAME}
git config user.name ${GIT_USER}
git config user.email ${GIT_USER_EMAIL}
git checkout ${TARGET_BRANCH}
git merge --no-commit --no-ff origin/${SOURCE_BRANCH}
if [[ "$?" = "1" ]]; then
echo "#### FAILED TO MERGE SOURCE_BRANCH(${SOURCE_BRANCH}) INTO TARGET_BRANCH(${TARGET_BRANCH})."
exit 1
fi
git commit -m "Merge branch ${SOURCE_BRANCH} into ${TARGET_BRANCH}"
git push origin ${TARGET_BRANCH} --no-verify
exit 0
テンプレート化
さて、ここまでで必要な機能は実現されましたが、
複数のプロジェクトに対して今回のようなCIを設定したい場合、このままではプロジェクトごとに上記のスクリプトファイルをコピー&ペーストする必要があり、とても面倒ですよね。
フォルシアでは、このようなプロジェクトに依存せず共通で使えるGitLab CI/CDのテンプレートをまとめたリポジトリが作られており、CIスクリプトも手軽に使えるように工夫されています。
テンプレートリポジトリへの配置
ここでは、gitlab-ci-templateというリポジトリに先ほどのスクリプトを登録して、他のプロジェクトからも手軽に呼び出せるようにしたいと思います。
先ほどの.gitlab-ci.ymlとauto_merge.shを上記リポジトリのauto_mergeディレクトリ以下に配置します。
さらに、ymlファイルのファイル名はtemplate.ymlと変更した上で、以下のようにテンプレートリポジトリの情報を追記しておきます。
auto_merge:
stage: auto_merge
variables:
# どのブランチへのpush時にマージを実行するかの設定
SOURCE_BRANCH: develop
# push時にどのブランチにマージをするのかの設定
TARGET_BRANCH: develop-feature
# submoduleの設定
GIT_SUBMODULE_STRATEGY: recursive
# マージを行うbotユーザーの設定
GIT_USER: bot
GIT_USER_EMAIL: bot@email.com
# 参照するスクリプトの情報
TEMPLATE_DOMAIN: "http://yourOwnDomain"
TEMPLATE_REPOSITORY: "gitlab-ci-template"
TEMPLATE_REVISION: "master"
before_script:
- echo "merge ${SOURCE_BRANCH} into ${TARGET_BRANCH} branch"
script:
- wget ${TEMPLATE_DOMAIN}/${TEMPLATE_REPOSITORY}/raw/${TEMPLATE_REVISION}/auto_merge/auto_merge.sh
- bash auto_merge.sh
rules:
- if: $CI_COMMIT_BRANCH == $SOURCE_BRANCH
呼び出すプロジェクト側での設定
ここまでくるとプロジェクト側での設定はシンプルで、
- プロジェクトの環境変数
AUTO_MERGE_SSH_KEY
への秘密鍵の登録 - .gitlab-ci.ymlへの下記記述
image: alpine/git
include:
- project: 'gitlab-ci-template'
ref: 'master'
file: '/auto_merge/template.yml'
stages:
- auto_merge
auto_merge:
variables:
SOURCE_BRANCH: develop
TARGET_BRANCH: develop-feature
これだけです。
GitLab CIでの設定は後勝ちになるので、includeされたtemplateの変数をプロジェクトの固有の設定で上書きすることができます。
注意点
- オートマージによってコンフリクトが発生する場合、jobはfailedとなり、マージは実行されません。必要に応じてslack等への通知を設定して、手動でコンフリクトを解消してください。
- 上述のrules設定では、
SOURCE_BRANCH
に対する全てのコミットをトリガーとしてオートマージが起動します。- 十分に検証された内容がコミットされるブランチを設定することを推奨します。
- フォルシアでは、developブランチ(レビューを経たマージコミットのみpushされる開発の本流ブランチ)を設定することがほとんどです。
最後に
今回はGitLab CI/CDを使って特定のブランチをオートマージする方法をご紹介しました。
スクリプト自体は3年近く前に書いたものがベースになっていますが、思いのほか別のチームでも使われることがあったので、当時は無かったCIテンプレートリポジトリへの追加と合わせてあらためて記事にまとめてみました。
開発業務で必ず行うGitへのコミットをトリガーに様々なアクションを実行できるCI/CDツールは、やはりとても便利ですね。
Discussion