elm-workshopをdockerで試してみたメモ

## やること

[初学者がelm-workshopを通じてElmを学ぶ][*1]をdockerで環境を作って試す。

## 動作環境

* windows10
* vagrant2.1.1
* virtualbox5.2.12
* ubuntu-18.04
* Docker version 18.05.0-ce, build f150324
* docker-compose version 1.21.2, build a133471

## elm環境作成

ディレクトリ構成は以下。

```
- bin
  - up.sh
- docker
  - elm
    - Dockerfile
    - ensure_libsysconfcpus.sh
    - replace_elm_make.sh
  - docker-compose.yml
+ elm-workshop
```

elm-workshopディレクトリは以下のようにcloneしたもの

```
git clone https://github.com/rtfeldman/elm-workshop.git --depth 1
```

## ファイル準備

```dockerfile:docker/elm/Dockerfile
FROM node:10.5.0

# Add Tini nodeはpid 1で動くようにできていないのでpid1でtiniを動かす。
# http://ngzm.hateblo.jp/entry/2017/08/22/185224
ENV TINI_VERSION v0.15.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]


# コンテナ上の作業ディレクトリ作成
WORKDIR /app

# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y

# elmインストール
RUN yarn add  elm
RUN yarn add elm-test 
RUN yarn add elm-css 
RUN yarn add elm-live

# elm 高速化
ADD ./ensure_libsysconfcpus.sh /app/ensure_libsysconfcpus.sh
ADD ./replace_elm_make.sh /app/replace_elm_make.sh

RUN /app/ensure_libsysconfcpus.sh
RUN /app/replace_elm_make.sh
```

```yaml:docker/docker-compose.yml
version: '3'
services:
  elm:
    build: ./elm
    volumes:
      - ../elm-workshop:/app
      - /app/node_modules # コンテナ内のnode_moduleをvolumesに登録
      - /app/sysconfcpus
      - cache1:/app/elm-stuff
    ports:
      - 8000:8000
    command: [yarn, start]

volumes:
  cache1: # 名前付きボリューム名
```

以下のpackage.jsonファイルをcloneしたディレクトリに追加。
docker上で動かすのでelm-liveに` --host=0.0.0.0`を追記している。

```json:elm-workshop/package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0", 
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "elm": "^0.18.0",
    "elm-css": "^0.6.1",
    "elm-live": "^2.7.5",
    "elm-test": "^0.18.12"
  }
}
```
```bash:bin/up.sh
#!/bin/bash

# このシェルスクリプトのディレクトリの絶対パスを取得。
bin_dir=$(cd $(dirname $0) && pwd)

# up.sh docker-compose.camp.yml
composeFile=${1:-"docker-compose.yml"}

# docker-composeの起動
cd $bin_dir/../docker && docker-compose -f $composeFile up
```


## 起動

 `./bin/up.sh`で起動を行う。

今回は仮想環境を192.168.50.10で動かしているので、http://192.168.50.10:8000/ にアクセス。

`You're all set!`が表示されるのを確認。
([この時点のソース](https://github.com/hibohiboo/develop/blob/023d4830a7f7c74386d5088d046b1277df3f3a23/tutorial/lesson/elm/elm-workshop/))

## part1

パート1を実行するときに、ファイルを編集してもリビルドが走らないことに気づいた。
windows + vagrant + ubuntu + dockerだとchokidarのポーリング設定をしてやらないとならないため、ファイルを修正。

### 修正ファイル

```dockerfile:docker/elm/Dockerfile
FROM node:10.5.0

# Add Tini nodeはpid 1で動くようにできていないのでpid1でtiniを動かす。
# http://ngzm.hateblo.jp/entry/2017/08/22/185224
ENV TINI_VERSION v0.15.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]


# コンテナ上の作業ディレクトリ作成
WORKDIR /app

# 後で確認出来るようにpackage.jsonを作成
RUN npm init -y

# elmインストール
RUN yarn add  elm
RUN yarn add elm-test 
RUN yarn add elm-css 
RUN yarn add elm-live

# elm 高速化
ADD ./ensure_libsysconfcpus.sh /app/ensure_libsysconfcpus.sh
ADD ./replace_elm_make.sh /app/replace_elm_make.sh

RUN /app/ensure_libsysconfcpus.sh
RUN /app/replace_elm_make.sh

# chokidarのポーリング設定
RUN sed -i -e "s/\(ignoreInitial: true,\)/\1\n    usePolling: true, /g" /app/node_modules/elm-live/source/elm-live.js

```

part1を実行するように修正。

```json:elm-workshop/package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "part1": "cd part1 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part2": "cd part2 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part3": "cd part3 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part4": "cd part4 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part5": "cd part5 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part6": "cd part6 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part7": "cd part7 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part8": "cd part8 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part9": "cd part9 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part10": "cd part10 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part11": "cd part11 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part12": "cd part12 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part13": "cd part13 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",
    "part14": "cd part14 && elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0",

    "start": "elm-package install --yes && elm-live Main.elm --open --pushstate --output=elm.js --host=0.0.0.0", 
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "elm": "^0.18.0",
    "elm-css": "^0.6.1",
    "elm-live": "^2.7.5",
    "elm-test": "^0.18.12"
  }
}
```


```yaml:docker/docker-compose.yml
version: '3'
services:
  elm:
    build: ./elm
    volumes:
      - ../elm-workshop:/app
      - /app/node_modules # コンテナ内のnode_moduleをvolumesに登録
      - /app/sysconfcpus
      - cache1:/app/elm-stuff
    ports:
      - 8000:8000
    command: [yarn, part1]

volumes:
  cache1: # 名前付きボリューム名
```

### 追加ファイル

コンテナの作り直しをするために以下のファイルを追加。

```bash:bin/remove_all_container.sh
#!/bin/bash
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
```

```bash:bin/container_build.sh
#!/bin/bash
bin_dir=$(cd $(dirname $0) && pwd)
cd $bin_dir/../docker && docker-compose build
```

コンテナを作り直した後、再度upしてURLを確認する。
この状態で、ファイルを編集するとリビルドがされる。
ただし、ブラウザのオートリロードはない(※あった。後述する)ようだったので、そこは手動で更新してやる必要がある。

([この時点のソース](https://github.com/hibohiboo/develop/blob/7be7c26dada84745fc342c8a52feb3e5a51990a9/tutorial/lesson/elm/elm-workshop/))

## オートリロードの設定

ブラウザのコンソールを確認したところ、`//0.0.0.0:35729/livereload.js?snipver=1`へのアクセスが404となっていた。
windowsで0.0.0.0は使えない模様。
今回は`192.168.50.10`で仮想環境を作成してあるので、そちらに向かせればうまくいくはず。
以下でホストを直接書き換える

```dockerfile:docker/elm/Dockerfile
# 省略

# chokidarのポーリング設定
RUN sed -i -e "s/\(ignoreInitial: true,\)/\1\n    usePolling: true, /g" /app/node_modules/elm-live/source/elm-live.js

# 追記： livereloadのホストが0.0.0.0だとwindowsではうまく動作しない
RUN sed -i -e "s/if (live.host) liveInjector.host = live.host/liveInjector.host ='192.168.50.10'/g" /app/node_modules/budo/lib/middleware.js

```

```yaml:docker/docker-compose.yml
version: '3'
services:
  elm:
    build: ./elm
    volumes:
      - ../elm-workshop:/app
      - /app/node_modules # コンテナ内のnode_moduleをvolumesに登録
      - /app/sysconfcpus
      - cache1:/app/elm-stuff
    ports:
      - 8000:8000
      - 35729:35729 # 追記: autoreload用のポート
    command: [yarn, part1]

volumes:
  cache1: # 名前付きボリューム名
```


([この時点のソース](https://github.com/hibohiboo/develop/blob/e5210257ecb8786d996a27afc69c7b12d6a63f5b/tutorial/lesson/elm/elm-workshop/))

## 参考
[初学者がelm-workshopを通じてElmを学ぶ][*1]
[work-shop][*2]
[solution][*3]
[drill][*4]

[*1]:https://qiita.com/akameco/items/a4b848c89ec7957d24be
[*2]:https://github.com/rtfeldman/elm-workshop
[*3]:https://github.com/dwyl/elm-workshop-tutorial-solutions
[*4]:https://github.com/jinjor/elm-drill