Julia パッケージをいわゆる公式パッケージとして使ってもらう方法 2021年 12 月版

# 本日は

- 自作 Julia パッケージを公式パッケージとして使ってもらう方法を紹介します.

# 前置き

- 例えば `YourPkg.jl` という Julia パッケージを作ったとします. それが自分だけでなく，他の人にも使ってもらう価値があるものだと信じてるとします. もちろんバージョン管理がGitHubにて行われている場合は

```julia
pkg> add https://github.com/<your github account>/YourPkg.jl
```

のように `add リポジトリのURL` としてやれば使ってもらうことができます. ですが Julia 公式パッケージとして登録すると

```julia
julia> using Pkg; Pkg.add("YourPkg")
julia> using YourPkg
julia> YourPkg.something_great(args) # なんかすごいことをしてくれる機能
```

というような感じで使ってもらうことができます.

`Pkg.add("YourPkg")` のようにサクッと入れることができるものをいわゆる`公式パッケージ`と呼ぶことが多いです. そうじゃないものを`野良パッケージ`と呼んだりもしますね. 

一応補足すると公式パッケージというのは難しく考えることをせずに `Pkg.add("YourPkg")` とできるものを単に公式パッケージという名前で呼んでいるだけで 「JuliaLang の中の人たちが厳密にチェックしてるからヨシ！（現場猫風）」とか「お墨付き」がもらえるとかそういう意味ではないです.

## レジストリのお話

そもそも論として

```julia
julia> using Pkg; Pkg.add(”Example”)
```

とするとこれの大元は https://github.com/JuliaLang/Example.jl.git のことだと認識できるのはなぜでしょう？ これはパッケージレジストリ（または単にレジストリ）というパッケージの情報（ソフトウェアバージョン x.y.z はどの時点のコードをチェックアウトすればよいか）と依存関係（このパッケージはどのパッケージに依存しているか）を格納するデータベースを Julia が参照しているからです. 

### それってどこにあるの？

いい質問ですね. ターミナルを開いて下記のコマンドを実行してみてください:

```julia
$ cd ~/.julia/registries
$ ls
General
$ cd 
$ General/E/Example
$ Compat.toml   Deps.toml     Package.toml  Versions.toml
```

とすると https://github.com/JuliaLang/Example.jl.git に関するパッケージ情報と依存関係に関する TOML で記述された設定ファイルが見えると思います.
この `General` というのが先ほど説明したレジストリというものです. Pkg.add すると自動的に降ってきます.

```julia
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.6.3 (2021-09-23)
 _/ |\__'_|_|_|\__'_|  |  Official https://julialang.org/ release
|__/                   |

(@v1.6) pkg> add Example
    Updating registry at `~/.julia/registries/General` # <--- この子
```

### General ってどこから降ってくるの？

それもよい質問ですね. https://github.com/JuliaRegistries/General というリポジトリからやってきます. README.md を見ると次のように書かれています:

>   General is the default Julia package registry. Package registries are used by Julia's package manager Pkg.jl and includes information about packages such as versions, dependencies and compatibility constraints.

つまり General は Julia のデフォルトのパッケージレジストリということになります. Pkg.jl はデフォルトで General の中身を参照するので何も考えず `Pkg.add("Example")` ができるわけです.

### ということは

- General ってところに「自分のJuliaパッケージの情報を追加」するプルリクエストを送ればいいのでは？？？

その通りです. (他にもオレオレレジストリを作るというてもありますね．ここではこれについては触れません.)

### まとめると

1. 自分の考えた最強のJuliaパッケージを作る
2. 体裁を整えて General に登録を申請する

ざっくりするとこの手続きによって晴れていわゆる公式パッケージとして使ってもらえます.

3. 作ったものを宣伝する

これも大事ですね.

はい．これで前置きはお終しまい(ケル)

# 具体的には？

さてようやく本題に入れます. ここでは系統的な説明のためにJuliaの野良パッケージの作り方からみていきましょう．

画質めっちゃ悪いですが

https://www.youtube.com/watch?v=KlXNVgv6b24&t=2689s
https://www.youtube.com/watch?v=RM2H2wyBl_o&t=2215s

でメイキング動画を撮っています. 動画の方は作業の雰囲気だけ掴んでもらえればOKです. （トホホ...）手順はQiitaで説明します.
ちなみに音源は All Musics by Khaim https://www.khaimmusic.com/ でございます.

# 雛形を作る

Julia のパッケージとして最低限の体裁は 

```julia
julia> using Pkg; Pkg.generate("YourPkg")
```

によって実現できます. 上記の手続きによって

`YourPkg/src/YourPkg.jl`, `Project.toml` ができます. 

自分だけの用途であればこれでよいですが，いわゆる公式パッケージとしてみんなに使ってもらう意図があるものは

