🚀
GitHub ActionsでMariaDBを使う方法【改】
概要
モチベーション
- サーバーサイドは Node.js を用いてサービス作りたいよ!
- テストをきちんと書いていきたいよ!
- DB はとりあえず無料で使える MariaDB でいきたいよ!
- しかもデータベースのテーブルの初期化を起動と同時にしたいよ!!
- 無料枠もある流行りの GitHub Actions の波に乗りたいよ!
という感じのモチベーションで開発を始めたのですが、基礎をつくるだけでもかなり苦労したので備忘録です。
技術的なポイント
- GitHub Actions を用いて品質を継続的に担保しながら開発
- Docker を用いてローカルでも CI 上でも MariaDB を立てて使用
- Jest を用いて JavaScript コードをテスト
なお、Node.js を用いてサーバーを立てる/API を作るところはこの記事の範囲外とします。
記事執筆時点での環境
ツール | バージョン |
---|---|
nodenv | 1.4.0 |
Node | v14.16.0 |
npm | 7.6.3 |
MariaDB | 10.5.9 |
Compose | 3 |
ファイル構成
─┬─ .gitignore
├─ .github/workflows/ci.yml
├─ package.json
├─ node_modules/
├─ docker
| ├─ docker-compose.yml
| └─ .env
├─ db-utils.js DBの基盤JS
├─ __tests__/mariadb.js ↑の動作確認用のテスト
└─ db.sql DB初期設定のSQLファイル
下準備
- Node.js および npm をインストールしておく
- Docker および Docker Compose をインストールしておく
- GitHub のリポジトリ -> Settings -> Secrets で MariaDB 用の環境変数を設定しておく
- ローカルでの動作確認のためにルートディレクトリで
npm install
しておく
Name | Value |
---|---|
MYSQL_ROOT_PASSWORD | QWERTY1234 |
MYSQL_USER | developer |
MYSQL_PASSWORD | ASDFGH5678 |
docker/.env
MYSQL_ROOT_PASSWORD=QWERTY1234
MYSQL_DATABASE=testdb
MYSQL_USER=developer
MYSQL_PASSWORD=ASDFGH5678
コード
GitHub に上がっています
.gitignore
.DS_Store
node_modules/
.env
.env
には本来公にしたくない環境変数が入る想定のため除外します。
Node の設定
package.json
{
"name": "mariadb-test",
"version": "0.0.1",
"private": true,
"scripts": {
"test": "jest",
"dcps": "docker-compose -f docker/docker-compose.yml ps",
"dcup": "docker-compose -f docker/docker-compose.yml up -d",
"dcdown": "docker-compose -f docker/docker-compose.yml down"
},
"dependencies": {
"dotenv": "^8.2.0",
"mariadb": "^2.5.3"
},
"devDependencies": {
"jest": "^26.6.3"
}
}
Docker コンテナの起動や停止をするための便利コマンドを指定してあります。
Docker の初期化ファイル
docker/docker-compose.yml
version: "3"
services:
db:
image: mariadb:10.5.9
container_name: mariadb_test
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
- ../db.sql:/docker-entrypoint-initdb.d/db.sql
ports:
- 3306:3306
もしも MariaDB のバージョンも指定したい場合は、そこを変更したり環境変数で差し込むといいと思います。
GitHub Actions の YAML
.github/workflows/ci.yml
name: CI
on:
push:
branches:
- develop
jobs:
mariadb_test:
runs-on: ubuntu-20.04
timeout-minutes: 5
env:
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
MYSQL_USER: ${{ secrets.MYSQL_USER }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
steps:
- uses: actions/checkout@v2
- name: Shutdown Ubuntu MySQL (SUDO)
run: sudo service mysql stop
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: "14.16.0"
- name: Check Node & npm Version
run: |
node --version
npm --version
- name: Setup MariaDB
shell: bash
env:
MYSQL_DATABASE: testdb
run: |
docker-compose -f ${{ github.workspace }}/docker/docker-compose.yml up -d
- name: Run Test
run: |
npm ci
npm run test
Action では、Node のセットアップと MariaDB の起動、Jest での JS 動作テストを行っています。
データベース初期化用の SQL ファイル
db.sql
USE testdb;
CREATE TABLE table1
(
user_id VARCHAR(8) UNIQUE NOT NULL,
user_name VARCHAR(20) UNIQUE NOT NULL,
);
INSERT INTO table1
(user_id, user_name)
values("A0001", "Mike");
INSERT INTO table1
(user_id, user_name)
values("A0002", "Jhon");
INSERT INTO table1
(user_id, user_name)
values("A0003", "Caitlyn");
適当なテーブルを作って初期データを挿入してみています。ファイルの権限の問題で、データベースの初期化がうまくいかないことがあるので、$ chmod +x db.sql
などで実行権限を付与しておくといいです。
js
MariaDB を Node.js で扱うための便利クラス
db-utils.js
require("dotenv").config({ path: "docker/.env" });
const mariadb = require("mariadb");
class DBUtils {
constructor() {
this.pool = mariadb.createPool({
host: "localhost",
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
});
}
poolEnd() {
this.pool.end();
}
async postQuery(queryStr) {
let conn;
try {
conn = await this.pool.getConnection();
return await conn.query(queryStr).then((array) => {
delete array.meta;
return array;
});
} catch (error) {
throw error;
} finally {
conn.end();
}
}
}
module.exports = {
DBUtils: DBUtils,
};
poolEnd()
を忘れると、テストが永遠に終わらなくなり、GitHub Actions を 6 時間消費することになるかもしれません。
上の便利クラスの動作確認をしつつ、MariaDB に初期データが読み込まれているか確認するためのテスト(Jest を使用します)
__tests__/mariadb.js
const { DBUtils } = require("../db-utils");
// console.error() の mock
jest.spyOn(console, "error").mockImplementation((...args) => {
console.log(args.join(", "));
});
const sut = new DBUtils();
afterAll(() => {
sut.poolEnd();
});
describe("DBの初期化の確認", () => {
it("Database: testdb が存在する", (done) => {
sut
.postQuery("SHOW databases")
.then((actual) => {
expect(actual).toEqual(
expect.arrayContaining([
{
Database: "testdb",
},
])
);
done();
})
.catch((error) => {
done(error);
});
});
it("Table: table1 が存在する", (done) => {
sut
.postQuery("SHOW tables FROM testdb")
.then((actual) => {
expect(actual).toEqual(
expect.arrayContaining([
{
Tables_in_testdb: "table1",
},
])
);
done();
})
.catch((error) => {
done(error);
});
});
it("table1 の中のデータが格納されている", (done) => {
sut
.postQuery("SELECT * FROM testdb.table1")
.then((actual) => {
expect(actual).toEqual(
expect.arrayContaining([
{
user_id: "A0001",
user_name: "Mike",
},
{
user_id: "A0002",
user_name: "Jhon",
},
{
user_id: "A0003",
user_name: "Caitlyn",
},
])
);
done();
})
.catch((error) => {
done(error);
});
});
});
ローカル環境での動作確認
Docker ですでにコンテナーが起動していないか確認
$ npm run dcps
コンテナーを起動
$ npm run dcup
テストを実行(うまく起動していれば jest のテストが実行されるはず)
$ npm run test
コンテナーを停止
$ npm run dcdown
GitHub Actions 上での動作確認
今回の例では、develop
ブランチへの push で Action が動くようにしてあります。
ハマりどころ
- 現状の GitHub Actions は
services.volumes
の機能が未完成なため、Docker Compose
を用いるのが吉。データベース初期化のための SQL を読み込ませる方法を模索するのにかなり時間を消費しました。getong/mariadb-actionの Action を使う手もありますが、ソースをよくみると、Docker コンテナ上で Docker コンテナを起動していて無駄です。 - Docker 経由するため、ポート関連のデバッグが大変でした。
-
npm run
を使用しているため、データベースを使用するユーザーがmariadb-test@0.0.1
のようにPackage.json
の設定に従うようになるみたいですが、それに気付くのに時間を要しました。 - シンプルに
node_modules
のmariadb
の扱い方の前例記事が役に立たず、試行錯誤しながら実装するのが大変でした。
Discussion