はじめに
Livebook 0.10.0 がリリースされましたね!
というわけで、公式の動画で Jose がやっていた Multi-Session Livebook Apps の構築をやってみました
基本的に公式動画に沿って実装するだけなので、英語が聞ける人は動画を見た方が良いです
また、公式動画では説明のためにわざと遠回りしていたところを端折って説明します
GitHub トークンや Slack トークンについては手順を紹介するので、そこで詰まった人には参考になると思います
Multi-Session Livebook Apps とは
Livebook Apps はノートブック上に実装した処理を、そのままアプリとしてリリースできる機能です
これによって、例えば以下のような画像識別AIアプリなどが簡単に構築できます
Livebook 0.9 の Livebook Apps は Single-Session だけでした
Single-Session では複数人がアプリにアクセスしたとき、全員で一つのセッションを共有します
セッションを共有することでチャットや共同編集など、チームでのコラボレーション機能を構築しやすくなっています
それに対して、新しい Multi-Session では、新しくアプリにアクセスしてきた人は、既存のセッションに参加するか、新しいセッションを作るか選択できます
別々のセッションを作った場合、それぞれに対する操作は完全に分離されます
例えば「CSV ファイルを読み込んで、指定した列の値をグラフ化する」という処理を Livebook Apps を実装したとき、セッション毎に別の CSV ファイル、列を指定することができます
アプリの実装内容
GitHub 上の指定したリポジトリーの情報を取得し、 Slack に取得結果を通知するアプリを作っていきます
実装したノートブックはこちら
事前準備
Livebook のインストール
@nako_sleep_9h さんの記事などを参考に Livebook をインストール、起動します
Livebook のバージョンは 0.10.0 以上が必須なので、既にインストールしている人は最新バージョンに更新してください
トークンの取得
GitHub と Slack の API を呼ぶため、それぞれの認証トークンが必要になります
GitHub のトークン
GitHub にログインします(アカウントがない場合はサインアップしてください)
GitHub の右上、自分をアイコンをクリックし、表示されるメニューから "Settings" を開きます
"Settings" 画面で左メニューの一番下、 "Developer Settings" を開きます
"Developer Settings" 画面の左メニュー "Personal access tokens" |> "Fine-grained personal access tokens" を開き、右上の "Generate new token"
"Fine-grained personal access tokens" は従来の "Tokens (classic)" よりも、より詳細に権限管理できるようになっています
まだ Beta 版ではありますが、 GitHub のトークンを使う場合、今後はこちらを使いましょう
今回は特別な操作はしないため、 "Token name" に適当な値を入れたら他はそのまま "Generate token" をクリックします
新しいトークンが発行され、値が表示されます
Slack のトークン
slack api にログインします(Slack のアカウントがない場合、サインアップして何らかのワークスペースを作ってください)
右上 "Your apps" を開き、 "Create your first app" をクリックします(初めての Slack アプリの場合)
"Your Apps" 画面で "Create an App" をクリックします
開いたモーダルで "From scratch" をクリックします
"App Name" に適当な名前を入力し、使いたいワークスペースを選択します
"Create App" をクリックすると、 Slack アプリが作成されます
左メニューから "OAuth & Permissions" を開きます
下に移動し、 "Scopes" で上側の "Bot Token Scopes" に "Add an OAuth Scope" から "chat:write" の権限を追加します
画面上に戻ると、 "Install to Workspace" が活性化しているので、クリックします
"許可する" をクリックします
Slack のトークンが表示されます
Slack でワークスペースを開くと、作成したアプリが追加されています
Slack チャンネルの設定
お試し用のチャンネルを作ります
Slack で "チャンネルを追加する" |> "新しいチャンネルを作成する" をクリックします
適当なチャンネル名を入力し "次へ" をクリックします
何も変更せずに "作成" をクリックします
新しいチャンネルが作成され、 "メンバーを追加する" というモーダルが開きますが、誰も追加しないので "後でする" をクリックします
左メニューのチャンネル名を右クリックし、 "チャンネル詳細を表示する" をクリックします
表示されるモーダルの "インテグレーション" タブを開き、 "App" の中の "アプリを追加する" をクリックします
表示されるアプリ一覧から、自分が作成したアプリの右側 "追加" をクリックします
これで準備は完了です
アプリの実装
セットアップ
新しいノートブックを開き、 Multi-Session Livebook App を構築してみましょう
以下のコードをセットアップセルに入力し、実行します
Mix.install([
{:req, "~> 0.3"},
{:kino, "~> 0.10"},
{:kino_slack, "~> 0.1"}
])
必要なモジュールがノートブック上にインストールされます
シークレットの登録
GitHub と Slack のトークンをシークレットとして登録します
Livebook 左メニューの錠前アイコンをクリックし、 "SECRETS" を開きます
"+ New secret" をクリックします
Name を "GITHUB_TOKEN" 、 Value を発行したトークンの値にし、 "in My Hub" を選択して "+ Add" をクリックします
"only this session" の場合、ノートブック上ではシークレットを読み込めますが、デプロイしたアプリからは読み込めなくなります
Slack のトークンについても Name を "SLACK_APP" にして同様に登録します
GitHub API の準備
gh =
Req.new(
base_url: "https://api.github.com",
auth: {:bearer, System.fetch_env!("LB_GITHUB_TOKEN")},
headers: [
{"Accept", "application/vnd.github+json"},
{"X-GitHub-Api-Version", "2022-11-28"}
]
)
Kino.nothing()
Req を使って GitHub API にアクセスするため、クライアントを作成します
先程登録したシークレットは名前の先頭に "LB_" が付いて環境変数になっています
System.fetch_env!("LB_GITHUB_TOKEN")
で、 GitHub トークンを取得し、 API の認証情報に設定しています
それ以外の設定は GitHub API の公式からコピーしています
最終行の Kino.nothing()
は作成したクライアントの中身をユーザーに見せなくするために入れています
GitHub API の呼び出し
repo =
"オーナー/リポジトリー"
|> Kino.Input.text()
|> Kino.render()
|> Kino.Input.read()
if not String.contains?(repo, "/") do
Kino.interrupt!(:normal, "オーナー/リポジトリー を入力してください")
end
report =
case Req.get!(gh, url: "/repos/#{repo}/security-advisories") do
%{status: 200, body: body} ->
"リポジトリー #{repo} には #{length(body)} 個の報告があります"
%{status: 404} ->
Kino.interrupt!(:error, "リポジトリー #{repo} は存在しません")
%{status: status} ->
Kino.interrupt!(:error, "GitHub はステータス #{status} でエラーを返しました")
end
GitHub API の "/repos/<オーナー>/<リポジトリー>/security-advisories" にアクセスし、セキュリティ報告を取得しています
今回は実際の報告結果ではなく、単に length(body)
を表示しているので、結果は常に 1 になります
実行すると、以下のようなフォームが表示されます
Kino.Input.text
によってテキスト入力を作成し、正しく入力されていない場合("/"を含んでいない場合)は Kino.interrupt
で先に進まないよう制御しています
テキスト入力に livebook-dev/livebook
と入力し、 Continue をクリックします
Slack へのメッセージ送信
req =
Req.new(
base_url: "https://slack.com/api",
auth: {:bearer, System.fetch_env!("LB_SLACK_TOKEN")}
)
channel =
"Slack Channel"
|> Kino.Input.text()
|> Kino.render()
|> Kino.Input.read()
if not String.starts_with?(channel, "#") do
Kino.interrupt!(:normal, "送信先チャンネルを指定してください")
end
response =
Req.post!(req,
url: "/chat.postMessage",
json: %{channel: channel, text: "新しい報告が来ました
#{report}"}
)
case response.body do
%{"ok" => true} ->
"報告が送信されました!"
%{"ok" => false, "error" => error} ->
Kino.interrupt!(:error, "Slack 送信でエラーが発生しました #{error}")
end
GitHub と同じく環境変数 "LB_SLACK_TOKEN" から Slack のトークンを取得し、 Slack API を呼び出します
Slack Channel に用意しておいたチャンネル名を入力し、 Continue をクリックします
Slack にメッセージが来ました
アプリのデプロイ
ここまでで処理の実装は完了です
あとはこれを Multi-Session Livebook App としてデプロイします
デプロイの設定、実行
Livebook の左メニューロケットアイコンから "APP" メニューを開き、 "Configure" をクリックします
"Slug" (アプリをデプロイしたときの URL の末尾) に適当な値を入れます
"Session type" は "Multi-session" を選択します
"Deploy" をクリックします
"APP" メニューにデプロイメントが追加されました
セッションの開始
http://localhost:8080/apps/multi を新しいタブで開きましょう
セッションの確認モーダルが表示されるので、 "+ New session" をクリックします
ノートブックからコードが除去され、入力フォームだけが残った画面が開きました
"オーナー/リポジトリー" に "livebook-dev/livebook" を入力し、 "Continue" をクリックします
"Slack Channel" にチャンネル名を入力し、 "Continue" をクリックします
ちゃんと Slack までメッセージが送信されました
新しいセッションの開始
http://localhost:8080/apps/multi をもう一つの新しいタブで開きましょう
すると、セッションの確認モーダルが表示され、既に動いているセッションが表示されます
"+ New session" をクリックすれば、新しいセッションが開始され、再び初期状態からアプリを操作できます
http://localhost:8080/apps/multi を更に新しいタブで開きましょう
既に開いている二つのセッションが表示されています
下の方のセッションをクリックします
この場合、既に実行済の状態でアプリが開きます
これは最初に開いていたタブと同じセッションなので、こちらのタブでリポジトリーを変更すると、その内容は最初のタブにも連動します
Livebook の "APP" メニューを見ると、二つのセッションが実行中になっていることが分かります
Livebook トップの "Apps" メニュー http://localhost:8080/apps を開くと、二つのセッションにそれぞれ何人が接続しているかも分かります
再デプロイ
Livebook の APP メニューから Configure を開き、 "List existing sessions" のチェックを外して "Deploy" をクリックします
この状態でアプリを開くと、既存のセッションが表示されず、新しいセッションを開くことしかできなくなります
デプロイする度にバージョン番号が増えていきますが、古いバージョンにアクセスしていたセッションはその時点のバージョンのままです
## 既存セッションへの操作
"APP" メニューから各セッションを操作できます
左下リンク アイコンを開くと、当該セッションに参加できます
右下の左側ターミナルアイコンを開くと、デバッグモードでアプリが開きます
デバッグモードではアプリにコードが表示され、通常のノートブックとして編集、操作できます
注意しないといけないのは、あくまでも当該セッションに対するデバッグなので、ここで変更した内容は元のノートブックに反映されません
変更結果を保存したい場合は普通のノートブックと同様、保存してください
セッションの右下停止アイコンをクリックすると、セッションを停止できます
停止したセッションには参加できなくなり、代わりに右下ゴミ箱アイコンから削除できるようになります
まとめ
0.9 の Livebook Apps の時点でも凄かったですが、 Multi-Session Livebook Apps によって更に実用的になりました
いろんな処理を Elixir + Livebook で自動化できそうですね