TerminalGat.jl の紹介

# 本日は

TerminalGat.jl の紹介です `gat` という Go で実装された `cat` コマンドの代替ツールを Julia の REPL から使えるようにしました．`gat` の本家は以下のリンクから行けます．

https://github.com/koki-develop/gat

`less` の対応物に相当する `gess` コマンドも使えるようになっています．

`gess` という名前は実は私が思いついたものを `gat` の著者が実現してくださったという背景が実はありました．普段から使っててこれを REPL で動かしたら便利だよなぁーっと思って数ヶ月経ちました．重い腰を上げてようやく General レジストリに登録することができました．

---

## Install

Julia の REPL を開いて下記のように実行します．

```julia
julia> using Pkg; Pkg.add("TerminalGat")
```

ユーザは `gat` コマンド自身はインストールは不要です．

## 使い方

Go 実装の README.md を読みましょう．

https://github.com/koki-develop/gat

それで心が踊ったら Julia 版を試してみましょう．

```julia
julia> using TerminalGat
julia> gat("file.jl") # ファイルを表示，色は Monokai テーマで表示される
julia> gess("longfile.jl") # less longfile.jl のカラフル版
julia> gat("README.md") # markdown ファイルをレンダリングする
julia> gat(@doc sin) # Julia の Markdown.MD オブジェクトも表示可能
julia> gat("image.png") # Sixel サポートしている
julia> gat("image.jpg") # PNG と JPEG は対応
```

TerminalGat.jl のソースコードは以下にあります．

https://github.com/AtelierArith/TerminalGat.jl

## `gat` コマンドが事前にインストール不要な理由

`TerminalGαt.jl` をインストールすると Julia パッケージとして `gat_jll.jl` というのがインストールされます．この `gat_jll.jl` がビルド済み `gat` コマンドをインストールしてくれます．つまり Julia 以外の実行形式をあたかも Julia のパッケージをインストールするように利用することができるのです．この仕組みを持っているのが Julia の強みの一つでありいます．

## `gat_jll.jl` が提供する `gat` はどこからくるの？

`gat_jll.jl` は下記のリンクにあるレシピファイル（Juliaスクリプト）をもとに作られます．

https://github.com/JuliaPackaging/Yggdrasil/blob/master/G/gat/build_tarballs.jl

```julia
using BinaryBuilder

name = "gat"
version = v"0.19.1"

# Collection of sources required to complete build
sources = [
    GitSource("https://github.com/koki-develop/gat.git", "173737297556cbcdf6c1e3145ae8baa1c4a68bdf")
]

# Bash recipe for building across all platforms
script = raw"""
cd ${WORKSPACE}/srcdir/gat
go build -o "${bindir}/gat${exeext}"
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = supported_platforms()

# The products that we will ensure are always built
products = [
    ExecutableProduct("gat", :gat)
]

# Dependencies that must be installed before this package can be built
dependencies = Dependency[
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6", compilers = [:go, :c])
```

やっていることは次のとおりです．

- `sources` で指定したソースコードを入手(`gat` をビルドするためのソースコードを入手)
- `script` に基づいて `gat` プロジェクトをビルド．これにより実行形式としての `gat` を作ることができます．この時ビルドする環境は Docker のコンテナ内で行われるのでローカル環境でデバッグする際はホストマシンはJuliaとDockerさえあればOKです．
- Windows, macOS, Linux の幅広いプラットフォームで `gat` をビルドしてくれます．（platforms = supported_platforms()）
- `gat_jll.jl` は `gat` コマンドを提供します． `products` に対応，
- ソースコードをビルドするためにもちろん Go が必要なので `compilers = [:go, :c]` を指定します．

Go に限らず Rust のコンパイラも利用できるので Rust で作られた便利なCLIツールやライブラリの共有ライブラリがあればそれをレシピファイルとしてプルリクを出せば Julia の人は喜びます．

