やる気駆動型エンジニアの備忘録

WPF(XAML+C#)の話題を中心に.NET/Android/CI やたまに趣味に関するブログです

(Power Automate)実行中のフロー名を関数を使って取得する

小ネタです。 Power Automate で特定のチャットツールにメッセージを送るようなフローをよく作るのですが、フローが増えてくるとどれで送ってるんだっけ?となりがちです。 個人的によくやる手として以下のような一文を最後につけて送信しています。

(このメッセージは Power Automate - <フロー名> によって送信されました。)

こうすることで「どのツールの」「どの機能を使っているか」を明確にすることができます。 ただ、フロー名を後で変更した場合など、固定値を入力していると変更し忘れて意味をなさなくなる可能性があります。 できれば、フロー名の変更にも追従したい&コピペできるようにしたい!

そんな場合は次の関数を呼び出せばOKです。

workflow()?['tags']?['flowDisplayName']

これをメッセージに埋め込んでおけばフロー名の変更にも対応できます。

ちなみに以下のリファレンスで記載されているworkflow().run.nameを呼び出すとフローのIDが取得できます。

docs.microsoft.com

初見だとこっちのほうがフロー名ぽいんですけどね。

Azure DevOps REST APIで大量のPull Requestを取得する際の注意点について

先日、Azure Repos で管理しているリポジトリの Complete 済みの全 Pull Request を取得したいという要件があったので Azure DevOps REST API を使いました。 その時、条件の指定方法によっては取得できる Pull Request の数が想定より少ない数しか取得できない、という現象が発生しました。 回避方法はあったのですが、割と使われやすい API であり、今後もハマリポイントになりそうなので備忘録として残しておきます。

今回使用する Azure DevOps REST API はこちら。

docs.microsoft.com

開発環境

今回は C# でしか検証していません。他の言語などでは挙動が変わる可能性があるのでご了承ください。 また、 Visual Studio CodeREST API でも試しましたが、こちらの場合は応答はありましたが結果が表示されないという現象が発生しました。原因については分かっていません。

問題点

まずはコードを。事前に NuGet でNewtonsoft.JsonSystem.Net.Httpを追加しておきます。

using System;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net.Http.Headers;

namespace AzureDevOps.Api
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(":<Personal Access Token>"));            
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);

                HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, $"https://dev.azure.com/<organization>/<project>/_apis/git/repositories/<repository>/pullrequests?api-version=6.0");
                HttpResponseMessage response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead);
                if (!response.IsSuccessStatusCode)
                {
                    throw new HttpRequestException(response.RequestMessage.ToString());
                }

                string json = await response.Content.ReadAsStringAsync();
                dynamic pullRequests = Newtonsoft.Json.JsonConvert.DeserializeObject(json);

                System.Console.WriteLine($"Pull-Requestの数:{pullRequests.count}");
            }
        }
    }
}

細かい説明は省きますが、目的は API で指定したリポジトリの Pull Request 数を出力しています。 検証対象の環境では Pull Request の数が 2000 を超えているものを使用しました。

この時点では、以下のように何の条件も指定していません。

https://dev.azure.com/<organization>/<project>/_apis/git/repositories/<repository>/pullrequests?api-version=6.0

これを実行すると以下のような結果が出力されます。

Pull-Requestの数:62

期待値としては 1000 以上ですが、そうはなっていません。

原因

ググると同じ問題にハマっている人がすぐに見つかりました。

stackoverflow.com

どうやらこの振る舞い自体は API 側の仕様のようで、条件が指定されていない場合、何千件もの Pull Request を返すことになるため返却される件数が少なくなっている(らしい)とのことです。 つまり、対策は上記のページにも書いてあるとおり、条件を指定して返却される件数を指定してやれば良いのです。

対策

今回のように Complete 済みの全 Pull Request を取得するといった要件の場合、次の URI パラメータを使用します。

  • $skip
  • $top
  • searchCriteria.status