- パッケージの概要の説明がされていること
  - 他人に使ってもらうに足り得る価値がある, 仕組みが整っているかを理解できるようになっているか
  - READMEの充実 or Documenter.jl などでドキュメントが書かれていること
- 適切にバージョン管理がなされている
  - GitHub や GitLab で管理されている
  - Project.toml に依存関係が記述されている
- ソフトウェアテストがされている
  - `test/runtests.jl`
  - GitHubActions などの CI 環境でテストがされているか

のようなものを揃えておくのが望ましいです.

[PkgTemplates.jl](https://github.com/invenia/PkgTemplates.jl) を使うと上記を満たすための雛形を作ってくれます.

約２年前に書いた

[PkgTemplates による Julia パッケージの作り方（前半）](https://qiita.com/SatoshiTerasaki/items/24443f20852ed60968bf)
[PkgTemplates による Julia パッケージの作り方（後半）](https://qiita.com/SatoshiTerasaki/items/f43a7730bcf57f4b9572)
[GitHubActions と組み合わせる方法](https://qiita.com/SatoshiTerasaki/items/88ff6a1e9351cec6aafe)

記事もあわせて読むとよいかなと思います. 

## スクリプトの用意

2021 年版は下記のようにすればOKです

```julia
using PkgTemplates

t = Template(;
    dir = pwd(),
    julia = v"1",
    plugins = [
        License(; name = "MIT"),
        Git(; manifest = false, ssh = true),
        GitHubActions(;
            extra_versions = ["1.6", "1.7", "nightly"]
        ),
        Documenter{GitHubActions}(),
        Readme(;
            inline_badges = true,
            badge_order = DataType[
                GitHubActions,
                Documenter{GitHubActions},
            ]
        ),
    ]
)

t("YourPkg") # ここの名前は適宜修正する
```

上記のスクリプトを実行すると `YourPkg` ディレクトリが生まれて

- 実装を格納する `src` ディレクトリ
- `src` の内容が期待した動作になるかを確認する `test` ディレクトリ
- ドキュメントを格納する `docs`
- README.md (言わずもがな)
- LICENSE ライセンスファイル
- `.github/workflows` ディレクトリ

が出来上がります. 

もしも `using PkgTemplates; t = Template()` の時点で Git 云々のエラーが生じた場合は

```console
$ git config --global github.user <your-github-account>
``` 

を試してください

参考: [このIssueのコメント](https://github.com/invenia/PkgTemplates.jl/issues/327#issuecomment-985982405)


## 名前づけ

Julia でのパッケージ命名に関するガイドラインは

https://pkgdocs.julialang.org/dev/creating-packages/#Package-naming-guidelines-1

にあります．既存のパッケージとバッティングしたり `A` とか短すぎたりしてもダメです. ここで名づけをよく悩んでください.

# 開発の開始

さてやっとスタートライン立てました. `src` と `test` を充実させていきましょう.

## 機能追加

既に各自で自分の考えた最強のJulia実装・アルゴリズムがあると思うので

`src/YourPkg.jl` の中身に機能を追加していきます.

機能追加していく一方でテストも書いていきましょう.

`test/runtests.jl` の中身に期待する動作するソースコードを書いていきます.

```julia:runtests.jl
using Test
using YourPkg

@testset "something great" begin
    @test YourPkg.something_great(args) == expectedvalue
end
```

こんな感じのを書きながら

```
# julia --project=@. で REPL を起動し julian mode から Pkg REPL へ `]` によって切り替える
pkg> test
```

をひたすら実行していきましょう.

ここでは詳しくは触れませんが，2021 年現在，開発には VSCode が便利です. Revise.jl を連携するとよいでしょう.

## GitHub にプッシュ

GitHub にコードをコミット・プッシュしていくと `.github/workflows/CI.yml` の内容を読み取って GitHub 側で用意されたクラウド環境で

- 計算機環境が立ち上がる
- コードのクローン・チェックアウト (git clone 相当)
- パッケージのアクティベート, 依存関係の解決 (`using Pkg; Pkg.activate("."), Pkg.instantiate()` 相当)
- テストの実行 (`test/runtest.jl` 相当)

をしてくれます. `.github/workflows/CI.yml` は PkgTemplates.jl によって自動的に作られるものです. ピュアJulia実装のパッケージであれば `CI.yml` を特に編集する必要はないです.

リポジトリのトップページで 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/39a8bb82-bb49-4c11-7fdd-51a48494d97f.png)

緑色のチェックマークが出ていれば GitHubActions での CI が無事に通ったことになります. つまり自分のマシン以外の環境で自作パッケージの動作を確認できたことになります．

# 一区切りついたら

開発の目処がたったら公式パッケージとして登録の手続きに入りましょう.

前置きに書いた通り https://github.com/JuliaRegistries/General に自分のパッケージに関する情報取り込むプルリクエストを送ればよいです. ただし
General へのプルリクエストを送る手続きは人間がするのではなく [Registrator.jl](https://github.com/JuliaRegistries/Registrator.jl) で管理されているボットを経由して行うことが強く推奨されています.

- https://github.com/JuliaRegistries/General
- https://github.com/JuliaRegistries/Registrator.jl

をよく読んでください. 以下はあくまでも参考程度で読んでください．

# botの登録

基本的に

https://github.com/JuliaRegistries/Registrator.jl

の説明に従ってください.

Registrator というものを導入します. [Registrator.jl](https://github.com/JuliaRegistries/Registrator.jl) 下記の図のように install app を選択します.

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/cfcfb0d9-09a2-3019-8679-e19cdc9ebc2c.png)

そうすると下記のような画面に遷移します.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/8f7e1a43-9310-1e06-3630-8ca503ae8119.png)

Select repositories のタブから `YourPkg.jl` を管理するリポジトリを選択します.

# bot をこき使う

以下の作業は General にプルリクエストを送る手続きは全て Registrator bot を経由して行います.

botをこき使うには `@JuliaRegistrator register` とコメントするだけです.

コメントは適当な Issue を立ててその上でコメントを行う方法と特定のコミットの履歴の上でコメントする方法があります．

初回の登録は Issue を立ててする方法で良いと思います. さてコメントすると下記のようにbotがレスポンスしてくれます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/36415c95-88f7-8caf-00cf-1017e4c2d9b4.png)

これによって General リポジトリへのプルリクエストが送られます.

botが喋ったコメントにある `JuliaRegistries/General/xxxxx` というリンクを押すとリポジトリのプルリクエストが行われる場所に行きます．下記のようなチェックリストが走っていることがわかります.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/54b8edde-bab1-b497-e8ad-90a97c94697e.png)

問題があると General に仕込まれている botによって怒られが発生します.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/cb772cf5-bc17-53d2-e166-7f9501c4bc4b.png)

