はじめに
Node.jsプロジェクトの品質を保つうえで、CI(継続的インテグレーション)の整備は避けて通れない。今回は個人学習として、GitHub Actions を使って、コードをプッシュするたびに自動で Lint と Test が走る環境を最短で構築した手順をまとめる。
テストフレームワークには最近勢いのある Vitest を、Lint ツールには ESLint(最新の Flat Config 形式)を採用した。構築中に実際にハマったエラーとその解決策も一緒に記録しているので、同じところで詰まった人の参考になれば幸いだ。
この記事の対象者
- 初めてCI(継続的インテグレーション)を設定する人
- GitHub Actionsの基本的な動かし方を知りたい人
- Node.jsプロジェクトでVitestや最新のESLint (Flat Config) の初期構築につまずいている人
参考にした記事
技術スタック
- Node.js (v20)
- Vitest (v1系)
- ESLint (Flat Config 対応)
- GitHub Actions
1. プロジェクトの初期設定
まずローカルで Node.js プロジェクトを作成し、必要なパッケージをインストールする。
mkdir my-node-ci-project
cd my-node-ci-project
npm init -y
続いて ESLint と Vitest、そして ESLint 上で Vitest の構文を解釈するためのプラグインを追加する。
npm install --save-dev eslint @eslint/js @vitest/eslint-plugin vitest
ポイント:ES Modules (ESM) の有効化
最近の Node.js エコシステムに合わせ、プロジェクト全体を ES Modules 化する。package.json に "type": "module" を追加し、実行スクリプトも設定しておこう。
// package.json(一部抜粋)
{
"name": "my-node-ci-project",
"version": "1.0.0",
"type": "module", // ← ここを追加!
"scripts": {
"lint": "eslint .",
"test": "vitest"
}
}
2. 各種設定ファイルの作成
ファイルディレクトリ構造
your-node-project/
├── .github/
│ └── workflows/
│ └── ci.yml
├── src/
│ └── index.js
├── tests/
│ └── index.test.js
├── .eslintrc.js
├── jest.config.js
├── package.json
├── package-lock.json
└── README.md
ESLint の設定(eslint.config.js)
ESLint v9 以降、古い .eslintrc.js ではなく Flat Config が標準となった。プロジェクトのルートに eslint.config.js を作成する。
// eslint.config.js
import js from "@eslint/js";
import vitest from "@vitest/eslint-plugin";
export default [
js.configs.recommended,
{
plugins: {
vitest,
},
rules: {
...vitest.configs.recommended.rules,
"no-unused-vars": "warn",
},
languageOptions: {
globals: {
...vitest.environments.env.globals,
},
},
},
];
Vitest の設定(vitest.config.js)
続いて Vitest の設定ファイルも用意する。
// vitest.config.js
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
globals: true,
include: ["tests/**/*.test.js"],
},
});
3. 実装コードとテストコードの作成
テスト対象の簡単な関数と、そのテストコードを用意する。ESM なので import/export 構文を使う点に注意。
// src/index.js
export function greet(name) {
return `Hello, ${name}!`;
}
// tests/index.test.js
import { describe, expect, test } from "vitest";
import { greet } from "../src/index.js"; // .js 拡張子が必須!
describe("greet function", () => {
test("returns greeting message", () => {
expect(greet("World")).toBe("Hello, World!");
});
});
ここでいったんローカルで npm run lint と npm run test を実行し、どちらも通ることを確認しておくと後が楽になる。
4. GitHub Actions のワークフロー作成
いよいよ本題の GitHub Actions 設定だ。リポジトリ直下に .github/workflows/ci.yml を作成する。
# .github/workflows/ci.yml
name: Node.js CI
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run tests
run: npm test
これを GitHub にプッシュすると、Actions タブで CI が自動的に動き始める。Lint とテストの両方に緑のチェックがついたら構築完了だ。
🛠 構築中に発生したエラーと解決策
実際の構築中、以下の 2 つのエラーで詰まった。同じところで止まっている人の参考になれば。
1. ESLint が動かない(exit code 2)
エラー内容
ESLint couldn't find an eslint.config.(js|mjs|cjs) file.
原因と解決策
最初は古い .eslintrc.js 形式で書いていたが、最新の ESLint がインストールされたことで Flat Config が見つからずエラーになっていた。.eslintrc.js を削除して eslint.config.js に書き直すことで解消した。
2. 'module' is not defined(no-undef エラー)
エラー内容
6:1 error 'module' is not defined no-undef
原因と解決策
ESLint が Flat Config によってプロジェクトを「ES Module」として評価する一方で、コード側では module.exports という CommonJS 構文を使っていたため、module が未定義変数として検出されていた。package.json に "type": "module" を追記し、JS ファイルを export / import 構文に統一することで解決した。
おわりに
Jest から Vitest への乗り換え、ESLint の Flat Config 移行など、最新の Node.js 事情に追いつきながら CI 環境を整えるのは思ったより手間がかかった。ただ、GitHub Actions 自体は数行の YAML で動くので、個人プロジェクトにも気軽に導入できる。品質を担保する仕組みを早めに入れておくと、後々の開発が格段に楽になると感じた。これからの個人開発に取り入れようと思う。