実際の URI は次のようになります。

https://dev.azure.com/<organization>/<project>/_apis/git/repositories/<repository>/pullrequests?$skip=n&$top=1000&searchCriteria.status=completed&api-version=6.0

$skip=nとしているのは複数回 API を呼び出さなければならないためです。 さて、これに対応したコードは次のようになりました。

using System;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using System.Net.Http.Headers;

namespace AzureDevOps.Api
{
    class Program
    {
        static async Task Main(string[] args)
        {
            int totalCount = 0;
            string auth = Convert.ToBase64String(Encoding.ASCII.GetBytes(":<Personal Access Token>"));            
            using (HttpClient httpClient = new HttpClient())
            {
                httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", auth);

                int numberOfCalls = 0;
                do
                {
                    HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, $"https://dev.azure.com/<organization>/<project>/_apis/git/repositories/<repository>/pullrequests?$skip={numberOfCalls * 1000}&$top=1000&searchCriteria.status=completed&api-version=6.0");
                    HttpResponseMessage response = await httpClient.SendAsync(message, HttpCompletionOption.ResponseContentRead);
                    if (!response.IsSuccessStatusCode)
                    {
                        throw new HttpRequestException(response.RequestMessage.ToString());
                    }

                    string json = await response.Content.ReadAsStringAsync();
                    dynamic pullRequests = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
                    int count = (int)pullRequests.count;                        

                    if (count == 0) break;

                    totalCount += count;
                    numberOfCalls += 1;
                } while (true);
            }

            System.Console.WriteLine($"Pull-Requestの数:{totalCount}");
        }
    }
}

これで 1000 件を超える Pull Request の取得にも対応できます。

おまけ

今回作成したコードで API の条件に指定した件数によって実行時間がどのくらい変わるのか試してみました。 件数は 100 / 500 / 1000 で3回実施しました。

回数 100件 500件 1000件
00:00:21.7547365 00:00:39.0122263 00:00:36.1842337
00:00:32.3838879 00:00:38.4844625 00:00:34.9170869
00:00:32.5571534 00:00:38.5509034 00:00:28.5829396

微妙なところですが、まぁ、誤差の範囲かなーと思います。 取得件数による応答時間はあまり変わらないので、取得する際の要件次第で1回あたりの取得件数は変えるのが良さそうです。

在宅勤務になったのでコーヒーミルを買いました

私の勤務している会社は緊急事態宣言に伴い在宅勤務となりました。 普段から勤務中は個包装ドリップタイプのコーヒーを飲んでいるのですが、それらは全部会社に置き忘れてしまったので、せっかくだしコーヒーミルを買って豆を挽いて飲むようにしました。

今回は新しく買ったコーヒーミルを紹介していきたいと思います。

買ったもの

買ったのは HARIO の手動ミルです。 手動ミルを買ったのは初めてでしたが、レビュー数が多く安定していたのとまとまった量の豆を挽きたかったのでこちらを選択しました。

このミルは挽いた豆をそのまま蓋をして保存できるのですが、100g とか 200g 挽いてるとさすがにたりなさそうなのでミニキャスターも購入しました。

なお、HARIO のコーヒーフィルターとドリッパーは形状が円錐になっているので、スーパーなどでは同型のものが売り場においていない可能性があるのでご注意ください。

私はもともと 60V のドリッパーは持っていたのですが、持っていない方はこちらも購入したほうが良いと思います。

無事必要なものが到着したので早速豆を挽いてみました。

予想はしていましたが、200g 挽くのに 30分くらいかかりました。 普段からインドア派な私にはなかなかの重労働です。。。 在宅勤務のちょっとした運動と考えれば結構いい感じなんですけどね。

それでもやっぱり挽きたてのコーヒーは香りが良く、それだけで気分が高揚します。 ついでにミニキャスターもインテリアとしてもなかなかに優秀です。計量スプーンがいい味出してます。

f:id:iyemon018:20200427135124p:plain

