Docker×Git×VSCodeを使ってElixirの開発を丁寧に始める

# 環境

- Windows 10 Pro
- Docker for Windows
    - Docker Engin (20.10.2)
    - docker-compose (1.27.4)
- Git for Windows (2.30.0.windows.2)
- Visual Studio Code (1.52.1)
    - 拡張機能
        - Docker
        - Japanese Language Pack for Visual Studio Code
        - Remote - Containers
        - ElixirLS

それぞれのインストールについては、[Docker×Git×VSCodeの環境構築は多分これが一番やさしい説明だと思います](https://qiita.com/Gsann/items/bc5b0b3bf31c55f8b8ec)を参照。

# やりたいことと注意点

## やりたいこと

1. DockerとVSCodeでElixirの開発環境を作る
2. mixでElixirプロジェクトを作って動作確認する
3. GitHubと連携してバージョン管理を行う

本記事では1,2,3の順番で説明。

## 注意点

本記事を読む際は以下の点に注意されたし。

- 環境構築の手順とそのハマりポイントについて記載しているので、DockerやElixirなどについての詳しい記載を避けている
- 画面のキャプチャを張り付けて説明している。アプリや拡張機能の仕様変更により記事掲載時と画面の内容が異なることがある
- 本記事の手順は上記環境欄で記載した環境を用意していることを前提とする
- 記事が長い

# STEP1 DockerとVSCodeでElixirの開発環境を作る

1．VSCodeを起動して[ファイル]->[フォルダーを開く]を押下

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/a9e3745d-3675-38ab-ac81-625ff66778a8.png)

　
2. 任意のフォルダを作成して、フォルダーの選択を押下（今回は「elixir_intro」のフォルダを作成）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/85ce20d4-6257-0b59-f41c-d3f42ee798bf.png)
　
3. エクスプローラーのところで右クリック->新しいファイルをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/98abe6b8-1b91-394a-c54f-f2e3f424c1b9.png)

　
4.新しく作成したファイルの名前を「Dockerfile」にして、以下の内容を記述

※ファイル名を「DockerFile」（fを大文字）にするとdocker-composeコマンドがエラーになるので注意（後述のハマりポイントを参照）

```docker:Dockerfile
FROM elixir:1.11.1

RUN mix local.hex --force && \
  mix archive.install hex phx_new 1.4.3 --force && \
  mix local.rebar --force

WORKDIR /app
```
　
5. 再度エクスプローラーのところで右クリック->新しいファイルをクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/552a29d0-81f4-ed97-6de9-73c5ade7a330.png)

　
6.新しく作成したファイルの名前を「docker-compose.yml」にして、以下の内容を記述
　

```yaml:docker-compose.yml
version: '3.2'
services:
  elixir:
    build: .
    volumes:
      - .:/app
```

　
7. VSCodeの左下のアイコンを選択（Remote - Containersの拡張機能が必要）

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/37e1bed3-a4f9-07b7-7b4f-2c32999151b1.png)

　
8. 「Remote - Containers:Reopen in Container」を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/637ed60b-6ec0-acf7-dd97-0bc8ca387f70.png)

　
9. 「From 'docker-compose.yml'」を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/6ec797ad-0e6c-42ce-f690-16500de3961a.png)

　
10. 左下に「Dev Container:Existing Docker Compose(...」と表示されたら成功

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/f37e78bd-300d-c9f2-b6d0-6bef1bc22cd1.png)
　
11. VSCodeの拡張機能を有効化する。「Ctrl」+「Shift」+「X」キーを押下して拡張機能を表示して、「Dev Container：Existin[^1]」を押下
[^1]: Existing Docker Compose (Extend)

※docker-compose(Docker)で接続したリモートコンテナ上でVSCodeが立ち上がっているので、拡張機能が無効化されている。

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/7f3fc9ac-803a-acbb-40d2-2c484ddb219b.png)

## ハマりポイント docker-composeコマンドがエラーになる（原因：Dockerfileのファイル名）

