rich な cat コマンドを1年越しにアップデートした話

この記事は、以前書いた「 [richなcatコマンドを作った話](https://qiita.com/yamamoto-yuta/items/5e67a9150f68ae0850ae) 」のその後について記した記事になります。

https://qiita.com/yamamoto-yuta/items/5e67a9150f68ae0850ae

以前開発したrichcatを1年ぶりにアップデートしました。この記事では、1年ぶりのアップデートを行った際にどんな問題が発生して、その解決の為にどんなことをしたのか等について記します。

※ 以前の記事から1年経ってないのは「開発 --[1年経過]--> アプデ --> 以前の記事を投稿 --> この記事を投稿」という時系列だからです。

## まず「richcat」って何？

コマンドシンタックスハイライトをしてくれたり、Markdown や Jupyter Notebook をプレビューしてくれたりと、出力が"rich"な Python 製 cat コマンドです。pip からインストールして使えます。

```
$ pip install richcat
```

https://github.com/richcat-dev/richcat

## アップデートしようと思った経緯

Jupyter Notebook のプレビュー機能の実現方法を思いついたから（以前の記事の「 `.ipynb` ファイルのプレビュー」の部分を参照）。

## 1年放置した結果発生した問題

### 当時のことを何も覚えていない

1年も間が空いてしまったので、当然、当時の開発状況等は何も覚えていませんでした…。そのため、アップデートしようにもどこを改修すれば良いのか分からない状況になっていました。特に困った点は次の通りです。

- 開発環境を仮想環境等ではなく素の環境に作っていたため、開発に必要なライブラリ等が分からなくなっていた
- ソースコードの構造がどうなっていたか忘れてしまったため、ほぼ全てのコードを読み直して思い出す作業が必要になった
- PyPI 登録や拡張子辞書（以前の記事の「シンタックスハイライトの自動決定処理の高速化」の部分を参照）の更新などについてのドキュメントが無かったので、過去に調べたことと全く同じことを調べ直さなければならなくなった

### 依存ライブラリがアップデートして動かなくなっていた

richcat は [rich](https://github.com/Textualize/rich) というライブラリを利用しているのですが、こちらが1年の間にかなりアップデートされていました（v9.8.2 → v11.1.0）。しかし、 richcat の requirements.txt で利用する rich のバージョンを指定していなかったため、 rich がアップデートされた後に richcat をインストールしたり、 richcat をインストールした状態で rich をアップデートしたりすると richcat が動かない、という問題が発生していました。

https://github.com/Textualize/rich

## やったこと

### 開発環境の仮想化

開発に必要なライブラリ等を調べるとともに、開発環境を Docker 化してローカル環境への依存を減らしました。

Docker 化する際、Docker コンテナ内で richcat を実行するとシンタックスハイライトが正常に動作しないという問題が発生しました。

![image](https://user-images.githubusercontent.com/55144709/151706372-1da90204-8c13-4f8d-8520-13712e6ad2bf.png)

原因は Docker コンテナ内のデフォルトの pager がカラー出力をサポートしていないからでした。 richcat ではカラー出力で pager を利用するよう指定していました。そのため、カラー出力をサポートしていない pager でカラー出力をしようとしてしまい、この問題が発生していた、という次第です。

> Since the default pager on most platforms don’t support color, Rich will strip color from the output. If you know that your pager supports color, you can set styles=True when calling the pager() method.
> 引用：[Console API — Rich 12.3.0 documentation](https://rich.readthedocs.io/en/latest/console.html#paging)

そこで Docker コンテナ内に less コマンドをインストールし、環境変数 `LESS` からカラー出力をサポートするよう指定することで解決しました。

![image](https://user-images.githubusercontent.com/55144709/151706468-f99f1376-3909-4d79-b303-9d9f7b84bbf4.png)

なお、解決まで試行錯誤した過程は以下の issue に残っていますので、気になった人は覗いてみてください:bow:

https://github.com/richcat-dev/richcat/issues/29

### ドキュメント整備

#### ドキュメントの自動生成＆ビルド

コードを理解しやすくするため、 [Sphinx](https://www.sphinx-doc.org/ja/master/) を導入してドキュメント整備を行いました。ドキュメントは sphinx-apidoc を利用し、 docstring から自動生成するようにしました。

https://www.sphinx-doc.org/ja/master/man/sphinx-apidoc.html

また、コード変更を即座にドキュメントへ反映して確認できるよう、 sphinx-autobuild を導入しました。これにより、 localhost を開くことで最新のドキュメントを確認できるようになりました。

https://github.com/executablebooks/sphinx-autobuild

これらのコマンドが実行しやすいよう、下記のような Makefile を作成しました。これにより、 make コマンドを利用して簡単に sphinx-apidoc や sphinx-autobuild が利用できるようになりました。

```Makefile
SPHINXDIR = sphinx
PROJECTNAME = richcat
PROJECTDIR = ${PROJECTNAME}
DEPLOYDIR = ${SPHINXDIR}/_build


.PHONY: sphinx-apidoc
sphinx-apidoc:
	sphinx-apidoc -f -H ${PROJECTNAME} -o "${SPHINXDIR}" "${PROJECTDIR}"

.PHONY: sphinx-autobuild
sphinx-autobuild:
	sphinx-autobuild --host 0.0.0.0 -b html "${SPHINXDIR}" "${DEPLOYDIR}"

.PHONY: sphinx-deploy
sphinx-deploy:
	sphinx-apidoc -f -H ${PROJECTNAME} -o "${SPHINXDIR}" "${PROJECTDIR}"
	sphinx-build -b html "${SPHINXDIR}" "${DEPLOYDIR}"
```

#### GitHub Pages へのホスティング

生成したドキュメントは GitHub Pages から見られるようにしました。これにより、 localhost を立てなくても Web から簡単にドキュメントを確認できるようになりました。

GitHub Pages へのホスティングに際して、 GitHub Pages でドキュメントを開くと CSS が効いていないという問題が発生しました。調べたところ、原因は GitHub Pages が利用している Jekyll が Sphinx に対応していないことのようでした。解決手段として、今回は Sphinx の拡張機能 [`sphinx.ext.githubpages`](https://www.sphinx-doc.org/ja/master/usage/extensions/githubpages.html) を適用する方法を採りました。

### CI/CD の整備

PyPIへの登録や拡張子辞書の更新など、特定のタイミングで毎回同じ処理を実行するものは Github Actions を使って CI/CD ツールに任せるようにしました。現在、 richcat では以下のワークフローが GitHub Actions に登録されています。

- Sphinx ドキュメントを GitHub Pages へホスティングする処理
- 拡張子辞書を更新する処理
- TestPyPI へアップロードする処理
- PyPI へアップロードする処理

#### ブランチフローの明確化

CI/CD の整備に際して、ブランチフローを明確にしました。これにより、どのタイミングでどのワークフローを実行するか考えやすくなった他、その後のワークフローのメンテナンスもしやすくなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/489030/1cfa2ebe-e30e-6ff9-6804-a69d3aed0ecd.png)

https://github.com/richcat-dev/richcat/issues/136

#### ローカルでの GitHub Actions 検証環境の導入

また、ワークフローの開発がやりやすくなるよう、 act というツールを導入してローカルでもワークフローの動作確認を行えるようにしました。

https://github.com/nektos/act

今回は開発環境が Docker コンテナ内だったので、 act の実行も Docker コンテナ内で行うようにしました。 act を Docker コンテナ内で実行できるようにするには、 Docker Daemon のソケットファイルをマウントしておく必要があるようです。

```yml
volumes:
    - type: bind
      source: "/var/run/docker.sock"
      target: "/var/run/docker.sock"
```

act である程度の動作確認はできたのですが、いくつかの処理はうまくいかず結局 GitHub Actions 上で動作確認をする、といったことがありました。うまくいかなかった処理は以下の通りです。

- [`workflow_run`](https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#workflow_run)
    - act で実行すると最初のワークフローしか動作しなかった
- [`step.<step_id>.conclusion`](https://docs.github.com/ja/actions/learn-github-actions/contexts#steps-context)
    - act で実行すると中身が空になっていた

うまくいかなかった原因はちょっと特定できなかったです…（そのうちちゃんと特定しておきたい…）。

なお、 GitHub Actions 及び act を導入するにあたって色々試したリポジトリ＆メモを以下に示します。気になった方は覗いてみてください:bow:

https://github.com/yamamoto-yuta/github-actions-sandbox/wiki

### 依存ライブラリのバージョン固定

requirements.txt に書かれているライブラリのバージョンを固定しました。固定したバージョンですが、ひとまず今回のアップデート作業時に利用しているバージョンで固定しました。

```text
# Base libraries
rich == 11.1.0

# For Sphinx
sphinx == 4.4.0
sphinx-autobuild == 2021.3.14
sphinx_rtd_theme == 1.0.0

# Testing
pytest == 6.2.5
```

今後は依存ライブラリのバージョンアップに合わせて、固定するバージョンに範囲を持たせるようにできればと思っています。ただ、どうやって依存ライブラリの正常動作するバージョン範囲を見つけていけばよいかが分からないので、もしご存知の方がいればコメント等でお教えいただければ幸いです:bow:

## 今後やりたいこと

今回 codecov 等も導入したのですが、まだ活用は出来ていないので今後活用していきたいところです。

## まとめ

この記事では、 richcat を1年ぶりにアップデートした際に発生した問題及びその解決の為にやったことについて紹介しました。

ここまでお読みいただきありがとうございました:bow:

## 参考記事

Sphinx + GitHub Pages：
- [SphinxとGitHub Pagesで技術ノートを公開しよう！ - Qiita](https://qiita.com/tutuz/items/88a32d94d700b33dc3ea#github-pages-%E3%81%A8%E3%81%AE%E9%80%A3%E6%90%BA)

act を Docker コンテナ内で実行する：

- [GitHub Actions を act でローカルテストする - Vがいる日々](https://vlike-vlife.netlify.app/posts/testtool_act#_docker%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E5%86%85%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%9F%E3%81%84)
- [Docker in Docker、Docker outside of Docker（ソケットファイルの共有）を試す - CLOVER🍀](https://kazuhira-r.hatenablog.com/entry/2021/07/22/204449)