コーヒー豆ごとに色違いのキャスターを使うとより一層おしゃれになります。 (背景が残念ですが)

f:id:iyemon018:20200427135512p:plain

HARIO (ハリオ) V60 計量スプーン Wood M-12WD

HARIO (ハリオ) V60 計量スプーン Wood M-12WD

  • メディア: ホーム&キッチン

注意点

このコーヒーミルは豆の粗さを調節することができます。 取説ではメモリを調節することで、豆の粗さを自由に変えられますと記載されていましたが、ぶっちゃけどれがメモリかわかりませんでした!

で、色々試した結果、どうやら以下の赤いところがメモリらしいです。

f:id:iyemon018:20200427140343p:plain

流石に初見でこれはわからんよ…

いくつか試してみた

コーヒーミルを買ってから以下の豆を試しに飲んでみました。

個人的にはキリマンジャロのほうが好きですがちょっとお高め。 UCC コーヒーは値段の割に美味しいので、よく飲む人は買っておいて損は無いでしょう。

また、5月1日より U ブレンド豆以外のコーヒー豆が 1,000円 から 1,200円 に価格が変更になるそうです。欲しい方は今のうちに少し買い置きしておいても良いかもしれません。

f:id:iyemon018:20200427140810j:plain

こんなご時世ですが、自宅で楽しめそうなことはどんどんやっていっても良いかもしれません♪

良きコーヒーブレイクを!

Azure Pipelines Task の拡張機能を開発する

前回は Azure DevOps の拡張機能Hello World を実施しました。

iyemon018.hatenablog.com

今回は Azure DevOps の中でも最も利用されるであろう Azure Pipelines に自作した Task を追加していきます。

参考ページ

docs.microsoft.com

今回は上記のページに沿って拡張機能を作っていきます。 ただ、上記では Node.js + TypeScript を使って開発されています。単純に手順に沿って作っていけば普通に作れてしまうため、あまりこのエントリで紹介する意味がありません。

なので、ちょっと趣向を変えて Node.js ではなく、PowerShell を使って開発していきます。 PowerShell を使った Task 拡張機能の作り方は Microsoft 公式では見当たらなかったため、以下のページを参考にしています。

codingsight.com

socraticprogrammer.com

環境構築

各種ツールを適宜インストールします。今回は Visual Studio Code を使っていますが、エディタはお好みのものを使ってください。

  1. Node.jsをインストールする
  2. TypeScript Compilerをインストールする
  3. VS Codeをインストールする
  4. npm i -g tfx-cliを実行してtfx-cliをインストールする

Azure Pipelines Task を開発する

今回作るもの

はじめに今回作るものをお見せしておきます。 サンプルなので特にタスクそのものが意味を持つわけではありませんが、画面で入力した値を作成した Task が受け取ってビルドログに表示します。

タスク設定画面

f:id:iyemon018:20200406000030p:plain

ビルドログ

f:id:iyemon018:20200406000111p:plain

カスタムタスクを作る

カスタムタスクを作るまでの手順は上記参考ページに従っていきます。 以下のリンク見出しのCreate task scaffoldingを実行します。

docs.microsoft.com

次に、task.jsonを作成します。こちらも上記のページからコピーして作ってください。 適宜編集した後、最後のexecutionのみ以下のように変更します。 targetには、実行する PowerShell スクリプト名を記載してください。

{
    "$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
    "id": "{{GUID}}",
    "name": "{{タスクの名称(英数字のみ、半角スペース使用不可)}}",
    "friendlyName": "{{タスクの表示名(ユーザーに見えるタスク名はこっち。未入力なら name が使われる}}",
    "description": "{{タスクの説明}}",
    "helpMarkDown": "",
    "category": "{{タスクのカテゴリ。Build とか Utility とか。}}",
    "author": "{{publisher 名と同じにする}}",
    "version": {
        ...省略...
    },
    "instanceNameFormat": "{{ここはパイプラインやビルドログに表示する内容。$(変数)で変数も使える。}}",
    "inputs": [
        ...省略...
    ],
    "execution": {
        "PowerShell3": {
            "target": "powershell.ps1",
            "platforms": ["windows"],
            "workingDirectory": "$(currentDirectory)"
        }
    }
}

