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

# はじめに

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

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

- [VSCodeでJava(Maven)プロジェクトを作成してDockerコンテナ上で開発する](https://qiita.com/mfunaki/items/dc09764cb08bb787c261)

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

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

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

```
code .
```
でローカルリポジトリのあるディレクトリからVSCodeを起動します。メニューの **ターミナル**
→ **新しいターミナル**でターミナルを起動しておくこともできます(下の写真では私のローカル環境のホスト名、MAYOCT-P73が表示されています)。
![Local Repository](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/ecde8829-53e6-5600-bb83-49f5772d293b.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 Gradle**を選択します。するとVSCodeが再起動されるのですが、左下の緑色の **><**に**Dev Container: Java**という記述が追加され、ターミナル上のbashのプロンプトもコンテナ中にいることを表す表記に変わりました。
![VSCode on Java Container](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/1049a762-8b3c-9a44-2211-4214d897bf1e.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とGradle(`gradle`)のバージョンを確認しておきます。
![Versions](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/f3e8dbfa-c286-3df0-ca2f-04cf27c35b91.png)
(Dockerfile中での`ARG INSTALL_GRADLE="false"`の値がtrue/falseでふるまいがどう変わるのかはわかっていないです。)

# (コンテナ上の)リポジトリ内にサンプルを作成する
Gradle公式サイトにある[Building Java Applications Sample](https://docs.gradle.org/current/samples/sample_building_java_applications.html)を例にとって、サンプルを作成していきます。

## プロジェクトフォルダの作成
VSCode上でターミナルを開き、その中でプロジェクトフォルダ(`demo`)を作成し、プロジェクトフォルダに移動します。
![cd demo](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/7c487609-d549-ab3d-54f7-2e5bd2ecbe0d.png)

## initタスクを実行
プロジェクトを初期化するために、`gradle init`を実行します。

- Select type of project to generate(生成するプロジェクト種別を選択)には、2: application(アプリケーション) を、
- Select implementation language(実装言語を選択)には、デフォルトの 3: Java を、
- Split functionality across multiple subprojects(複数サブプロジェクトにまたがる機能を切り出す)には、デフォルトの 2: no(いいえ) を、
- Select build script DSL(ビルドスクリプトを記述するドメイン固有言語(DSL)を選択)には、デフォルトの 1: Groovy を、
- Select test framework(テストフレームワークを選択)には、デフォルトの 1: JUnit 4 を、
- Project name(プロジェクト名)には、デフォルトの demo を、
- Source package(ソースパッケージ)には、デフォルトの demo を、

それぞれ指定します。
![gradle init](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/5be25a53-3de4-cc1c-90b9-5b15b9a8e1b1.png)

またdemoディレクトリ配下に生成されたディレクトリやファイルは、VSCodeのエクスプローラでは次のように表示されます。
![demo](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/fc4d0fd0-5988-249c-467b-ec033ed4b286.png)

## アプリケーションを実行
ターミナルで`./gradlew run`を実行すると、(Gradleの実行に必要なファイルがローカルの`~/.gradle/wrapper/dists`フォルダにダウンロードされ)`run`タスクが実行され、**Hello World!**と出力されます。
![gradlew run](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/ac2e8d94-7dfc-890f-785a-947de4777d02.png)

## アプリケーションをバンドル
ターミナルで`./gradlew build`を実行すると、`build`タスクが実行されます。
![gradle build](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/e5eda291-59f8-0ebe-b563-06c73ce91bf4.png)
その結果、次の2つのファイルが生成されます。

- app/build/distributions/app.tar
- app/build/distributions/app.zip

![distributions](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/c06d7d4f-c8e6-934d-631d-6763c702fa21.png)

# Build Scanをパブリッシュする
ビルドに問題がある際に、Gradleが提供してくれる調査の助けとなる情報を[Build Scan](https://scans.gradle.com/?_ga=2.177027232.425892384.1609125046-728775349.1609125046)と呼びます。Build Scanをパブリッシュするには、ターミナルで`./gradlew build --scan`を実行します。
![Build Scan](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/680aa8cc-9da6-1a91-5f12-661aceebe5ea.png)
途中、

>Publishing a build scan to scans.gradle.com requires accepting the Gradle Terms of Service defined at https://gradle.com/terms-of-service. Do you accept these terms?
>(Build Scanの scans.gradle.com へのパブリッシュには、https://gradle.com/terms-of-service で定義されたGradle利用規約を承諾する必要があります。規約を承諾しますか?)

と聞かれた場合、承諾するのであればyesと入力して先に進めます。
最終的にBuild ScanのURLが表示されるので、URLをクリックした画面でメールアドレスを入力すると、メールが届き、メールの中のURLをクリックするとBuild Scanが見られるという進め方になります。
![Build Scan](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/3250ab30-26ab-a2bc-1225-156be5bdb152.png)

# さいごに
コンテナ上での作業は、実際には(`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`ディレクトリや`demo`ディレクトリが確認できるでしょう。
![GitHub](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19248/ddd4ebcf-4ed4-1b69-10bc-809fc8aa34ed.png)
次回は、CircleCIを使ったビルド＆テストの自動化のお話をさせていただきます。
それではまた、お会いしましょう。さようならー。