GCPを使って公開のmavenリポジトリを作る(Gradle)

Gradleには標準機能でS3やCloud Storageをmavenリポジトリとして扱う機能がついています。
社内や個人でプライベートなリポジトリとして活用する例は見られるのですが、今回は一般公開設定にしてPublicにしてみます。

Cloud Storageの料金は[こちら](https://cloud.google.com/storage/pricing?hl=ja)。実験的にいくつかファイルを置いているくらいであれば0円になることもあります。
アイオワリージョンであれば、公開日時点で1ヶ月あたり$0.020/GBの設定のようです。東京リージョンではもう少し高い値段設定です。

## 環境

* Java 17
* Gradle 8.2

## 事前準備

ファイルをアップロードするためのCloud Storageのバケットを用意してください。
実験的に試すのであれば、シングルリージョンにしておくのが良いと思います。

## `build.gradle.kts`の用意

今回はKotlin DSLで書いていますがGroovyでもほぼ同様のコードで動くと思います。

mavenに公開するので、`maven-publish`のプラグインを使用します。
```kotlin
plugin {
    id("maven-publish")
}
```

Publishの設定は以下のようにします。
今回は`koma-yumemi-resources`のバケットの中の`gcp-maven-test/maven`にファイルをおくことにします。

```kotlin
publishing {
    publications {
        // この辺りは適当に設定して下さい。
        create("mavenJava", MavenPublication::class) {
            from(components.named("java").get())
        }
    }
    repositories {
        maven {
            // ここのURLがCloud Storageにアップロードするために必要です。
            url = uri("gcs://koma-yumemi-resources/gcp-maven-test/maven")
        }
    }
}
```
ここの`MavenPublication::class`の指定はGroovy DSLの場合`MavenPublication.class`とする必要があります。`mavenJava(MavenPublication)`といった書き方でもOKです。

ここではURLを`gcs://example/maven`といった形式で与える必要があります。GCPでよく使われれる`gs://`ではないので、書き換えてください。

## 認証

Gradleからは[アプリケーションのデフォルト認証情報](https://cloud.google.com/docs/authentication/provide-credentials-adc?hl=ja)を使ってGCPにアクセスします。
ローカル環境であれば、`gcloud auth application-default login`のコマンドを実行することで認証ファイルの作成ができます。
内部的には[google-api-clientのライブラリ](https://github.com/googleapis/google-api-java-client)を呼び出しているようです。
[この辺り](https://github.com/gradle/gradle/blob/master/subprojects/resources-gcs/src/main/java/org/gradle/internal/resource/transport/gcp/gcs/GcsClient.java#L176-L189)から認証情報を得ているようです。

サービスアカウントを使用している場合は`GOOGLE_APPLICATION_CREDENTIALS`に設定されたJSONを見て認証してくれるようですが、GitHubのOIDCから得た認証情報を使うと以下のエラーになってしまったので設定が違うのかもしれません。
```
Caused by: java.io.IOException: Error reading credential file from environment variable GOOGLE_APPLICATION_CREDENTIALS, value '.../gha-creds-d5777338b048ce4b.json': Error reading credentials from stream, 'type' value 'external_account' not recognized. Expecting 'authorized_user' or 'service_account'.
```

## 公開

### Publish

`./gradlew publish`を実行するとCloud Storageにアップロードされます。
![gcp-uploaded.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355242/ab657f82-7c04-46ad-a878-0a47f8d25bfe.png)
バケット以下の指定したパスにファイルが保存されています。

### 一般公開の設定

ここではバケット内のすべてのオブジェクトを公開設定にします。
公開したくないファイルが含まれている場合はバケットを分けることをお勧めします。

まず、権限タブからアクセス権の追加を選択します。
![2023-07-20 16.26.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355242/ba31ce06-29b5-560e-9e0c-9add28383e9b.png)

プリンシパルに`allUsers`を指定し、ロールには「Storageオブジェクト閲覧者」を選択します。そして保存をクリックします。
![2023-07-20 16.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355242/fe6a6989-ae08-6fbe-39a3-8031e15e9a35.png)

確認画面が出てくるので、「一般公開アクセスを許可」を選択します。
![2023-07-20 16.27.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355242/c56f46cc-37d7-94c4-82d0-f81c6894d7c5.png)
これで一般公開の操作は完了です。
ファイルの一覧を表示すると、公開アクセスの欄がインターネットに公開となっています。
![2023-07-20 16.27.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3355242/419533a6-7f51-1296-bd70-49cd5dab5d4c.png)


## リポジトリを使用する

公開されたプロジェクトを使用する側ではまずrepositoriesの設定を追加します。
`gcs://koma-yumemi-resources/gcp-maven-test/maven`に公開していた場合は、以下のような設定になります。
```kotlin
repositories {
    maven {
        url = uri("https://storage.googleapis.com/koma-yumemi-resources/gcp-maven-test/maven")
    }
}
```
`https://storage.googleapis.com/` 以下にパスを付け加えるだけです。

その上でdependencyをMaven Centralのプロジェクトと同じように追加します。
これで公開したプロジェクトを他の人が使えるようになりました。

## 参考

* https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:supported_transport_protocols
* https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:gcs-repositories
* https://cloud.google.com/storage/docs/access-control/making-data-public?hl=ja