Help us understand the problem. What is going on with this article?

アプリ公開に伴ってStorybookとドキュメントを自動で最新化+デプロイする

More than 1 year has passed since last update.

概要

アプリ公開時に以下の要件を満たす環境が欲しかったため、試行錯誤した際の備忘録。

  • GitHubへのプッシュに伴って自動的にアプリをビルドして公開したい。
  • Storybookも自動的にビルドして公開したい。
  • ドキュメントも自動的に最新化して公開したい。

環境

  • ソースコード管理: GitHub
  • ホスティングサービス: Netlify
  • CIツール: Travis CI

使用ツール

Storybook
UIコンポーネントのカタログを作成するツール。アプリとしてビルドすることも出来る。

TypeDoc
TypeScriptソースコードからドキュメントを自動生成するツール。

公開方法

いずれも本番環境のアプリとは無関係なものであるため、アプリと独立した環境で公開するものとする。

Netlify(ブランチデプロイ)

Storybookもまた動的なアプリであるため、ホスティングサーバー上でビルド&デプロイするべくNetlifyのブランチデプロイを用いる。

GitHub Pages

ドキュメントは静的なHTMLファイル群であるため、予め生成してGitHub Pagesで公開する。

デプロイの流れ

デプロイの大まかな流れは以下の通りである。詳細な解説は、後述の詳解を参照されたい。

リモートへのプッシュからTravis CIまで

[ローカル]
1. ソースコードをGitHubへプッシュする。

[Travis CI]
2. masterブランチを基準にstorybookブランチを作成する。
3. Netlifyの設定ファイルをStorybook用のものに置き換える。
4. リモートリポジトリのstorybookブランチにプッシュする。
5. masterブランチを基準にgh-pagesブランチを作成する。
6. ドキュメントをTypeDocで自動生成する。
7. /docs 内のデータのみをリモートリポジトリのgh-pagesブランチにプッシュする。

CI後からサイト公開まで

前項の処理が完了したのち、Storybookとドキュメントは以下の処理を経て公開される。

Storybook
Travis CIがstorybookブランチに対して実行したプッシュを検知してビルドが実行される。Netlifyの設定ファイルがmasterと異なるため、このビルドではStorybookのビルドが実行され、出力データの格納先である storybook-static がサイトとして公開される。

ドキュメント
gh-pagesブランチにプッシュされたデータは即座にGitHub Pagesとして処理を経て公開される。

手順

プロジェクト作成

プロジェクトを作成してGitHubへプッシュする。

$ create-react-app deploy-app-storybook-docs --typescript
$ cd deploy-app-storybook-docs
$ git remote add origin git@github.com:hogehoge/deploy-app-storybook-docs.git
$ git push -u origin master

サービス連携

Netlify

リポジトリ登録

Netlifyのダッシュボードへアクセスし、New site from Git をクリックしてリポジトリを登録する。途中のステップはそれぞれ以下のように進めること。

  1. Connect to Git provider: GitHubを選択する。
  2. Pick a repository: リポジトリを選択する。
  3. Build options, and deploy!: デフォルトのまま `Deploy site` をクリックする。

ブランチデプロイの設定

master以外にstorybookブランチもデプロイするよう、設定を追加する。Setting → Build & deploy → Deploy contexts → Branch deploysへと進んで Let me add individual branches を選択し、storybook と入力して Save をクリックする。

Travisと連携

travis-ci.comのアカウント設定ページへアクセスし、Manage repositories on GitHub をクリックしてリポジトリを登録する。

ローカル環境構築

ツールのインストール

以下のコマンドにて、StorybookとTypeDocをインストールする。

$ sb init
$ yarn add -D typedoc

各種設定

Netlify

以下のファイルをプロジェクトのルートディレクトリに配置する。それぞれmaster、storybookブランチ用にビルドとデプロイを個別に設定している。

netlify.toml
[build]
  publish = "build/"
  command = "yarn build"
netlify.storybook.toml
[build]
  publish = "storybook-static/"
  command = "yarn build-storybook"

Travis CI

設定ファイル配置

以下のファイルをプロジェクトのルートディレクトリに配置する。

