WPFアプリケーションの公開作業をAzure Pipelinesを使って自動化する

前回の記事で行った作業を自動化できたらいいなと思っていたらAzure Pipelinesでいい感じにできたので、その時の記録です。Azureの他のサービスとの連携が簡単で、Azure Blob Storageにアップロードする作業も簡単に書くことができました。

準備

前回の記事の続きとして書くので、記事の中で作成したWPFアプリケーションが既にあることを前提とします。今回の作業では、前回の「公開」作業を行った時点で生成される「ClickOnceProfile.pubxml」を使います。Azure Blob Storageも前回と同じコンテナーを使う想定で書きます。

必要なもの

前回の記事に加えて、本記事では以下のもの利用します。

記事の構成

この記事は以下の9つの手順でまとめます。

  1. プロジェクトにランタイムを指定する
  2. Azure DevOpsでプロジェクトを作成する
  3. Azure Reposにコードをプッシュする
  4. Azure Pipelinesでパイプラインを作成し、 Gitリポジトリと紐づける
  5. Pipelineを編集する
  6. Pipelineを実行する
  7. バージョンアップしてGitリポジトリにプッシュする
  8. Pipelineが自動実行することを確認する
  9. アプリが自動更新するか試す

1. プロジェクトにランタイムを指定する

SimpleWPFApplication.csprojRuntimeIdentifiers タグを追加します。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    ...
    <RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
    ...
  </PropertyGroup>
</Project>

ちょっと説明

RuntimeIdentifiers タグを追加しておかないと、手順6でパイプラインを実行したときに以下のようなエラーが出てしまいます。

##[error]C:\Program Files\dotnet\sdk\5.0.402\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(241,5): Error NETSDK1047: Assets file 'D:\a\1\s\SimpleWPFApplication\obj\project.assets.json' doesn't have a target for 'net5.0-windows/win-x64'. Ensure that restore has run and that you have included 'net5.0-windows' in the TargetFrameworks for your project. You may also need to include 'win-x64' in your project's RuntimeIdentifiers.

このエラーを避けるため、エラーメッセージで勧められている通りランタイムを指定しておきます。

2. Azure DevOpsでプロジェクトを作成する

Azure DevOpsの自分の組織(Organization)のページを開き、 [+ New project] から新しいプロジェクトを作成します。
今回は [SimpleWPFApplication] という名前で作成しました。

3. Azure Reposにコードをプッシュする

プロジェクトの中にいくつかアイコンがありますが、その中の [Repos] を開きます。

クローンしたりプッシュするためのURLやコマンド例が載せられているので、手元の Git クライアントを使ってコードをプッシュします。手順は省略します。ちなみに .gitignore はGitHub公式のこちらが役に立ちます。注意点として、この .gitignore は今回使いたい pubxml ファイルが含まれているので pubxml をGit管理できるように変更する必要があります。

プッシュするとReposの画面は以下のようになります。

4. Azure Pipelinesでパイプラインを作成し、 Gitリポジトリと紐づける

プロジェクトの中から [Pipelines] を開き、 [Create Pipeline] から新しいパイプラインを作成します。接続先を選択する画面では、 [Azure Repos Git] から先程作成したリポジトリを選択します。ちなみに、候補にあるGitHubやBitbucketなどのGitホスティングサービスと連携することもできます。試しにGitHubとの連携をやってみましたがとても簡単にできました。以降の手順はそれらのサービスと連携した場合も同じように行えます。

5. Pipelineを編集する

この記事のメインです。パイプラインのテンプレートを選べるので [.NET Desktop] を選択します。

そうすると画面上で直接yamlが編集できるようになるので、以下のように編集します。yamlの編集が完了したら [Save] します。

# .NET Desktop
# Build and run tests for .NET Desktop or Windows classic desktop solutions.
# Add steps that publish symbols, save build artifacts, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net

trigger:
- main

pr: none

pool:
  vmImage: 'windows-latest'

variables:
  solution: 'SimpleWPFApplication.sln'
  project: 'SimpleWPFApplication\SimpleWPFApplication.csproj'
  buildPlatform: 'Any CPU'
  buildConfiguration: 'Release'

steps:
- task: NuGetToolInstaller@1

- task: NuGetCommand@2
  inputs:
    restoreSolution: '$(solution)'

