SkyWayを使ったアプリ開発が爆速になるCLIツールを作った話

この記事は、 NTT Communications Advent Calendar 2024 9日目の記事です。

この記事では、SkyWayを使ったアプリ開発が爆速になるCLIツールを作った話を紹介します。 CLIツールにどのような機能を実装したのか、機能を実装する際にどのようなことを考えたのかについて、詳しく説明します。

はじめに

皆さまこんにちは。イノベーションセンター SkyWay DevOps プロジェクト所属の@sublimerです。

SkyWayは、ビデオ・音声通話機能を簡単にアプリケーションに実装できる、リアルタイムコミュニケーションを実現するためのプラットフォームです。 SkyWayではこれまで、JavaScript、iOS、Androidの各プラットフォーム向けにSDKを提供してきました。 今年は、これら3つのプラットフォームに加えて、Linux向け、Unity向けのSDKも提供を開始しました(Unity向けSDKは現在ベータ版です)。 また、録音・録画機能を利用するためのRecording API、SkyWayのChannelの作成と情報の参照をするためのChannel APIの提供も始めました。

このようにSkyWayではさまざまな機能をどんどんリリースしているため、これまで実現が難しかった機能も簡単に実装できるようになっています。 一方で、機能が増えることでSkyWayを使ったアプリの開発をいかに効率よく進めていくかが課題となります。

そこで、SkyWayを使ったアプリ開発がより効率的に進められるようにするためのCLIツール、「skyway-cli」を作成しました!!

github.com

注意事項

今回開発したCLIツールは公式として提供している機能ではありません。 あくまでも個人的に開発したものであり、SkyWay公式のサポートは提供していません。 利用される場合は、公式のツールではないことをご理解の上でご利用ください。 もし不明点や不具合があった場合は、GitHubリポジトリのissueとして連絡いただければ、できる限り対応いたします。

skyway-cliの機能

skyway-cliには、以下の11個のサブコマンドが実装されています。

  • skyway-cli
    • token: SkyWayの認証情報であるSkyWay Auth Tokenを生成します。 --admin フラグをつけると、SkyWay Admin Auth Tokenを生成します。
      • decode: SkyWay Auth Token、SkyWay Admin Auth Tokenをデコードします。 --pretty フラグをつけると、デコード結果を整形して表示します。
      • serve: SkyWay Auth Tokenを払い出すためのHTTPサーバーを起動します。
      • verify: SkyWay Auth Tokenが有効なものかどうかを検証します。無効なパラメーターが入っている場合、エラーとしてその情報を出力します。
    • channel
      • create: Channel APIを用いて、SkyWayのChannelを作成します。
      • find: Channel APIを用いて、SkyWayのChannelを検索します。
      • get: Channel APIを用いて、SkyWayのChannelの情報を取得します。
      • watch: SkyWayのChannelにおいて発生したイベント情報をリアルタイムで取得し、表示します。
    • recording
      • start: Recording APIを用いて、SkyWayの録音・録画を開始します。
      • stop: Recording APIを用いて、SkyWayの録音・録画を停止します。
      • get: Recording APIを用いて、SkyWayの録音・録画の情報を取得します。

これらのサブコマンドのうち、特に便利なものを2つ紹介します。

skyway-cli channel watch

このコマンドを使うと、以下の例のようにSkyWayのChannelにおいて発生したイベント情報をリアルタイムで取得し、表示できます。 (実行結果はあくまでも説明用のものです)