.travis.yml
language: node_js
node_js:
- 11.1.0
before_install:
script:
- yarn test
after_success:
# Prepare for GitHub
- openssl aes-256-cbc -K $encrypted_************_key -iv $encrypted_************_iv -in ./id_rsa.enc -out ~/.ssh/id_rsa -d
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- git config --global user.email "hogehoge@fugafuga.com"
- git config --global user.name "hogehoge"
- git config --global url."git@github.com:".insteadOf "https://github.com/"
# Build Storybook
- git checkout -b storybook master
- mv netlify.storybook.toml netlify.toml
- git add .
- git commit -m "$TRAVIS_COMMIT_MESSAGE"
- git push -u origin storybook --force
# Generate docs by TypeDoc
- git checkout -b gh-pages master
- git subtree add --prefix docs origin gh-pages
- rm -rf docs
- node node_modules/.bin/typedoc --out docs src
- touch docs/.nojekyll
- git add docs --force
- git commit -m "$TRAVIS_COMMIT_MESSAGE"
- git subtree push --prefix docs origin gh-pages

branches:
  only:
  - master
SSHキー発行

Travis CIからGitHubへプッシュを行うため、SSHキーの設定を行う。以下のコマンドにて、GitHubにSSH接続する際に用いている ~/.ssh/id_rsa を暗号化する。

$ travis login --pro
$ travis encrypt-file --repo hogehoge/deploy-app-storybook-docs ~/.ssh/id_rsa ./id_rsa.enc --pro
encrypting /Users/hogehoge/.ssh/id_rsa for hogehoge/deploy-app-storybook-docs
storing result as ./id_rsa.enc
storing secure env variables for decryption

Please add the following to your build script (before_install stage in your .travis.yml, for instance):

    openssl aes-256-cbc -K $encrypted_************_key -iv $encrypted_************_iv -in ./id_rsa.enc -out ~\/.ssh/id_rsa -d

Pro Tip: You can add it automatically by running with --add.

Make sure to add ./id_rsa.enc to the git repository.
Make sure not to add /Users/hogehoge/.ssh/id_rsa to the git repository.
Commit all changes to your .travis.yml.

出力されたコマンド openssl.travis.yml に記述されているものと置き換える。
🚨このコマンドには不要なバックスラッシュが含まれているため、削除して用いること。

バックスラッシュの削除
-out ~\/.ssh/id_rsa -d
      ↓
-out ~/.ssh/id_rsa -d

その他

.gitignore

node_modulesのような動的に生成されるディレクトリやファイルはリポジトリから除外することが望ましい。StorybookとTypeDocが生成するデータの出力先を.gitignore に追記し、リモートリポジトリにプッシュされないようにする。

.gitignore
npm-debug.log*
yarn-debug.log*
yarn-error.log*

+ /storybook-static
+ /docs

動作確認

設定後の初回プッシュ

設定完了後に全ての変更ファイルをステージングしてコミットし、リモートリポジトリへプッシュする。

$ git add .
$ git commit -m "Integrated with Storybook and TypeDoc"
$ git push

検証

プッシュ完了後、下記の項目が期待通り満たされているかを検証する。

  • GitHub上でmaster、storybook、gh-pagesブランチが存在すること。また、いずれもコミットが反映されていること。
  • masterブランチがビルドされて https://hogehoge.netlify.com/ にデプロイされていること。
  • storybookブランチがビルドされて https://storybook--hogehoge.netlify.com/ にデプロイされていること。
  • gh-pagesブランチが https://hogehoge.github.io/deploy-app-storybook-docs/ にデプロイされていること。

更新をプッシュ

各種ファイルを更新してプッシュする。

アプリ用の更新
src/App.tsx に以下の更新を行う。

src/App.tsx
class App extends Component {
  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <p>
-           Edit <code>src/App.tsx</code> and save to reload.
+           Storybook, docs and I are every time up to date!
          </p>

Storybook用の更新
src/stories/index.js に以下の更新を行う。

src/stories/index.js
storiesOf('Button', module)
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)
+ .add('avec text', () => <Button onClick={action('clicked')}>Bonjour Bouton</Button>)
  .add('with some emoji', () => (
    <Button onClick={action('clicked')}>
      <span role="img" aria-label="so cool">
        😀 😎 👍 💯
      </span>
    </Button>
  ));