- task: VSBuild@1
  inputs:
    solution: '$(solution)'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: VSTest@2
  inputs:
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'

- task: VSBuild@1
  displayName: 'Publish'
  inputs:
    solution: '$(project)'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
    msbuildArgs: '-t:publish -p:PublishProfile=ClickOnceProfile.pubxml -p:PublishDir="bin\publish\\"'

- task: AzureFileCopy@3
  displayName: 'Upload setup.exe'
  inputs:
    SourcePath: 'SimpleWPFApplication\bin\publish\setup.exe'
    azureSubscription: '(サブスクリプション名)'
    Destination: 'AzureBlob'
    storage: '(ストレージアカウント名)'
    ContainerName: '$web'

- task: AzureFileCopy@3
  displayName: 'Upload SimpleWPFApplication.application'
  inputs:
    SourcePath: 'SimpleWPFApplication\bin\publish\SimpleWPFApplication.application'
    azureSubscription: '(サブスクリプション名)'
    Destination: 'AzureBlob'
    storage: '(ストレージアカウント名)'
    ContainerName: '$web'

- task: AzureFileCopy@3
  displayName: 'Upload Application Files'
  inputs:
    SourcePath: 'SimpleWPFApplication\bin\publish\Application Files'
    azureSubscription: '(サブスクリプション名)'
    Destination: 'AzureBlob'
    storage: '(ストレージアカウント名)'
    ContainerName: '$web'
    BlobPrefix: 'Application Files'

ちょっと説明

ブラウザの右側にある [Tasks] から使いたいタスクを選択し、フォーム入力すればタスクのyamlを自動生成してくれます。まずはこの機能を使ってyamlを自動生成し、必要であれば直接編集する、という流れが良いと思います。

pr: none はプルリクエストを作成した時にパイプラインを実行しないように指定します。

ClickOnce公開のためのコマンドは公式ページを参考にしました。yamlでは [VSBuild] となっていますが、おそらくMSBuildを表していて同じオプションで動きました。PublishProfileオプションを使えば、Visual Studioで操作したときに生成された [ClickOnceProfile.pubxml] を指定してオプションをすっきりさせることができます。しかし、公式ページにもある通りPublishDirオプションはここでも指定する必要があります。

[AzureFileCopy] というタスクでAzure Blob Storageにアップロードしています。[azureSubscription] には、自分のAzureサブスクリプションを指定してください。執筆時点では最新版であるバージョン4 [@4] では動作しませんでした。アップロードしているファイルやフォルダは基本的に前回の記事と同じです。ただしインストールするときに使えるWebページ Publish.html はMSBuildコマンドでは出力されないようで、今回は含めていません。インストール用のリンクが載ったページが必要であれば自分で作成する必要がありそうです。

6. Pipelineを実行する

画面右上の [Run] からパイプラインを実行します。うまくいけば下の図のように成功します。

7. バージョンアップしてGitリポジトリにプッシュする

最後に手順7から9で、コードを変更→mainブランチにプッシュ→パイプライン実行→アプリを更新の流れをやってみます。

ここはVisual Studioで作業します。前回の記事の「発行」機能を使えばリビジョンが自動で増加しましたが、今回はそういうわけにはいかないので ClickOnceProfile.pubxml を自分で編集する必要があります。バージョン番号はなんでもいいですが 1.0.1.0 としてみました。

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <ApplicationRevision>0</ApplicationRevision>
    <ApplicationVersion>1.0.1.*</ApplicationVersion>
    ...
  </PropertyGroup>
</Project>

前回と同様に MainWindow.xaml を編集して画面にバージョン番号を表示するようにします。

<Window
    ...
    >
    <Grid>
        <TextBlock Text="Ver.1.0.1.0" />
    </Grid>
</Window>

これらの変更分をgit commitしてmainブランチにプッシュします。

8. Pipelineが自動実行することを確認する

Azure DevOpsのパイプラインのページを見にいくとパイプラインが実行しています。

9. アプリが自動更新するか試す

アプリを起動すると更新ダイアログが表示され、更新すると新しいバージョンになっていることが確認できます。

WPFアプリケーションをClickOnce発行してAzure Blob Storageに公開する

タイトル通りなのですが、開発したWindowsデスクトップアプリケーションをインターネットからインストールして使えるようにしたいということがあったので、その時の備忘録です。

