Java/Kotlin
PR

Mavenリポジトリを簡易的に作るための3つの方法(AWS, GitHub)

saratogax
記事内に商品プロモーションを含む場合があります

Kotlin や Java など、gradle でライブラリ参照する際に利用している Maven リポジトリ。

使う側にはなっても、自分で Maven リポジトリでライブラリを提供する機会がやってくるとは思ってもいませんでした。

Maven Central Repository などパブリック向けに一般公開する場合とは違い、社内や部署限定となると認証の問題が出てきます。

今回は認証(権限)を限定できて、簡易的に作れる方法を検証しましたので備忘録として残しておきます。

Mavenリポジトリの候補

それなりの規模の会社なら、社内にも「NEXUS」や「jFrog」など使えそうな Maven リポジトリが存在している可能性があります。

しかし、都合の良い権限や接続方法が考慮されているかというと難しい・・・。

VPN 接続が必要だったり、そもそも IP 制限されているとかセキュリティで守られていますからね。

結果的に、チーム内で面倒が見れそうな環境に構築するという選択肢に追い込まれるわけです。

自分で権限を管理してリポジトリを公開するとした場合、候補となりそうなのは以下の通り。

  • AWS CodeArtifact
  • GitHub Pages
  • GitHub Packages

どれも手軽な感じではありますが、実際に構築してみると一筋縄でいかないところがありまして・・・。

1 つずつ見ていきましょう。

AWS CodeArtifact

AWS が提供しているフルマネージドのアーティファクトリポジトリサービスです。

「Maven」「Gradle」「npm」「pip」など主要な言語のパッケージ管理に対応しています。

リポジトリの作成はシンプルで、大きく以下の 2 つの命名を決めてしまえば完了となります。

  • ドメイン名
  • リポジトリ名

1 つのドメインに対して、複数のリポジトリを構築していくといった運用が一般的なようです。

AWS CodeArtifactの接続方法

リポジトリを選択した画面の中に、パッケージをプッシュしたりダウンロードするための接続方法が書かれているので、これを参考に接続を試してみましょう。


(ドメイン名、リポジトリ名は伏せてます。ご了承ください)

AWS CodeArtifactの接続方法

mvn の例を見てみましょう。

設定内容がコピーできるようになっていて便利ですね。

AWS CodeArtifactの接続方法

プッシュする場合も簡単です。

ライブラリパッケージを追加する

では早速、上記の設定サンプルを使ってライブラリ(今回はjarファイル)をプッシュ(パブリッシュ)してみましょう。

AWS のサービス特有となるのが接続に必要なトークンです。

このトークンは最大で 12 時間までの有効期限が設定されるので、定期的に再取得が必要となります。

設定サンプルの通り、aws クライアントのコマンドが用意されているので、このまま CODEARTIFACT_AUTH_TOKEN の環境変数へ定義しましょう。

aws codeartifact get-authorization-token

ユーザは aws 固定となるので、settings.xml は以下のようになります。

ドメイン名とリポジトリ名は Artifact の設定時に決めたものになりますが、サンプルに埋め込まれているので設定サンプルをコピペすれば問題ありません。

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
    <servers>
        <server>
            <id>[ドメイン名]-[リポジトリ名]</id>
            <username>aws</username>
            <password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
        </server>
    </servers>
</settings>

maven なので pom.xml も用意します。
(後々、gradle も試してみたいですね)

pom.xml に記述する「distributionManagement」の設定サンプルが、接続情報に書かれているのでこれもコピペで追記します。

<distributionManagement>
  <repository>
    <id>[ドメイン名]-[リポジトリ名]</id>
    <name>[ドメイン名]-[リポジトリ名]</name>
    <url>https://[ドメイン名]-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/[リポジトリ名]/</url>
  </repository>
</distributionManagement>

「src/main/java/com/github/example」配下に、定番の HelloWorld.java でも作ってみますか。

ざっくりですが、pom.xml の最終形を以下としておきます。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.example</groupId>
    <artifactId>hello-world</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
    <name>HelloWorld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <distributionManagement>
        <repository>
            <id>[ドメイン名]-[リポジトリ名]</id>
            <name>[ドメイン名]-[リポジトリ名]</name>
            <url>https://[ドメイン名]-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/[リポジトリ名]/</url>
        </repository>
    </distributionManagement>

</project>

mvn コマンドでプッシュしちゃいましょう。