Project.toml にある `[compat]` の項目を入力する必要があります. 例えばどのJuliaのバージョン以上で動作するのか？という情報を設定する必要があります.

詳しくは https://pkgdocs.julialang.org/v1/compatibility/ をよく読んでください.

修正が完了したら自分のリポジトリを更新します. その後上記で作った Issue に再度 `@JuliaRegistrator register` を入力します.

形式的なチェックはbotによって行われています. ちなみにあまりにも内容が単純だったり，個人ユースの色いあいが強いものだと下記のようなやりとりが発生します.

https://github.com/JuliaRegistries/General/pull/46613#issuecomment-942742749

そんなわけで一応人力でも監視されているようです. 

# ３日間待機

General の CI によってチェック項目が満たされれば下記のようなメッセージが出ます.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135978/20535763-8a23-5d84-6a94-add49304873b.png)

とりあえず3日待って特に文句が言われなければプルリクエストが自動的にマージされます.

要するにある程度体裁が整っていれば bot の経由で 自分のリポジトリと General の間のコミュニケーションが成り立つことになります.

# 公式リポジトリとして登録できたら

やったね！Generalにマージされると PkgTemplates.jl で生成された `.github/workflows/TagBot.yml` が動いて自動的にタグが生成されます. タグのバージョンは Project.toml に格納されているバージョンをもとに作られます. 初回は `0.1.0` になっていると思います.

## バージョンアップする際は

もちろん機能追加がどんどんされていくでしょう. その時は特定のコミット履歴の時点でタグを打たせたいはずなので

`https://github.com/<your github account>/<your Julia package>/commits` に進んで登録したいコミットまで行きます.
そうするとコメントする場所があるのでそこで `@JuliaRegistrator register` とコメントします. これにより General にプルリクエストが投げられます. あとは初回の登録手順と同様です. 初回は３日間待機が必要ですが一旦登録されれば 30 分程度まつと自動的にマージされます. botをこき使う原則は新しいバージョンをリリースする場合も同様ですね．もちろん新しいリリースもつくられます. あとはこれを繰り返していきます.

# アピールしましょう

せっかく公式パッケージとしてなったのでいろんな人に使ってもらうための周知をしましょう.

Julia の Slack に投稿したりTwitterで拡散させてもらったり Julia Discourse で自分はこういうものを作ったよ！と宣伝すると効果があります．

ちなみに自分が最近書いたものは

[TerminalClock.jl](https://discourse.julialang.org/t/ann-terminalclock-jl/71650)
[Replay.jl](https://discourse.julialang.org/t/ann-replay-jl/71655)

にて見ることができます. シェル芸ならぬREPL芸は結構ウケがよいみたいです.

# まとめ

長くなりましたが，いわゆる公式パッケージとして使ってもらう方法の手続きを解説しました.
野良から公式への昇格は難しそうですが，ソフトウェアとしての体裁がしっかりしていればあとは機械的に登録・リリース手続きができるようになっています.
我こそはというアイデアがあればそれを実装して使ってもらうと意外なところから反応があるかもしれないですね．