あいつの日誌β

働きながら旅しています。

クーリエ・ジャポン・ハッカソンに行ってきた

f:id:okamuuu:20170319155613j:plain

あらすじ

3/18(土)と3/19(日)に人生初のハッカソンに行ってきました。

http://courrierjapon024.peatix.com/courrierjapon024.peatix.com

初日

初日は以下のスケジュールで進行しました。

初日午前: アイデアソン

f:id:okamuuu:20170319151303j:plain

最初に仮チームへの振り分けがされます。進行にしたがってチーム内で簡単なゲームをこなして行きます。ゲームが進むうちにだんだんとクーリエの課題をどうにかしようというブレーンストーミングになってきて、いつのまにやらアイデアソンが開始されていました。

最終的にはクーリエジャポンが抱えている問題点を参加者全員が共有して、各自で気になる問題点に対して改善案を考えてA4用紙に書き出して行きます。

参加者全員が個人としてプレゼンを行います。

初日午前: 個人のプレゼンタイム

先ほど考えたA4の企画書を全員で見回って良いと思った改善案に対して☆印をつけて行きます。☆が多いほど優先的にプレゼンを行う権利を得られます。このプレゼンをする側はこの時にチームに欲しい人物像(エンジニアだったら企画が得意な人、デザイナーだったらエンジニア募集など)と一緒に発表するので、プレゼンを聞いている側は自分にマッチするチームに各自で加わっていきます。

私は改善案として3つほど書き出したのですが「あとで読む機能を追加する」という改善案に星が5個ついただけだったのプレゼンせず。他にも類似した改善案でインターフェースを工夫した案があったのでそのチームに参加しました。

ここからハッカソンが開始されます。

初日午後: ハッカソン

軽く打ち合わせして早速手を動かすことにしました。どういうわけかこのチーム4人中4人サーバーサイドエンジニアが集まりました。私はとにかく手を動かしたかったので実装を担当させてもらいました。率先する人から役割が決まります。

今回は実装メインだったので次回は違う役割でチームに貢献したいと思います。

中間発表ができる段階まで画面をつくります。あとは画面をインタラクティブにするなりデザインを考える作業を翌日に行う予定でこの日は実装終了

初日午後: 中間発表

用事があったので私は早退しました。経過報告については翌日他のメンバーに聞く事にし会場を後に。

2日目

2日目は以下のスケジュールで進行しました。

私は前日の中間発表を聞いていなかったのでチームメイトからヒアリング ほとんどのチームが実装ができてないという報告を受けたのですが、1チームだけTinder のようにクーリエの写真を見て見たい記事を選別するといったアプリを作ったチームがいたそうで、かなりキャッチーなものだったそうです。

中間発表に参加したメンバーから現時点の実装ではプレゼン力が弱いという事で画面の動線を追加してほしいという依頼があったので、この日はその作業を起こってから画面をかっこよくする作業をする事にしました。

2日目午後

午前中で画面を追加する作業が完了したのであとはデザインを何とかしたいです。なんですがこのチームは全員サーバーサイドエンジニア。デザインについて詳しいメンバーが一人もいません。そのことを運営側に相談し、クーリエジャポンのデザイナーさんに直接画面をみてもらって意見をもらいました。このフィードバックを反映し、これで実装は完了です。結構時間が足りないなあと思いました。実際支給されたお弁当をみんな食べる時間がなかったようです。

f:id:okamuuu:20170319151035j:plain

プレゼン

各チーム5分間のプレゼンを行った後、審査員からの質疑応答に5分間の時間が割り当てられます。以下私が記憶している限りのメモですので間違っている箇所があると思いますがご容赦ください。

チームH: 夢のイベント実現 カラダで選ぶNEWS

発表者は普段から Kinect を使っているらしく、デバイスに近づくとスクリーンに文字が投影され、最後にジャンプするとクーリエのHPへ画面が切り替わるデモンストレーションが行われました。プレゼン終了後は質疑応答、審査員からは「普通のブラウジングではなく kinect を使う優位性は?」という質問に対してはクーリエジャポンの会員は新しいモノに敏感な層であるので kinect を使う事自体でキャッチーになるのではないか。という事をおっしゃってました。 分程度で滞在2,3時間だと思います。

チームA: 月間編集者Ranking機能

