はじめに
目的
- MRのdescriptionでGitLabのCI/CDの流れを変えること
利益
- 流れを細分化した一つのpipelineで類似しているテストをまとめて管理できます
- 文字列を解析するから直感的な記述でCI/CDをコントロールすることができます
使用例
- マルチのリポジトリをまとめてビルドできます
- MRがブロックされてる場合ビルドをスキップできます
PipelinesからMRを読み取り流れを変える
シナリオ
readyステージのCheck MRというジョブでMRを読み取り以後のパイプラインで使う環境変数を設定すると仮定します。
シナリオの最終目的は
- REPO1でMRがpipelineをトリガーした時
- REPO1のMRに記載された依存情報を利用して
- REPO2のMRの情報を読み取り
- ビルドに使用する依存先のブレンチの名前を得る事です。
適用例
Merge request
Pipelineスクリプト
variables:
GITLAB_URL: "http://gitlab_url"
#MRはREPO-1で作成されREPO-1はREPO-2に依存していると仮定
PROJECT_REPO1_ID: "120" # REPO-1
PROJECT_REPO2_ID: "121" # REPO-2
stages:
- ready
Check MR:
stage: ready
only:
- web
- schedule
- merge_request
script:
- |-
if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]
then
echo "Initialing..."
MR_DESCRIPTION=$(curl --silent --header "PRIVATE-TOKEN:$GITLAB_PRIVATE_TOKEN" "${GITLAB_URL}/api/v4/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}")
echo "MR_DESCRIPTION=$(echo "$MR_DESCRIPTION" | python3 -c 'import sys, json; print(json.load(sys.stdin)["description"])')" >> build.env
DEPEND_MR_IID=$(echo "$MR_DESCRIPTION" | python3 -c 'import sys,re; m=re.search(“MY_PROJECT_REPO2!
\\\{0,1}!([0-9]+)”, str(*sys.stdin)); print("-1") if m is None else print(m.group(1))')
echo "DEPEND_MR_IID=$DEPEND_MR_IID" >> build.env
echo "REQUEST_SOURCE_BRANCH_NAME=${CI_MERGE_REQUEST_SOURCE_BRANCH_NAME}" >> build.env
if [ "$DEPEND_MR_IID" == "-1" ]
then
echo "DEPEND_REPO2_BRANCH=master >> build.env
else
echo "DEPEND_REPO2_BRANCH=$(curl --silent --header "PRIVATE-TOKEN:$GITLAB_PRIVATE_TOKEN" "${GITLAB_URL}/api/v4/projects/${PROJECT_REPO2_ID}/merge_requests/${DEPEND_MR_IID}" | python3 -c 'import sys, json; print(json.load(sys.stdin)['source_branch'])')" >> build.env
fi
else
{
echo "REQUEST_SOURCE_BRANCH_NAME=${CI_COMMIT_REF_NAME}"
echo "DEPEND_REPO2_BRANCH=master"
} >> build.env
fi
artifacts:
reports:
dotenv: build.env
使用している変数
Predefined variables1
- CI_PIPELINE_SOURCE:トリガーの種類。上記の例ではマージリクエストを判断するために使いました。
script:
- |-
if [ "$CI_PIPELINE_SOURCE" == "merge_request_event" ]
then
~~~~~~
else
~~~~~~
fi
- CI_MERGE_REQUEST_PROJECT_ID:MRのプロジェクトID
- CI_MERGE_REQUEST_IID:MRのID
- CI_MERGE_REQUEST_SOURCE_BRANCH_NAME:MRのソースブレンチの名前
- CI_COMMIT_REF_NAME:パイプラインが作動されたブレンチの名前
Variables in the .gitlab-ci.yml file
- GITLAB_URL:GitLabのURL
- PROJECT_REPO1_ID:現在パイプラインが動作しているリポジトリのID。
- PROJECT_REPO2_ID:REPO1が依存しているリポジトリのID。
PROJECT_REPO1_IDとPROJECT_REPO2_IDは二つ以上のリポジトリを使うシナリオのための変数です。単一のリポジトリを使う場合はこの二つの変数が関わっている部分は無視してください。
Project CI/CD variables
- GITLAB_PRIVATE_TOKEN:GitLab APIのためのTOKEN
Variables in artifact "build.env" file (ステージ間の値の受け渡しのための変数)
- MR_DESCRIPTION:MRのDESCRIPTIONを読み取り保管する変数
- DEPEND_MR_IID:依存先のMRのIDを保持する変数
- REQUEST_SOURCE_BRANCH_NAME:REPO1のMRのブレンチの名前
- DEPEND_REPO2_BRANCH:REPO2のMRのブレンチの名前
スクリプトの解説
MRのDESCRIPTIONが読みたい
GitLabのAPI(/api/v4/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}
)を叩くことでMRに関する情報をJSONの形で得ることができます。DESCRIPTIONの所はPYTHONを使って読み取ります。
MR_INFO=$(curl --silent --header "PRIVATE-TOKEN:$GITLAB_PRIVATE_TOKEN" "${GITLAB_URL}/api/v4/projects/${CI_MERGE_REQUEST_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}")
MR_DESCRIPTION=$(echo "$MR_DESCRIPTION" | python3 -c 'import sys, json; print(json.load(sys.stdin)["description"])')
MRのDESCRIPTIONを解析したい
MRのdescriptionを得たのであとは文字列をどう解析して使うかの問題です。ここは決められた方法はありません。自分の目的のデスクリップションに記載されたMR間の依存情報を読み取ることです。
上記の例ではデスクリップション欄にDepends: MY_PROJECT_REPO2!100
のように依存先のMRのリンクを記しておいてPythonで100という数字(GitLabでいうMR_IID)だけをパーシングしました。このIIDを利用してまた依存先のREPOのMRの情報を獲得できるようになります。
DEPEND_MR_IID=$(echo "$MR_DESCRIPTION" | python3 -c 'import sys,re; m=re.search(“MY_PROJECT_REPO2\\\{0,1}!([0-9]+)”, str(*sys.stdin)); print("-1") if m is None else print(m.group(1))')
MRが依存している外部のリポジトリのブランチ名を知りたい
上記で読み取ったIIDを利用して今回は依存先のMRの情報を読み取ります。ここではsource_branch
を利用してREPO2のどのブレンチを利用するべきかを決めます。
DEPEND_REPO2_BRANCH=$(curl --silent --header "PRIVATE-TOKEN:$GITLAB_PRIVATE_TOKEN" "${GITLAB_URL}/api/v4/projects/${PROJECT_REPO2_ID}/merge_requests/${DEPEND_MR_IID}" | python3 -c 'import sys, json; print(json.load(sys.stdin)['source_branch'])')
-
Predefined variables reference, https://docs.gitlab.com/ee/ci/variables/predefined_variables.html ↩