GitLab CI/CDでMRごとにStorybookを公開しよう

初めまして.20年度新卒の今枝です.

AIサービス開発部のAI Camera開発ユニットに所属しています.
普段はGolangで画像処理モジュールの追加機能の実装や,TypeScript + Vueで画像解析結果の確認画面の実装を行ってます.

GitLabCI/CDの機能を用いて,StorybookをMR(Merge Request)ごとにビルド・公開することでレビューがお手軽になったので,こちらの環境構築の方法を紹介したいと思います.

今回の目標

今回は GitLabのCI/CDを用いて,MR(Merge Request)ごとに Storybook を静的サイトとしてビルド・公開,MRがCloseされたときに静的サイトを非公開にする環境を構築します. 構成図は下記のようになり,赤枠線中の部分の公開を目標にします. (下記の構成図は,gitlab公式の図の派生画像です.ライセンスについて )

GitLabCI/CD構成図

事前知識

Storybook とは

Storybookは,React,Vue,Angularを分離してUIコンポーネントを開発するためのオープンソースツールです.Storybookを使用することで,フロントエンド のコンポーネントを効率的に構築できます.

storybook.js.org

GitLab CI/CD とは

GitLabでは,GitLab CI/CD を使用することで,アプリケーションのビルドや単体テストのオペレーションを自動化し,品質維持を手助けしてくれます. リポジトリ直下に .gitlab-ci.yml を配置することで,プロジェクトのブランチ更新やMerge時に各JobをGitLab Runner上で実行します.

docs.gitlab.com

Storybook 環境を構築(Vue)

公開するstorybookを用意するために,Vueでプロジェクトを作成します.

❯ vue create storybook-demo

Vue CLI v4.4.6
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In package.json
? Save this as a preset for future projects? No

Vue CLI v4.4.6
✨  Creating project in /Users/hoge/fuga/storybook-demo.
🗃  Initializing git repository...
⚙️  Installing CLI plugins. This might take a while...

yarn install v1.22.4
info No lockfile found.
[1/4] 🔍  Resolving packages...
success Saved lockfile.
✨  Done in 23.79s.
🚀  Invoking generators...
📦  Installing additional dependencies...

yarn install v1.22.4
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

success Saved lockfile.
✨  Done in 14.79s.
⚓  Running completion hooks...

📄  Generating README.md...

🎉  Successfully created project storybook-demo.
👉  Get started with the following commands:

 $ cd storybook-demo
 $ yarn serve

StoryBook公式に記載されているAutomatic setupを用いて,Storybookを導入します.

 ❯ npx -p @storybook/cli sb init --type vue 

 sb init - the simplest way to add a storybook to your project.

 • Installing Storybook for user specified project type. ✓
yarn add v1.22.4
.
.
.
yarn install v1.22.4
[1/4] 🔍  Resolving packages...
success Already up-to-date.
✨  Done in 0.51s.

 • Installing dependencies. ✓

To run your storybook, type:

   yarn storybook

For more information visit: https://storybook.js.org

storybookの準備はこれで完了です.

command 動作
yarn storybook storybookの起動
yarn build-storybook 静的ページをビルド
❯ yarn storybook                                                                                                           [16:18:59]
yarn run v1.22.4
$ start-storybook -p 6006
info @storybook/vue v5.3.19
info
info => Loading presets
info => Loading presets
info => Adding stories defined in ".storybook/main.js".
info => Using default Webpack setup.
webpack built b96225350e4acec8e978 in 5232ms
╭───────────────────────────────────────────────────╮
│                                                    │
│   Storybook 5.3.19 started                         │
│   6.27 s for manager and 5.85 s for preview        │
│                                                    │
│    Local:            http://localhost:6006/        │
│    On your network:  http://192.168.11.5:6006/     │
│                                                    │
╰───────────────────────────────────────────────────╯

GitLab CI/CD パイプラインを構成する

gitlab-ci.ymlを記載して,GitLab CI/CD パイプラインを構成します.

image: node:12.16.3-alpine3.11

# Deploy用の変数
variables:
  STAGE_HOST: "ホスト名"
  STAGE_USER: "ユーザー名"

stages:
  - setup
  - build
  - deploy

# node_modulesを準備
setup:
  stage: setup
  script:
    - yarn install
  artifacts:
    paths:
      - node_modules/
    only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

# StoryBook Build (後のステージで使用)
storybook:build:
  stage: build
  script:
    - yarn build-storybook
  artifacts:
    paths:
      - storybook-static
    expire_in: 30 min
  only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

# レビュー環境構築
review:
  stage: deploy
  image: centos:latest
  script:
    # レビュー環境を構築するスクリプト
  dependencies:
    - storybook:build
  environment:
    name: review/${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}
    url: # 公開先のURL
    on_stop: stop_review
  only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

# レビュー環境撤去
stop_review:
  stage: deploy
  image: centos:latest
  variables:
    GIT_STRATEGY: none
  script:
    # レビュー環境撤去するスクリプト
  when: manual
  dependencies: []
  environment:
    name: review/${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}
    action: stop
  only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

storybook-static のdeploy先は,Netlifyなどのホスティングサービスを使用すると良いと思います.
弊チームでは,チーム用のWebServer(Nginx)があり storybook-static は,rsync + ssh でデプロイしました.
rsync + ssh を用いたデプロイ方法については下記に記載します.(不要な方は読み飛ばして下さい)

rsync + ssh を用いたデプロイ方法

静的ページをデプロイ

review:
  stage: deploy
  image: centos:latest
  script:
    - yum install rsync openssh-clients -y
    - mv storybook-static "${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}"
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - touch ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - ssh-keyscan $STAGE_HOST >> ~/.ssh/known_hosts
    - rsync -av -e ssh --delete --checksum "${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}" $STAGE_USER@$STAGE_HOST:/var/www/review
  dependencies:
    - storybook:build
  environment:
    name: review/${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}
    url: # 公開先のURL
    on_stop: stop_review
  only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

デプロイした静的ページを削除

stop_review:
  stage: deploy
  image: centos:latest
  variables:
    GIT_STRATEGY: none
  script:
    - yum install rsync openssh-clients -y
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - touch ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - ssh-keyscan $STAGE_HOST >> ~/.ssh/known_hosts
    - ssh $STAGE_USER@$STAGE_HOST "cd /var/www/review; rm -rf ${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}"
  when: manual
  dependencies: []
  environment:
    name: review/${CI_PROJECT_NAME}-${CI_BUILD_REF_SLUG}
    action: stop
  only:
    refs:
      - merge_requests
    changes:
      - "*.vue"
      - "*.stories.js"
      - "*.stories.ts"

$SSH_PRIVATE_KEYは,プロジェクト内の Settings > CI/CD で設定しています.

Merge Request 作成

MR-ReviewApps

パイプラインが成功して,無事Storybookが確認できました!

StoryBook-ReviewApps

おわりに

レビューする人がstorybookをbuildせずとも,ブラウザからStorybookを確認できることによりレビューがより簡単になりました! OPTiMでは,ものづくりが大好きなエンジニアのみなさんを募集しています! www.optim.co.jp

ライセンス表記