Node.js製のターミナルアプリケーションをつくりました。
ソースコードは GitHub にあります。
どんなアプリ?何ができる?
ターミナル上で GitHub の Project board と Issue の詳細が確認できます。GitHub の Project board 専用ビューワーです。
ターミナルで表示したときの Project board 画面
動機
このアプリを作ろうと思ったきっかけは、単純にターミナルアプリを作りたいなと思ったことでした。WEB アプリに比べてターミナルアプリだとデザインをそこまで考える必要がありません。私はフロントエンドがそこまで得意ではないのですが細かい挙動や見た目にこだわってしまうので、WEB アプリを作るとなったときにデザインとその実装に一番時間がかかってしまいます。本来一番つくりたいのがバックエンドの部分だったりするので、デザインにそこまで気を使う必要のない(と勝手に思っている)ターミナルで何かアプリを作りたいなと思いました。
ターミナルアプリを作るとなったときにお題を何にしようかなと考えたとき、当時 GitHub の Project boards と Issue、Actions を組み合わせてタスク管理できないかなと色々検証していました。Issue でタスクを管理し、Project boards でタスクの全体を可視化し、Actions で Issue のステータスの変更を自動化させようとしていました。そのときの検証内容は Qiita に書いています。
- GitHub CLI を使ってファイルから issue を作成する
- 【GitHub】Actions と Projects を使って issue の状態を可視化する
- 【GitHub】GraphQL で issue を登録する
GraphQL や Hub コマンドで色々試していたのですが、ターミナルで操作してブラウザでその結果を確認するという行為が何回も繰り返していると煩わしくなってきます。そこで、ターミナルで操作してその結果をターミナルで確認できるようなれば便利だなと思い、このアプリを作ろうと思いました。
使用技術
Node.js で動いています。GitHub API との通信には GraphQL を使用しており、開発したアプリケーションは、GitHub Actions の workflow によって自動的に docker image が作成され GitHub のコンテナレジストリにアップロードされるようになってます。GitHub が提供している機能を最大限利用しています。アプリケーションをすぐに使いたいときは、docker image を pull してください。ソースコードをクローンして依存ライブラリをインストールすることなくアプリケーションを立ち上げることが出来ます。
JavaScript, Node.js
私が使用できる言語は PHP, JavaScript, Python で、この中からターミナルアプリを実装できそうなのは JavaScript または Python でした。もしかしたら PHP でも実装できたのかもしれませんが、当時の私は JavaScript か Python のどちらかをより深く学ぼうとしていたので、PHP は選択肢から外していました。Golang で実装することも検討しましたが、その場合だと 未経験の Golang の学習とターミナルアプリの開発という 2 つの問題に対処しなければならず、今回はターミナルアプリの開発にのみ焦点を当てたかったので、Golang での実装は見送りました。いずれは Golang での実装にも挑戦しようと思っています。
私はサーバーサイドよりの開発者ですので、当初 Python でこのアプリを開発していました。しかし、GraphQL のレスポンスが遅く感じられた(体感で 1 秒くらい)ので Node.js の方を試してみると、こちらの方がレスポンスが高速だったので、Node.js で実装し直すことにしました。
Node.js でターミナルアプリを作るにあたって使用したライブラリは、blessed とそれを拡張した blessed-contrib です。
blessed と blessed-contrib を採用した理由は、グリッドレイアウトが簡単に実装できそうだったからです。Project boards をターミナル上に表示させるため、グリッドレイアウトを実現できることがライブラリの選定基準でありました。blessed-contrib には Grid layout を簡単に実現できるインターフェースが用意されていたので、これらのライブラリを採用することにしました。
GraphQL
GitHub から Project boards や Issue の情報を取得するために、GitHub GraphQL API を使用しました。REST API ではなく GraphQL を使用した理由は、API のリクエスト回数が少なくできるのと、GraphQL を使ってみたかったからです。
GraphQL は実務では使用したことがありませんでしたが、こちらのサイトでフロントエンドとバックエンドのチュートリアルを実施していたので、GraphQL についての一通りの知識はありました。GraphQL を使用したアプリケーションを作ってみたく、また、今回は GitHub が提供している GraphQL API を利用するので、リゾルバーの設計・実装をする必要がなくクライアント側のみ実装すればよいのでそこまで負担にはないだろうと思い GraphQL を採用しました。実際、GraphQL API でどの クエリ を使用すればよいか調べるのに時間はかかりましたが、実装で苦労することはありませんでした。
Docker, GitHub Actions
実装したアプリケーションを docker image から起動できるようにしました。レジストリには GitHub のコンテナレジストリを使用しています。docker image の作成からレジストリへのプッシュまでは Actions を使用して自動化しています。
工夫したこと
だらだらと開発するよりかは短期間で一気に実装し終えるために、時間の使い方を工夫しました。また、GitHub の API を利用するので、GitHub に負荷をかけないようにアプリ起動中は GitHub へのリクエストをなるべく少なくするようにもしました。
時間の使い方
アプリケーション開発の目的が「Project board と Issue の内容をターミナル上で確認できるようにする」だったので、その他部分でつまずいた場合は深追いせず、回避策を考えてなるべく時間をかけないようにしました。例えば、Python で GraphQL の通信処理を実装していたとき、レスポンスが遅く感じられました。本来ならばなぜ遅いのかを調査しなければならないのですが、今回は Node.js でも同様の処理を実装してみて、こちらの方ではレスポンスの速度に問題がなかったので、Node.js で実装するほうに切り替えました。
このように、何か問題があったときに代替手段があればそちらを試し問題がなければそのまま採用するようにし、ターミナル上で表示させる方に時間を使う多く使うようにしました。
通信回数を減らす
アプリを起動すると、最初にプロジェクトボードを表示するために GitHub API をリクエストし、Issue にフォーカスをあててエンターキーを押すとその Issue の詳細を表示するために再度 GitHub と通信しています。GitHub へのリクエスト回数を減らすために、一度取得した Issue の詳細は配列に保存し二度目以降再度表示する場合は GitHub にリクエストを送信せず配列から取り出して使用するようにしています。アプリ起動中に Issue が追加されたり内容が修正された場合は、アプリを終了し再度立ち上げることで最新のデータを表示することができます。
苦労したこと
ターミナルに表示させる以外の箇所でつまずいた場合は、代替方法やより時間のかからない方法で乗り切っていましたが、ターミナルで表示させる部分、blessed/blessed-contrib に関わる部分は代替手段がなかったのでどのように実装するかは苦労しました。
イベント処理
今回のアプリはキーボードの入力に応じて画面表示するコンポーネントを変えています。イベントに応じてどのように部品を切り替えるのかについては悩みました。blessed についての概要の情報はありましたが、踏み入った解説は Qiita はともかくネット上にもなかったので、blessed の README が頼りでした。blessed の README には公開されているAPI(プロパティ・メソッド・イベントとか)がまとめられていますが、そこまで詳しく書かれていません。ですので、最終的にはソースコードを直接確認するか勘で実装していきました。
アプリをつくってみて
想像通りのものが作れて出来上がりには満足していますが、ビューワーだけだとさすがに使い道がありませんので、Issue 作成・修正機能が必要そうです。
ターミナルアプリを作ってみて、GUI に負けないくらいの操作・表現ができることが分かり、 TUI に密かに可能性を感じています。また、Actions を発表したあたりから GitHub にも魅かれており、GitHub を使って何が自動化できるかを考えています。Issue、Actions そしてターミナルを使った個人的最強タスク管理アプリを気が向いたら開発しようかなと思います。