feature image

2025年9月2日 | ブログ記事

自分のドメインがGoのモジュールパスに入ってたらかっこよくないですか? はい、かっこいいです

この記事はtraP 2025 夏のブログリレーの 16 日目の記事です。

ikura-hamu です。traP では主に SysAd 班でサーバーアプリケーションを書いています。Go が好きです。

この記事では、GitHub Pages を 使って、Go のモジュールを自分が所持しているドメインで配布する方法を書きます。つまり、

go run {自分のドメイン}/some/cmd@latest

みたいなことができる、ということです。かっこいいですね。

Go のモジュール

Go のモジュールとは、ざっくり言うとパッケージの集合です。Go は 1 つのディレクトリに原則 1 つのパッケージが存在しますが、これをまとめたものがモジュールであり、go.mod ファイルによってその識別子や依存関係が管理されています。

https://go.dev/ref/mod#introduction

よくあるモジュールパス

モジュールの識別子(パス)は go.mod の module という項目に書かれています。大体のサードパーティのモジュールは GitHub のリポジトリの URL から https:// を取り除いたやつになっています。例えば github.com/jmoiron/sqlxgithub.com/spf13/cobra です。パッケージのパスはこのモジュールのパスにディレクトリ名をつなげたものになります。

この仕組みは別に GitHub に依存しているわけではありません。Bitbucket のような他の Git ホスティングサービスでもよいですし、なんなら Git である必要すらありません。公式ドキュメントによると、Git、Subversion、Mercurial、Bazaar、Fossil が対応しており、モジュールパスに VCS qualifier(.git など) が含まれていた場合は自動で認識してそれを使ってくれるようです。

https://go.dev/ref/mod#vcs

また、GitHub などのよくある VCS のホスティングサービスでは、VCS qualifier がなくてもパッケージパスからいい感じに依存を解決してくれます。

https://pkg.go.dev/cmd/go#hdr-Remote_import_paths

実際には Module Proxy というサーバーが存在してそこにモジュールがキャッシュされているので、キャッシュが存在しないときのみ、VCS へのリクエストが飛ぶことになります。

VCS 以外の URL を使ったモジュールパス

多くのモジュールが github.com を使っている一方で、それ以外のモジュールもちょくちょくあります。たとえば準標準パッケージの golang.org/x 以下のモジュールや gorm.io/gormrsc.io/omap などです。これらのモジュールパスには VCS qualifier も含まれていません。このようなときは、go コマンドはモジュールパス(もしくはパッケージパス)に対して、https://example.org/pkg/foo?go-get=1 のような HTTP リクエストを送り、そのレスポンスの HTML の go-import という名前の meta タグを調べます。

https://go.dev/ref/mod#vcs-find

たとえば、https://golang.org/x/mod?go-get=1 のようなリクエストを送ると以下のような HTML が返ってきます。

<!DOCTYPE html>
<html lang="en">
<title>The Go Programming Language</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="golang.org/x/mod git https://go.googlesource.com/mod">
<meta http-equiv="refresh" content="0; url=https://pkg.go.dev/golang.org/x/mod">
</head>
<body>
<a href="https://pkg.go.dev/golang.org/x/mod">Redirecting to documentation...</a>
</body>
</html>

<meta name="go-import" content="golang.org/x/mod git https://go.googlesource.com/mod"> という要素があるのが分かると思います。これは、リクエストされたモジュール(もしくはパッケージ)のモジュールパスは golang.org/x/mod で、VCS には Git を使っており、リポジトリのルートは https://go.googlesource.com/mod にある、という意味になります。go コマンドはこれを読んでリポジトリにソースコードを取りに行きます。この HTML ファイルによって、GitHub で開発を行いつつモジュールを好きなドメインで配布できるようになります。

これを実現するためのツールとして、rsc.io/go-import-redirector というモジュールがあります。これは 動的な HTTP サーバーで、リクエストのURIに対して HTML を組み立てて返す、という動作をします。

GitHub - rsc/go-import-redirector: HTTP server for a custom ‘go get’ domain
HTTP server for a custom ‘go get’ domain. Contribute to rsc/go-import-redirector development by creating an account on GitHub.

作ったもの

rsc.io/go-import-redirector は動的なアプリなため、それを動かすサーバーが必要です。Go Module Proxy もあって直接リクエストが来ることは少ないので、Google Cloud の Cloud Run の無料枠の範囲に収まりそうではありますが、内容の変化しない HTML を配信するだけでそのようなサーバーを動かすのはもったいない気がするので、HTML を組み立てて GitHub Pages で HTML を配信する GitHub Actions の Action を作りました。

https://github.com/ikura-hamu/go-import-pages

GitHub - ikura-hamu/go-import-pages: Go custom domain import path with GitHub Pages
Go custom domain import path with GitHub Pages. Contribute to ikura-hamu/go-import-pages development by creating an account on GitHub.

ざっくりとした使い方

詳しい使い方はドキュメントの方を確認してほしいのですが、ざっくりとした説明をします。
現時点では、1 つのドメインから複数のモジュールを配布することを考えています。そのような場合は、Go のソースコードを管理するリポジトリとは別に、HTML ファイルを管理するリポジトリを用意する必要があります。それぞれのリポジトリでこの Action を使った GitHub Actions のワークフローを書き、ソースコードが更新されたら HTML も必要に応じて更新するようにします。

