Grimoire.jsのCI/CD環境について

今回はGrimoire.jsそのものではなく、どのようにして管理しているかの話をまとめます。
他のOSS管理している方々も参考になれば。

# 前提

Grimoire.jsはプラグインで拡張されることを前提としたWebGLフレームワークだ。
(もし、全く知らない方がいたら[こちら](https://qiita.com/kyasbal_1994/items/b6672af2e75a8f27df10)を見ていただきたい)

そのため、実質的にコアとされている部分はデータ構造のみを扱うのでこれだけでは何もできない。この上にレンダラーとしての機能を提供しているプラグインが載っている。
以下は、通常僕らがメンテナンスしているパッケージだ。

* grimoirejs (コア部分、タグベースの仕組みなど)
* grimoirejs-math (数学関連)
* grimoirejs-fundamental (レンダラー周り)
* grimoirejs-forward-shading (シェーディング)
* grimoirejs-animation (アニメーション)
* grimoirejs-gltf (glTF形式のモデルローダー)

本当はもっとやりたい機能があるのだが、現状は以上のプラグインがある。また、いくつかのプラグインをまとめたプリセットというものもある。

* grimoirejs-preset-basic (grimoirejs,fundamental,mathをひとまとめにしたもの)

このため、一つのライブラリをメンテナンスしていると言ってもレポジトリが多数に分離しており、そのためこれらを簡単にまとめていかなければならなかった。

また、以下の環境はすべてCircleCIを中心に実行されている。

# テスト周り

## ビルド

ビルドはテストではない気がするが、すべてのパッケージがTypescriptを使っているので、依存先のパッケージのAPIが変わった場合即座にそれが存在しないことがわかる。
他にも、strictにしておけば、明示的に指定されないanyを容認しないので安心できる。

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "es6",
    "declaration": true,
    "moduleResolution": "node",
    "outDir": "./lib",
    "declarationDir": "./ref",
    "noImplicitReturns": true,
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "strict": true
  },
  "include": [
    "src/**/*.ts"
  ]
}
```

## ユニットテスト

![](https://i.gyazo.com/998e9d78b2e42e2b9c964a62330bbb8e.png)

すべてのパッケージでやっているわけではないが、ユニットテストが効果的な部分(コアやmath)などではユニットテストが動いている。なお、ユニットテストもTypescriptで書かれている。

つい先日まではユニットテストはJavascriptで書かれていたためメンテナンスの負債化が激しかったのだが、そもそも型のあるものをベースにしたプロジェクトなので絶対にアンチパターンであった。


**使っているもの**

* [ava](https://github.com/avajs/ava)(Test runner)
* [sinon](http://sinonjs.org/)(Mocker)

## テストカバレッジ

![](https://i.gyazo.com/da80fed1dcd606c5857443ddaa7a9462.png)

ユニットテストを行なっている一部のプロジェクトではテストカバレッジを測定している。少なくともテストをするならカバレッジがわかないとテストを書く気にならないからだ。

また、これらのテストカバレッジは自動で各プロジェクトとブランチ名に対応したページにアップロードされる。このページはS3上でホスティングされていて、CircleCIで生成されたフォルダから`aws cp`コマンドでアップロードしているだけである。

* [http://coverage.grimoire.gl/core/master/ (コアのmasterブランチ)](http://coverage.grimoire.gl/core/master/)

**使っているもの**

* [nyc](https://github.com/istanbuljs/nyc) (Istanboolのコマンドライン版)

## APIドキュメント

APIドキュメントは基本的にソースコードから自動生成している。コードカバレッジと同様に`aws cp`によってCircleCIで実行されたものをアップロードする。

* http://api.grimoire.gl/core/ (コアのAPIドキュメント)
* http://api.grimoire.gl/grimoirejs-fundamental/ (レンダラー周りのAPIドキュメント)

**使っているもの**

* [TypeDoc](http://typedoc.org/)

(ドキュメントのカバレッジ測定機能が欲しいなあ)

## Lint

まだ全部のパッケージでCIでチェックする様にはできていないが、基本的に満たすべきソースコードの品質はTSLintによって担保している。
publicなメソッドやprivateなメソッドの順番、使っていないimportは許可しないなど読みやすさがこれによって向上する。

なお、以下が使っている設定ファイルだ。

* https://github.com/GrimoireGL/common-configurations/blob/master/tslint.json

**使っているもの**

* [tslint](https://palantir.github.io/tslint/)

## E2Eテスト

パッケージが複数個に分かれているので、実際に結合して見ないと動くかどうかはわからない。そこで、特定のURLパラメーターが来るとそのバージョンにライブラリを差し替えるための仕組みを用いて、変更のあったライブラリだけ差し替えてテストができる。

**使っているもの**

* [puppeteer](https://github.com/GoogleChrome/puppeteer)
* [blink-diff](https://github.com/yahoo/blink-diff)

さらに自動でプルリクエストに以下の様な差分をコメントする。(赤くなっているのが差分)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/41402/103f7976-5d8f-eb36-01b4-f08d6604e854.png)

[PRの例](https://github.com/GrimoireGL/grimoirejs-fundamental/pull/164)

どのようなサンプルでテストするかは一つのレポジトリに固まっているので、それぞれのレポジトリでテストを作る必要がない。
[テスト設定](https://github.com/kyasbal-1994/circleci-headless-chrome/tree/master/test)
また、codepenなどにあげているサンプルをそのままテストできる様になっているので、サンプルを作るついでにE2Eテストができる利点もある。

また、各コミットごとに以下の様なページが作られるので後からテスト結果を見れて便利だ。
https://github.com/GrimoireGL/E2EResult/blob/master/docs/c67db3a0bb32d9a441cf4e13bcc31a942b49a8d3.md

# デプロイ周り

## npm publish

`semantic-release`を用いればnpmへの公開を自動化することができる。新しいバージョンをいつも手書きで修正する必要もない。自動でコミットメッセージから適切なバージョン番号が計算されpublishされる。また、githubのリリースページには自動でリリースノートがコメントから生成される。(コミットメッセージが適当すぎてひどいものしか今のところ生成されてないが)

**使っているもの**

* [semantic release](https://github.com/semantic-release/semantic-release)


## presetのアップデート

複数個のプラグインをバンドルしただけのものがあるので、依存先が変わった場合は自動でプラグインのバージョンを変えたpackage.jsonを生成してpushされた後、PRが生成される。依存先のパッケージが更新された際、Herokuの特定のエンドポイントを叩く様になっていて、そうするとHeroku側にあるプログラムがnpm updateをすることにより、このpackage.jsonを生成する。

https://github.com/GrimoireGL/grimoirejs-preset-basic/pull/137

## Greenkeeper

[Greenkeeper](https://greenkeeper.io/)を使えば、依存先のパッケージが変わった場合、自動的にCIを起動する様なpushを別ブランチにしてもらって、問題がないか検証してくれる。Github Marketplaceで簡単に有効にできるし、OSSならば無料で利用できるので活用すると楽だ。

ちなみに、一つ前のpresetのアップデートはGreenkeeperでどうにかなる気がするが、問題のなかった場合はGreenkeeperはPRを生成しない。つまり、依存先がバンドリング内容に含まれる様な状況下では依存先を含めたものをリリースできないので適さない。

# まとめ

結構大量に思えるが、実際には各パッケージごとに多少異なるものの、以下の様なcircle.ymlで成し遂げている。
これはコアのもの。

```yaml:circle.yml
machine:
  node:
    version: 6.9.5
test:
  post:
    - sh release.sh
    - >-
      aws s3 cp ./coverage/ s3://coverage.grimoire.gl/core/$CIRCLE_BRANCH
      --recursive --region ap-northeast-1 --acl public-read
  override:
    - npm run lint && npm run build && npm run coverage
    - sh -x shell/e2e.sh
deployment:
  deploy:
    branch: master
    commands:
      - npm run doc
      - >-
        aws s3 cp ./docs/ s3://api.grimoire.gl/core --recursive --region
        ap-northeast-1 --acl public-read
```

# さいごに

単純にリスト化して少しずつ書いただけであるが、参考になる部分があれば他の方のプロジェクトでもぜひ使って欲しい。
また、こういったテスト環境に興味がある人が周りになかなかいないので、もし興味があればぜひ[プロジェクトのslack](https://grimoire-slackin.herokuapp.com/)などに気軽に参加して見てもらいたい。