必要なもの

この記事で紹介している方法を試すには以下のものが必要です。

記事の構成

この記事は以下の7つの手順でまとめます。

  1. Azure Blob Storageに静的Webサイト用コンテナーを作成する
  2. WPFアプリケーションを作成する
  3. WPFアプリケーションをClickOnce発行する
  4. アプリのファイル一式をAzure Blob Storageにアップロードする
  5. アプリをインストールする
  6. アプリをバージョンアップして再アップロードする
  7. アプリが自動更新するか試す

1. Azure Blob Storageに静的Webサイト用コンテナーを作成する

Azure Portalに移動して、ストレージアカウントを作成します。

作成し終わったら、左メニューから [静的なWebサイト] を選択します。

[有効] を選択して [保存] をクリックします。(裏で $web という名前のコンテナーが生成されます)

表示されるプライマリエンドポイントをコピーしておきます。(手順3で使います)

2. WPFアプリケーションを作成する

ここからはVisual Studioで作業します。新しいプロジェクトの作成で [WPFアプリケーション] を選択します。

プロジェクト名は [SimpleWPFApplication] とします。

ターゲットフレームワークは [.NET5.0] とします。

以下のようなファイルが自動生成されます。

MainWindow.xaml を少し書き換えてバージョン番号を表示するようにしておきます。

<Window
    ...
    >
    <Grid>
        <TextBlock Text="Ver.1.0.0.0" />
    </Grid>
</Window>

3. WPFアプリケーションをClickOnce発行する

Visual Studioのソリューションエクスプローラーでプロジェクトを右クリックし [発行] をクリックします。

[ClickOnce] を選択します。

発行場所はデフォルトの [bin\publish\] のままでいきます。

インストール場所は [Webサイトから] を選択し、手順1でコピーしたBlob Storageのプライマリエンドポイントを貼り付けます。

設定はデフォルトのままでいきます。ClickOnceには更新プログラムを確認して自動で更新する機能があるのでそれを使ってみます。

今回マニフェストの署名はなしでいきます。

構成はいろいろいじります。「配置モード」は [自己完結] 、「ターゲットランタイム」は [win-x64] を選択し、 [単一ファイルの作成] をチェックします。ここは全てのパターンを試してないですが、この構成でうまく動いたのでこれでいきます。

[完了] をクリックすると下の画像のような画面になるので、 [発行] をクリックします。

発行後、発行場所を見に行くといろいろなファイルが生成されています。

4. アプリのファイル一式をAzure Blob Storageにアップロードする

Azure Storage Explorerを使って作業します。以下のファイルとフォルダを$webコンテナーにアップロードします。Azure Storage Explorerを使えば簡単にドラッグアンドドロップでアップロードできます。

  • setup.exe
  • SimpleWPFApplication.application
  • Publish.html
  • Application Files フォルダ

アップロード後、Azure Portalでも確認できます。

5. アプリのインストール

ブラウザで「Publish.html」を見に行きます。(URLは https://ストレージアカウント名.z11.web.core.windows.net/Publish.html のようになります。)

[インストール] をクリックするとsetup.exeがダウンロードされます。setup.exeを起動すると確認ウィンドウが表示されるので発行元を確認して [実行] をクリックします。

[インストール] をクリックするとインストールが始まります。

インストールが完了するとアプリが起動します。

左下のスタートメニューから起動することができるようになりました。

6. アプリをバージョンアップして再アップロード

ここからは少し蛇足になりますが、新バージョン版をアップロードしてアプリが自動更新するところを手順6と7で記載します。まず、バージョンアップしたことが分かるように画面 MainWindow.xaml のバージョン番号を変えておきます。

<Window
    ...
    >
    <Grid>
        <TextBlock Text="Ver.1.0.0.1" />
    </Grid>
</Window>

手順3の再度で行った [発行] を再度クリックします。リビジョン(バージョン番号の一番右)が自動で増加するようになっているので、ここで発行したバージョンは「1.0.0.1」となります。

手順4で行ったアップロードをもう一度行います。

7. アプリの自動更新

手順5でインストールしたアプリを起動します。すると更新を促すウィンドウが出るので、そのまま [OK] をクリックすると更新されます。

更新後にアプリを開くと新しいバージョン(1.0.0.1)になっていることが確認できます。