$ skyway-cli channel watch --id 0ea5bcde-a6f4-4931-9223-f5fdace5ec78
{"type":"MemberAdded","data":{"channel"... // Memberの入室
{"type":"StreamPublished","data":{"channel"... // Publicationのpublish
{"type":"StreamUnpublished","data":{"channel"... // Publicationのunpublish
{"type":"MemberRemoved","data":{"channel"... // Memberの退室

このコマンドを使うことで、SkyWayのChannelへのMemberの参加・退出の情報や、Publication、Subscriptionのpublish、subscribeの情報を、手軽にリアルタイムで確認できます。

内部の実装としては、RTC-APIサーバーにWebSocketで接続し、指定したChannelのイベント情報を受信して表示しています。 通常、この処理はSDK内部で行われますが、SDKにおいて実行している処理と同等の処理をCLIツール内で実装することで、CLIツールからもSkyWayのChannelのイベント情報をリアルタイムで取得できるようにしました。

なお、SkyWayでは、通信状況を記録、可視化するためのAnalytics機能を提供しています。 Analytics機能は、過去のデータも含めてWebブラウザ上で簡単に通信状況や品質を確認できます。 運用時に過去の通信状況を確認したい場合はAnalytics機能、アプリ開発時に手軽にリアルタイムで通信状況を確認したい場合は skyway-cli channel watch コマンドのように、状況に合わせて使い分けることをおすすめします。

skyway-cli token serve

このコマンドを使うと、デフォルトでは8080番ポートでHTTPサーバーを起動し、SkyWay Auth Tokenを払い出すAPIが利用できるようになります。

$ skyway-cli token serve
⇨ http server started on [::]:8080

通常SkyWay Auth Tokenは、ユーザーのアプリケーションの認証認可ロジックを踏まえて適切な権限を設定して払い出す必要があります。 こうすることで、セキュアなアプリケーションを実装できますが、「ちょっと動作を確認できればOK」という場合には払い出し用のサーバーを作るのが少し手間になってしまいます。 SkyWay Auth Tokenの生成はフロントエンドでも実行可能ですが、シークレットキーをフロントエンドのソースコードに書き込むのはセキュリティ上好ましくありません。 skyway-cli token serve コマンドを使うことで、フロントエンドのソースコードにシークレットキーを書き込まずに、手軽にSkyWay Auth Tokenを払い出すAPIを利用できるようになります。

skyway-cliを設計するときに考えたこと

今回skyway-cliを設計するにあたって、以下の2つのポイントを考慮しました。

  1. Unix哲学に則ること
  2. 設定値を柔軟に変えられること

Unix哲学に則ること

skyway-cli token decode コマンドを例に説明します。

このコマンドは、標準入力からSkyWay Auth Tokenを受け取り、デコード結果を標準出力に出力します。 エラーがあれば、標準エラー出力にエラーメッセージを出力し、終了ステータス 1 で終了します。 この挙動は、「過度の対話的インタフェースを避ける」、「すべてのプログラムをフィルタにする」というUnix哲学に則っています。

SkyWay Auth Tokenの入力方法には、標準入力から受け取る方法以外にも、ファイルとして読み込んだり、対話型のプロンプト経由で読み込む方法が考えられます。 これらの方法でも入力を受け付けることはできますが、標準入力から受け取る方法は他のプログラムの出力をパイプで受け取ることもでき、対話型のプロンプトのルールを覚える必要もありません。 このように、シンプルで自動化もしやすい点が、標準入力からデータを受け取ることのメリットです。

もちろん、 --input-flie のようなオプションを追加してファイルからの入出力に対応することもできますが、リダイレクトやパイプを使えば必ずしもコマンド側でファイルの入出力ができるようにする必要はありません。 このような、「Worse is better(劣る方が優れている)」の考え方もCLIツールを設計する際には重要だと考えています。

設定値を柔軟に変えられること

skyway-cliは、コマンド実行時のパラメーターを以下の3つのいずれかの方法で受け取ることができます。

  1. 設定ファイルに記述された設定値
  2. 環境変数に設定された設定値
  3. コマンドライン引数で指定された設定値

通常は設定値を明示的に指定する必要はなく、設定ファイルに記述された設定値を利用します。 一時的に設定値を変更したい場合は、環境変数やコマンドライン引数で指定できます。

SkyWayでは、アプリケーションIDとシークレットキーの2つの認証情報を使ってSkyWay Auth Tokenを生成します。 これらの認証情報は、制限の範囲内であれば複数払い出すことができます。 ユーザーによっては、開発用、試験用、本番環境用のように、複数の認証情報を使い分けたいケースが想定されます。 複数の設定値を柔軟に変えられるようにしておけば、普段は設定ファイルに書かれた開発用の認証情報を使い、試験や本番環境の不具合調査の時だけ環境変数で試験用の認証情報を指定するといった使い方ができるようになります。

skyway-cliを実装するときに考えたこと

skyway-cliを実装するにあたっては、以下の2つのポイントを考慮しました。

  1. インストールを簡単にする
  2. テストを書きやすくする

インストールを簡単にする

CLIツールを開発するにあたって、インストールのしやすさは非常に重要です。 インストール方法は、以下のようなやり方が考えられます。

  1. OSごとのパッケージマネージャー経由でインストールできるようにする
  2. GitHubリリースページからバイナリをダウンロードして使う
  3. プログラミング言語ごとのパッケージマネージャー経由でインストールできるようにする

1つめの方法は、APTやHomebrewを使う方法ですが、配布のための追加の作業が必要になるため、学習コストなどが発生します。

2つめの方法はシンプルで配布の自動化もしやすいですが、OSによってはダウンロードしたバイナリファイルの実行がブロックされたり、ユーザーにパスの設定をしてもらう必要があるなど、ユーザー側に手間が発生します。

3つめの方法は、プログラミング言語のランタイムを準備してもらうというユーザー側の手間が発生しますが、メジャーなプログラミング言語であればそこまでハードルは高くないと考えられます。 また、パッケージマネージャーにはインストールだけでなくバージョン管理やアップデートの機能も入っているため、ツールの配布の観点では非常に使いやすい仕組みです。

以上を踏まえ、今回は3つめの方法を採用しました。 また、利用するプログラミング言語はGoを採用しました。

GoでCLIツールを実装して配布することには、以下のようなメリットがあります。

  1. OSごとの差分をある程度吸収できる
  2. パッケージレジストリへの公開が不要
  3. CLIツールを作るための実績のあるライブラリがある

Goは、各OS向けのバイナリを容易にビルドできます。 CLIツールは複数のOSに対応するのが望ましいため、移植性の観点が重要となります。

また、GoはGitHubリポジトリをパッケージレジストリとしてそのまま利用できるため、パッケージレジストリへの公開の処理が不要です。 これにより、CI/CDパイプラインでのリリース処理を省略でき、GitHubリポジトリにpushするだけで容易にリリースができるようになります。

加えて、実績のあるCLIツール開発用ライブラリがある点もGoを選択した理由です。 CLIツール開発用のライブラリには、Cobraを利用しています。 Cobraは、KubernetesのCLIツールであるkubectlやGitHubのCLIツールであるGitHub CLIなど、多くのCLIツールで採用されているライブラリです1。 CLIツールを作るための技術選定にはあまり自信がなかったので、今回は実績のあるライブラリを選択しました。

CLIツールをGoで実装したことで、 go install コマンドを実行するだけで簡単にインストールができるようになり、さまざまなOSにも対応している利便性の高いCLIツールを実装できたと考えています。

テストを書きやすくする

前述の通り、CLIツールは最終的な入出力が標準入出力であるため、そのままではテストが難しくなります。 CLIツールのテストが難しくなる要因は、入出力のインターフェースが標準入出力である点だと考えています。 入出力のインターフェースが標準入出力となっている場合、stdinやstdoutをモックする必要があり、テストの実装コストが高くなります。 ただし、標準入出力も含めてE2Eテストのようなことをする必要は必ずしも無く、CLIツールの内部のロジックと標準入出力の処理を分離すれば、CLIツールのテストを容易に書くことができます。

今回CLIツールを作るにあたって、以下のルールを決めて実装を進めることにしました。

  1. 標準入出力、標準エラー出力、終了コードに関する処理は Command.Run 2に実装する
  2. Command.Run 内でCLIツール固有のロジックを呼び出し、そのロジックに対してテストを書く

このルールに従うことで、CLIツールの内部のロジックと標準入出力の処理を分離し、CLIツールの内部のロジックに対してテストを書くことでテストを書きやすくしています。

余談ですが、Cobraには CheckErr 3という関数があり、エラーを渡すといい感じにエラーメッセージを標準エラー出力に出力し、終了コード 1 でコマンドの実行を終了します。 この関数を使うことで、以下のようなエラーハンドリングのコードを無くすことができます。

// before
result, err := doSomething()
if err != nil {
    fmt.Fprintln(os.Stderr, err)
    os.Exit(1)
}

// after
result, err := doSomething()
cobra.CheckErr(err)

おわりに

本記事では、CLIツールにどのような機能を実装したのか、機能を実装する際にどのようなことを考えたのかについて紹介しました。

GoでCLIツールを作るのは初めてだったのですが、Cobraの開発体験が非常に良く、作っていてとても楽しかったです。

また、Unix哲学はCLIツールを作る上で非常に参考になる考え方だと感じました。 Unix哲学に沿っている多くのCLIツールのように、skyway-cliも長く使われるツールになると良いなと思っています。 使ってみた感想や機能追加の要望、バグ報告などがありましたら、GitHubリポジトリのissueに投稿していただけると嬉しいです。

明日10日目の記事は、tetterさんのMedia over QUICに関する記事の予定です。 それでは、明日もお楽しみに!!

参考情報

© NTT Communications Corporation 2014