VSCodeでJava(Maven)プロジェクトを作成してDockerコンテナ上で開発する

# はじめに

2020年を振り返ると、[VSCode](https://code.visualstudio.com/)の[Remote Development Extension Pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)に出会った時の衝撃は、言葉では語りつくせません。

- [Remote-SSH](https://code.visualstudio.com/docs/remote/ssh) - Hyper-V上のLinuxだろうと、クラウド上のLinuxだろうと、ターミナルからSSHログイン→viで編集ではなく、VSCodeがターミナルであり、エディタであり、エクスプローラになってしまう衝撃
- [Remote-WSL](https://code.visualstudio.com/docs/remote/wsl) - Hyper-Vではなく、ローカルのWSL環境であれば、Windows側にVSCodeがインストールされていれば、`code .`で当該ディレクトリからVSCodeを起動でき、(上記同様)...衝撃
- [Remote-Containers](https://code.visualstudio.com/docs/remote/containers) - Hyper-VもWSLも用意しなくても、Dockerfile用意しなくても(用意してもよい)、コンテナ起動して、起動したコンテナ上でローカルディレクトリを透過的に見ながら開発できる衝撃

の3衝撃のどれもが力強いものだったのですが、私にとってはRemote Containerで[これらの8種類の言語](https://github.com/Microsoft/vscode-dev-containers#sample-projects)に関してはMS謹製の開発環境が整った開発コンテナサンプルがあるということで、全部制覇するぞ！ と意気揚々だったものの、日ごろの忙しさにかまけて何もできていなかったので、まずは、Java(Maven)から手を付けてみました。

# GitHub上にリポジトリを作成する
GitHub上に新規リポジトリを作成します。ここでは、`CCI-SMP-Java-Maven`という名前にしておきました(CCIは次回以降使おうとしているCircleCIの略、SMPはSaMPleの略)。お好きな名前を使っていただいて結構です。なお、Add .gitignoreではMavenテンプレートを指定しています。
![GitHub - New Repository](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/63cafb88-2d82-81a1-c076-e6ab8ac5cb6f.png)

# ローカルにプルして、VSCodeを起動する
作成したリポジトリをローカル環境にプルします。この記事の環境の場合、

```
git clone git@github.com:mfunaki/CCI-SMP-Java-Maven.git
```
といった感じになります。プルが終わったら、作成されたディレクトリに移動して(例えば、`cd CCI-SMP-Java-Maven`)、

```
code .
```
でローカルリポジトリのあるディレクトリからVSCodeを起動します。メニューの **ターミナル**
→ **新しいターミナル**でターミナルを起動しておくこともできます(下の写真では私のローカル環境のホスト名、MAYOCT-P73が表示されています)。
![Local Repository](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/4d3bb9d5-f376-6b44-060e-e6b14678aaca.png)

# ローカルからコンテナ上に開発環境を移る
左下にある緑色の**><**をクリックすると、コマンドパレットが表示され、Remote Developmentに関連した選択肢が表示されるので、この中から **Remote-Containers: Open Folder in Container**を選びます。
![Remote Development](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/50fb7f5a-3b1f-3b39-aaca-5d4f9c94de46.png)

どのフォルダからVSCodeを開くのかを聞いてくるので、カレントディレクトリを指定したままで、 **Open**ボタンを押します。すると、(自動生成され、追加される)コンテナ環境を聞いてくるので、**Java**を選択しておきます。
![Java](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/ba25fb83-6a7a-47cf-1383-60c638bceb1e.png)

次にJavaのバージョンを聞かれるので **15**を、次にMaven, Gradle, Node.jsのどれをインストールするか聞いてくるので、ここでは、**Install Maven**を選択します。するとVSCodeが再起動されるのですが、左下の緑色の **><**に**Dev Container: Java**という記述が追加され、ターミナル上のbashのプロンプトもコンテナ中にいることを表す表記に変わりました。
![VSCode on Java Container](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/5fb368ea-de47-0d21-3806-46f0470cc2ea.png)

それ以降の作業はコンテナの中です。自動生成された`.devcontainer/Dockerfile`を見ていただければ、あなたのいる世界がMicrosoft謹製の`mcr.microsoft.com/vscode/devcontainers/java:0-15`であることがわかります。

```Dockerfile
# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.154.0/containers/java/.devcontainer/base.Dockerfile

# [Choice] Java version: 11, 15
ARG VARIANT="15"
FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT}

# [Option] Install Maven
ARG INSTALL_MAVEN="false"
ARG MAVEN_VERSION=""
# [Option] Install Gradle
ARG INSTALL_GRADLE="false"
ARG GRADLE_VERSION=""
RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "source /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \
    && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "source /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi

# [Option] Install Node.js
ARG INSTALL_NODE="true"
ARG NODE_VERSION="lts/*"
RUN if [ "${INSTALL_NODE}" = "true" ]; then su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi

# [Optional] Uncomment this section to install additional OS packages.
# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
#     && apt-get -y install --no-install-recommends <your-package-list-here>

# [Optional] Uncomment this line to install global node packages.
# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g <your-package-here>" 2>&1
```

ターミナル上でJavaとMaven(`mvn`)のバージョンを確認しておきます。
![Java Version](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/28d9d3ed-1d3f-b6f9-66cb-ceb62996f946.png)
(Dockerfile中での`ARG INSTALL_MAVEN="false"`の値がtrue/falseでふるまいがどう変わるのかはわかっていないです。)

# (コンテナ上の)リポジトリ内にサンプルを作成する
Maven公式サイトにある[Maven in 5 Minutes](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html)を例にとって、サンプルを作成していきます。

Maven in 5 Minutesのはじめの方に、[Creating a Project](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html#creating-a-project)という節があるので、そこにある`mvn`から始まる行をターミナル上で入力して、Enterを押します。

```
mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false
```
すると(あれやこれやダウンロードされた後)、こんな感じになります。
![mvn result](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/cc8573ad-b2ee-2c2f-5c84-614bd113b5bb.png)

また、左側のエクスプローラーを見ると、
- `my-app`ディレクトリが作成されたこと
- `my-app`ディレクトリ配下に`src`(ソースコード用)ディレクトリが作成されたこと
- `src`ディレクトリの下、`main/java/com/mycompany/app`ディレクトリの下には`App.java`(アプリケーションのソースコード)、
- `src`ディレクトリの下、`test/java/com/mycompany/app`ディレクトリの下には`AppTest.java`(アプリケーションのテストコード)
- `my-app`ディレクトリ直下には、pom.xmlファイル(Mavenが使用する構成ファイル)
がそれぞれ作成されていることがわかります。
![Explorer](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/434363d6-2aad-ef4a-7beb-dab9f2883d4b.png)

# 手作業でサンプルをビルド＆動作確認する
それでは、ビルドです。Visual Studio Code右下のターミナル上で、サンプルアプリケーションのディレクトリに移動します。

```
cd my-app
```
次に実際にビルドを進めます。

```
mvn package
```
途中、自動生成されていたテストが実行されます。
![TESTS](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/c5653828-cc02-c539-1a75-ad76ff157bfb.png)
最終的にビルド完了です(つまり、`/workspaces/CCI-CMP-Java-Maven/my-app/target/my-app-1.0-SNAPSHOT.jar`が生成されます)。
![BUILD SUCCESS](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/33003a8c-015d-682a-a72c-8444775b2259.png)
次に、動作確認に移ります。

```
java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
```
あとは`Hello World!`が出力されればOKです。

# さいごに
コンテナ上での作業は、実際には(`git clone`した)ローカルのディレクトリをマウントして行っているので、左下の緑色の **><** をクリックし、コマンドパレットから **Remote-Containers: Reopen Locally** を選びます。
![Command Palette](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/6aac91b2-7d92-db93-08d2-561d3ba9898f.png)

作業内容を追加、更新した上で、コミットして、GitHubにプッシュしておきましょう。うまくいっていれば、GitHubリポジトリ上で`.devcontainer`ディレクトリや`my-app`ディレクトリが確認できるでしょう。
![GitHub](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/50ee217b-9e7a-8c2c-4226-dd03698e56e1.png)
次回は、CircleCIを使ったビルド＆テストの自動化のお話をさせていただきます。
それではまた、お会いしましょう。さようならー。