日々 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
クリック
さらに、
-
Empty job
で
さらに
-
Stage 1
をクリック -
Tasks
タブをクリック
さらに、
-
Agent job
の+
をクリック -
Utility
タブをクリック -
GitHub Release
を捜してきて、クリックして、Add
をクリック
さらに、
-
GitHub release (create)
をクリック。右側にプロパティ一覧らしきブレードが出てくるので埋めていきます -
GitHub connection (OAuth or PAT)*
は、~ 埋めてください。 -
Repository*
も、~ 埋めてください。 -
Action*
はCreate
のままで -
Target*
は$(Build.SourceVersion)
のままで -
Tag source*
は$(Release.ReleaseName)
にすると、Release-1
のような名前が付きます -
Release title
は$(Release.ReleaseName)
にしました -
Release notes source
はInline release notes
にしました -
Release notes
はby DevOps $(Release.ReleaseWebURL)
にしました -
Assets
は$(System.DefaultWorkingDirectory)/_COMPANYNAME.PROJECTNAME/drop/*
のようにしました。-
_COMPANYNAME.PROJECTNAME
を何にすべきかは、2 つ下の画像の (1)Source alias *
を参考に -
drop
は、のちほど指定するartifactName
です
-
-
Draft release
はオフ (これは好みで) -
Pre-release
はオン (これも好みで) -
Add changelog
はオフ (これも好みで) - 上部の
Save
で保存
続きはまた今度
ビルドごとに 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
をプルダウンした時にリストされる名称です。下図参照
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.bat
と signtool.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 などをアップロードします
操作手順です
ひとまずわたしは Authorize for use in all pipelines
にチェックを入れておきました。
CodeSign.pfx ファイルのパスワードを格納する手立て
Azure DevOps でプロジェクトを開きます。
1. 左端の Pipelines
を選択
2. Library
をクリック
3. Variable groups
タブをクリック
4. + Variable group
をクリックします
さらに、
-
Variable group name
に適当な名称を設定。 -
+ Add
をクリック -
Name
にvariable name
を入力。PFX-PASSWD
にすると$(PFX-PASSWD)
のように参照できます。 -
Value
にパスワードを入力。 - 南京錠をクリックして、
Value
を保護します。保護というのはわたしの認識では、- Azure Pipelines 上では表示できなくする
- ビルドログについても
***
などで隠蔽される
-
Save
で保存
Builds で CodeSign.pfx を使用する場合の準備
Builds の場合の Variable group
割り当て方法
- 左端の
Pipelines
を選択 -
Builds
をクリック - 対象のパイプラインをクリック
-
Edit
をクリック
さらに、
- 右上の
...
をクリック -
Variables
をクリック
さらに、
-
Variable groups
をクリック -
Link variable group
をクリック
さらに、
-
Sign.pfx.passwd
選択 -
Link
クリック
さらに
-
Save & queue
クリック -
Save
クリック -
Save
クリック
これで 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
が、Library
の Secure Files
でアップロードしたファイル名、
$(PFX-PASSWD)
は、 Library
の Variable groups
で定義した Variables
PFX-PASSWD
の Value
が反映されます。
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