Dockerfileのファイル名を「DockerFile」（fが大文字）にした場合、以下のエラーが発生する。
原因は「～Cannot locate specified Dockerfile: Dockerfile'～」と記載されているように、
docker-compose実行時にCPythonが実行され、内部コードの辞書に存在する「Dockerfile」の文字列に合致しないため。

ファイル名を「Dockerfile」に変更すると解消する。

```
--------------------------中略--------------------------
[2021-01-24T00:14:09.868Z] [PID 18988]   File "compose\service.py", line 346, in ensure_image_exists
  File "compose\service.py", line 1147, in build
compose.service.BuildError: (<Service: elixir>, {'message': 'Cannot locate speci
fied Dockerfile: Dockerfile'})

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "docker-compose", line 3, in <module>
  File "compose\cli\main.py", line 78, in main
TypeError: can only concatenate str (not "dict") to str
[7012] Failed to execute script docker-compose
```


# STEP2 mixでElixirプロジェクトを作って動作確認する

STEP1でコンテナにリモート接続し、VSCodeのターミナルで以下のコマンドを実行

```shell
root@:/workspace# mix new . --app elixir_intro     #カレントディレクトリに「elixir_intro」のアプリ名でPJを作成する
```

実行したら以下のディレクトリが構成される

```
.
├── .devcontainer
│   ├── devcontainer.json
│   └── docker-compose.yml
├── .elixir_ls
│   └── build
├── lib
│   └── elixir_intro.ex
├── test
│   ├── elixir_intro_test.exs
│   └── test_helper.exs
├── .formatter.exs
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── mix.exs
└── README.md
```

動作確認では、以下のElixirファイルをコンパイル・実行する。（プロジェクト作成時から訂正しているのでコピペすること）

```elixir:lib/elixir_intro.ex
defmodule ElixirIntro do
  @moduledoc """
  Documentation for `ElixirIntro`.
  """

  @doc """
  Hello world.

  ## Examples

      iex> ElixirIntro.hello()
      :world

  """
  def main([]) do
    IO.puts "Hello World"
  end
end
```

## 動作確認① iexからプログラムを実行する場合

1. VSCodeのターミナルで以下のコマンドを実行して、iexを起動

```shell
root@:/workspace# iex -S mix     # -Sオプションでインタラクションモード実行前にmixを実行する
Erlang/OTP 23 [erts-11.1.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Compiling 1 file (.ex)           # mixを実行したためにコンパイル実行
Generated elixir_intro app
Interactive Elixir (1.11.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> 
```
　
2. iexで以下を実行して動作確認

```
iex(1)> ElixirIntro.main([])
Hello World
:ok
```

## 動作確認② コマンドラインからmixを実行して動作確認

1. VSCodeのターミナルで以下のコマンドを実行して、コンパイルを実行

```shell
root@:/workspace# mix compile
Compiling 1 file (.ex)            # -Sオプションでインタラクションモード実行前にmixを実行する
Generated elixir_intro app
```
　
2. 続いてVSCodeのターミナルで以下のコマンドを実行して動作確認

```shell
root@:/workspace# mix run -e "ElixirIntro.main([])"
Hello World
```

## 動作確認③ 実行可能ファイルを作成・実行して動作確認

1. VSCodeのターミナルで以下のコマンドを実行して、実行可能ファイルを作成

実行後、ファイル名「elixir_intro」のファイルが作成される。

```shell
root@:/workspace# mix escript.build
Compiling 1 file (.ex)
Generated elixir_intro app
Generated escript elixir_intro with MIX_ENV=dev
```
　
2. 実行可能ファイルを実行して動作確認

```shell
root@:/workspace# ./elixir_intro 
Hello World
```

# STEP3 GitHubと連携してバージョン管理を行う

## 前準備 Gitの初期設定

既に初期設定している場合は読み飛ばしてOK

※前提として、GitHubアカウントを作成していること

1. Git Bashを実行してコマンドラインを起動（Git BashはWindows検索ボックスなどで検索して実行）
2. ユーザー名の登録(GitHubアカウントのユーザー名)

