GitLab CI/CDを使ってマージリクエスト作成時に自動でテストジョブを実行する環境を構築する
自身の勉強と所属している会社内への布教を兼ねて、GitLabのCI/CD環境を構築をして導入ドキュメントも兼ねてまとめることにしました。公式ドキュメントを参考にしましたが我流なので「CI/CDについて何も知らない」&「何でも許せる人」向けとしてよろしくお願いします。(CI/CDと言ってますが今回はテスト自動化の構築のみになります。)
この記事でやっていくこと
- GitLab CI/CD、GitLab Runnerを既存のプロジェクトに導入します。
- マージリクエスト作成時に自動でテストジョブを実行する仕組みを作ります。
- 最終的にテストCIが成功していないとマージを出来ないようにします。
上の図はマージリクエスト画面で、テストCIが失敗してマージができないようになっています
前提
- Ubuntu(WSL2)で構築してます。
- (Debian系Linux以外の方は適宜コマンドを変えて進めてください) - バックエンド言語はPHPを使用してます。
- (他の言語でも本手順は有効です。適宜コマンドを変えて進めてください) - 事前にDockerがインストールされている事。
- Dockerインストール公式ドキュメント
目次
- GitLab Runner をインストール
- ランナーの作成
- ランナーをGitLabに登録する
- .gitlab-ci.yml、テスト用ファイルを追加する
- マージリクエスト作成時、CI成功しないとMerge出来ないようにする
GitLab Runner をインストール
まずGitLab Runnerとは→ 「GitLab Runner は、GitLab CI/CD と連携してパイプラインでジョブを実行するアプリケーションです。」(公式ドキュメント抜粋)
ざっくりと説明すると、ランナーとは様々なジョブを実行を制御する環境、ジョブとはテストなど予め定義されたどのような条件で実行されるべきかを示す処理です。
まずは、公式ドキュメント を参考にGitLab Runnerをインストールしていきます。
### 公式の GitLab リポジトリを追加します。
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
### GitLab Runner の最新バージョンをインストールする
sudo apt-get install gitlab-runner
バージョン情報が表示されたらGitLab Runnerのインストール成功
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ gitlab-runner -v
Version: 15.9.1
Git revision: d540b510
Git branch: 15-9-stable
GO version: go1.18.10
Built: 2023-02-20T21:03:05+0000
OS/Arch: linux/amd64
ランナーの作成
次に、ランナーを作成していきます。ここで言うランナーは実際にテストツールを実行するマシンのことを指しています。今回は、WSL2上に新たにUbuntuコンテナを起動してその中にPGM言語やテストツールをインストールする方法でランナーを作っていきます。
ランナーとなるUbuntuコンテナの用意
### Ubuntuコンテナを名前を付けて起動します。(イメージがない場合はpullしてきます)
docker run -it -d --name gitlab_runner ubuntu:22.04
### コンテナが起動していたら成功
yoneko@DESKTOP-JUAPL98:~/gitlab-ci$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f5e674faa197 ubuntu:22.04 "/bin/bash" 7 seconds ago Up 5 seconds gitlab_runner
### コンテナにログインします。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ docker exec -it gitlab_runner /bin/bash
root@f5e674faa197:/#
ここから先の手順はUbuntuコンテナ内の操作になります。
バックエンド言語、テストツールのインストール
UbuntuコンテナにPHP、PHPUnitをインストールしていきます。
※他の言語を使用したい方は適宜コマンドを変えて進めてください。
root@f5e674faa197:/$ cd
### Ubuntuコンテナはデフォルトで名前解決ができないのでnameserverをGoogleのDNS変更します。
root@f5e674faa197:~$ sh -c "echo 'nameserver 8.8.8.8' > /etc/resolv.conf"
### PHPUnitのインストールに必要なものをインストールします。
root@f5e674faa197:~$ apt update
root@f5e674faa197:~$ apt install curl php-cli php-mbstring git unzip php-xml vim
composer公式ドキュメントを参考にcomposerをインストールしていきます。
root@f5e674faa197:~$ php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
root@f5e674faa197:~$ php -r "if (hash_file('sha384', 'composer-setup.php') === '55ce33d7678c5a611085589f1f3ddf8b3c52d662cd01d4ba75c0ee0459970c2200a51f492d557530c71c15d8dba01eae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
root@f5e674faa197:~$ php composer-setup.php
root@f5e674faa197:~$ php -r "unlink('composer-setup.php');"
### 任意のディレクトリから簡単に呼び出しためにcomposer.pharを移動する。
root@f5e674faa197:~$ mv composer.phar /usr/local/bin/composer
composer.jsonを生成します。対話型のコマンドですが基本はデフォルトで大丈夫です。
root@f5e674faa197:~$ composer ini
Welcome to the Composer config generator
This command will guide you through creating your composer.json config.
Package name (<vendor>/<name>) [root/root]:
Description []:
Author [n to skip]: n
Minimum Stability []:
Package Type (e.g. library, project, metapackage, composer-plugin) []:
License []:
Define your dependencies.
Would you like to define your dependencies (require) interactively [yes]?
Search for a package:
Would you like to define your dev dependencies (require-dev) interactively [yes]?
Search for a package:
Add PSR-4 autoload mapping? Maps namespace "Root\Root" to the entered relative path. [src/, n to skip]:
{
"name": "root/root",
"autoload": {
"psr-4": {
"Root\\Root\\": "src/"
}
},
"require": {}
}
Do you confirm generation [yes]? yes
Generating autoload files
Generated autoload files
PSR-4 autoloading configured. Use "namespace Root\Root;" in src/
Include the Composer autoloader with: require 'vendor/autoload.php';
root@f5e674faa197:~#
viコマンドでPHPのオートロードの設定を修正するため、
composer.json
の"Root\\Root\\": "src/"
を "App\\": "./"
に修正します。
{
"name": "root/root",
"autoload": {
"psr-4": {
"App\\": "./"
}
},
"require": {}
}
修正したらcomposer dump-autoload
で修正を反映させる。
root@f5e674faa197:~$ composer dump-autoload
PHPUnitをインストールします。
root@f5e674faa197:~$ composer require --dev phpunit/phpunit ^8
PHPUnitがインストールされていたら成功。ひとまずコンテナ内の作業は一旦終了です。
root@f5e674faa197:~$ /root/vendor/bin/phpunit --v
PHPUnit 8.5.33 by Sebastian Bergmann and contributors.
option --v is ambiguous
次に、これまでに行ったコンテナ内の作業を保存します。
一度、コンテナからデタッチ(CTRL+P → CTRL+Qの順に押す)するか別ターミナルからdocker commit
でUbuntu内の状況を保存します。
### 別ターミナルを起動してコンテナIDを確認しておく。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f5e674faa197 ubuntu:22.04 "/bin/bash" 8 hours ago Up 8 hours gitlab_runner
### PHPUnitをインストールしたUbuntuコンテナをphpunitとという名前で保存する。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo docker commit f5e674faa197 phpunit
sha256:49d865acd97e96cba349286a56def64c1cead306145f20ab8e296793063d3ff9
### phpunitというイメージが出来ていたら保存成功。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
phpunit latest 49d865acd97e 21 seconds ago 330MB
コンテナが保存出来たらランナーの作成はいったん終了です。
ランナーをGitLabに登録する
ここからは、GitLabにランナーを登録していきます。
登録するにはGitLabのプロジェクト内にある登録トークンが必要なので控えておきます。
1, GitLabのプロジェクト内の、[Settings] → [CI/CD] → [Runners]画面のRegister the runner with this URL:
とAnd this registration token:
の下の文字列を控えてきます。
2, 併せてEnable shared runners for this project
のチェックを外しておきます。
公式ドキュメントを参考にして次のコマンドを実行します。
ランナーの登録はsudo gitlab-runner register
コマンドを使用します。対話型コマンドになっており必要な情報を入力していきます。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo gitlab-runner register
Runtime platform arch=amd64 os=linux pid=13818 revision=d540b510 version=15.9.1
Running in system-mode.
Enter the GitLab instance URL (for example, https://gitlab.com/):
https://gitlab.com/
Enter the registration token:
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Enter a description for the runner:
[DESKTOP-JUAPL98]: ci sample
Enter tags for the runner (comma-separated):
Enter optional maintenance note for the runner:
WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 15.6 and will be replaced with support for authentication tokens. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/380872
Registering runner... succeeded runner=GR1348941roJkAcrN
Enter an executor: docker-ssh+machine, instance, custom, docker, docker-ssh, virtualbox, kubernetes, parallels, shell, ssh, docker+machine:
docker
Enter the default Docker image (for example, ruby:2.7):
phpunit
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
対話型コマンドの入力説明
Enter the GitLab instance URL (for example, https://gitlab.com/):
GitLabのURL。控えていた「Register the runner with this URL:」の文字列。
Enter the registration token:
控えていた「And this registration token:」の文字列。
Enter a description for the runner:
ランナーの説明。任意入力のためスキップ可能。
Enter tags for the runner (comma-separated):
ランナーに付けるタグ名。任意入力のためスキップ可能。
Enter optional maintenance note for the runner:
メンテナンスノートを任意に指定します。任意入力のためスキップ可能。
Enter an executor: custom, docker-ssh, parallels, instance, docker, shell, ssh, virtualbox, docker+machine, docker-ssh+machine, kubernetes:
ランナーの種類。今回はDockerコンテナなのでDockerを指定する。
Enter the default Docker image (for example, ruby:2.7):
ランナーとして使用するイメージ。今回はdocker commit
で指定したphpunit
を指定します。
登録が終わったら、ランナーを起動します。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo gitlab-runner run
ランナーを起動後、GitLabのプロジェクト内の[Runners]画面に登録されたランナーが表示されました。
ランナー設定ファイルを編集する。
登録が成功すると、/etc/gitlab-runner/config.toml
に登録したランナーの設定値が書き込まれます。デフォルトの設定ではdocker commitで保存したイメージをpullしてこられないので、config.toml
のrunners.docker
にnetwork_mode
とpull_policy
を追加します。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
shutdown_timeout = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "ci sample"
url = "https://gitlab.com/"
id = 22484451
token = "XXXXXXXXXXXXXXXXXX"
token_obtained_at = 2023-04-02T18:24:19Z
token_expires_at = 0001-01-01T00:00:00Z
executor = "docker"
[runners.cache]
MaxUploadedArchiveSize = 0
[runners.docker]
tls_verify = false
image = "phpunit"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
network_mode = "host" #この行を追加する
pull_policy = "if-not-present" #この行を追加する
shm_size = 0
config.toml
を追加した後設定を反映させます。
yoneko@DESKTOP-JUAPL98:~/gitlab_ci$ sudo gitlab-runner restart
Runtime platform arch=amd64 os=linux pid=19958 revision=d540b510 version=15.9.1
これでGitLab側のランナーの設定は終わりです。
.gitlab-ci.yml、テスト用ファイルを追加する。
ここからは、.gitlab-ci.yml
とテスト用のソースファイルを用意します。
.gitlab-ci.yml
とは、実行するスクリプトやアプリケーションをデプロイする場所などを示すパイプラインの定義ファイルです(公式ドキュメント)
今回は、テストツールを実行するだけなので以下のように作成します。
image:
name: phpunit
stages:
- test
phpunit_job:
stage: test
script:
- /root/vendor/bin/phpunit test
続いて、テスト用のソースファイルと、テストファイルを作って生きます。
<?php
namespace App\src;
class Sample
{
public function hello()
{
return "Hello";
}
}
<?php
use App\src\Sample;
class SampleTest extends PHPUnit\Framework\TestCase
{
public function testHello()
{
$sample = new Sample();
$result = $sample->hello();
//Sample.phpのhello()の返り値が Hello ならテストOK
$this->assertEquals("Hello", $result);
}
}
今回のファイルの階層はこのようになります。
└── App
├── src
│ └── Sample.php
├── test
│ └── SampleTest.php
├── .gitlab-ci.yml
└── README.md
ファイル追加後、手動でパイプラインを実行する。
ファイルを追加出来たら、CIが機能するかどうか確かめます。GitLabのプロジェクト内の、[CI/CD] → [Pipelines]画面内右上にあるRun pipelines
を押してCIが成功するか確認します。
CI成功!
マージリクエスト作成時、CI成功時でないとMerge出来ないようにする
より実用的な運用を想定して、マージリクエスト時にCIを実行して自動テストが成功していないとMerge出来ないようにしていきます。
まずは、公式ドキュメントを参考に.gitlab-ci.yml
を編集します。マージリクエスト作成時のみパイプラインが実行されるようにします。
image:
name: phpunit
stages:
- test
phpunit_job:
stage: test
script:
- /root/vendor/bin/phpunit test
only: #この行を追加する
- merge_requests #この行を追加する
次に、GitLab側のマージリクエスト設定で、マージするときはCI成功を必須にする設定を行います。
GitLabのプロジェクト内の、[Settings] → [Merge requests]画面内で Pipelines must succeed
にチェックを入れます。GitLab側の設定はこれで終わりです。
次に、ソースを修正して、マージリクエストを作成していきます。
わざとCIが失敗するように、$this->assertEquals
の比較する文字列を変えます。そのあとリモートにpushします。
pushした後、GitLab側でマージリクエストを作成します。New merge request
画面でCreate merge request
ボタンを押します
CI失敗時
マージリクエスト作成後、パイプラインが実行されます。CIが失敗したら画像のようにマージボタンが非表示になります。
CI成功時
同じ手順で今度はCIが成功するように、ソースを修正してマージリクエストを作成していきます。CI成功時はマージボタンが表示されます。
以上になります。最後までご覧いただきありがとうございました。
Discussion