Asana のタスクに CI からコメントを残したり、特定プロジェクトのタスクを CSV で抜き出したり — こうしたちょっとした操作を、Python スクリプトを書かずにシェル 1 行で済ませたい場面はそれなりにあります。
# CI 完了通知を Asana タスクへコメント投稿
asana-api stories create-story-for-task --task "$TASK_GID" \
--body '{"data":{"text":"デプロイ完了"}}'
# 特定プロジェクトのタスクを CSV で取り出す
asana-api tasks get-tasks --project "$PID" --all-items --output csv
そのために、公式の python-asana SDK を薄くラップして、Asana の全エンドポイントをコマンドラインから叩ける CLI ツールを作って公開しました。
つくったもの
pip install asana-api-cli
# あるいは独立した CLI として入れたい場合
pipx install asana-api-cli
インストールすると asana-api コマンドが使えるようになります。
export ASANA_ACCESS_TOKEN="1/12345..." # 必須
export ASANA_DEFAULT_WORKSPACE="12345678" # 任意(指定すると workspace 引数を省略できる)
# ワークスペース一覧
asana-api workspaces get-workspaces
# プロジェクト一覧
asana-api projects get-projects-for-workspace
# タスク一覧(全ページ自動取得)
asana-api tasks get-tasks --project <PROJECT_GID> --all-items
# 確認用に先頭 5 件だけ
asana-api tasks get-tasks --project <PROJECT_GID> --max-items 5
# タスクを作成(body は JSON 文字列)
asana-api tasks create-task \
--body '{"data":{"name":"new task","projects":["<PID>"]}}'
# table / csv で出力したり、jq でクエリしたり
asana-api tasks get-tasks --project <PID> --output table
asana-api tasks get-tasks --project <PID> --query '.data' --output csv
アクセストークンは Asana Developer Console で発行できます。
特徴
Asana のほぼ全 API をカバー
世の中には Asana を CLI から叩けるツールがいくつかありますが、多くはよく使われる一部の機能(タスク作成・一覧など)しか実装していません。
asana-api は 公式 SDK の *Api クラスを走査して、そのメソッドを呼び出すサブコマンドを事前に生成 しています。生成済みのコードはパッケージに組み込まれて PyPI にリリースされます。
その結果、SDK が提供している API は基本的にすべて呼び出せます。実際、現時点で 46 個のサブコマンドグループがあります。
access-requests custom-types memberships project-templates
allocations events organization-exports rates
attachments exports portfolio-memberships reactions
audit-log-api goal-relationships portfolios roles
batch-api goals project-briefs rules
budgets jobs project-memberships sections
custom-field-settings project-statuses ...
custom-fields workspaces
SDK のバージョンが上がったときは、こちらでコード生成を回し直し、新しいバージョンとして PyPI にリリースする運用にしています。利用者側は pip install -U asana-api-cli するだけで新しいエンドポイントが使えるようになります。
リスト取得が便利
Asana の一覧系 API は 1 リクエストで最大 100 件しか返しません。asana-api ではオプションを付けるだけで、自動でページをまたいで指定件数を取得できます。
-
--max-items N— N 件まで取得(確認用に先頭の数件だけ欲しいときに便利) -
--all-items— 全ページを取得して 1 つの JSON にまとめて出力
SDK を直接使う場合は自分でループを書いて件数を制御する必要がありますが、CLI なら 1 行で済み、結果がまとまった JSON で返ってくるので --query (jq) や --output csv にそのまま流せます。
# 1 ページ分だけ(最大 100 件)
asana-api tasks get-tasks --project <PID>
# 確認用に先頭 5 件だけ
asana-api tasks get-tasks --project <PID> --max-items 5
# 全ページを取得
asana-api tasks get-tasks --project <PID> --all-items
--query (jq) と --output で結果を整形
レスポンスには --query で jq 式をそのまま渡せます。--output は json / table / csv / text から選択でき、欲しい形のまま他のツールに流せます。
# タスク名と担当者だけを抜き出して CSV に
asana-api tasks get-tasks --project <PID> --all-items \
--query '.data | map({name, assignee: .assignee.name})' \
--output csv
--help が SDK の docstring から自動生成される
サブコマンドの --help は SDK の docstring から生成しています。
そのため「このエンドポイントはどんな引数が要るんだっけ?」と毎回ドキュメントを調べる必要がなく、--help を見れば必須引数・任意引数が分かります。
asana-api tasks --help
asana-api tasks get-tasks --help
asana-api tasks create-task --help
シェル補完に対応
Python のコマンドラインライブラリ click の機能を利用してシェル補完にも対応しています。bash の場合は ~/.bashrc に以下を追記するだけです(zsh / fish も同様)。
eval "$(_ASANA_API_COMPLETE=bash_source asana-api)"
サブコマンドやオプションが TAB で補完できるようになります。
タスクへのコメント投稿(CI 連携の例)
冒頭で挙げた stories create-story-for-task の補足です。タスク GID は Asana のブラウザ版でタスクを開いたときの URL から拾えます(https://app.asana.com/.../task/<TASK_GID>/... の <TASK_GID> 部分)。
CI 完了時や障害通知時にタスクへ自動でコメントを残す、といった連携もシェルスクリプトで数行で書けます。コメント本文を動的に組み立てるなら jq -nc で JSON を安全に作るのが楽です。
# CI のジョブ末尾で実行する例
asana-api stories create-story-for-task \
--task "$ASANA_TASK_GID" \
--body "$(jq -nc --arg t "デプロイ完了: $GIT_COMMIT" '{data:{text:$t}}')"
なぜ作ったか
一番の動機は、実際のシステムに SDK を組み込む前に、コマンドラインから API の挙動をひと通り確認できるようにしたかったことです。
SDK をいきなりアプリケーションに組み込んでしまうと、レスポンスの構造が想定と違っていたり、必須パラメータが不足していたりしたときに、コードを書き直して再実行するサイクルを何度も回すことになります。CLI から同じ SDK 経由で叩いて、引数・レスポンス・エラーの形を先に確認しておけば、組み込み時の試行錯誤を大幅に減らせます。
加えて、Asana を業務で使っていると、API をシェルから直接叩きたい場面もそれなりに出てきます。
-
業務日報
- テキストファイルに記述した日報をテンプレートに流し込んで Asana に投稿
-
システム連携(障害通知など)
- 監視システムからの障害通知を Asana のタスクとして自動起票する
- 既存タスクのステータスを外部システムから書き換える
しかしその都度 Python スクリプトを書くのは大げさで、かといって既存の CLI ツールでは目的の API がカバーされていないことも多々ありました。
そこで「SDK をラップして呼び出すコマンドを生成してしまえば、API の網羅性は SDK と同じになる」という発想でこのツールを作りました。呼び出しコードを自動生成する形にしておけば、SDK のバージョンアップにも追従しやすくなります。
アーキテクチャ概要
中身は非常にシンプルで、python-asana SDK の *Api クラスを走査し、そのメソッドを呼び出す click のサブコマンドを事前に生成しているだけです。生成済みのコードはリポジトリに含めてそのまま PyPI にリリースしているため、ユーザー環境で SDK の解析処理は走りません。
ライブラリとしての利用
CLI が主目的ですが、AsanaSession を経由して SDK を呼ぶこともできます。
from asana_api_cli import AsanaSession
import asana
session = AsanaSession(token="1/12345...", paginate=True)
tasks_api = asana.TasksApi(session.client)
for task in tasks_api.get_tasks({"project": "123"}):
print(task)
とはいえ、Python から使うなら asana SDK を直接使うのが普通だと思うので、おまけ程度の機能です。
まとめ
-
pip install asana-api-cliでasana-apiコマンドが入る - SDK を薄くラップしているので、Asana のほぼ全 API がコマンドラインから叩ける
-
--max-items/--all-itemsで自動ページ送り、--query(jq) /--output(json/table/csv/text) で実用的な使い勝手 - SDK のバージョンが上がったら、こちらでコード生成を回し直して新バージョンをリリース。利用者は
pip install -Uで追従できる
Asana の API を「ちょっと触ってみたい」「シェルから一発で叩きたい」と思っていた方の参考になれば嬉しいです。
不具合報告や「このオプションが欲しい」といった要望は GitHub Issues / PR で歓迎します。記事へのコメントや LGTM もモチベーションになるので、参考になったらぜひ。