ドキュメント用の更新
以下のファイルをプロジェクトのルートディレクトリに配置する。

typedoc.json
{
  "exclude": [
    "src/index.tsx",
    "src/serviceWorker.ts",
    "src/**/*.test.tsx"
  ]
}

検証

プッシュ完了後、下記の項目が期待通り満たされているかを検証する。

  • GitHub上のmaster、storybook、gh-pagesブランチの全てに更新が反映されていること。
  • https://hogehoge.netlify.com/ にアプリの更新が反映されてデプロイされていること。
  • https://storybook--hogehoge.netlify.com/ にstorybookの更新がデプロイされていること。
  • https://hogehoge.github.io/deploy-app-storybook-docs/ にドキュメントの更新がデプロイされていること。

詳解

.travis.yml

GitHubとの通信設定

 9: - openssl aes-256-cbc -K $encrypted_************_key -iv $encrypted_************_iv -in ./id_rsa.enc -out ~/.ssh/id_rsa -d
10: - chmod 600 ~/.ssh/id_rsa
11: - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
12: - git config --global user.email "hogehoge@fugafuga.com"
13: - git config --global user.name "hogehoge"
14: - git config --global url."git@github.com:".insteadOf "https://github.com/"

9行目
暗号化された ./id_rsa.enc~/.ssh/id_rsa に復号している。

11行目
~/.ssh/known_hosts に記述のないホストへアクセスする際のプロンプトを抑制している。これを忘れると Are you sure you want to continue connecting (yes/no)? とプロンプトが表示されて処理が停止してタイムアウトしてしまう。

12〜13行目
プッシュを行うアカウントを登録している。

14行目
Travis CIはデフォルトでGitHubとの通信をhttpsで行っているため、公開鍵を用いるSSHとは認証方法が異なる。自分は普段SSHを使っていてhttpsの扱いに不慣れであったため、リモートリポジトリのURLをSSH用のgit@github.com:に変更している。

Storybook

16: - git checkout -b storybook master
17: - mv netlify.storybook.toml netlify.toml
18: - git add .
19: - git commit -m "$TRAVIS_COMMIT_MESSAGE"
20: - git push -u origin storybook --force

16行目
masterブランチを基準にstorybookブランチを新規作成している。

17行目
netlify.tomlをStorybook用の設定ファイルで置き換えている。

19行目
ローカル環境からプッシュした時のコミットメッセージを流用してコミットしている。

20行目
リモートリポジトリのstorybookブランチにプッシュしている。Travis CI上のstorybookブランチはリモートリポジトリ上のそれとヒストリが繋がっていないため、--force オプションを付与して強制的にプッシュしている。

ドキュメント

22: - git checkout -b gh-pages master
23: - git subtree add --prefix docs origin gh-pages
24: - rm -rf docs
25: - node node_modules/.bin/typedoc --out docs src
26: - touch docs/.nojekyll
27: - git add docs --force
28: - git commit -m "$TRAVIS_COMMIT_MESSAGE"
29: - git subtree push --prefix docs origin gh-pages

22行目
masterブランチを基準にgh-pagesブランチを新規作成している。

23行目
origin/gh-pagesブランチをサブツリーとして追加し、/docs と同期している。

24行目
TypeDocは出力先ディレクトリを都度クリアしてドキュメントを生成するが、TypeDocが生成し得ないファイルが存在するとエラーを発生させる。これを避けるため、出力先ディレクトリを削除している。

25行目
TypeDocにてドキュメントを自動生成している。

26行目
GitHub Pagesはjekyllの処理を経て公開されるが、TypeDocが出力する多くのファイルに見られるようなアンダースコアから始まるファイルは除外される。これは避けるため、gh-pagesブランチのルートディレクトリとなる場所に .nojekyll を配置し、jekyllの処理を通さず公開している。

27行目
ローカル環境からプッシュした時のコミットメッセージを流用してコミットしている。

27行目
/docs.gitignore に記述されているため、--force オプションを付与して強制的にステージングしている。

29行目
/docs をサブツリーとしてorigin/gh-pagesブランチにプッシュしている。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした