Dockerを使った開発環境を考える - ElixirをインストールせずAtom IDEで開発をできるようにしてみる


本記録は半分くらいネタです。

要するに、elixirをインストールせずに、Atom Editorでコード支援ををうけつつコードが書いてビルドできるか試みただけです。
ちなみにうまくいきました。

# いきさつ

改めて開発環境にDockerを導入することを考えてみる。
まずは導入していく段階と目的を考えてみう。

1. Dockerさえインストールしていれば動作確認ができる
2. Dockerさえインストールしていればコードをいじって動作確認ができる
3. Dockerとエディタさえインストールしていれば、エディタから開発支援が受けられる

こんなところだろうか。
雑にいうと、elixirとかnodeとかrubyとかインストールしたくないのである。

## Dockerさえインストールしていれば動作確認ができる

開発にDockerを導入シていく上で一番簡単そうなメリットが動作確認の環境構築のハードルを下げることではないだろうか。
フロントエンドの人がサーバサイドのアプリケーションを起動するのに使ったり、マネージャが動作確認するのに使える。

実現には、Docker Imageを渡すだけですむ場合もあったり、docker-compose.ymlを渡す感じになるだろう。(環境変数の設定も必要なことがありそう)

## Dockerさえインストールしていればコードをいじって動作確認ができる

普段プロジェクトには参加していないが、ちょっと動作を変えたいということがあるかもしれない。そんなときに開発環境を整えるのは面倒である。dockerさえはいっていれば、コードをいじって反映されたアプリケーションが動くと嬉しいことがあるかもしれない。

これも実現には、docker-compose.ymlをリポジトリに用意しておけば良さそうである。
docker-compose up してもらえばよいようにすることはできるだろう。

## Dockerとエディタさえインストールしていれば、エディタから開発支援が受けられる

突然「ペアプロしようぜ！」となることがあるかもしれない。ガッツリコードを書くとなればエディタの支援が欲しいはずだ。こういう場合でもdockerさえ入っていれば、必要なpluginやパッケージをインストールするだけで済むなら便利だろう。
実際に開発するときも便利かもしれない。普通に開発環境用意しろよ感はすごい。
asdfやなんたらenvのようなツールに依存する必要がなくなるメリットがあるかもしれない。

実現を考えると、vimやemacsをdockerで動かせばいいかもしれないが、ちょっとおもしろくない。Atom EditorやVSCodeはローカルで動作させて、Language ServerをDockerで動かす方法があるのではないかと考えた。

これができると、ファイル動機の問題が残るが、Docker Hostをクラウドサービスにおいて、ハイスペックなマシンでなくとも開発できるかもしれない。

# 実現例

ということで、挑戦してみよう。
Elixirのライブラリを開発することを想定して実現してみます。

`mix`と`iex`ぐらいは実行したいです。Language Serverの起動も必要です。
コンテナを構築するのにdocker-compose.ymlを用意します。

```docker-compose-.yml
version: '3'
services:
  build:
    image: elixir
    volumes:
      - .mix:/root/.mix # mixの一時ファイルを格納
      - ~/.atom/packages/ide-elixir/elixir-ls-release:/erl_libs # language serverをide-elixir付属のものを利用する。
      - .:${PWD} # 作業用ファイルをおくところ。pathをローカルと同じにしておく
```


iexを実行するには以下の感じになります。

```
docker-compose run build sh -c "cd $PWD; iex -S mix"
```


ちょっと面倒ですね。
bin/iexに shellで置いておくと便利です。

```bin/iex
#!/bin/sh
cd $(dirname $0)/..  # カレントディレクトリがプロジェクトルートでない場合に対処するため、プロジェクトルートに移動する
docker-compose run build sh -c "cd $PWD; iex -S mix"
```

環境変数PATHに `bin`を加えることで、iex を起動できるようになります。

mixも同様にしてみましょう。

```bin/mix
#!/bin/sh
cd $(dirname $0)/..
docker-compose run build sh -c "cd $PWD; ERL_LIBS=/erl_libs:\$ERL_LIBS mix $*"
```

Language Serverは `mix elixir_ls.language_server`で動かすことになります。
language serverが見つけられるように ERL_LIBSを設定しています。
`docker-compose.yml`で設定しておきたいですが、現在のide-elixirの実装上このようにしています。(普通に使う人に影響がないように変更しないといけないので、プルリク方法は検討中/ide-elixir@0.2.3)

mixにはいろんな引数が渡ってくるので、`$*`を渡しています。

あとは`ide-elixir`をインストールしておきましょう。

```
apm install ide-elixir
```

環境変数 PATHに binを加えている状態なら、docker上のmixが利用され、elixirをインストールせずともlanguage serverを起動でき、通信することができます。
atomを起動して開発してみてください。

# まとめ

* docker-compose.ymlを作成した
* docker-compose run [image名] のラッパースクリプトを用意した
* ide-elixirをインストールした

language-serverが使えるエディタなら同じことはできそうである。

# 参考にしたもの

* __FILE__をbashでするのに参考にした https://jinim.jp/archives/571
* elixir-ls https://github.com/JakeBecker/elixir-ls
* ide-elixir https://github.com/JakeBecker/ide-elixir

# メモ

## ERL_LIBSはide-elixirに上書きされてしまう。
https://github.com/JakeBecker/ide-elixir/blob/0c478c4912209864bd6770e9a81af952fccd6c44/lib/ide-elixir.js#L35

# `mix elixir_ls.language_server`をspawnしたときのエラーや出力を確認する
https://github.com/JakeBecker/ide-elixir/blob/0c478c4912209864bd6770e9a81af952fccd6c44/lib/ide-elixir.js#L34-35
あたりに以下を挿入シて確認した。

```
const p = cp.spawn(command, ["elixir_ls.language_server"], { env: env });
p.stderr.on('data', (data) => { const d = (new TextDecoder('utf-8')).decode(data); console.log(d)})
p.stdout.on('data', (data) => { const d = (new TextDecoder('utf-8')).decode(data); console.log(d)})
```

# atom-languageclientのdebug

```
atom.config.set('core.debugLSP', true)
```

開発者ツールひらいてコンソールに入力して、atom再起動するとlanguage-serverとのやりとにが表示されるようになる。

参考 https://github.com/atom/atom-languageclient#debugging