Help us understand the problem. What is going on with this article?

.NET Framework デスクトップアプリの自動ビルドを Azure Pipelines 上で流す

日々 Windows デスクトップアプリの開発に勤しんでいます💦

『Windows アプリは自分のパソコンでビルドするモンだ!』
『ビルド環境の構築は一苦労』
という固定観念から抜けてみたく、試行錯誤していました。

結論的には

  • Azure Pipelines (Azure DevOps) を利用すれば、.NET Framework デスクトップアプリの自動ビルドは可能
  • GitHub の Releases にリリースを作成可能 (執筆時点では GitHub Release は PREVIEW 扱い)
  • ビルドごとに Arfifacts を残すことが可能
  • signtool で署名することも可能 (signtool は持ち込みしました)
  • CodeSign.pfx ファイルを格納する手立ても Azure DevOps にあります
  • CodeSign.pfx ファイルのパスワードを格納する手立ても Azure DevOps にあります
  • PowerShell, NSIS, MinGW などが使えます
  • VM の細かいスペックは Azure Pipelines Hosted VS2017 image のページが詳しいです

では大雑把に見ていきます

GitHub の Releases にリリースを作成可能

Note: つぎのトピック ビルドごとに Arfifacts を残すことが可能 で Artifacts が生成されていることをあてにしています。

対象プロジェクトを選択
1. Pipelines クリック
2. Releases クリック
3. + New クリック
4. New release pipeline クリック

image.png

さらに、

  1. Empty job

image.png

さらに

  1. Stage 1 をクリック
  2. Tasks タブをクリック

image.png

さらに、

  1. Agent job+ をクリック
  2. Utility タブをクリック
  3. GitHub Release を捜してきて、クリックして、Add をクリック

image.png