雑誌ではなく WEB ならではの機能として人気のある編集者を投票する機能を提案。Like であったり Follow であったり。

質疑応答ではランキングの定義について議論がされていました。ランキングという名目にすると何を持って優劣なのか、という話になるのでまあ難しい議題ではあるのでなかなか全員を満足する解が見つけづらいという話ではあります。

それから実はクーリエジャポンは編集者が8名との事です。

チームB: クーリエジャポンが持っていた紙の読書体験を再現する

チーム内に雑誌時代のクーリエジャポン愛読者がいたそうで、その方の意見を取り入れてスマホ用のビューアーを作成していました。

質疑応答で、紙の良さを再現するのは良いアイデアだと思いますが、扉以外はどのようにすると良いかアイデアはありますか?という具体的な質問がありました。紙から WEB へ移行したクーリエにとっては命題ともなっているテーマだったので質疑応答が活発に行われました。

クーリエジャポンとしては雑誌は定期購買、 WEB はいつでも更新可能。紙とWEB の良さをまとめるアイデアが欲しいんだろうなあと思いました。

チームC: from 国での配信最適化

ユーザーが興味を持つ記事を検索しやすくする、という課題に対して、興味がある国を選択する機能を提案していました。

質疑応答では審査員からトランプ氏がアフリカに、国内の美術館にオランダのといった場合はどの国にで起きた出来事になるのか?という質問がありました。

これは読者次第によるので結構判断難しいとは思いますが、編集者側から「我々で定義する事は可能」との事でした。公開されている記事を全て読む必要はないのだけれど関心がある国の記事は全て読みたいという読者もいると思うので良いアイデアだと思いました。

チームD: カード型レイアウト

WEB らしいレイアウトを提案。読みたい記事をフィルタリング、並び替えを行えるようにしていました。個人的にはよくできたデザインだったのでそのまま差し替えてもいいような気がしましたが審査員側からは色々な機能があり、なんの問題を解決したかったのかが伝わっていないようでした。

チームE: 有料記事の一定期間シェア機能

Facebook などで有料記事を行って期間シェアする機能を提案。審査員のうちの一人がずいぶんと Facebook における承認欲求のメカニズムに関心があったようでかなり好感触のプレゼンでした

チームF: 一度読んだ記事を見つけやすくする仕組み

我々は問題点として使いづらいから使いやすくしましょうというごく真っ当な改善案を提出しました。まあ割とありきたりの機能なのでだれもが予想のつく機能でした。残念ながら審査員の反応が芳しくありませんでした。

チームH: Tinder のように写真を選ぶサービス

クーリエジャポンは写真がきれいです。その写真だけで興味があるかどうかを選んで取捨選択する仕組みを提案。WEB ならではのサービスということでとても好評のサービスでした。

結果発表

編集長より結果発表。審査の基準は色々あり、審査員側でも紙媒体の延長で行くのかWEBならではで行くの議論が別れたそうです。

というわけで結果は以下の通り

懇親会

参加せず

Hackathon で役に立つ技術

役に立った知識と、役に立つであろう知識を紹介します。

json-server

Single Page Application と json-server は Hackathon では強力な武器になると思いました。反面 Node.js の使い手が少ない為他のメンバーの負担が増えてしまい申し訳なかったです。

絵心

初日の午前に参加者全員がA4でのプレゼンを行ったのですが、やはりデザイナーさんとそうでない人ととでは見栄えが全く違いました。やっぱ絵心大事すねえ。

Line API

今回参加しなかったのですがオススメの記事の要約を Line Bot が教えてくれるという企画があり、すごく興味がありました。なんとなくですが Hackathon では頻出アイデアな気がするので実装経験ある人は戦力になるかも。

BitBucket

ソースコードを共有する方法として無料で team と private な repository を作れるのでとても便利です。

感想

所属したチームがなぜか全員サーバーサイドエンジニアになってしまい、フロントエンドができるのが私だけだったので私は実装を担当しました。そんなわけで React.js で SPA する事にしました。

とはいえ機能を実装する事はできましたが情報を伝えたり楽しませたりする能力が足りないなと感じました。今後はフロントエンド側もやる事が増えるのでまだまだ修行が山積みだなと思いました。

あとは他人のアイデアに触れてきた事が良い経験でした。うまく言えないのですが発想力を分けてもらえた気がします。たぶんこの感覚は参加してみないとわからないかも。

