Azure DevOps で Java ライブラリのパイプラインを組んだりホストしたりする

# Azure DevOps で Javaライブラリパイプラインを組んだりホストしたりする

以前、Azure DevOps から Maven Central Repository へデプロイするための、パイプラインを組んだことがあるのですが、その頃からはAzure DevOps の機能も変ったりしているので、復習ついでにBLOGを書こうと思います。当時は確か、マルチステージパイプラインがなかったりたきがします。

取りあえずひな形的なものは、試行錯誤して組まざるをえないので、パイプラインを組んでみたいと思います。

今回の想定としては、プロジェクト共通のライブラリを、ローカルのMaven Repository にデプロイするというところを目標にパイプラインを組みます。Azure DevOpは、Artifactの機能をつかって、 Maven やら npm やら nuget に対応してライブラリをホストすることができます。企業内、プロジェクト内でプライベートなリポジトリとして使用できます。

Azure DevOps は無料出始めることが出来ますので、詳細は以下を参照してください。

[Azure DevOps Services | Microsoft Azure](https://azure.microsoft.com/ja-jp/services/devops/)

# リポジトリを用意する

Azure DevOpsでプロジェクトを作成し、リポジトリを用意します。作り方によって、デフォルトブランチが `main` になったりすることがありますので、ご注意を。適当なライブラリ的なコード（テスト含む）を書いて、リポジトリにプッシュしておきましょう。

以下は既にパイプラインファイル作ってしまった後ですが、スクリーンショットです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/81c0b837-3444-5c35-a775-22149f56258c.png)

# パイプラインを組む

手順としては簡単で2つです。これを YAMLファイルで記述します。

1. mvn package する
2. mvn deploy する

パイプラインタブの右上で新規作成できます。以下は既に作成済の画面です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/989b050c-31da-1f32-f695-3af98010d5a1.png)

新規作成すると、対象のリポジトリを選択します。Azure DEvOps内のリポジトリ以外にも選ぶことが出来ますがここでは、Azure DevOpsないのリポジトリを使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/ac94f9a7-329b-5e70-18c8-b2d360660b4c.png)

選択していくと、パイプラインの構成画面がでてきます。Maven を選ぶと簡単なひな形が展開されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/bb0a33cf-e76d-a92f-afc6-73504dcd4704.png)

最初のひな形はこんな感じですが、マルチステージになっていないので、`steps`以降はざっくりと消します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/3878b152-08b3-f499-aad9-a0d948b58b05.png)

先頭にある`trigger` はパイプラインを実行するためのトリガです。ここでは、`master` 基点でパイプラインを実行しますが、色々条件を付けることができます。実際にビルドを走らせるVMは、`ubuntu-latest` を選んでおけばよいでしょう。Javaですし、どこでコンパイルしても同じですし。

```yaml
trigger:
- master

pool:
  vmImage: ubuntu-latest
```

## ビルドステップ

ビルドステップには、`mvn package`するタスクを設定します。オプションはドキュメント参照していただくとして、ポイントはオプションに、`--no-transfer-progress` を付けないと、延々とダウンロードプログレスが表示されてログが見にくくなるくらいでしょうか。あとは、Java のバージョンに気をつけてください。`1.11` が JDK11となっています。

Azure DevOps ポータルのパイプライン編集画面で編集すると、プロパティの補完があって、ダイアログベースでも設定を変更できます。また、リスト選択もできますので、慣れないうちはポータルでやると便利です。

[Maven のビルドおよびリリースタスク - Azure Pipelines | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/devops/pipelines/tasks/build/maven?view=azure-devops&WT.mc_id=AZ-MVP-5001265)

```yaml
  - stage : build
    displayName : build
    jobs:
      - job:
        displayName: build job
        steps:
          - task: Maven@3
            inputs:
              mavenPomFile: 'pom.xml'
              options: '--no-transfer-progress'
              publishJUnitResults: true
              testResultsFiles: '**/surefire-reports/TEST-*.xml'
              javaHomeOption: 'JDKVersion'
              jdkVersionOption: '1.11'
              mavenVersionOption: 'Default'
              mavenAuthenticateFeed: false
              effectivePomSkip: false
              sonarQubeRunAnalysis: false
              goals: package
```

`package` まで走らせますので、テストコードがあれば実行されます。テスト結果は、統計情報をして蓄積されるので、Azure DevOps ポータルでモニタすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/76796c62-8b84-0e07-c80f-8273431d41b8.png)


## Azure DevOpsの フィード準備

作成したJARをデプロイするには、Aritifactの機能を使います。Create New Feed から新しいフィードを作成できます。フィードを作成したら、Connect Feed で、`pom.xml` に埋め込む情報を取得します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/18b2808c-4d12-af95-7dca-64640c75c0ec.png)

Mavenを選択すると、`repositories`と`distributionManagement` に配置する情報と、外部からアクセスする際に認証する情報が表示されます。ここでは割愛しますが、適当に`poml.xml` の修正と、`~/.m2~` に資格情報を置いてください。今回のシナリオでは後者は使わないです。