さらに、

  1. GitHub release (create) をクリック。右側にプロパティ一覧らしきブレードが出てくるので埋めていきます
  2. GitHub connection (OAuth or PAT)* は、~ 埋めてください。
  3. Repository* も、~ 埋めてください。
  4. Action*Create のままで
  5. Target*$(Build.SourceVersion) のままで
  6. Tag source*$(Release.ReleaseName) にすると、Release-1 のような名前が付きます
  7. Release title$(Release.ReleaseName) にしました
  8. Release notes sourceInline release notes にしました
  9. Release notesby DevOps $(Release.ReleaseWebURL) にしました
  10. Assets$(System.DefaultWorkingDirectory)/_COMPANYNAME.PROJECTNAME/drop/* のようにしました。
    1. _COMPANYNAME.PROJECTNAME を何にすべきかは、2 つ下の画像の (1) Source alias * を参考に
    2. drop は、のちほど指定する artifactName です
  11. Draft release はオフ (これは好みで)
  12. Pre-release はオン (これも好みで)
  13. Add changelog はオフ (これも好みで)
  14. 上部の Save で保存

image.png

image.png

続きはまた今度

ビルドごとに Arfifacts を残すことが可能

azure-pipelines.yml を編集します。

- task: CopyFiles@2
  inputs:
    contents: 'Setup_*.exe'
    targetFolder: $(Build.ArtifactStagingDirectory)

- task: PublishBuildArtifacts@1
  inputs:
    pathtoPublish: '$(Build.ArtifactStagingDirectory)' 
    artifactName: 'drop' 

成果物を CopyFiles@2 タスクで $(Build.ArtifactStagingDirectory) へコピーして、
PublishBuildArtifacts@1 タスクを実行します。

artifactName: 'drop' は、ビルド結果画面右上の Artifacts をプルダウンした時にリストされる名称です。下図参照

image.png

signtool で署名することも可能 (signtool は持ち込みしました)

MySign というツール

わたしは MySign という実行ファイル署名用のコマンドラインツールを自作しています。

使用感 MySign a.exe b.dll

署名しない環境では空の MySign.bat を作成します。

自分のパソコンでは、パスの通っている場所に GUI ツールの MySign.exe を設置しています。

バックグラウンドタスクで署名が必要な環境では、つぎのような内容で MySign.bat を書くと良いかもしれません。

%~dp0\signtool.exe sign /tr "http://rfc3161-url" /fd sha256 /n "COMPANY NAME" %*

フォルダ構成:

ソースフォルダ
- BuildTools フォルダ
  - signtool フォルダ
    - MySign.bat
    - signtool.exe

NSIS でも活用可能

わたしは NSIS を多用をするので MySign が必須です。

!system 'MySign "bin\DEBUG\${APP}.exe"'
!finalize 'MySign "%1"'

!system でビルド後のファイルに署名を施し、
!finalize でセットアップファイルに署名を施します。

デプロイしたい

azure-pipelines.yml を編集します。

CopyFiles@2 で、MySign.batsigntool.exe をソースフォルダにコピーしてきます。

- task: CopyFiles@2
  inputs:
    contents: 'BuildTools/signtool/*'
    targetFolder: '.'
    overWrite: true
    flattenFolders: true

CodeSign.pfx ファイルを格納する手立て

Note: pfx ファイルはエクスポートする際に、暗号化を TripleDES-SHA1 に設定しておいた方が良いでしょう。 AES256-SHA256 を選択すると、証明書をインポートできない、という問題が発生しました。この選択欄が存在しない場合は無視で問題ありません。

Azure DevOps でプロジェクトを開きます。
1. 左端の Pipelines を選択
2. Library をクリック
3. Secure files タブをクリック
4. + Secure file をクリックします
5. CodeSign.pfx などをアップロードします

操作手順です

image.png

ひとまずわたしは Authorize for use in all pipelines にチェックを入れておきました。

CodeSign.pfx ファイルのパスワードを格納する手立て

Azure DevOps でプロジェクトを開きます。
1. 左端の Pipelines を選択
2. Library をクリック
3. Variable groups タブをクリック
4. + Variable group をクリックします

image.png

さらに、

  1. Variable group name に適当な名称を設定。
  2. + Add をクリック
  3. Namevariable name を入力。PFX-PASSWD にすると $(PFX-PASSWD) のように参照できます。
  4. Value にパスワードを入力。
  5. 南京錠をクリックして、Value を保護します。保護というのはわたしの認識では、
    1. Azure Pipelines 上では表示できなくする
    2. ビルドログについても *** などで隠蔽される
  6. Save で保存

image.png

Builds で CodeSign.pfx を使用する場合の準備

Builds の場合の Variable group 割り当て方法

  1. 左端の Pipelines を選択
  2. Builds をクリック
  3. 対象のパイプラインをクリック
  4. Edit をクリック

image.png

さらに、

  1. 右上の ... をクリック
  2. Variables をクリック

image.png

さらに、

  1. Variable groups をクリック
  2. Link variable group をクリック

image.png

さらに、

  1. Sign.pfx.passwd 選択
  2. Link クリック

image.png

さらに

  1. Save & queue クリック
  2. Save クリック
  3. Save クリック

image.png

これで Sign.pfx.passwd を使用する準備が整ったと思います。

azure-pipelines.yml を編集

steps:
- task: DownloadSecureFile@1
  inputs:
    secureFile: CodeSign.pfx

- powershell: |
    $secureString = ConvertTo-SecureString $env:MySecret -AsPlainText -Force
    Import-PfxCertificate -Exportable -FilePath $env:DOWNLOADSECUREFILE_SECUREFILEPATH -CertStoreLocation 'Cert:\CurrentUser\My' -Password $secureString
  displayName: Import pfx
  env:
    MySecret: $(PFX-PASSWD)

secureFile: CodeSign.pfx が、LibrarySecure Files でアップロードしたファイル名、

$(PFX-PASSWD) は、 LibraryVariable groups で定義した Variables PFX-PASSWDValue が反映されます。

DownloadSecureFile@1 で取得したファイルは環境変数 DOWNLOADSECUREFILE_SECUREFILEPATH に格納されることになっているようです。
PowerShell での参照方法は $env:DOWNLOADSECUREFILE_SECUREFILEPATH です。

これで証明書を VM にインストールするので、あとから signtool.exe で参照できるようになるはずです。

signtool の動作チェックをする

パイプラインの最後の方でミスが発覚すると、消費した時間がむなしくなります…
先に動作チェックを走らせた方が良いでしょう。

- script: MySign EmptyExe.exe
  displayName: Sign test

EmptyExe.exe は自分用に作ったものですが、共有いたします。 https://github.com/HiraokaHyperTools/EmptyExe/releases

chcp 65001 で動作

VM のコンソールはコードページ 65001 (UTF-8) で動作しているようです。
しかしながら Unicode 対応ではないプログラムの言語 の設定は日本語ではありませんでした。

つまり、bat ファイルに CP932 (Shift_JIS) を使用すると、文字化けなどの問題が出ます。

bat ファイルと nsi ファイルについては CP932 → UTF-8 に文字コード変換するタスクを書きました。

- script: |
    for %%f in (*.bat) do (
      echo ##[debug]Convert: %%f
      iconv -f cp932 -t utf-8 %%f > %%f.new
      move /y %%f.new %%f
    )
    for %%f in (*.nsi) do (
      echo ##[debug]Convert: %%f
      iconv -f cp932 -t utf-8 %%f > %%f.new
      move /y %%f.new %%f
    )
  displayName: Needs to be UTF-8

UTF-8 に変換した bat nsi ファイルは、ビルド後に破棄されます。使い捨てです。
これらが Git レポジトリに書き戻ってくることは通常ありません。

makensis

NSIS セットアップを作成したい。

1 つの exe に対して多数のセットアップを作成するプロジェクトなので、こうしています。

- script: |
    for %%f in (*.bat) do (
      echo ##[debug]Run: %%f
      %%f || exit /b 1
    )
  displayName: Build NSIS installers
  env:
    AZURE_PIPELINES_NSIS_OPTIONS: /INPUTCHARSET UTF8

command || exit /b 1 は短絡評価の応用ですね…
command が成功 (ERRORLEVEL が 0) であれば、つぎの行へ。
command が失敗 (ERRORLEVEL が 1 以上) であれば… || の後の exit /b 1 を実行。ERRORLEVEL 1 を返してビルド失敗を報告します。

AZURE_PIPELINES_NSIS_OPTIONS: /INPUTCHARSET UTF8 は、先ほどの文字コード変換の結果、必要になった記述です。

その NSIS ビルドに使う bat ファイルは、Git レポジトリには CP932 で保存しておきます。通常の Windows 環境でも使いたいので…

"C:\Program Files (x86)\NSIS\Bin\makensis.exe" /DSPEC="顧客名" %AZURE_PIPELINES_NSIS_OPTIONS% Setup_ABC.nsi
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away