それからプレゼンを見て思ったのは実装が足りなくても伝える事はできるなと感じました。もし営業担当から「この機能がないと売りづらい」と言われて実装してあげるのも優しさだと思いますが、「いや、あなただったら上手く説明できる。他に大事な実装があるから私はそっちに注力します」とつっぱねるのもやっぱり大事なんだなと思いました。タイムイスマネー。

色々と自分自身に足りないものが見つかって補完がされた2日間でした。なのでハッカソンに出場した事ない人は参加すると良いとおもいます。

というわけで今回フロントエンドとして参加したわけですがデザインだったりインタラクティブなところだったりとかちゃんと勉強しないとだめだなあと思いました。もうしばらく勉強の日々が続きそうです。

ソースコード

今回使用したデモアプリはこちらにおいてあります。

github.com

Ansible での provisioning を継続的にインテグレーションしたいので circleci で Docker を使う

あらすじ

Provisioning を継続インテグレーションしたくなったのですが、そんな時は CircleCI で Docker を使えばいいんじゃないかと思いました。

コンテナ内で ServerSpec を実行する方法などがあると思いますが、ここではコンテナ内に SSHD を使って外部からプロビジョンします。

テストに関しては Serverspec などがあると思いますが、ここではシンプルに 外部から SSH でコマンドを実行して結果の確認をするだけにします。

ソースコード

ここで紹介しているソースコードは下記の置いてあります。

github.com

準備

mkdir circleci-docker-provisioning && cd $_
mkdir -p keys provisioning/inventory provisioning/roles/nodejs/tasks
touch .gitignore Makefile Dockerfile circle.yml 
touch provisioning/playbook.yml provisioning/inventory/docker provisioning/roles/nodejs/tasks/main.yml keys/.gitkeep