設定例としては以下の通りです。

```xml
  <distributionManagement>
    <repository>
      <id>SandboxProject</id>
      <url>https://pkgs.dev.azure.com/foo/bar/_packaging/SandboxProject/maven/v1</url>
      <releases>
        <enabled>true</enabled>
      </releases>
      <snapshots>
        <enabled>true</enabled>
      </snapshots>
    </repository>
  </distributionManagement>
```
## デプロイステップ

次は、`mvn deploy` するタスクを配置します。`dependsOn` で前ステップに依存していることを明示します。前タスクが失敗すると、こちらは実行されません。また、認証タスク `MavenAuthenticate@0` を設定しておかないとデプロイで失敗しますのでご注意を。フィードを指定するだけで特に特殊な設定は必要ないです。

```yaml
 - stage : deploy
    displayName : deploy
    dependsOn: build
    jobs:
      - job:
        displayName: deploy library
        steps:
          - task: MavenAuthenticate@0
            inputs:
              artifactsFeeds: 'SandboxProject'
          - task: Maven@3
            inputs:
              mavenPomFile: 'pom.xml'
              options: '--no-transfer-progress'
              publishJUnitResults: true
              testResultsFiles: '**/surefire-reports/TEST-*.xml'
              javaHomeOption: 'JDKVersion'
              jdkVersionOption: '1.11'
              mavenVersionOption: 'Default'
              mavenAuthenticateFeed: false
              effectivePomSkip: false
              sonarQubeRunAnalysis: false
              goals: deploy
```

## パイプラインの実行

今回の設定ですと、`master`に変更があるたびにパイプラインが実行されます。成功しますと以下の表示になるでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/589ac693-c985-90a8-e6a1-7dd00cd64294.png)

各ステップの詳細をログを見ることも出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/f13d44ae-d69c-0487-293f-31d668bd922f.png)


## 確認

最終的にArtifactタブからパッケージがデプロイされているのが確認できればOKでしょう。`com.example.moris`以外は、アップストリームのものがキャッシュされている感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34017/96491ebc-b5f0-ab17-f469-b5c008e76622.png)

# 改善点

今回のパイプライン構成ですと、ビルドステップでパッケージングまでしたバイナリファイル群が、後続のデプロイステップで再利用されません。それはリポジトリが毎回クローンされ利用されるからです（クローンしない設定もできます）

そのために、パイプライン中の成果物を一時的に保存しておくことができます。これを、ビルドステップとデプロイステップの間に入れることによって、ビルドステップで生成したバイナリファイル群を引き継ぐことが出来ます。

[パイプラインでの成果物の発行と使用 - Azure Pipelines and TFS | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/devops/pipelines/artifacts/pipeline-artifacts?view=azure-devops&tabs=yaml&WT.mc_id=AZ-MVP-5001265)

色々試行錯誤したんですが、この機能を使うとダウンロードしたファイル群はファイル日付がかわっていて、`mvn pakcage`すると ソースファイルが変更されたとして、ビルド->テスト->パッケージと再度実行されてしまいました。テスト程度ならスキップできますが、結局色々ビルドプロセスが走ってしまい、無駄なCPUを使ってしまいます。ビルドそのものをスキップする事も出来そうでしたが、試していません。

次に、`mvn deploy:deploy` しようと思い立ったんですが build artifact が含まれていないとエラーが出てデプロイできない感じでしたので、自分の知識不足でよい解決案が浮かびませんでした。大きなプロジェクトだと、ビルド時間も馬鹿にならないですし、無料枠、料金にも影響してきそうです。

補足として、ビルドステップとデプロイステップの間に、承認を設定することもできますので、実際にデプロイするのになんらかのチェックが必要でしたら、そういうことをすることも出来ます。ただ、もう少しトリガを真面目に設計したほうがよいのかもしれません。

# Javaのバージョンについて

ちなみに、Java のバージョンは以下の通りです。`sh`タスクで`java -version`を実行して表示させました。Zulu かと思ったら、 AdoptOpenJDK でした。近い将来は、Microsoft謹製のOpenJDKに置き換わるのかもしれません。

```sh
/usr/bin/bash --noprofile --norc /home/vsts/work/_temp/2c28180c-347e-4efe-8a59-234727097245.sh
openjdk version "11.0.11" 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-11.0.11+9 (build 11.0.11+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-11.0.11+9 (build 11.0.11+9, mixed mode)
Finishing: Bash
```

試したことはありませんが、外部からJDKを持ち込めるようです。Java ツールインストールタスクを利用すると、外部のストレージからインストールできそうでした。ライセンスあることが前提だと思いますが、Oracle VM とか持ち込めるのかもしれません。あとマイナーバージョンまで固定で勝手に変って欲しくない場合、JDKのバージョンをフルコントロールしたいSIな環境ですと、役に立つかもしれません。

[Java ツールインストーラータスク - Azure Pipelines | Microsoft Docs](https://docs.microsoft.com/ja-jp/azure/devops/pipelines/tasks/tool/java-tool-installer?view=azure-devops&WT.mc_id=AZ-MVP-5001265)