VstsTaskSdkを使う

VstsTaskSdk を Node.js で使用する場合は参考ページの手順に従ってください。 ここでは PowerShell の VstsTaskSdk を使用する方法について説明します。

  1. VstsTaskSdkのモジュールをインストールする
  2. インストールしたモジュールを拡張機能のps_modulsフォルダに保存する
  3. PowerShellスクリプトからVstsTaskSdkを呼び出す

VstsTaskSdkのモジュールをインストールする

以下のコマンドを実行してローカル端末に VstsTaskSdk をインストールします。

Install-Module -Name VstsTaskSdk -AllowClobber

インストールしたモジュールを拡張機能のps_modulsフォルダに保存する

インストールできたら拡張機能のフォルダ配下にあるps_modulesフォルダ上で以下のコマンドを実行します。

Save-Module -Name VstsTaskSdk -Path "<ps_modules フォルダ>" -Force

これを実行するとps_modulesフォルダ配下にVstsTaskSdkフォルダが作成されます。 その配下にバージョン番号名のフォルダが出来上がりますが、このバージョン番号フォルダ配下のファイルをVstsTaskSdkフォルダに移動させてください。

以下のようになるのが正解です。

f:id:iyemon018:20200406001844p:plain

PowerShellスクリプトからVstsTaskSdkを呼び出す

スクリプトは次のように書いたらOKです。 以下はGet-VstsInputを呼び出しています。

[CmdletBinding()]
param()

try {
    $samplestring = Get-VstsInput -Name "samplestring"
} finally {
    Trace-VstsLeavingInvocation $MyInvocation
}

なお、SDK のモジュールはそこそこの数があります。 以下のページを参照するとちょっとしたヘルプが記載されているので参考にしてください。

github.com

manifestファイルを作ってパッケージを公開する

manifest ファイル(vss-extension.json)の作り方とパッケージの公開方法は前回の記事を参照してください。

作ったTaskを実行する

ここは特に迷いませんね。Azure Pipelines のビルドパイプラインからタスクを追加するだけです。

f:id:iyemon018:20200406002714p:plain


このように割と簡単に Azure Pipelines Task を作ることができました。 PowerShell に慣れている方ならわざわざ Node.js を使う必要もありません。

SDK もそれなりに API が用意されているようなので、今後も色々試していきたいと思います。

Azure DevOpsの拡張機能を自作する -Hello World編-

Azure DevOps の拡張機能を自作する機運が高まってきたので色々調べながらとりあえず Hello World まで到達したので、その手順を備忘録として残しておきます。 なお、ここで記載している内容は2020年3月28日時点の以下のページに記載されている手順に則っています。 このページは英語で書かれていますが、一通りの手順を実施することで全く知識が無くても Hello World に到達できるようになっています。

docs.microsoft.com

なお、今回作成するのは以下のように Azure Repos にMy Hubというメニューを追加して、ログインユーザー名を表示する画面を追加します。

f:id:iyemon018:20200328223636p:plain

環境構築

Azure DevOpsに自分自身がownerになるorganizationを作る

Azure DevOps では拡張機能を自身が所属する organization にインストールするには権限が必要です。 最も手っ取り早いのは個人用の organization を作成することです。

会社で Azure DevOps を使用していて拡張機能をインストールする権限を持っていない場合、organization の owner に権限を与えてもらうか、owner 自身に拡張機能のインストールを依頼する必要があります。

拡張機能を作成するための環境を構築する

  1. Node.jsをインストールする
  2. Azure DevOps用の拡張機能パッケージツール(TFX)をインストールする