$ mvn deploy -s settings.xml

これで、CodeArtifact のリポジトリの中のパッケージ一覧に反映されているハズです。

ライブラリパッケージを取得する

ライブラリを取得する際にもトークンが必要になってきます。

よって、ライブラリを取得する側にも AWS に接続するための権限が必要。

CodeArtifact の getAuthorizationToken が実行できる権限(IAM ポリシーなど)を追加しておきましょう。

settings.xml の参考例を紹介しておきます。

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
    <profiles>
        <profile>
            <id>[ドメイン名]-[リポジトリ名]</id>
            <activation>
            <activeByDefault>true</activeByDefault>
            </activation>
            <repositories>
            <repository>
                <id>[ドメイン名]-[リポジトリ名]</id>
                <url>https://[ドメイン名]-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/[リポジトリ名]/</url>
            </repository>
            </repositories>
        </profile>
    </profiles>

    <servers>
        <server>
            <id>[ドメイン名]-[リポジトリ名]</id>
            <username>aws</username>
            <password>${env.CODEARTIFACT_AUTH_TOKEN}</password>
        </server>
    </servers>

</settings>

ライブラリを参照する java ファイル(App.java)は「src/main/java/com/github/example/app」配下に作成してみましょうか。

以下のように import でライブラリを参照するソースコードにしておけば中身は何でも大丈夫です。

package com.github.example.app;

import com.github.example.HelloWorld;

pom.xml は接続情報の設定サンプルを参考に構成します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.example.app</groupId>
    <artifactId>app</artifactId>
    <version>0.0.1</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>[ドメイン名]-[リポジトリ名]</id>
            <name>[ドメイン名]-[リポジトリ名]</name>
            <url>https://[ドメイン名]-[AWSアカウントID].d.codeartifact.ap-northeast-1.amazonaws.com/maven/[リポジトリ名]/</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.github.example</groupId>
            <artifactId>hello-world</artifactId>
            <version>0.0.1</version>
        </dependency>
    </dependencies>
</project>

mvn コマンドでビルドして実行してみます。

$ mvn compile exec:java -s settings.xml -Dexec.mainClass=com.github.example.app.App

トークンを取得していなかったり有効期限が切れていると、以下のように 401 エラーが発生するので注意してください。

Authentication failed for [ライブラリの pom ファイルのエンドポイント] 401 Unauthorized

トークンの取得さえ手間にならなければ、ライブラリを提供する側も利用する側も、それほど負担なく使うことができそうですね。

ドメインやリポジトリに対するポリシーも設定できるようなので、権限周りはもう少し細かい対応が可能でしょうか。

GitHub Pages

GitHub Pages を利用して簡易的に Maven リポジトリとして利用しようという方法もあるようです。

動きが少し特殊で、mvn deploy で一旦ローカルにデプロイし、その後 mvn site で GitHub に反映します。

しかも、maven plugin の最終更新が 2015 年と古いので少し不安な部分もあります。

なお、GitHub Pages はリポジトリのデフォルト設定で無効化されているので注意しておきましょう。
(public か private が選択可能ですが internal は存在しない)

ライブラリのサンプルコードは先ほどと同じものを使用するとして、ここでは設定ファイルを中心に紹介します。

ライブラリパッケージを追加する

認証には github ユーザのパーソナルアクセストークンが必要になりますが、権限も用途に合わせて追加してあげます。

  • repo(Full control)
  • user:email(read-only)

「user:email」の権限については、本当に必要なのかわかりませんが、別途エラーにハマったので切り分けのため付与しています。

この権限の必要有無については以下の公式の Issue で確認してみてください。

まずは認証に必要な settings.xml から作成していきましょう。

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      https://maven.apache.org/xsd/settings-1.0.0.xsd">
    <servers>
        <server>
            <id>github</id>
            <password>[パーソナルアクセストークン]</password>
        </server>
    </servers>
</settings>

pom.xml の設定が厄介で、maven plugin の記述に悩まされました。

