この記事を書いた背景
microCMSの記事の更新情報をWebhook経由で送信しCI/CDツールでデプロイする場合に、ソースをGitHubで管理している場合は、GitHub Actionsなどを使うケースが多いと思います。
ただクライアントによってはGitを管理しているのがGitLabだった場合GibLabのCI/CDを使いたいケースがあり、そのために調べたことをメモしておこうと思い書きました。
GitLab CI/CDのパイプラインのトリガーの仕様
以下のように基本的にはGitLabのCI/CDの外部からのパイプラインのトリガーでは、POSTクエリにアクセスのためのTOKENなどを入れる形にする必要があります。
curl --request POST \
--form token=<token> \
--form ref=<ref_name> \
"https://gitlab.example.com/api/v4/projects/<project_id>/trigger/pipeline"
micrCMSのWebhookの仕様
各サービスに合わせた設定はあるのですが、2023年5月8日時点ではGitLab用の設定項目はありません。
そうしますとカスタム通知を使用する形となります。
カスタム通知の設定ではPOSTクエリを追加する項目は無く、追加できる項目はカスタムリクエストヘッダーとなります。
以下の公式にもあるようなGETクエリに入れる方法では問題なくジョブを実行できるのですが、GETクエリにトリガーのTOKENを入れるのはセキュリティ的な懸念があります。
curl --request POST \
"https://gitlab.example.com/api/v4/projects/<project_id>/trigger/pipeline?token=<token>&ref=<ref_name>"
またカスタムリクエストヘッダーにTOKENを入れて更新情報の送信し、それをGitLab単体で上手く処理をするという方法で上手く動くやり方を見つけることができませんでした。
発想の転換 - 中継サーバーの使用
そこで発想を転換して中継サーバー経由で行うという形をしてみました。
以下のような順序で実現できると思いサンプルを作成しテストをしてみました。
- microCMSのWebhookのカスタム通知に
TOKENなど必要なデータを格納し、一旦中継サーバーに送信させます。 - 中継サーバーにて受け取ったデータのカスタムリクエストヘッダーから
TOKEN、ref_nameやgitlab-ci.ymlの中で使用するであろうvariablesなどを抽出します。 - 抽出して各データをPOSTクエリにいれてGitLabに送信します。
- GitLabでは受け取ったデータをもとに
gitlab-ci.ymlで指定したCI/CDのジョブを実行する
具体的な設定例
各サービスの具体的な設定例を記載していきます。
GitLabの設定
GitLabのクラウド版(SaaS)を使用する想定で設定します。
gitlab-ci.ymlの作成
左メニューのビルドのパイプラインエディタにてgitlab-ci.ymlを作り編集します。
stages: # List of stages for jobs, and their order of execution
- build
- test
- deploy
variables:
RUN_BUILD_MODE: "test"
test-job:
stage: test
script:
- echo "$RUN_BUILD_MODE"
rules:
- if: '$RUN_BUILD_MODE == "test"'
build-job:
stage: build
script:
- echo "$RUN_BUILD_MODE"
rules:
- if: '$RUN_BUILD_MODE == "build"'
テスト用としてなので、ビルドが走ったらRUN_BUILD_MODE変数の内容によってジョブを振り分けてRUN_BUILD_MODEの内容をechoするだけの処理になります。
トリガーTOKENの追加
次にGitLab管理画面の設定の左メニューのCI/CDからパイプラインのトリガーで、トリガートークンの追加をしておきます。
micrCMSのWebhookの設定
API設定のWebhookの設定をしていきます。
基本的な設定箇所
Webhookの識別名(任意)、中継サーバーのURL、シークレットを入力していきます。
シークレットはWebhookリクエストがmicroCMSからのものであることを検証するためのヘッダ値となります。
micrCMSマニュアル | シークレット
カスタムリクエストヘッダーの設定
- GitLabのパイプラインのトリガー用トークンのためにKeyを
X-GITLAB-TOKENとしてトークンを格納 -
X-GITLAB-REF-NAMEにjobを実行するブランチ名を格納 -
X-BUILD-MODEにはgitlab-ci.ymlでjobを実行するstageを振り分けるためにvariablesで設定した変数へ渡す値を格納させる
ここへの値は、buildやtest、reviewなどとしてgitlab-ci.ymlで処理を振り分けるための変数に入れるための値となります。
中継サーバーの設定
適当なサーバーを用意します。
例としてのサンプルはPHPで記載しておきますが、その他の言語でも良いと思います。
環境変数の追加
micrCMSのシークレット(キー)やGitLabのプロジェクトIDなどあまり外に漏らしたくないものはハードコーディングしないようにします。
環境変数にこれらを追加して使用します。httpd.confやphp.ini、.htaccessなどに追加して設定します。
例:
setEnv MICROCMS_SECRET_KEY <micrCMSで設定したシークレット>
setEnv GITLAB_PROJECT_ID <GitLabのプロジェクトID>
PHPプログラム
次にPHPの記述をしていきます。
<?php
$data = file_get_contents('php://input');
/*
* microCMSの環境変数に格納させたシークレットをgetenvで取得
*/
$secret = getenv('MICROCMS_SECRET_KEY');
/*
* 検証するための処理
*/
//署名を生成
$expectedSignature = hash_hmac('sha256', $data, $secret);
// リクエストヘッダーから署名を取得
$signature = $_SERVER['HTTP_X_MICROCMS_SIGNATURE'];
// 署名の検証
if (!hash_equals($signature, $expectedSignature)) {
//署名の検証がエラーならthrow new Exceptionで処理をストップさせる
throw new Exception('Invalid signature.');
}
/*
* microCMSのシグネチャチェックが通った場合処理を実行
*/
//各変数に仮の値を入れておく
$token = 'TOKEN';
$refName = 'REF_NAME';
$buildMode = 'test';
/*
* GitLabのプロジェクトIDも環境変数に入れて管理
* 汎用性を高めるならばmicrCMS側のカスタムヘッダに入れる方法が良い
*/
$projectId = getenv('GITLAB_PROJECT_ID');
// headerからTOKENやRefNameを取得し変数に入れておく
$headers = getallheaders();
foreach($headers as $header => $value) {
switch($header) {
case "X-GITLAB-TOKEN":
$token = $value;
break;
case "X-GITLAB-REF-NAME":
$refName = $value;
break;
case "X-BUILD-MODE":
$buildMode = $value;
break;
default:
// 他のヘッダーに対する処理があれば追加する
break;
}
}
// microCMSから必要な情報がこない場合はエラー処理
if($token === 'TOKEN' || $refName === 'REF_NAME') {
throw new Exception('Value Not Set');
}
/*
* curlの準備
*/
$curl = curl_init();
$data = array(
'token' => $token,
'ref' => $refName,
'variables[RUN_BUILD_MODE]' => $buildMode,
);
curl_setopt_array($curl, array(
CURLOPT_URL => "https://gitlab.com/api/v4/projects/$projectId/trigger/pipeline",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
));
$response = curl_exec($curl);
curl_close($curl);
?>
動作のチェック
記事の作成
microCMSで適当な記事を作成して公開ボタンをクリックします。
ジョブの確認
GibLab側のビルドのパイプライン上でジョブの実行状況が確認できます。
今後の対応
とりあえず中継サーバーを挟むことでCI/CDのジョブを実行することができました。
ただ、microCMSさんは開発の速度がとても速い会社なので、もしかして近くにGitLab用の設定ができるようになるかもしれません。
そうなるとこの方法は無用になるかと思われます。