keys/* に公開鍵と秘密鍵を保存します。あくまで CI 用の鍵なので git で管理しても問題無いのですが、万が一これを見て秘密鍵github.com で公開している事を心配する人がいるかもしれないので一応このようにします。

create .gitignore

keys/*
!.gitkeep

とりあえず commit します。

git init && git add . && git commit -m "initial commit"

秘密鍵と公開鍵を作成

パスワード無しで作成します。keys/docker_id_rsakeys/docker_id_rsa.pub が生成されます。

ssh-keygen -P "" -f keys/docker_id_rsa

Dockerfile

通常は Docker コンテナーに SSHD を入れないのですがここでは SSHD でのプロビジョニングを行いたいのでこのようにしています。

Docker に ansible をマウントして Ansible を local connection してテストする方法もありますが今回はこのようにしています。

create Dockerfile

FROM ubuntu:16.04

RUN apt-get update && apt-get install -y sudo openssh-server python
RUN mkdir -p /var/run/sshd

RUN useradd -m -d /home/docker -s /bin/bash docker
RUN echo "docker:docker" | chpasswd
RUN mkdir /home/docker/.ssh
RUN chmod 700 /home/docker/.ssh
COPY ./keys/docker_id_rsa.pub /home/docker/.ssh/authorized_keys
RUN chmod 600 /home/docker/.ssh/authorized_keys
RUN chown -R docker:docker /home/docker/.ssh

RUN echo "docker ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

EXPOSE 22
CMD ["/usr/sbin/sshd", "-D"]

Build

docker build -t provisioning_sshd .

Run

docker run -d -p 40122:22 --name test_sshd provisioning_sshd

ssh で接続確認をします。

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 

Ansible

とりあえず Node.js をインストールしてみます。

create provisioning/inventory/docker

docker ansible_user=docker ansible_port=40122 ansible_host=localhost ansible_ssh_private_key_file=./keys/docker_id_rsa

create provisioning/roles/nodejs/tasks/main.yml

---
- name: install Node.js
  become: yes 
  apt: name={{ item }} state=installed
  with_items:
    - nodejs
    - npm

- name: nmp cache clean
  command: npm cache clean

- name: install n command
  become: yes 
  command: npm install n -g

- name: install node v6.10.0
  become: yes 
  command: n 6.10.0

create provisioning/playbook.yml

---
- hosts: docker
  become: true
  roles:
    - nodejs

provision 開始

ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

動作確認

ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'
v6.10.0

これを以下のコマンドでテストします。status code を echo $? で確認します。 0であればOKです。

test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
echo $? 

Makefile

以下のような Makefile を作っておきます。

docker.keygen:
    ssh-keygen -P "" -f keys/docker_id_rsa

docker.build:
    docker build -t provisioning_sshd .

docker.start:
    docker run -d -p 40122:22 --name test_sshd provisioning_sshd

docker.stop:
    ssh-keygen -R "[localhost]:40122"
    docker rm -f `docker ps -aq`

docker.provision:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker

docker.provision.ci:
    ansible-playbook ./provisioning/playbook.yml -i ./provisioning/inventory/docker --private-key=./keys/docker_id_rsa

docker.test:
    test `ssh docker@localhost -p 40122 -i keys/docker_id_rsa 'node -v'` = "v6.10.0"
    @echo "It looks like optimal"

CircleCI

CircleCI 上ではホストOS上で Docker を使います。Circle CI のホストOS上で Python と Ansible を install して、コンテナに対して SSH 接続を行います。

create circle.yml

machine:
  python:
    version: 2.7.10
  services:
    - docker

dependencies:
  pre:
    - pip install ansible
  override:
    - docker info
    - make docker.keygen
    - docker build -t provisioning_sshd .

test:
  override:
    - docker run -d -p 40122:22 provisioning_sshd
    - make docker.provision.ci
    - make docker.test

最終チェック

これらをコミットして実際に Circle Ci が動作する事を確認します。

まとめ

というわけで無事 CircleCI 上で Docker のコンテナに対して provisioning を行う事ができました。

サーバーサイドエンジニアがインフラエンジニアに代わってどれだけ作業すべきなのかは良く分からないのですが、インフラエンジニアにソースコード渡す場合はきちんと動作チェックをするのが礼儀だと思うのでこういうやり方があるという事は覚えておいたほうがいいなあと思いました。おしまい。

okamuuu.com を Gatsby と Netlify に移行しました

f:id:okamuuu:20170306115548p:plain

Gatsby x Netlify

netlify.com github.com

あらすじ

先日まで okamuuu.com を Google App Engine 上で Go と React.js で運用していましたが github.com の gh-pages に移動しました。Google App Engine を使う案件にアサインされていたので練習のために運用しようと思ったのですが案件が終了したのでとりあえずお役目御免にしました。

あと、諸事情で okamuuu.com から www.okamuuu.com へ変えました。

Gatsby

日本人男性だと Gatsby は整髪料のアレを連想すると思いますが Static Generator です。markdown でブログを書くアレだと言えば話は早いのですが、類似のツールと違う点として Template Engine が React であるということです。

xxxx.md といった makrdown あるいは xxxx.js といった React Components をそのまま記事にする事ができるので、割と自由にカスタマイズをする事ができます。最近 React にはまっていて、オレオレ React Components を作りたい今日この頃の私にとっては涎が出るような Static Generator です

Just Do it

gatsby のインストールとプロジェクトの作成。プロジェクトの作成にやや時間がかかります。

npm install -g gatsby
gatsby new okamuuu.com2 https://github.com/gatsbyjs/gatsby-starter-blog

開発サーバーを起動

cd okamuuu.com2
gatsby develop

open "http://localhost:8000" で画面を確認します。

記事一覧表示を日付の新しい順にする

pages/index.js を以下のように修正します。

 import React from 'react'
 import { Link } from 'react-router'
 import sortBy from 'lodash/sortBy'
+import reverse from 'lodash/reverse'
 import get from 'lodash/get'
 import { prefixLink } from 'gatsby-helpers'
 import { rhythm } from 'utils/typography'
@@ -12,7 +13,7 @@ import Bio from 'components/Bio'
 class BlogIndex extends React.Component {
   render () {
     // Sort pages.
-    const sortedPages = sortBy(this.props.route.pages, 'data.date')
+    const sortedPages = reverse(sortBy(this.props.route.pages, 'data.date'))
     // Posts are those with md extension that are not 404 pages OR have a date (meaning they're a react component post).
     const isPublished = page => (get(page, "data.isPublished"))
     const visiblePages = sortedPages.filter(isPublished).filter(page => (

記事に下書き機能を追加する

pages/index.js を以下のように修正します。

@@ -14,7 +14,8 @@ class BlogIndex extends React.Component {
     // Sort pages.
     const sortedPages = sortBy(this.props.route.pages, 'data.date')
     // Posts are those with md extension that are not 404 pages OR have a date (meaning they're a react component post).
-    const visiblePages = sortedPages.filter(page => (
+    const isPublished = page => (get(page, "data.isPublished"))
+    const visiblePages = sortedPages.filter(isPublished).filter(page => (
       get(page, 'file.ext') === 'md' && !include(page.path, '/404') || get(page, 'data.date')
     ))

記事を追加

すでに作成されている記事をコピーします。

mkdir pages/articles
cp pages/2015-05-01-hello-world/index.md pages/articles/index.md

記事の冒頭を以下のように修正します。本文は適宜変更してください

 ---
-title: Hello World
-date: "2015-05-01T22:12:03.284Z"
-readNext: "/my-second-post/"
-path: "/hello-world/"
+title: "Using Bootstrap4 with React"
+date: "2016-12-25T00:00:00.284Z"
+isPublished: true
+path: "/articles/using-bootstrap4-with-react/"

config を修正

-blogTitle = "My Awesome Blog"
-authorName = "Kyle Mathews"
-linkPrefix = "/gatsby-starter-blog"
+blogTitle = "okamuuu.com"
+authorName = "okamuuu"
+linkPrefix = ""

Bio component を修正

Profile 画像を修正します。ここでは Gravatar を使います。

npm install --save

Netlify へアップロード

もしくは CLI を使ってデプロイも可能です。その場合は gatsby build であらかじめ静的ファイルを作成しておく必要があります。

npm install netlify-cli -g
gatsby build
netlify deploy
? No site id specified, create a new site Yes
? Path to deploy? (current dir) public
Deploying folder: public

注意点としては netlify deploy で対話モードが始まるのでデプロイするディレクトリを public に指定します。 このコマンドが実行されると設定ファイル .netlify が生成されて、以後 netlify update を実行するだけで build したファイルが upload されます。

もしくは Github と repository を Linking する方法もあります。 master branch への upload がトリガーになって Netlify 上で gatsby build が実行されます。

f:id:okamuuu:20170306115659p:plain

まとめ

静的ファイルの配信であれば Github page や S3 といった選択肢ががあるとおもいますが Netlify は無料のSSL/HTTPS が提供されているところが今の所、頭ひとつ出ている感がします。

Gastby はテキスト主体の記事は markdown で記述して、動的な処理を埋め込みたい時に Javascript(React Component) で記事を記述する事ができるので React.js を使っているエンジニアにとってはとても面白い CMS だと思います。

おしまい

完全栄養食パスタ BASE PASTA を調理してみた

2017-03-04 追記あり

懸念点として茹でた栄養素について記述したところコメントを頂いたので追記しました。

f:id:okamuuu:20170301154328j:plain

あらすじ

実は以前から完全栄養食に関心があったので早速 Amazon.jp で注文してみましたので食感をレポートしました。

BASE PASTAパスタとは

厚生労働省が定めた「日本人の食事摂取基準(2015年版)」に基いて、1食に必要な栄養素をすべて賄えるように作られたパスタです。

完全栄養食とは

人間が生きていくために必要な栄養素がすべて入った食品のことです。ソイレントと呼ばれるミルクのような流動食などがそうであると言われています。私は完全栄養食の概念は Geek House のブログで知りました。完全栄養食を自作するなんてとてもギークですね。

それはさておき、栄養素は一要素でも不足すると不健康になりやすいという「生命の鎖」理論というものがあるので以前から食生活に取り入れたいと思っていました。

生命の鎖理論とは

世界的に有名な栄養学者であるロジャー・ウィリアムス博士によると、生命を維持するには必須となる栄養素が存在し、それらのうち1つが水準以下となれば不健康や病気となる、という 46種類だと言われています。

内海 聡氏の facebook から引用:

ロジャー・ウィリアム博士によると、生命の鎖を構成する栄養素は九種類の必須アミノ酸、一六種類のミネラル、二〇種類のビタミンなどだが、これらのどれにも絶対必要最低水準というものがあり、どの一つがその水準以下になっても不健康となったり、病気となると提唱する。

簡単にいうとまんべんなく栄養とりましょう。という理論です。

完全栄養食を食べたら後は何も要らないのか?

そもそも人間が生きていくために必要な栄養素が入っていてもそれを100%体内で吸収できるかどうかは別問題だと思います。 なのですが、生命の鎖理論のように、各栄養素は単体では働かない、ということを鑑みると、栄養のとりこぼしを防ぐために完全栄養食を選ぶという選択肢が増えたことはとても良いことなんじゃないかと思います。

調理してみた

お皿に取り出したところです。生麺です。美味しそうですね。実際に麺は割と美味しかったです。

f:id:okamuuu:20170301154902j:plain

パスタソースはこちらを使いました。生麺とは相性よくないのかな?まあインスタントですからねえ。

f:id:okamuuu:20170301154514j:plain

はい、できました。

f:id:okamuuu:20170301155243j:plain

懸念点

表記されている栄養価は茹でる前であって茹でた後はどうなるのかわからない。

f:id:okamuuu:20170301154242j:plain

追記: 2017-03-04

茹でた際の栄養素に関しては以下のコメントを頂きました。ありがとうございます。

まとめ

割と美味しいですし、栄養価が高いので悪い印象はなかったです。 茹で汁に栄養素が流れていると思うのでなにか有効活用できればいい感じになるかも。できるのかな?

日頃から常用するとなるとちょっとお値段が高いのかもしれません。マルチビタミンマルチミネラルサプリメントとるほうがコスパはいいと思います。

逆に言い換えるとサプリメントで栄養をとるより食事でとりたい人にとってはとてもいいものになるんじゃないかなあと思います。おしまい。

Amazon アソシエイツ

参考URL

ES2015 でとまどいがちな記法 Object initializer と Spread Operator

あらすじ

ES6 の記法を全て覚えてから ES6 を書くよりも、書きながら勉強したほうが効率が良かったりします。 なんですが写経している時に分からない記法が出てきた場合検索しづらくて困ると思います。

というわけで私がどうやって Google に質問すればいいのか分からなかった記法を2つ紹介します。

Object initializer: オブジェクト初期化子

以下のような ES5 の記法が

const a = 1
const b = 2 
const c = 3
const object = {a: a, b: b, c: c}

ES6 では以下のように省略できます。

const a = 1
const b = 2 
const c = 3
const object = {a, b, c}

また、以下のような ES5 の記法が

function x(obj) {
  const a = obj.a
  const b = obj.b
  console.log(a, b)
}

const a = 1 
const b = 2 
x({a: a, b: b})

ES6 では以下のように省略できます。

function x({a, b}) {
  console.log(a, b)
}

const a = 1 
const b = 2 
x({a, b}) 

この書き方は最初慣れないと気持ち悪いですが使っているうちにだんだん気持ちよくなります。

Spread Operator: スプレッド演算子

...array とか ...object を初めて見た人は多分意味が分からなくて困ると思います。

array を spread する

ES5 では以下のように記述していたものが

function f(array) {
  console.log([array[0], array[1], array[2], 4, 5])
}

f([1, 2, 3])

ES6 では以下のように省略できます。

function f(array) {
  console.log([...array, 4, 5])
}

f([1, 2, 3])

object を spread する

ES5 では以下のように記述していたものが

function f(obj) {
  const a = obj.a
  const b = obj.b
  const c = obj.c
  console.log({a: a, b: b, c: c, d: 4, e: 5}) 
}

const a = 1 
const b = 2 
const c = 3 
f({a: a, b: b, c: c}) 

ES6 では以下のように省略できます。

function f(obj) {
  console.log({...obj, d: 4, e: 5}) 
}

const a = 1 
const b = 2 
const c = 3 
f({a, b, c})

npm publish を念頭において React Components 用のプロジェクトを作成する

あらすじ

先日 react-paginators という React components な module を publish しました。

github.com

その時に色々調べた事があるので備忘録として残します。

作成手順

create-react-appstorybook を使ってプロジェクトの雛形を作成します。create-react-app を使うと webpack の事を考えなくて良いのでとても楽でよいです。

create-react-app react-xxxxx && cd $_
getstorybook

必要なディレクトリとファイルを作成します

mkdir test
touch .envrc .babelrc .npmignore .travis.yml CHANGELOG.md test/xxxxx.test.js 
npm install --saave react react-dom
npm install --save-dev babel babel-cli babel-preset-es2015 babel-preset-react babel-preset-stage-0
npm install --save-dev assert eslint eslint-plugin-react rimraf
npm install --save-dev in-publish safe-publish-latest

create CHANGELOG.md

# Change Log

## v0.1.0
 - Initial commit

そういえば $_ って何ですか?と聞かれる事があったのですがこれは前回使用したコマンドの最後の引数が格納される変数です。 上記のコマンドだと create-react-app react-xxxxxreact-xxxxx となります。

npm run test の環境を整える

雛形ができたら npm test の環境を整えます

create test/xxxxx.test.js

import assert from 'assert'

describe('XXX', function() {
  it('should ...', () => {
    assert.ok(true)
  })  
})

動作確認します。雛形として生成された src/App.test.js と test/xxxxx.test.js が実行されるのを確認します。 ちなみに環境変数 CI=true を忘れると launch mode で起動します

CI=true npm test

> react-xxxxx@0.1.0 test /Users/okamuuu/react-xxxxx
> react-scripts test --env=jsdom

 PASS  src/App.test.js
 PASS  test/xxxxx.test.js

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.167s
Ran all test suites.

src/App.test.js は不要となった時点で適宜 remove してください

npm run version の環境を整える

check-changelog と check-only-changelog-changed

airbnb が公開している react-dates の真似をします。

https://github.com/airbnb/react-dates

github.com

彼らは version コマンドを実行する時は CHANGELOG.md 以外の差分が無い状態でしか実行できないようにしています。 npm-scripts に check-changelogcheck-only-changelog-changed を追加します。

edit package.json:

--- a/package.json
+++ b/package.json
     "start": "react-scripts start",
     "build": "react-scripts build",
     "test": "react-scripts test --env=jsdom",
-    "eject": "react-scripts eject"
+    "eject": "react-scripts eject",
+    "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",
+    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0"
   }
 }

以下のような使い方を想定しいています。

CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed

上記のコマンドは以下の状態でないとエラーが発生するので version up の際に一貫性が生まれます。

git status
  modified:   CHANGELOG.md

postversion, version, preversion を準備

npm version をそのまま使うと Git working directory not clean. と怒られてしまうので --no-git-tag-version オプションをつけて実行します。 package.jsonnpm run version:patch, npm run version:minor, npm run version:major を用意します。

preversion に先ほど準備した test の実行と CHNAGELOG.md のチェックを行います。 postversion で package.json(verson が変更されている) と CHANGELOG.md をcommit します。コミットも自動的に行います。git tag でタグを切り、その tag も push します。

edit package.json

--- a/package.json
+++ b/package.json
     "test": "react-scripts test --env=jsdom",
     "eject": "react-scripts eject",
     "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",
-    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0"
+    "check-only-changelog-changed": "(expr $(git status --porcelain 2>/dev/null| grep -v \"CHANGELOG.md\" | wc -l) >/dev/null && echo 'Only CHANGELOG.md may have uncommitted changes' && exit 1) || exit 0",
+    "tag": "git tag v$npm_package_version",
+    "version:patch": "npm --no-git-tag-version version patch",
+    "version:minor": "npm --no-git-tag-version version minor",
+    "version:major": "npm --no-git-tag-version version major",
+    "preversion": "CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed",
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags"
   }
 }

後ほど postversion の最後に npm publish を追加しますす

gh-pages へのデプロイを準備する

react の component を公開するので実際に見た目がどのように表示されるのかを皆さんに伝える必要があります。 gh-pages に static page をデプロイする方法は色々ありますが、@kadira/storybook-deployer を install するだけで実現できます。

npm install --save-dev @kadira/storybook-deployer

edit package.json

     "version:major": "npm --no-git-tag-version version major",
     "preversion": "CI=true npm run test && npm run check-changelog && npm run check-only-changelog-changed",
-     "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag"
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag",
+    "deploy-storybook": "storybook-to-ghpages"
  }
}

npm run deploy-storybook を実行して gh-pages を確認します。

npm run build

src/ 以下に ES6 で書かれた Javascript を lib/ 以下に ES5 に変換したものを配置します。 npm publish しないのであれば不要な作業ですが、現時点では node_modules/ 配下に install するモジュールは es5 が前提だと思いますのでその習慣に従います。

babel コマンドを実行します。もしかしたら react-scripts が bundle しない 6to5 なコマンドを用意しているのかもしれないのですがよくわからないので自前で babelrc を用意します。

create .babelrc

{
  "presets": ["react", "es2015", "stage-0"]
}

以下を実行し、lib 以下に es5 のコードが生成されることを確認します。

$(npm bin)/babel -d lib/ src/

lib 配下は git で管理する必要が無いので .gitignore に追加します

echo "/lib/" >> .gitignore

また lib 配下は npm publish で使用されるので build 直前で clearn にするようにしておきます。 そして main, files も同様に npm publish される前提の修正を行います。

edit package.json

--- a/package.json
+++ b/package.json
-  "private": true,
+  "license": "MIT",
+  "main": "lib/index.js",
+  "files": "lib",
+  "author": "okamuuu<okamuuu@gmail.com>",
   "devDependencies": {
     "@kadira/storybook": "^2.21.0",
     "@kadira/storybook-deployer": "^1.2.0",
   },
   ...
   "scripts": {
     "start": "react-scripts start",
-    "build": "react-scripts build",
+    "build": "rimraf lib && babel -d lib/ src/",
     "test": "react-scripts test --env=jsdom",
     "eject": "react-scripts eject",
     "check-changelog": "expr $(git status --porcelain 2>/dev/null| grep \"^\\s*M.*CHANGELOG.md\" | wc -l) >/dev/null || (echo 'Please edit CHANGELOG.md' && exit 1)",

npm run publish

最後に publish の設定を行います。

postversion のタイミングで npm publish が実行されるようにします。 npm prepublish は、npm install 時に実行してしまいます。npm run buildnpm install の時に実行しないようにします。 それから npm publish が成功した場合は gh-pages の更新を行うようにします

-    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag",
+    "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && git push && git push --tags && npm publish --registry=https://registry.npmjs.org/",
...
+    "build-storybook": "build-storybook -s public",
+    "prepublish": "in-publish && safe-publish-latest && npm run build || not-in-publish",
+    "postpublish": "npm run deploy-storybook"
   }

package されるファイルを確認する

npm publish --dryrun のようなコマンドを実行したいのですが現時点では存在しないようです:(

代わりに npm pack を利用します。 http://qiita.com/inuscript/items/5b3c1466a6ddb9ba6231

実際に package されるファイルを確認するコマンドは下記のとおりです。 npm pack -s はこの記事の通りに設定していると react-xxxxx-0.1.X.tgz という標準出力を一行だけ吐き出します。 npm buildprebuild, postbuild の設定にもよると思うのでもし同じ状況にならなかったら各自修正をお願いします。

tar -tf $(npm pack -s) && $(npm bin)/rimraf $_

一連の作業をコマンド化します。npm run build で lib 配下を最新版にし、$(npm pack -s) で packing とファイル名を取得し、それに含まれるファイル一覧を取得します。ついでに不要となった tgz ファイルも削除しましょう。 再び $_ が登場しますが、これは最後に実行したコマンドが tar -tf ARG なので tgz ファイルを指しています。

+    "build:test": "npm run build >/dev/null && tar -tf $(npm pack -s) && rimraf $_",

このコマンドで視認しながら .npmignore に不要なファイルを追加していきます。

.babelrc
.envrc
.storybook
.travis.yml
stories
src
test
public

期待しているファイルが publish されることを確認します。

% npm run build:test

> react-xxxxx@0.1.3 build:test /Users/okamuuu/react-xxxxx
> tar -tf $(npm pack -s) && rimraf $_

package/package.json
package/.npmignore
package/README.md
package/lib/App.js
package/lib/App.test.js
package/lib/index.js
package/CHANGELOG.md

.travis.yml

手元の環境では動くのだけどもデプロイすると動かなかったりしますので CI 環境を用意します。 ここでは travis.yml を使っていますが CircleCI でも Wercker でも OK だと思います。

注意点として create-react-app で作成したプロジェクトの test は環境変数に CI=true をセットしていない場合は launch します。

create .travis.yml

language: node_js
node_js:
  - "6"
  - "4"
env:
  CI: true
sudo: false

npm publish

最後に npm publish を実行します。無事成功したら最後に package.json を以下の通りに修正します。

- "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && gitpush && git push --tags",
+ "postversion": "git commit package.json CHANGELOG.md -m \"Version $npm_package_version\" && npm run tag && gitpush && git push --tags && npm publish",

おしまい