SyntaxHighlighter

2022年3月7日月曜日

Azure DevOps 向けの拡張機能 PublishMarkdownReports

Azure DevOps 向けの拡張機能 PublishMarkdownReports

Microsoft の統合開発環境である Azure DevOps 向けに、拡張機能 Publish Markdown Reports を作成したので、その紹介です。

拡張機能自体の作り方は、 Web 拡張機能の開発のページに詳しい方法が載っているので、こちらを参照してみてください。

やりたかったこと

ソースコードから自動生成した Markdown の API 仕様書を、 Azure DevOps の CI/CD である Azure Pipelines のページで閲覧できるようにしたい、というのがやりたかったことでした。
Azure Pipeline から Azure DevOps Wiki を更新する方法もあるのですが、こちらの記事のように Wiki 用の Git Repository を自動で更新する方法では、「同じぐらいのタイミングで CI/CD が走ったとき」や「CI/CD タスクが Re-queue され、古いコミットに対するタスクが後で実行されたとき」に、古い内容で上書きしてしまわないような仕組みが必要になります。

今回は、そういったことを考慮しなくてすむように、 Publish Markdown Reports では Azure Pipelines のビルド結果の画面に任意の Markdown ドキュメントを表示できるようにすることにしました。
このようにすることで、別の Wiki 用の Git Repository は不要になり、 CI/CD タスクの実行順序などを考えなくてもよくなります。
ただし、ビルド結果は設定によっては古い結果が削除されるので、正式なドキュメントとして残す場合は別途対応が必要です。

完成した表示画面は下記になります。
screenshot

Publish Markdown Reports について

まずは、 Publish Markdown Reports の概要について説明しようと思います。
なお、ソースコード全体は GitHub に置いてあります。

表示用 Markdown ファイルのアップロード

この拡張機能では、 Markdown として表示するファイルをビルドタスクの中でアップロードします。
下記のような YAML の記述を Azure Pipelines に加えることで、指定されたディレクトリのファイルをアップロードします。

上記の場合、ソースコードのディレクトリの下の markdown ディレクトリの下にあるファイルすべてをアップロードします。
また、既定のページとして main_page.md を指定しています。このファイルは contentPath で指定したディレクトリからの相対パスで指定します。
headingId は、 Markdown の # 見出し のような章のタイトル (<h1> などのタグで囲まれた部分) に対して、どのような ID を付与するかを指定します。
現時点では完全ではないのですが、 Python の Markdown の HTML 変換ライブラリである Python-Markdown や、 Doxygen の結果を Markdown に変換する Doxybook2 の ID のつけ方に対応しています。
なお、 Pydoc-Markdown などのツールでは、 <a> タグでリンク先の場所が定義されているので、 headingId の指定は不要です。

内部的には、これらのファイルはビルド中に Attachment として、ビルド結果に対する添付ファイルの形でアップロードされています。
このファイルは API でしか取得できないもので、ビルド生成物として登録する Artifact とは異なるものです。

Markdown ファイルの表示

この拡張機能では、アップロードされた Markdown ファイルを Marked で HTML 変換して表示しています。

ただし、 Azure DevOps の拡張機能の仕組み上、少し面倒ですが、下記のように表示を実現しています。

  1. 拡張機能に含まれる frame.html をビルド結果のタブ内に表示
  2. frame.html から Azure DevOps の SDK を用いて Attachment として保存されている Markdown を JavaScript で取得
  3. 取得した Markdown ファイルを Marked ライブラリを用いて HTML に変換
    その際に、リンクを処理する際には、 frame.html?page=/link-to-page.md のように、 frame.html に対して page パラメータとして Markdown のファイルのパスを指定するようにします。
    また、画像を表示するために、画像に対するファイル名に対して Attachment を取得し、得られたデータを Blob 変換した上で、 URL.createObjectURL で生成した URL に差し替えます。
  4. 生成された HTML をタブに表示

高速化

上述した方法では、表示されるまでに少しタイムラグが発生します。これは、 Azure DevOps の SDK の性質上、ロード時の初期化に 1~2 秒かかることが多いためです。
これを解消するために、少し高速化を行います。

上述した frame.html をラップする main.html を用意し、 frame.htmlmain.html の中で iframe として表示するようにします。

その上で、それぞれのファイルで以下のような処理を行うことで、 Azure DevOps の SDK の初期化処理がページ遷移のたびに動作しないようにしています。

main.html
  • Azure DevOps の SDK である VSS.SDK.js をロードし、初期化する。
  • onmessage ハンドラで frame.html からのメッセージを待ち受け、メッセージに応じて Attachment をダウンロードし、 frame.html に送る。
frame.html
  • 表示すべき Markdown ファイルを main.html に要望する。
  • main.html から送り返されたデータを処理し、 Marked ライブラリで HTML に変換する。

このようにすることで、画面が切り替わる際は frame.html だけがロードされ、 main.html 側は破棄されないため、 VSS.SDK.js による初期化処理は動作しないことになります。
そのため、 Markdown ファイルの切り替えによる画面遷移時には、 Markdown ファイルや画像のロードのみで動作が完結し、高速な動作が可能となります。

このようにして、それなりに実用に耐えうる拡張機能ができました。