Yggdrasil　および　BinaryBuilderは `apt-get install ...`, `brew install`, `winget install ...` のようなパッケージマネージャの Julia に相当するものです．Yggdrasil はレシピの集まり，BinaryBuilderはレシピから生成物を作成する仕組みを持っています．OS,プラットフォームごとに異なる事前準備を踏まず，「Pkg.add すれば必要なものが降ってきて Julia のパッケージを利用できる！」ということが可能になっています．

# `gat_jll.jl` を作った流れ

「Pkg.add すれば必要なものが降ってきて Julia のパッケージを利用できる！」と言いましたが，レシピはJulia コミュニティの方がコントリビューションしています．gat_jll は元からある訳ではなかったので自分で作る必要がありました．

## ローカルで確かめる

PR を出す前に BinaryBuilder v0.5 系をインストールして前述のレシピファイルを build_tarballs.jl という名前で保存します．

```sh
julia build_tarballs.jl
```

これでまずビルドができるかを試行錯誤します

## レシピファイルを追加する

レシピファイルを追加するプルリクエストを作成します．

https://github.com/JuliaPackaging/Yggdrasil/pull/9893

`gat` 自体は MIT ライセンスで公開されてるものですが Yggdrasil にレシピを登録するのは初めてだったので，一応念の為， `gat` 側の Issue を立てて Julia のコミュニティのエコシステムに取り入れる許可をもらいました．他のPRを見てると Deno v2.0 を入れてたりするので（ライセンスの許す範囲で）思ったよりも好き勝手に突っ込んでる印象があります．

いくつか細かい suggestion をもらいましたが，とりあえず言われるまま，suggestion を受け入れて細かい変更のやり取りがありました，が，ほぼ一瞬で終わりました．

導入の背景を書いておくとレビューワはすんなり通してくれる印象があります．

## Yggdrasil でのPRがマージされると

ここからは General というリポジトリに自動的にPRが発行されます．

https://github.com/JuliaRegistries/General/pull/120387

これはほぼ形式的なもので `Pkg.add("gat_jll")` が可能になるための仕組みとして General というレジストリがあり，そこに登録する儀式を行ってると理解すればOKです．

２０分のサイクルで自動的にマージされます．思ったよりもシンプルでびっくりしました．

---

## TerminalGat.jl の登録

(みんなにも使ってもらいたい価値があるものであると信じて) General レジストリに登録をします．

https://github.com/JuliaRegistries/Registrator.jl?tab=readme-ov-file#how-to-use

の説明文を読んで登録作業をします．GitHub App からボットを使って命令するか JuliaHub のページから手動で登録の手順を行う二通りの方法があります．ソースコードがGitHubで登録されていれば　GitHub App の方が簡単です．

ここからは人間は何もする必要はありません．事前にローカルのテストが通っている，README.md にパッケージの概要が書いてある，ネーミングのガイドラインを守っているなどなどをしていれば特に言われることはないです．

https://github.com/JuliaRegistries/General/pull/120463

３日間待ちます．3日間待つのは，その間に人間のレビューワーが「このパッケージはなんだろう，このパッケージはマトモなものか？，README.md がなかったら流石にまずいだろ，説明ぐらいつけろ」という判断のための待機時間です．この期間はひたすら待ちます．寝ましょう．

この記事が出ていることには Pkg.add("TerminalGat") で利用できるようになるはずです．ぜひ使ってくださいね．Go，Rust 系のCLIツールで有用なものがあれば（ライセンスの許す範囲で）登録していくのもありかなと感じました．

ちなみに `gh_jll.jl` というのもあり `gh` コマンドが使えるようになります．これで `gh auth login` を Julia のスクリプトとして実行・GitHub API を gh コマンドで使う操作をJuliaスクリプトとして行うといったことができるようになります．最初はやや違和感があると思いますが，Julia というのを高級なシェルスクリプトとして割り切ればまぁ，そういうのもあるよな・みっけたラッキーぐらいの気持ちになっていくと思います．

# 最後に

`gat` 作っていただきありがとうございました．

https://github.com/koki-develop

結構面白いものを作っていらっしゃるので皆さんも眺めてみてください．