やったこと
職場でCircleCIを導入しているプロダクトが増えていることもあり、勉強がてらCircleCIを使ってNuxt.jsをGitHub Pagesにデプロイして公開してみました。
本記事ではその手順について記載しています。
デプロイのプロセスとしてはこのような流れで行うようにしています。
❶. Nuxt.jsのソースをmasterブランチにpushする
❷. CircleCIでlint, testを実施する
❸. CircleCIで公開ファイルを生成して、それをgh-pagesブランチへpushしてサイトを公開
開発環境は以下で行いました。
os: mac
npm: 6.4.1
node: v8.11.1
実際のソースはこちらで公開しています。
GitHub: https://github.com/yagisuke/nuxt-circle-ci
公開したサイト: https://yagisuke.github.io/nuxt-circle-ci
手順
開発手順です。これに沿って記載していきます。
なお、lintやtestの設定はしていますが、今回のミッションは「CircleCIを使ってNuxt.jsをGitHub Pagesにデプロイすること」なので、とりあえず設定したという程度で記載しています。
- CircleCIに登録する
- Nuxt.jsのプロジェクトを用意する
- lintを書いて設定する
- testを書いて設定する
- CircleCIの実行ファイルを用意する
- GitHub Pages用の公開ファイルを生成する
- GitHub Pagesへのデプロイ処理を追加する
- CircleCIのプロジェクトに追加する
- CircleCIに環境変数を設定する
- 秘密鍵と公開鍵を登録する
- CircleCIのステータスバッヂをつける
CircleCIに登録する
さっそくですが、CircleCIに登録していきます。
それではCicleCIの登録画面を開きます。
GitHubアカウントと連携してアカウントを作成。
CircleCIへの登録が完了しました。
Nuxt.jsのプロジェクトを用意する
続いてGitHubにリポジトリを作り、そこにNuxt.jsのプロジェクトをpushします。
Nuxt.jsは以下のようにVue CLIで準備しました。
$ npm install -g @vue/cli @vue/cli-init
$ vue init nuxt-community/starter-template nuxt-circle-ci
$ cd nuxt-circle-ci
$ npm install
$ npm run dev
起動しておなじみの画面が表示されたら、用意したGitHubのリポジトリにpushしときます。
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/b23eed087fcf10dd1af752e386bc24bcbcdcec97
lintを書いて設定する
今回はNuxt.jsのドキュメント と同じく、ESLintとPrettierを設定しました。
設定内容もNuxt.jsのドキュメントと同じようにしています。ただし、JSのフォーマットは、ダブルクオートよりシングルクオートの方が好みなので、ドキュメントのサンプルに加えて .eslintrc.js
に "singleQuote": true
のオプションを追加しています。詳しくは、下記commit logの.eslintrc.jsを参照ください。
また、package.json
のscriptsに、lintコマンドを忘れずに登録しておいてください。
CircleCIのタスクで後ほど使用します。
"scripts": {
...略
"lint": "eslint --ext .js,.vue --ignore-path .gitignore ."
...略
}
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/d31a11e15e1fd8ba345fd6a898a96a598d72c83d
testを書いて設定する
lintの設定と同様に、testについてもNuxt.jsのドキュメントに揃えてAVAで設定しました。
ドキュメントのサンプルでは、 page/index.vue
を test/index.test.js
でテストをしていますが、トップページを変更したくなかったので、 page/ava.vue
を test/ava.test.js
でテストするようにしています。詳しくはcommit logを参照ください。
また package.json
のscriptsに、testコマンドを忘れずに登録しておいてください。
CircleCIのタスクで後ほど使用します。
"scripts": {
...略
"test": "ava"
...略
}
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/23fc386761af0ff15e312ed785d53910e4ee1f8e
CircleCIの実行ファイルを用意する
CircleCIの実行ファイル .circleci/config.yml
を用意します。
実行ファイルの中身は以下のように記載し、プロジェクトのrootディレクトリに配置します。
version: 2
jobs:
build:
docker:
- image: circleci/node:8
steps:
- checkout
- restore_cache:
keys:
- npm-{{ checksum "package.json" }}
- run: npm install
- save_cache:
paths:
- node_modules
key: npm-{{ checksum "package.json" }}
- run: npm run lint
- run: npm run test
deploy:
docker:
- image: circleci/node:8
steps:
- checkout
- restore_cache:
keys:
- npm-{{ checksum "package.json" }}
- run: npm install
- run: git config user.name $GITHUB_NAME
- run: git config user.email $GITHUB_EMAIL
- run: npm run generate:gh-pages
- run: npm run deploy:gh-pages
workflows:
version: 2
build-deploy:
jobs:
- build
- deploy:
requires:
- build
filters:
branches:
only: master
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/0afeb3c163e2ed6f106e09cd4d4a21262367937b
実行ファイルを以下の順にもう少し見ていきます。
config.ymlの全体像
ざっくり説明すると、ブランチにソースをpushしたら、lintとtestを実施して、
gh-pagesブランチに公開ファイルをpushするようにしています。
冒頭でも記載しましたが、今回のGitHub Pagesへのデプロイはこのような流れとなっています。
❶. Nuxt.jsのソースをmasterブランチにpushする
❷. CircleCIでlint, testを実施する
❸. CircleCIで公開ファイルを生成して、それをgh-pagesブランチへpushしてサイトを公開
この❷、❸がCircleCIのジョブとなり、このように記載しています。
version: 2
jobs:
# ジョブを登録
build: # ❷のジョブ
# lintを実施する
# testを実施する
deploy: # ❸のジョブ
# 公開ファイルを生成する
# 公開ファイルをgh-pagesブランチへpushする
workflows:
# ❷のジョブと❸のジョブを呼び出す
buildジョブについて
buildジョブでは、package.jsonに定義したlintとtestを実施します。
(ジョブの名前はtestとかにしておけば良かったかも)
build:
docker:
- image: circleci/node:8
steps:
- checkout
- restore_cache:
keys:
- npm-{{ checksum "package.json" }}
- run: npm install
- save_cache:
paths:
- node_modules
key: npm-{{ checksum "package.json" }}
- run: npm run lint
- run: npm run test
まず、lintとtestを実行するためには、nodeが必要なので、docker上にnode環境を作り、そこにソースをチェックアウトして、npm install
で必要なパッケージをインストールします。インストールが終わったら、lintとtestを実施してbuildジョブは終了します。
(npmとかyarnはdockerイメージに用意してくれている)
restore_cache
とsave_cache
については、npm installでできたnode_modules
をsave_cache
でキャッシュして、package.jsonに変更がなければ、restore_cache
でキャッシュを再利用しています。
deployジョブについて
deployジョブでは、公開ファイルを生成して、それをgh-pagesブランチへpushしています。
deploy:
docker:
- image: circleci/node:8
steps:
- checkout
- restore_cache:
keys:
- npm-{{ checksum "package.json" }}
- run: npm install
- run: git config user.name $GITHUB_NAME # GitHubのアカウント情報を設定
- run: git config user.email $GITHUB_EMAIL
- run: npm run generate:gh-pages # 公開ファイルを生成
- run: npm run deploy:gh-pages # gh-pagesブランチへpush
「GitHubのアカウント情報を設定」のタスクで指定している$GITHUB_NAME
と$GITHUB_EMAIL
は、CircleCIのサイト上で変数を登録することができ、それを使用しています。
また、「公開ファイルを生成」と「gh-pagesブランチへpush」するタスクは、package.jsonに後ほど登録します。これらについては後ほど説明します。
workflowsについて
config.ymlの一番下にあるworkflowsのブロックです。
workflowsは、jobsに指定したジョブを実行をすることができ、またその実行を制御することができます。
ジョブを実行させる
ジョブを実行させるにはこのように書きます。
この場合、「build」と「deploy」ジョブは並列で実行します。
今回は「build -> deploy」の順に実行させたいので、その制御を次で行ないます。
jobs:
build:
...略
deploy:
...略
workflows:
version: 2
build-deploy:
jobs:
- build <-- buildジョブを実行
- deploy <-- deployジョブを実行
実行の順番を制御する
実行の順番を制御するには、requires
を使います。
「build -> deploy」というジョブの順番にしたい場合は下記のように記載します。
ただし、「deploy」ジョブは、masterブランチにpushされた時しか実行して欲しくないので、その制御を次で行います。
jobs:
...略
workflows:
...略
- build
- deploy:
requires: <-- ここ
- build
特定のブランチでしか実行しないようにする
ジョブを実行するかどうかの条件はfilters
で指定し、特定のブランチでしか実行したくないジョブは、branches
に記載します。今回の「deploy」ジョブは、masterブランチでしか実行したくないので、このようになりました。
jobs:
...略
workflows:
...略
- deploy:
...略
filters: <-- ここ
branches:
only: master
workflowsについて詳しく調べたい方は本家の解説や、こちらの記事をオススメします。
GitHub Pages用の公開ファイルを生成する
Nuxt.jsは、アプリケーションをビルドして、ルートごとにHTMLファイルを生成してくれるnuxt generate
という便利なコマンドを用意しています。
参照: https://ja.nuxtjs.org/guide/commands/
そのコマンドを実行すれば簡単に公開ファイルを生成できますが、GitHub PagesのURLは、https://<username>.github.io/<repository-name>
となるため(カスタムドメインがない場合)、アプリケーションのベースURLに/repository-name/
を設定する必要があります。
具体的には以下のようにソースを修正しています。
package.jsonのscripts
"scripts": {
...略
"generate:gh-pages": "DEPLOY_ENV=GH_PAGES nuxt generate"
}
nuxt.config.js
// GitHub Pageへのデプロイ時には、ベースURLを変更している。
var baseRoute = env => (env === 'GH_PAGES' ? '/nuxt-circle-ci/' : '/')
module.exports = {
...略
link: [{
rel: 'icon',
type: 'image/x-icon',
href: baseRoute(process.env.DEPLOY_ENV) + 'favicon.ico'
}]
},
router: {
base: baseRoute(process.env.DEPLOY_ENV)
},
...略
}
上記のソースは、Nuxt.jsのドキュメントに記載してあるものと一緒ですので、詳しくはそちらをご覧ください。
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/ca55cd1655e2a08b01c95d18bd0b2cf3c8c05d16
GitHub Pagesへのデプロイ処理を追加する
上記で生成された公開ファイルをgh-pagesブランチへpushする処理を追加していきます。
今回はこちらを参考に、gh-pagesというパッケージを使用してpushするようにしました。
まずは、gh-pages
のインストールを行います。
$ npm install gh-pages --save-dev
次に、package.jsonのscriptsにdeployコマンドを設定します。
"scripts": {
...略
"deploy:gh-pages": "gh-pages -d dist -m 'chore: update [skip ci]' -t"
...略
}
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/7bd778e1eda378119aa1cca5a9fc39241f7be30a
gh-pagesパッケージのオプションについて
gh-pages
のオプションを見ていきます。
-d dist
pushするディレクトリを指定します。
公開ファイルが配置される dist
ディレクトリを指定します。
-m 'chore: update [skip ci]'
コミットメッセージを指定します。
リポジトリにコミットすると、CicleCIのジョブが起動してしまいますが、[skip ci]
をコミットメッセージに含めると、起動せずにskipすることができます。
-t
pushするディレクトリ内のファイルを、ドットファイルを含めて
pushするように指定しています。
GitHub Pagesは、defaultでjekyllという静的サイトジェネレータを採用しており、そのjekyllが、_(アンダースコア)
から始まるディレクトリを読み込まないようにしてしまいます。
参照: https://blog.github.com/2009-12-29-bypassing-jekyll-on-github-pages/
CircleCIのプロジェクトに追加する
CircleCIにGitHubのリポジトリを紐づけていきます。
まずは、CircleCIの「Add Projects」画面を開き、作業リポジトリの「Set Up Project」ボタンを押下します。
「Set Up Project」画面が開くので、以下のように設定して、「Start building」ボタンを押下します。
Operating System: Linux
Language: Node
すると、CircleCIの実行ファイルのジョブが起動して、エラーになります。
これは、実行ファイルに定義した$GITHUB_NAME
と$GITHUB_EMAIL
が未設定なためです。
また、CircleCIからリポジトリにpushするためには、リポジトリのwrite権限が必要になるのでそちらも後ほど設定していきます。
CircleCIに環境変数を設定する
CircleCIの実行ファイル.circle/config.yml
に設定した$GITHUB_NAME
と$GITHUB_EMAIL
の変数をCircleCIに登録していきます。
まずは「Jobs」画面を開いて、リポジトリの歯車アイコンを押下します。
プロジェクトの「Settings」画面が開くので、「Environment Variables」メニューを選び、「Add Variable」ボタンを押下して、変数を登録していきます。
こちらは$GITHUB_NAME
の設定です。
こちらは$GITHUB_EMAIL
の設定です。
ここで設定した変数は、実行ファイルから直接利用することができます。
秘密鍵と公開鍵を登録する
リポジトリに公開ファイルをpushするためには、CircleCIに秘密鍵を登録し、GitHubに公開鍵を登録する必要があるので、それを行なっていきます。
秘密鍵と公開鍵を作成する
以下のコマンドを叩いて、秘密鍵(id_rsa)
と公開鍵(id_rsa.pub)
を作成しました。
$ ssh-keygen -t rsa -b 4096 -C 任意の文字列 -f ./id_rsa # パスワードは空にすること
GitHubに公開鍵を登録する
先ほど作成した公開鍵(id_rsa.pub)
をGitHubに登録していきます。
まずはリポジトリの「settings」を開き、「Deploy keys」画面を開きます。
すでにCircleCIにはread権限が付与されていますが、今回はwrite権限が必要なので、
「Add Deploy key」ボタンから新たに追加します。
公開鍵(id_rsa.pub)
のファイルの中身を貼り付け、Allow write access
にチェックをつけて追加してください。
登録が完了したら、元からあったread onlyのKeyは不要になるので削除して、次はCircleCIに秘密鍵を設定します。
CircleCIに秘密鍵を登録する
秘密鍵(id_rsa)
をCircleCIに登録していきます。
CircleCIのプロジェクトの「Settings」画面を開いて、「SSH Permissions」メニューを選択します。
「Add SSH Key」ボタンを押下して秘密鍵を登録していきます。
以下のように秘密鍵(id_rsa)
を入力して、「Add SSH Key」を押して登録します。
また、GitHubで削除したread権限しかないkeyを削除しときます。
CircleCIのプロジェクトの「Settings」画面を開いて、「Checkout keys」メニューを選択します。
そこで登録されている認証keyの×
ボタンを押して削除します。
参考: https://mikoto2000.blogspot.com/2018/05/circleci-sphinx-gh-pages.html#gh-pages-ブランチへ-push-するための設定
CircleCIのステータスバッヂをつける
CircleCIのプロジェクトの「Settings」画面を開いて、「Status Badges」メニューを選択します。
以下のようにして、出力されたEmbed Code
をREADME.mdに追加してpushしました。これで、masterブランチのCircleCIのステータスがREADME.mdに表示されるようになります。
Branch: master
API Token: None
Embed Code: Markdown
commit log: https://github.com/yagisuke/nuxt-circle-ci/commit/a7f56b89cfc27bba8ea613383067571a4778cc87
おわり
CircleCIのジョブが無事に終了すれば、GitHub Pagesが公開されます。
長文でまとまりのない文章になってしまいましたが、私個人としては色々はまって良い勉強の機会とすることができました。
お付き合いいただきありがとうございました。