Check! Maven のリポジトリにデプロイする仕組みを理解する

10 min read読了の目安(約9500字

Prologue

とあるプロジェクトで、Maven リポジトリへのデプロイに関してメンテナンスする作業を担当することになりまして、こんがらがってきたので記事に起こして整理を試みます🤯

ここでは、成果物を独自リポジトリ、または OSSRH (Central Repository) にデプロイする方法について調べたことを書き留めます。なお、当方は Java を利用した開発経験が乏しいので、間違いのご指摘や、もっといい方法あるよのアドバイスありましたら、コメントいただけるとうれしいです!

リポジトリにデプロイする仕組み (見よう見まね)

まず、Maven のパッケージは、Central Repository によって管理されています。

https://maven.apache.org/repository/index.html

オープンソースとしてそこに登録するには、Sonatype が管理する OSSRH (Open Source Software Repository Hosting) を利用することができます。この記事でも、 OSSRH を利用する想定です。

OSSRH のリポジトリを経由して Central Repository へデプロイするには、OSSRH のステージングリポジトリにデプロイしたのち、Repository Manager の GUI を利用して公開します。なお、事前にネームスペースの登録やアクセス許可の依頼などが必要です。詳細は下記のガイドをご参考ください。(ここでは、前任者がすでに済ませてくれているので割愛します。)

http://central.sonatype.org/pages/ossrh-guide.html

ここで整理するのは下記です。

  • デプロイ先のリポジトリおよび接続情報の指定
  • GPG による署名
  • デプロイプラグインの設定

pom.xmlsettings.xml, settings-security.xml のおさらい

作業当初、これらのファイルのことさえよく理解していなかったので、一応メモしておきます😳

まず、pom.xml は、プロジェクトの設定を記述しておくファイルです。

https://maven.apache.org/pom.html

settings.xml は、プロジェクトに紐づかない設定を記述するもので、 ${maven.home}/conf/settings.xml または ${user.home}/.m2/settings.xml に配置します。

http://maven.apache.org/settings.html

settings-security.xml は、暗号化を利用する場合に、暗号化したマスターパスワードを保持しておくファイルです。

下記のようにマスターパスワードを暗号化し、 settings-security.xml に記述します。(サンプルコードで表示されている暗号化された文字列はダミーです。)

mvn --encrypt-master-password <password>

{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}
${user.home}/.m2/settings-security.xml サンプル
<settingsSecurity>
  <master>{jSMOWnoPFgsHVpMvz5VrIt5kRbzGpI8u+9EF1iFQyJQ=}</master>
</settingsSecurity>

サーバーパスワードを暗号化するには、下記コマンドを実行し、出力された文字列を settings.xml に記述します。(サンプルコードで表示されている暗号化された文字列はダミーです。)

mvn --encrypt-password <password>

{COQLCE6DU6GtcS5P=}
settings.xml サンプル
<settings>
...
  <servers>
    <server>
      <id>my.server</id>
      <username>foo</username>
      <password>{COQLCE6DU6GtcS5P=}</password>
    </server>
  </servers>
...
</settings>

詳細は下記をご確認ください。

http://maven.apache.org/guides/mini/guide-encryption.html

自前の Nexus Repository manager の準備

さて、いきなり OSSRH へのデプロイを試すには不安があるので、自前でリポジトリを建てて練習してみます。

OSSRH は、リポジトリをホストするために Sonatype Nexus Repository Manager を利用しているので、ここでもそれを使います。バージョンは最新版は 3 ですが、OSSRH と併せて 2 を選定します。ここでは構築・設定について詳しく扱いませんが、下記が参考になりました。

https://help.sonatype.com/repomanager2/installing-and-running

https://qiita.com/masanori1102/items/c4a2d79321fef84279f5

Repository manager を立ち上げたら、Maven2 用のリポジトリを追加します。

接続情報は「Summary」タブを開くと参照できます。

リポジトリに接続するための設定

リポジトリへの接続情報を、 pom.xmlsettings.xml に記述します。

プロジェクトの pom.xml には <distributionManagement> ブロックを記述し、リポジトリのURLを指定します。 <repository><snapshotRepository> の違いは、後者は <version> の末尾に -SNAPSHOT が含まれている場合のデプロイ先になります。

そして、 settings.xml に、リポジトリへの認証に利用するユーザー名とパスワードを記述します。<password> の値は、暗号化したパスワードを指定しましょう。

pom.xml<distributionManagement>.<repository>.<id>settings.xml<servers>.<server>.<id> は一致する文字列を記述します。

自前リポジトリの場合

自前リポジトリの場合は、前述のように Repository manager から確認できます。この記事では、この自前リポジトリへデプロイする前提で進めます。

pom.xml サンプル(自前リポジトリの場合)
<project>
  <groupId>com.example.your-project<groupId>
  <artifactId>your-artifact</artifactId>
  <version>0.0.1</version>
  ...
  <distributionManagement>
    <repository>
      <id>your-repository</id>
      <url>http://<your server>:8081/nexus/content/repositories/your-repository</url>
    </repository>
  </distributionManagement>
</project>
settings.xml サンプル(自前リポジトリの場合)
<settings>
  <servers>
    <server>
      <id>your-repository</id>
      <username>your-username</username>
      <password>{encrypted password}</password>
    </server>
  </servers>
</settings>

OSSRH を対象にする場合

OSSRH を対象にする場合は下記のように記述します。

pom.xml サンプル(OSSRHの場合)
<project>
  ...
  <distributionManagement>
    <snapshotRepository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
  </distributionManagement>
</project>
settings.xml サンプル(OSSRHの場合)
<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>your-jira-id</username>
      <password>{encrypted password}</password>
    </server>
  </servers>
</settings>

詳細は下記をご参考ください。

https://central.sonatype.org/pages/apache-maven.html

GPGによる署名

GPGの署名を行うには、Maven GPG plugin を利用できます。下記を参考に設定します。

https://central.sonatype.org/pages/apache-maven.html#gpg-signed-components

https://maven.apache.org/plugins/maven-gpg-plugin/usage.html#Configure_passphrase_in_settings.xml_with_a_keyname

なお、GPGキーは事前に作成(またはインポート)しておく必要があります。詳しくはこちらをご確認ください。

https://central.sonatype.org/pages/working-with-pgp-signatures.html
pom.xml サンプル
<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-gpg-plugin</artifactId>
      <version>1.6</version>
      <executions>
        <execution>
          <id>sign-artifacts</id>
          <phase>verify</phase>
          <goals>
            <goal>sign</goal>
          </goals>
          <configuration>
            <gpgArguments>
              <arg>--pinentry-mode</arg>
              <arg>loopback</arg>
            </gpgArguments>
          </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
</build>
settings.xml サンプル
<settings>
  <servers>
    <!-- encrypted private key passphrase -->
    <server>
      <id>your.keyname</id>
      <passphrase>clear or encrypted text</passphrase>
    </server>
  </servers>

 ...

  <profiles>
    <profile>
      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>
      <properties>
        <gpg.keyname>${gpg.keyname}</gpg.keyname>
        <gpg.passphraseServerId>${gpg.keyname}</gpg.passphraseServerId>
      </properties>
    </profile>
  </profiles>
</settings>

mvn deploy 中にパスフレーズを入力させる GUI が出てしまう場合の対処

上記の pom.xml サンプルにも記載した下記の部分、これは GPG のバージョンによっては mvn deploy の実行中にパスフレーズを入力させるGUI入力画面が開いてしまう場合があり、それに対処するものです。

<configuration>
  <gpgArguments>
    <arg>--pinentry-mode</arg>
    <arg>loopback</arg>
  </gpgArguments>
</configuration>

これは、 GPG 2.1 から仕様が変わり、 settings.xml からパスフレーズを入力させるには、gpg コマンドの --pinentry-mode というオプションに loopback を指定しないとならなくなったための対処です。詳細は下記記事をご参照ください。

https://myshittycode.com/2017/08/07/maven-gpg-plugin-prevent-signing-prompt-or-gpg-signing-failed-no-such-file-or-directory-error/

Tips: プロパティによって GPG 署名の有無を切り替える

なお、開発時のビルドでは、GPG署名を含める必要はないので、 profile build を使って条件を指定したときのみ含めるようにするとよいようです。下記が参考になります。

https://web-dev.hatenablog.com/entry/maven/plugin/skip-gpg-sign

Maven のタグを使って、普段は GPG プラグインを実行しないようにしておきます。サインするときだけ、コマンドで -DperformRelease=true を渡します。

pom.xml サンプル
<profiles>
  <profile>
    <id>release-sign-artifacts</id>
    <activation>
      <property>
        <name>performRelease</name>
        <value>true</value>
      </property>
    </activation>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-gpg-plugin</artifactId>
          <version>1.6</version>
          <executions>
            <execution>
              <id>sign-artifacts</id>
              <phase>verify</phase>
              <goals>
                <goal>sign</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

デプロイプラグインの設定

デプロイするプラグインは複数あるようで、 nexus-staging-maven-pluginmaven-deploy-plugin が利用できるようです。ここでは、maven-deploy-plugin を利用します。

https://central.sonatype.org/articles/2014/Mar/25/using-a-local-repo-manager-and-ossrh/
pom.xml サンプル
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-deploy-plugin</artifactId>
        <version>2.8.2</version>
      </plugin>
    </plugins>
  </build>

コマンドによるデプロイ

ここまで準備ができたら、デプロイしてみましょう!

ターミナルでデプロイコマンドを実行します。

mvn clean deploy

GPG署名を前述の方法で切り替えする方は、下記のようにオプションを指定してください。

mvn clean deploy -DperformRelease=true

無事にビルドが進み、 BUILD SUCCESS が表示されれば成功です!🎉

[INFO] --- maven-deploy-plugin:2.8.2:deploy (default-deploy) @ your-artifact ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:17 min
[INFO] Finished at: 2020-12-01T05:52:12Z
[INFO] ------------------------------------------------------------------------

Epilogue

この内容は、もともとある自動化のコードをもとに担当を引き継いだので、正解があるから何とかなるだろーって思ったらなかなか大変でしたw

苦戦したのは GPG 署名… どうしてもパスフレーズのダイアログが表示されて悩みあぐねてやっと解法を見つけた次第です。今動いている環境では、GPGのバージョンが古いかどこかでよしなにされているのかも… 後ほど余力があれば調べてみようと思います。

ということで、本当はこれを GitHub Actions のワークフローに仕立てたいので、それはまた別の記事にしたためます!お楽しみに!🙌