Copilot に tomcat のコンテナをビルドするコードを書かせてみた

# Copilot によるコード生成

Java のコンテナをビルドしてコンテナレジストリに登録するソースコードのサンプルを作成する必要があり、Copilot の Agent モードを使用して書かせてみました。**ソースコードを自分で修正することは一切せずに Copilot Agent への指示のみで完成させました。**
コードの解説として、すべてのチャットの内容を記事内に含めています。
ただし、Agentの利用においてはなかなかこちらの思ったようにコードを生成してもらうことができず、ここで掲載しているものは 5、6回の試行錯誤の結果うまく行ったものを採用した形となります。正直、Copilot Agent の依頼だけでコーディングを完成させるのはまだまだ難しいと言えます。骨格の生成後は自分でコードを修正する方が早い場合も多数ありますので、あまり Agent only にこだわらずに柔軟に対応されることをお勧めします。
また、同じ依頼文でもう一度リポジトリを作成してみましたが、いろいろな点で異なるものが生成されました。異なる理由は不明ですが、この記事の通り操作しても同じものにはならないことをご了承ください。
この記事で作成したリポジトリは https://github.com/mit0223/tomcat-docker で公開しています。

## Github のアカウントの準備
この記事では、 Github CodeSpaces と Github Copilot を使用していますが、それぞれ有償サービスとなりますので、同様のことを試していただくには、まず、その有償サービスを利用できる Github のアカウントを用意する必要があります。
CodeSpaces については無料枠もありますが、料金については[公式ページ](https://docs.github.com/ja/billing/managing-billing-for-your-products/managing-billing-for-github-codespaces/about-billing-for-github-codespaces)を参考にしてください。
Copilot の購入手順は Chat-GPT などで質問していただければすぐにわかりますので、ここでは省略します。ただし、Github Copilot はログインしているアカウントに対して1個しか対応付けられません。所属組織で Copilot のサブスクリプションに対応付けられている場合は、そのアカウントで使用する Copilot はすべてその組織に課金されます。その状態で個人のサブスクリプションに結びつけると組織のアカウントとの結び付けが解除されてすべの Copilot 利用が個人のサブスクリプションに課金されるようになる可能性がありますので注意してください。

## 新規リポジトリを CodeSpaces で開く手順
新規リポジトリを CodeSpaces で開く手順を示します。

### 1. 新規リポジトリの作成

まず、開発のベースとなるリポジトリを作成します。

1.  [GitHub のダッシュボード](https://github.com)にアクセスします。
2.  画面右上の「**New**」ボタンをクリックします。
    ![RepositoryNew.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/7a48380f-4aa0-4121-aba1-373ffaba30b6.png)
3.  以下の情報を入力します。
    * **Owner**: ご自身のアカウントまたは所属する Organization を選択します。
    * **Repository name**: `tomcat-docker` と入力します。
    * **Description**: `Tomcat のコンテナイメージをリリースするリポジトリ` と入力します。
    ![CreateNewRepository.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/48acdd88-7463-4121-b15d-09c27df5157f.png)
  
4.  「**Create repository**」ボタンをクリックします。

:::note info
以降、スクリーンショット中の入力文字列は "tomcat-builder" になっていますが、 "tomcat-docker" で読み替えてください
:::

### 2. Codespace の作成と起動

次に、作成したリポジトリで Codespace を起動します。

1.  リポジトリの新規作成ページが表示されたら、「**Create a codespace**」ボタンをクリックします。
    ![CreateCodespace.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/2be71cdd-4aa8-4a17-9371-c69e2226e0a6.png)
2.  ドロップダウンメニューから「**Codespaces**」タブを選択します。
3.  開いたページで「**Create new codespace**」をクリックします。
    ![CreateCodespace2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/35fccec4-4f8d-4e5f-879b-aa70602d05b1.png)

Codespace の環境構築が開始され、しばらくするとブラウザベースの VS Code が起動します。

### 3. GitHub Copilot 拡張機能のインストール

Codespace が起動したら、GitHub Copilot 拡張機能をインストールします。

1.  画面左端のアクティビティバーから「**拡張機能**」（四角が組み合わさったようなアイコン）をクリックします。

2.  表示された検索バーに「`copilot`」と入力します。
3.  検索結果の「**GitHub Copilot**」の「**インストール**」、クリックします。
    ![InstallCopilot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/29f4fddd-e611-4c6b-a123-e31305dd40c9.png)
インストールが完了すると、GitHub アカウントとの連携を求められる場合があります。画面の指示に従って認証を行ってください。

### 4. Copilot Chat の設定変更 

Copilot Chat の動作モードや使用するモデルを変更するために以下の手順で設定します。

1.  画面上部のナビゲーションバーの右端にある、セカンダリサイドバーの表示/非表示を切り替えるアイコン（通常はレイアウトに関連するアイコン）をクリックして、セカンダリサイドバーを表示します。
もしくは、`Ctrl+Shift+P` (Windows/Linux) または `Cmd+Shift+P` (Mac) でコマンドパレットを開き、「`View: Toggle Secondary Side Bar`」と入力して実行することでも表示/非表示を切り替えられます。

2.  セカンダリサイドバーに Copilot Chat が表示されていることを確認します。
    もし Copilot Chat が表示されていない場合は、アクティビティバーの Copilot アイコン（吹き出しのようなアイコン）をクリックするか、コマンドパレットで「`Chat: Focus on GitHub Copilot View`」などを検索して開いてください。

3.  Copilot Chat ウィンドウの下部（または設定箇所）で、以下の設定を変更します。
    * **モード**: 「`Ask`」から「**Agent**」に変更します。
        * `Ask`: 特定の質問に対して回答を得るモードです。
        * `Agent`: より広範なタスク（コード生成、リファクタリング、ドキュメント作成など）を対話的に実行するモードです。
    ![AgentMode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/a0fac2bc-ec30-422a-b0d1-23eaf82e2956.png)

    * **モデル**: 「`GPT-4.1`」 (またはデフォルトのモデル) から「**Claude 3.7 sonnet**」 (利用可能な場合) に変更します。
        * 利用できるモデルは、GitHub Copilot のプランやベータ機能への参加状況によって異なる場合があります。
    ![Claude.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/c8d71d0c-41fc-4c11-a818-b6d57895f97a.png)


これで、GitHub Codespaces 上で GitHub Copilot を利用する準備が整いました。快適な開発ライフをお楽しみください！

## 1. Java ソースコードと開発コンテナの生成
ここでは、Copilot Agent に依頼してJava ソースコードと開発コンテナの設定ファイルを作成してもらえいます。
### 1-依頼文
tomcat で Hello World を表示する HTTP サーバのコードを書いてください。それを動かすための Dockerfile を書いてください。Dockerfile はビルドフェーズと実行フェーズで分けてください。ビルドは maven で行うこととしてください。java は ellipse terumin を使用してください。java と tomcat のバージョンは環境変数で指定できるようにして、デフォルトでは最新を使用するようにしてください。
.devcontainer/devcontainer.json を作成し、開発コンテナをビルドするようにしてください。 開発コンテナには docker-in-docker feature を含み、開発時はコンテナイメージをビルドしてそれを実行する前提で開発コンテナには Java 等を含めないでください。Dockerfile, java, サーブレット, maven のソースコードの開発に便利な VScode extension とその設定を devcontainer.json に組み込んでください。

### 1-回答
こんにちは、ご要望を承りました。Tomcatを使用してHello Worldを表示するHTTPサーバーのコードとDockerfile、開発環境の設定を作成します。

まず、プロジェクト構造を確認し、必要なファイルを作成していきます。

#### 1-回答-1:プロジェクト構造の作成
以下の構造でプロジェクトを作成します:
ターミナルでコマンドを実行します。
```sh
mkdir -p /workspaces/tomcat-docker/src/main/java/com/example/webapp /workspaces/tomcat-docker/src/main/webapp/WEB-INF
```
:::note info
「続行」をクリックします
:::
1. Maven用のプロジェクト構造
1. Hello Worldを表示するサーブレット
1. ビルド/実行フェーズを分けたDockerfile
1. 開発コンテナの設定

まずはMavenプロジェクトの基本構造を作成しましょう。
→ ```pom.xml``` が作成されました

次に、サーブレットのソースコードを作成します。
→ ```HelloWorldServlet.java``` が作成されました

続いて、web.xmlファイルを作成します。
→ ```web.xml``` が作成されました

インデックスページも作成しておきます。
→ ```index.html``` が作成されました

次に、ビルドフェーズと実行フェーズを分けたマルチステージビルド用のDockerfileを作成します。環境変数でJavaとTomcatのバージョンを指定できるようにします。
→ ```Dockerfile``` が作成されました

次に、開発コンテナの設定ファイルを作成します。
→ ```devcontainer.json``` が作成されました

最後に、使い方を説明するREADMEファイルを更新します。
→ ```README.md``` の 1 から 3 行を読み取り
```README.md+53-2```

#### 1-回答-2:使用方法の説明
以上でTomcatでHello Worldを表示するHTTPサーバーのコードとDockerfile、および開発環境の設定が完成しました。構成の概要は以下の通りです：

1. プロジェクト構造
    - Maven標準のプロジェクト構造を採用
    - HelloWorldServlet.java：シンプルなHTTPレスポンスを返すサーブレット
    - web.xml：Webアプリケーションの設定
    - index.html：ウェルカムページ
2. Dockerfileの特徴
    - マルチステージビルドを採用
        - ビルドフェーズ：Maven を使用
        - 実行フェーズ：Eclipse Temurin + Tomcat を使用
    - 環境変数によるバージョン指定
        - JAVA_VERSION：Javaのバージョン
        - TOMCAT_VERSION：Tomcatのバージョン
    - デフォルトでは最新バージョンを使用
3. 開発コンテナ (.devcontainer)
    - Docker-in-Docker機能を含む開発環境
    - Java関連のVS Code拡張機能
        - Java Extension Pack
        - XML Support
        - Docker
        - Maven for Java
        - Tomcat for Java など

使い方
1. VS Codeで開発コンテナを開く（「Reopen in Container」を選択）
1. コンテナ内でDockerコマンドを実行してアプリケーションをビルド・実行
    ```
    # ビルド
    docker build -t tomcat-hello-world .
    
    # 実行
    docker run -p 8080:8080 tomcat-hello-world
    ```
1. ブラウザで http://localhost:8080 にアクセスしてアプリケーションを確認

特定のバージョンでビルドしたい場合は、ビルド時に引数を指定できます：
```sh
docker build --build-arg JAVA_VERSION=17 --build-arg TOMCAT_VERSION=10 -t tomcat-hello-world .
```
これで、指定された要件を満たすTomcatベースのHello Worldアプリケーションと開発環境が整いました。

:::note info
「ファイルが更新されました」の表示が出たら「保持」をクリックします
:::



## 2. Dockerfile の修正
実行フェーズ用のコンテナのビルドが apt-get コマンドでインストールする方式になっていたので、オフィシャルイメージベースとするように変更してもらうことにした。
### 2-依頼文
Dockerfile の実行フェーズの方は tomcat の dockerhub 上のオフィシャルイメージを使用するように変更できますか
### 2-回答
GitHub Copilot:
はい、Dockerfileの実行フェーズをTomcatの公式イメージを使用するように変更しましょう。公式イメージを使うことで、より信頼性が高く、メンテナンスもしやすくなります。

Dockerfileを修正します:
→ ```Dockerfile+5-23```

READMEも更新して、変更点を反映させましょう：
→ ```README.md+3-1```

```Dockerfile``` の 1 から 29 行を読み取り

Dockerfileの実行フェーズを正常にTomcatの公式イメージを使用するように変更しました。この変更には以下のメリットがあります：

1. シンプル化：自前でTomcatをインストールする複雑なプロセスが不要になりました
1. 信頼性向上：公式イメージはDocker Hubチームによって適切に管理されています
1. 最適化：公式イメージは最適化されており、サイズとパフォーマンスのバランスが取れています
1. 安全性：セキュリティパッチが定期的に適用されています

新しいDockerfileでは：

1. ビルドステージ：
    - 引き続きMavenイメージを使用してアプリケーションをビルドします
    - JAVA_VERSION引数でJavaバージョンを指定できます
2. 実行ステージ：
    - Tomcatの公式イメージを使用するようになりました
    - TOMCAT_VERSION引数でTomcatバージョンを指定できます
    - デフォルトのwebappsディレクトリをクリアして、ビルドしたアプリケーションのみを配置します

使い方は以前と同じですが、バージョン指定に関するオプションが少し変わりました：
```sh
# デフォルトのバージョン（最新）でビルド
docker build -t tomcat-hello-world .

# 特定のTomcatバージョンを指定してビルド
docker build --build-arg TOMCAT_VERSION=10.1 -t tomcat-hello-world .

# ビルドステージのJavaバージョンも指定する場合
docker build --build-arg JAVA_VERSION=17 --build-arg TOMCAT_VERSION=10.1 -t tomcat-hello-world .
```
これで、よりメンテナンスしやすく、標準的なDockerfileになりました。何か他にお手伝いできることはありますか？

:::note info
「ファイルが更新されました」の表示が出たら「保持」をクリックします
:::

## 3. ビルドエラーの修正
ビルドエラーが出たので、修正を依頼します。依頼文はエラーメッセージだけで十分でした。
### 3-依頼文
ERROR: failed to solve: failed to parse stage name "tomcat:": invalid reference format
### 3-回答
この問題は、Dockerfileでの変数の使用方法に関するエラーです。マルチステージビルドでFROM命令で変数（ARG）を使用する場合、変数はその前に宣言されている必要がありますが、現在のDockerfileでは2段階目のFROM命令の前にTOMCAT_VERSIONを宣言しているため問題が発生しています。

この問題を修正しましょう：
→ ```Dockerfile+4-2```

問題を修正しました。Dockerfileでの変数（ARG）の使用には以下のルールがあります：

1. FROM 命令の前に宣言された ARG はグローバルスコープを持ち、すべての FROM 命令で使用できます
1. FROM 命令の後に宣言された ARG は、そのステージ内でのみ有効です
1. 各ビルドステージには独自のスコープがあります

今回のエラーの原因は、TOMCAT_VERSION 変数が 2 つ目の FROM 命令の直前に宣言されていたため、その FROM 命令自体ではまだ使用できなかったことです。

修正したDockerfileでは、両方の変数（JAVA_VERSION と TOMCAT_VERSION）を最初に宣言し、それぞれの FROM 命令で使用できるようにしました。

この変更により、次のようにビルドコマンドを実行できるようになります：
```sh
# デフォルトのバージョン（最新）でビルド
docker build -t tomcat-hello-world .

# 特定のTomcatバージョンを指定してビルド
docker build --build-arg TOMCAT_VERSION=10.1 -t tomcat-hello-world .

# ビルドステージのJavaバージョンも指定する場合
docker build --build-arg JAVA_VERSION=17 --build-arg TOMCAT_VERSION=10.1 -t tomcat-hello-world .
```

これで「failed to parse stage name "tomcat:"」というエラーは解消されるはずです。

:::note info
「ファイルが更新されました」の表示が出たら「保持」をクリックします
:::

## 4. テスト
Codespace の中でビルド＆実行してみます。
#### 4-1. ビルド
以下のコマンドでビルドします。
```sh
docker build -t tomcat-hello-world .
```
#### 4-2. 実行
以下のコマンドで tomcat を起動します。
```sh
docker run -p 8080:8080 tomcat-hello-world
```

1. 「ポート 8080 で実行されているアプリケーション は使用可能です。」のダイアログが表示されたら「ブラウザーで開く」をクリック
    ![OpenBrowser.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/f97811b4-82de-466e-a3e3-58f9884086ab.png)
    このダイアログは時間が経つと消えるので、消えてしまった場合には下ペインポートタブを開いて、8080 ポートの地球マークをクリック
    ![OpenPort.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156395/15c9336a-e81c-4aa2-8988-8ca6d8549bfc.png)


1. 新しく表示されたタブで「Click here to see the servlet response」をクリック
「Hello World Application」と表示されたら成功です。


## 5. Github Actions のワークフローの追加
前項までで動作が確認できたので、commit を契機に Github Container Registry にコンテナイメージを登録する Github Actions のワークフローファイルを追加します。
### 5-依頼文
commit を契機に Github Container Registry にコンテナイメージを登録する Github Actions のワークフローファイルを追加してください。
### 5-回答
GitHub Container Registry (GHCR) にコンテナイメージを自動登録するための GitHub Actions ワークフローファイルを作成します。このワークフローは、リポジトリへのコミットを契機に実行されます。

まず、GitHub Actions のワークフローディレクトリとファイルを作成します：

→ '''docker-publish.yml``` が作成されました

READMEにも、GitHub Container Registry へのイメージの公開に関する情報を追加しましょう：

```README.md+22-0```

設定した GitHub Actionsワークフローと、その内容について説明します：

#### 5-回答-1: GitHub Actionsワークフローの概要
作成したワークフローファイル（docker-publish.yml）は以下の機能を提供します：

##### トリガー設定
```yaml
on:
  push:
    branches: [ main, master ]
    paths-ignore:
      - 'README.md'
      - '.github/**'
      - '!.github/workflows/**'
  workflow_dispatch:  # 手動実行用のトリガー
  ```
- mainまたはmasterブランチへのプッシュで自動実行
- README.mdの変更や.github内のファイル変更（ワークフロー自体を除く）では実行されない
- 手動での実行も可能（workflow_dispatch）

##### 環境変数
```yaml
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}
```
- REGISTRY: GitHub Container Registry のURL
- IMAGE_NAME: リポジトリ名から自動的に構築（ユーザー名/リポジトリ名）

##### ビルドと公開ジョブ
1. リポジトリのチェックアウト
    - 最新のコードを取得
1. Docker Buildx のセットアップ
    - マルチプラットフォームビルドをサポートするBuildxを使用
1. コンテナレジストリへのログイン
    - GitHub Container Registryへの認証
    - GITHUB_TOKENシークレットを使用（アクションによって自動提供）
1. メタデータの抽出
    - タグとラベルの生成
    - 以下のタグが生成されます：
        - セマンティックバージョンのタグ（v1.0.0など）
        - メジャー.マイナーバージョンのタグ（1.0など）
        - latestタグ（デフォルトブランチの場合）
        - ブランチ名のタグ
        - コミットSHAのタグ
1. Dockerイメージのビルドとプッシュ
    - イメージのビルドと公開
    - GitHub Actionsのキャッシュ機能を利用

#### 5-回答-2: イメージの使用方法
公開されたイメージは以下のようにプルして使用できます：

```sh
# イメージをプル
docker pull ghcr.io/あなたのユーザー名/tomcat-docker:latest

# イメージを実行
docker run -p 8080:8080 ghcr.io/あなたのユーザー名/tomcat-docker:latest
```

#### 5-回答-3:注意事項
1. GitHub Actionsのワークフローが実行されるためには、リポジトリに対して適切な権限設定が必要です。ワークフロー自体には必要な権限（packages: write）が含まれています。
1. プライベートリポジトリの場合、イメージをプルするには認証が必要になります。
1. プルするときは、リポジトリ名を含む完全なパスを指定する必要があります。

これで、コミットをトリガーとして、Tomcatアプリケーションのコンテナイメージが自動的にGitHub Container Registryに公開される設定が完了しました。

:::note info
「ファイルが更新されました」の表示が出たら「保持」をクリックします
:::

## 6. まとめ

ここまで、tomcat のサーブレットのコンテナイメージを開発するためのリポジトリが完成しました。ソースコードを修正すると自動的にコンテナイメージが Github Contaiainer Registry に登録されます。これを AWS ECS などにデプロイしてアプリケーションとして動作させることができます。