以下から Node.js をインストールします。とりあえず LTS をインストールしておけば問題ありません。

nodejs.org

Node.js をインストールしたら、TFX はコマンドプロンプトから以下のコマンドを実行することでインストールできます。

npm install -g tfx-cli

拡張機能を公開するためのPublisherを作成する

Azure DevOps のすべての拡張機能はパブリッシャーによって提供されます。 Marketplace で公開されているものだけでなく、自作して個人や特定の organization 内だけで使用するものについても同様です。

Publisher を作成するタイミングは拡張機能を作ってからでもいいのですが、ここで入力した以下の情報を拡張機能マニフェストファイルに埋め込む必要があるので先に作っておいたほうが楽ちんです。

  • ID

Publisher を作成するには以下の URL から必要な情報を入力します。

https://aka.ms/vsmarketplace-manage

なお、この手順の詳細は以下のページのほうがわかりやすいので、わからない場合はこちらを参照してください。

docs.microsoft.com

拡張機能を作る

基本的に以下の手順に従えば拡張機能を作成することができます。

docs.microsoft.com

ここではざっくり手順を紹介していきます。

  1. 拡張機能を作成するためのフォルダを作成する。場所はどこでもいい。
  2. npm パッケージマニフェストを初期化する。上記で作成したディレクトリでnpm init -yコマンドを実行する。
  3. Microsoft VSS Web Extension SDK パッケージをインストールする。npm install vss-web-extension-sdk --saveコマンドを実行する。
  4. 手順1.で作成したフォルダにvss-extension.json1ファイルを追加する。ファイルの内容は上記 URL の 4. を参照してください。
  5. Publisher 作成時に入力したIDvss-extension.jsonpublisherに記載する。
  6. 今回追加する画面のmy-hub.htmlファイルを追加する。こちらも、ファイルの内容は上記 URL の 5. を参照してください。
  7. 手順1. で作成したディレクトリでtfx extension createを実行する。

=== Completed operation: create extension ===と出力されたら完成です。 作成したフォルダ内に.vsixファイルが作成されるはずです。

ここまで実行すると以下のようなフォルダ構成になります。 (私は VS Code を使って Git で管理していたので.git, .vscode フォルダがあります)

f:id:iyemon018:20200328231928p:plain

Azure DevOpsにインストールする

Azure DevOps に作成した拡張機能をインストールするには先ほど作成した Publisher から.vsixを公開します。 拡張機能の管理ポータルへアクセスして [New extension] - [Azure DevOps] を選択します。 ダイアログに先程作成した.vsixをドラッグ・アンド・ドロップして [Upload] ボタンを押したら完了です。

f:id:iyemon018:20200328232936p:plain

Version にチェックマークがついていればアップロードは完了です。

f:id:iyemon018:20200328234157p:plain

続いてアップロードした拡張機能を organizatin にインストールします。 今回は private な拡張機能を作っているため、organization に対して拡張機能を共有する必要があります。 共有はアップロードした拡張機能のメニューから [Share/Unshare] を選択して共有先の organization 名を入力すればOKです。

f:id:iyemon018:20200328234955p:plain

拡張機能のメニューから [View Extension] を選択すると拡張機能のページに遷移するので、あとは他の拡張機能と同じようにインストールすれば完了です。 organization の Azure Repos メニューにMy Hubが追加されているはずです。

f:id:iyemon018:20200329000620p:plain


ドキュメントがしっかりしているおかげでほとんど詰まること無く、また大体1時間程度で Azure DevOps の拡張機能のインストールまでできました。 この時点では Hello World 程度で SDK の機能は殆ど触れていません。しかし、.vsixの作成方法やアップロード手順を知るには十分です。

What's next...

Azure Pipelines やその他サービスのチュートリアルです。 作りたい拡張機能に合わせてこの辺を通していけばよさそうです。


  1. vss-extension.jsonは Azure DevOps 専用のマニフェストファイルです。詳細はこちらのページを参照してください。