例として https://github.com/ikura-hamu/card (Go のソースコード) と https://github.com/ikura-hamu/go.ikura-hamu.work (HTML)というリポジトリを作ったので参考にしてください。go.ikura-hamu.work/card からモジュールが配信されるような設定になっています。以下のコマンドを実行することで、ターミナル上で動く名刺アプリケーションが起動します。

go run go.ikura-hamu.work/card@latest

card

仕組み

この Action (群)の処理の流れを説明します。

1. モジュールの情報を取得する

Go のモジュールを管理するリポジトリで、モジュール名とモジュールに含まれるパッケージ名を調べます。これには go list コマンドを使うことができ、モジュール名は go list -m 、パッケージ名は go list ./... で取得できます。

2. モジュールの情報を通知する

1 で得た情報を元に HTML を生成しますが、Go のリポジトリと HTML のリポジトリは別なため、GitHub Actions の Workflow を呼ぶには何かしらの方法で通知する必要があります。GitHub Actions では repository_dispatch というイベントを使うことができます。

https://docs.github.com/ja/actions/reference/workflows-and-actions/events-that-trigger-workflows#repository_dispatch

このイベントを使うことで、任意のペイロードを持ったメッセージを送って Workflow から別リポジトリの Workflow を起動できます。しかし、このイベントを発行するためには、受け取り側のリポジトリへの contents:write のスコープを持った Personal Access Token や GitHub App Token が必要になります。このスコープはかなり強力なものになっており、トークンが漏れてしまったときのことを考えると心配です。

そこで、今回の実装では Issue comment を使った通知を行っています。Go のリポジトリの Workflow から HTML のリポジトリの指定された Issue にコメントを書きこみます。HTML のリポジトリでは、Issue のコメントが作成されたときに起動する Workflow を用意し、その中でソースコードの書き換えを行っています。この場合は、HTML のリポジトリへの issues:write のスコープを持ったトークンが必要になります。トークンが必要なのは変わりないですが、contents:write に比べて漏洩したときの影響が小さくすむと考えられます。

この実装のアイディアは、csm-actions/securefix-action を参考にしました。こちらは GitHub Actions の Artifact と Issue label を使った、リポジトリ間で Workflow を呼び出す、よりセキュリティが強い方法になっていますが、GitHub App が2つ必要になるなど手間がかかると考えたため、容易に実装できる Issue comment を使った方法を採用しました。

GitHub - csm-actions/securefix-action: GitHub Action to fix code securely
GitHub Action to fix code securely. Contribute to csm-actions/securefix-action development by creating an account on GitHub.

3. HTML を更新する

HTML のリポジトリでは、受け取ったモジュールの情報をもとに、HTMLファイルを生成し、差分が生じたらコミットしたり PR を作ったりします。

GitHub Pages へのデプロイは自分で書く必要があります。ですが、HTML を生成するだけなので、自分のドメインを設定した他のホスティングサービスからも配信できるということになります。

おわり

自分の好きなドメインでモジュールを配布できるようになって嬉しいですが、自分が持っているドメインが長い (ikura-hamu.work)ので、どう頑張っても rsc.io とか gorm.io とか go.uber.org みたいにかっこよくならないことに気づいてしまいました。短いドメインを持っている方はぜひ検討してみてください。今後も開発を続けていこうと思います。

夏のブログリレー明日の担当は @mutv625 さんです。

参考にした記事

パッケージのimport pathを好みのURLにする - Plan 9とGo言語のブログ
この記事はQiitaで公開されていました Goのパッケージ名は、リポジトリのURLをそのまま使うことが多いと思いますが、リポジトリはそのままで、別のURLをパッケージ名にすることもできます。 例えば、あるパッケージのリポジトリをGitHubに置いていて、リポジトリのURLは github.com/lufia/ken_all だとします。 (現在、上記URLは存在しません) 通常は、 import ( “github.com/lufia/ken_all” ) のようにパッケージをimportします。 ですが、 ホスティング先が変わってもパッケージパスを変えたくない 名前に ‘_’ が含まれていて…
S3 + CloudFrontで実現する独自ドメインGoパッケージ | blog.miyamo.today
はじめに go.uber.org/mock google.golang.org/grpc gorm.io/gorm 独自ドメインで配布されているGoパッケージはカッコいい 、 のような のPrefixがない分、importディレクティブがスッキリして見える 今回は当ブログのバ…
ikura-hamu icon
この記事を書いた人
ikura-hamu

SysAd班、ゲーム班 いろいろやりたい

この記事をシェア

このエントリーをはてなブックマークに追加
共有

関連する記事

2021年8月12日
CPCTFを支えたWebshell
mazrean icon mazrean
2021年5月19日
CPCTF2021を実現させたスコアサーバー
xxpoxx icon xxpoxx
2024年6月21日
ハッカソン参加記 4班"Slide Center"
Alt--er icon Alt--er
2024年3月15日
個人開発として2週間でWebサービスを作ってみた話 〜「LABEL」の紹介〜
Natsuki icon Natsuki
2023年12月25日
オレオレRustプロジェクトテンプレート
H1rono_K icon H1rono_K
2023年10月20日
DIGI-CON HACKATHON 参加記事「Comic DoQ」
mehm8128 icon mehm8128
記事一覧 タグ一覧 Google アナリティクスについて 特定商取引法に基づく表記