Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Scalaのアーティファクトの公開を楽にするツールscala-pgp-bootstrapを作成した

Maven Centralへのアーティファクト登録の際に必要になるPGP鍵の生成と公開、そしてGitHub Actionsシークレットへの登録を同時にやってくれるツールとして、scala-pgp-bootstrapを書いた。

github.com

使い方は簡単で、Coursierを持っているならcs launch dev.capslock::scala-pgp-bootstrap:<VERSION>(執筆時点最新は0.0.2)で起動できる。依存ライブラリの都合で、Java 21以上が要件に入っている。

この記事はScala Advent Calendar 2025の10日目の記事です。

qiita.com

このツールを書くに至った背景

Scalaは基本的に*1JVM型の言語であり、アーティファクトはJARファイルで提供されている。

その配布基盤・ホスティングサービスとして標準的に利用されているのがMaven Centralであり、Javaなどと同じメカニズムでJARファイルをパブリッシュしたりダウンロードしたりしている。要するに、RubyのRubyGems.orgであり、PerlのCPANであり、JavaScriptのnpmjs.comに相当するものが、Maven Centralなのである。

Maven CentralはPGP署名を要求する

そのMaven Centralだが、アーティファクトそのものに加えてアーティファクトのPGP署名が要求される。アーティファクトがどの出自であるか、本当に作者からアップロードされたものなのかを証明するために、いわば出生証明書としての役割がこの署名には担わされている。この機構はDebianのソフトウェアリポジトリでも同じである。最近はサプライチェーン攻撃なども話題になっており、ソフトウェアの安全性においてアーティファクトの真正性は重要である。

実例を見てみよう。今回紹介するscala-pgp-bootstrapが保管されているディレクトリはhttps://repo1.maven.org/maven2/dev/capslock/scala-pgp-bootstrap_3/0.0.2/に保管されている:

ディレクトリにはコンパイルされたクラスファイルが格納されたJAR、ソースコードが格納されたJAR、ドキュメントが格納されたJAR、ライセンスやライブラリ依存情報が記されたPOMファイルが配置されており、そしてそれらのそれぞれに対してMD5/SHA1によるダイジェストファイルと、それに対するPGP署名ファイルが配置されていることがわかる。

このアーティファクトが正当な署名を持っていることはgpgを利用すると確認できる:

% wget https://repo1.maven.org/maven2/dev/capslock/scala-pgp-bootstrap_3/0.0.2/scala-pgp-bootstrap_3-0.0.2-javadoc.jar
2025-12-10 01:15:38 (8.03 MB/s) - ‘scala-pgp-bootstrap_3-0.0.2-javadoc.jar’ saved [2735142/2735142]

% wget https://repo1.maven.org/maven2/dev/capslock/scala-pgp-bootstrap_3/0.0.2/scala-pgp-bootstrap_3-0.0.2-javadoc.jar.asc
2025-12-10 01:15:46 (42.6 MB/s) - ‘scala-pgp-bootstrap_3-0.0.2-javadoc.jar.asc’ saved [228/228]

% gpg --recv-keys  --keyserver hkps://keyserver.ubuntu.com 3B64A39AAF8E07EC9C273DFF4F311A7F31E6CCC3
gpg: key 4F311A7F31E6CCC3: public key "windymelt/scala-maven-central-pgp-key-bootstrap CI bot <windymelt@capslock.dev>" imported
gpg: Total number processed: 1
gpg:               imported: 1

% gpg scala-pgp-bootstrap_3-0.0.2-javadoc.jar.asc
gpg: Signature made 20251209022850秒 JST
gpg:                using EDDSA key 3B64A39AAF8E07EC9C273DFF4F311A7F31E6CCC3
gpg: Good signature from "windymelt/scala-maven-central-pgp-key-bootstrap CI bot <windymelt@capslock.dev>" [unknown]
Primary key fingerprint: 3B64 A39A AF8E 07EC 9C27  3DFF 4F31 1A7F 31E6 CCC3

PGP鍵を作成するのが面倒

さて、PGP署名をする必要があることが分かったところで、ではどの鍵を利用すれば良いのかという疑問が浮かんでくる。有名なsbt用のパブリッシュプラグインであるsbt-ci-releaseによれば、プロジェクトごとに鍵を作成するのが良い、としている。というのも、CIで自動的に署名されることを想定しているため、開発者個人に紐付いた秘密鍵をGitHubにアップロードするのは危険だからだ。

このため、プロジェクトをいざリリースしてパブリッシュするぞというときには最初にPGP鍵を作成しなければならない。しかしこの手順がやや面倒で、しかも公開鍵を鍵サーバに送ったり、Base64でエンコードしてGitHubのActions Secretに設定しなければならない。毎日やるなら覚えるだろうが、たまにプロジェクトを公開したいという時に限ってこういう面倒な手順は忘れてしまうのだ。

アーティファクト公開に必要な作業は他にもあり、この面倒さがScalaのアーティファクト公開のハードルを引き上げているなー、という感覚があり、PGP鍵の部分だけでもなんとかしたかった。

scala-pgp-bootstrap

そこで作成したのがscala-pgp-bootstrapである。このツールは以下の3つの処理を自動化する:

  • gpgコマンドを呼び出してPGP鍵を作成する
    • ユーザのkeyringを汚染せず、ローカルに作成したkeyringで作業する
    • 認証や暗号化の機能を持たない、署名専用の鍵を生成する
    • ランダムなパスワードを設定する
  • 公開鍵を鍵サーバに送信し、他のユーザが取得できるようにする
  • 秘密鍵とパスフレーズをGitHubに送信し、リポジトリのGitHub Actions Secretとして設定する

必要な事項はインタラクティブに質問されるようになっているので、基本的に複雑な操作をすることなく鍵の作成ができるようになっている。

また、処理対象のリポジトリは自動検出されるほか、PGP鍵の作成に必要なメールアドレスの候補はGitHubから自動的に取得してくれる。このため、基本的にツールを起動してからはyを押していればよい。

ちなみに、このツールのリリースに必要なPGP鍵もこのツールで生成して登録したものを使った。まさにブートストラップしたわけだ。

使い方

使い方といっても、cs launch dev.capslock::scala-pgp-bootstrap:<VERSION>と打つだけで後は勝手にやってくれるのだが、前後の手順としては以下の通りである。

  • 事前準備(ユーザあたり一度やっておけば良い)
    • Maven Centralのアカウントを作成しておく
    • Maven CentralでNamespaceを取得する
  • sbt-ci-releaseをセットアップしておく
  • scala-pgp-bootstrapで鍵を発行する
  • もし個人のPGP鍵を持っているなら、CI用の鍵に署名して正統性を持たせる
  • Maven CentralでUser Tokenを発行してGitHub Actionsに登録する

このあたりは改めて記事にして解説したい。

*1:近年はScala.jsやScala Nativeなど、ECMAScriptにビルドするものやLLVM経由でネイティブコンパイルできる基盤も整っている

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?