完成したファイルを紹介します。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.example</groupId>
    <artifactId>hello-world</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
    <name>HelloWorld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <github.global.server>github</github.global.server>
    </properties>

    <distributionManagement>
        <repository>
            <id>internal-repo</id>
            <name>Internal Repository</name>
            <url>file://${project.build.directory}/mvn-repo</url>
        </repository>
    </distributionManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>com.github.github</groupId>
                <artifactId>site-maven-plugin</artifactId>
                <version>0.12</version>
                <executions>
                    <execution>
                    <goals>
                        <goal>site</goal>
                    </goals>
                    <phase>site</phase>
                    <configuration>
                        <server>github</server>
                        <repositoryName>[githubのリポジトリ名]</repositoryName>
                        <repositoryOwner>[githubのオーナー名(ユーザや組織)]</repositoryOwner>
                        <message>[コミットメッセージ]</message>
                        <outputDirectory>${project.build.directory}/mvn-repo</outputDirectory>
                        <branch>refs/heads/[ブランチ名(main や master など)]</branch>
                        <path></path>
                        <merge>true</merge>
                    </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>3.7.1</version>
                <configuration>
                    <locales>en</locales>
                    <inputEncoding>UTF-8</inputEncoding>
                    <outputEncoding>UTF-8</outputEncoding>
                    <outputDirectory>${project.build.directory}/mvn-repo</outputDirectory>
                </configuration>
                <dependencies>
                    <dependency>
                    <groupId>org.apache.maven.doxia</groupId>
                    <artifactId>doxia-site-renderer</artifactId>
                    <version>1.9.1</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>3.0.0</version>
            </plugin>
        </plugins>
    </build>
</project>

リポジトリ名やオーナー名を間違えていると、creating blob のところで 404 エラーになるので注意が必要です。

[ERROR] Failed to execute goal com.github.github:site-maven-plugin:0.12:site (default) on project hello-world: Error creating blob: Not Found (404)

GitHub Packages

こちらは GitHub Pages と違って標準で利用できるサービス。

Maven リポジトリにも対応していますね。

GitHub Packages Maven リポジトリにも対応しています

GitHub Pages 同様、認証には github のユーザのパーソナルアクセストークンが必要になりますが、必要な権限が少し変わってきます。

  • repo(Full control)
  • write:packages

ちなみに、認証に問題がある場合は 403 エラーとなります。

二段階認証などを設定している場合は、認証の状態を確認しておきましょう。

ライブラリパッケージを追加する

1 つだけ罠があって、pom.xml の artifactId を設定する文字列に、大文字が含まれていると 422 エラーが発生します。

これはエラー原因がわかりにくい・・・。

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project HelloWorld: Failed to deploy artifacts: Could not transfer artifact (途中省略) HelloWorld/0.0.1/HelloWorld-0.0.1.jar 422 Unprocessable Entity

pom.xml の参考例です。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github.example</groupId>
    <artifactId>hello-world</artifactId>
    <version>0.0.1</version>
    <packaging>jar</packaging>
    <name>HelloWorld</name>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <distributionManagement>
        <repository>
            <id>github</id>
            <name>GitHub Apache Maven Packages</name>
            <url>https://maven.pkg.github.com/[オーナー名]/[リポジトリ名]</url>
        </repository>
    </distributionManagement>
</project>

ライブラリパッケージを取得する

ライブラリの参照だけがしたい場合、「write:packages」の権限は不要で、「read:packages」だけで問題ありません。

プッシュする側と大きく変わるところはありませんが、pom.xml にライブラリの情報だけ追記してあげましょう。

<dependencies>
    <dependency>
        <groupId>com.github.example</groupId>
        <artifactId>hello-world</artifactId>
        <version>0.0.1</version>
    </dependency>
</dependencies>

あとは mvn コマンドでビルドが正しくできれば問題ありません。

$ mvn clean

$ mvn compile

まとめ

手軽に使えそうな Maven リポジトリとして、以下の 3 つを紹介してきました。

  • AWS CodeArtifact
  • GitHub Pages
  • GitHub Packages

最終的に CI/CD で活用する場面も出てくるので、親和性が高いのは GitHub Packages になるんでしょうか。

GitHub Actions を使う機会も多いですしね。

AWS はリポジトリがマネージドなので便利ですが、AWS 都合でバージョンアップなどに巻き込まれるリスクもあります。

セルフホストランナーを使えば AWS との連携も苦じゃないのですが・・・。

どこまで本格的にリポジトリ運用をするかによりますが、プロジェクトの特性に合わせて検討してみたいですね。

ABOUT ME
saratoga
saratoga
フリーランスエンジニア
仕事にも趣味にも IT を駆使するフリーランスエンジニア。技術的な TIPS や日々の生活の中で深堀りしてみたくなったことを備忘録として残していきます。
記事URLをコピーしました