```shell
$ git config --global user.name 'username'
```
　
3. アドレスの登録(GitHubアカウントのアドレス)

```shell
$ git config --global user.email 'username@example.com'
```
　
4. Gitのエディタとマージツールの設定をVSCodeに指定

```shell
$ git config --global core.editor 'code --wait'
$ git config --global merge.tool 'code --wait "$MERGED"'
```
　
5. pushの方式を指定（追跡ブランチに対してpush）

```shell
$ git config --global push.default simple
```

## ローカルリポジトリの作成と変更内容のコミット

1. ローカルリポジトリを初期化する。VSCodeで「Ctrl」+「Shift」+「G」キーを押下して、ソース管理画面のInitialize Repositoryを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/7878cb83-c514-f2dd-bbeb-9d6c2f5d3ed5.png)
　
2. 変更内容を全てステージングする。画像の「+」マークを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/53ae45be-d60a-beda-4b23-7dedc4e45545.png)
　
3. 変更内容を全てコミットする。①コミットメッセージを記載し、②レ点を押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/4defb7e2-960e-28c4-ee95-ca0e2dff1184.png)

## リモートリポジトリの作成

1. GitHub (https://github.com/) にアクセス、ログインして画面左上のNewを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/a5385a4e-3819-c483-047f-24a488eca511.png)
　
2. 「Repository name」を入力し、Create repositoryを押下

※今回はリポジトリ名を「elixir_intro」とし、Publicリポジトリにした（誰でもアクセス可能）
※READMEファイルや.gitignoreファイルはmixでプロジェクトを作成した時に作成されたので今回はチェックを外した。ライセンスは関係ないのでチェックを外した

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/60fbc569-e483-2177-74f9-9d94339adba7.png)
　
3. 画面に表示されたHTTPSのURLをコピーしておく（後にpushするときに必要）

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/bdd3b85b-90a7-c60a-6222-abb84b3d91b1.png)


## ローカルからリモートリポジトリへpushする

1. リモートリポジトリを追加する。VSCodeで「Ctrl」+「Shift」+「G」キーを押下して、ソース管理画面の横三点リーダからAdd Remoteを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/3a796a55-9e5d-7299-d664-57b011352a0d.png)
　
2. テキストボックスにリモートリポジトリのHTTPS URLを入力

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/acb81634-3379-6cf1-97bc-4a61652cc3fd.png)
　
3. テキストボックスにリモート名を入力（今回は「origin」と入力）

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/f3c4250f-c50c-8365-042a-730905da171e.png)
　
4. ソース管理画面の横三点リーダからFetchを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/7bb09fdb-2967-2faa-0e1d-02b7e225c962.png)
　
5. ソース管理画面の横三点リーダからPushを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/ef9f28a9-f67b-d853-3b9a-df6b5619dbc6.png)
　
6. 「上流ブランチが無いので、このブランチをpublishするか？」と聞いてくるのでOKを押下

![vs-code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/8affd6bc-9b2a-cf52-8504-7eda82591b6a.png)
　
7. GitHubでリモートリポジトリを確認すると、反映されている

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173839/f707dc30-ed16-297a-3d23-2b02e2d71943.png)


**以上で環境の構築は完了。**

# 謝辞

今回は様々な記事を参考にさせて頂きました。
この場をお借りしてお礼申し上げます。

- [Docker ComposeでElixir/Phoenixの開発環境を構築する](https://qiita.com/Tsuyoshi84/items/336f31f6dcc2cd8a077b)
- [Elixir のはじめ方](https://blog1.mammb.com/entry/2015/08/04/004748)
- [【Git】Visual Studio Code × GitHub の連携](https://iwasawa-officialweb.com/2019/08/27/%E3%80%90git%E3%80%91visual-studio-code-x-github-%E3%81%AE%E9%80%A3%E6%90%BA/)
- [docker-composeを実行しようとしたら、エラー「Cannot locate specified Dockerfile: Dockerfile」](https://qiita.com/Eitaro/items/a6f0edb04cd0a73ddc17)