WindowsアプリのインストーラーをGitLab CIでビルド&デプロイする方法
こんにちは。株式会社SHIFT、RGAに出向中の水谷です。
少し前にある案件でWindowsアプリのCI/CD環境を作った際に、Windows インストーラーパッケージ(.msiファイル)も自動作成して、テスト環境にインストールするようにしたのですが、その手順が書かれている記事があまりないようなので書いておこうかと思います。
WiX Toolset
Windowsのインストーラーの作成方法はいくつかあるのですが、WiX Toolsetを使うのが一般的かなと思います。
WiX Toolsetは無料で使えるし、VisualStudioのエクステンションも存在するので、今回はストレートにこれを使うことにしました。
WiX Toolsetのインストール
WiX Toolsetのダウンロードはこちらからできます。
赤色のDOWNLOAD WIX V3.11.2をクリックして、WiX本体をダウンロードしてインストールします。続いて、WiX Toolset Visual Studio 2019 Extension(あるいはWiX Toolset Visual Studio 2019 Extension)もクリックしてインストールします。
インストーラープロジェクトの作成
Visual Studioのソリューションにインストーラーのプロジェクトを追加します。この際に、プロジェクトの種類としてSetup Project for WiX v3を選択します。
プロジェクトを追加すると、このようにProduct.wxsというxmlファイル形式のソースコードが自動生成されます。
Product.wxsの編集
インストーラーのソースコード(wxsファイル)は記述法にやや癖があるので、熟知した人以外はネットで調べながらトライ&エラーで進めることになります。
今回は、CSTestApp.exeというバイナリファイルを1だけをコピーし、デスクトップにショートカットアイコンを作るだけのシンプルなインストーラーにしたため、コードは以下のようになりました。
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="CSTestApp" Language="1033" Version="1.0.0.0" Manufacturer="RGA Inc" UpgradeCode="93763e51-01bc-4ffa-8971-3f20913d7c8b">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Platform="x64" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Feature Id="ProductFeature" Title="SetupProject" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="ShortcutComponents" />
</Feature>
<MediaTemplate EmbedCab="yes" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
<UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText"/>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="INSTALLFOLDER" Name="CSTestApp" />
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent1" Guid="{611067BF-9D3B-49E8-AFD5-776230C9B6AC}" Win64="yes" >
<File Id="CSWinAppEXE" KeyPath="yes" Source="..\CSWinApp\bin\Release\net4.7.2-windows\CSWinApp.exe" />
</Component>
</ComponentGroup>
</Fragment>
<Fragment>
<ComponentGroup Id="ShortcutComponents" Directory="DesktopFolder">
<Component Id="ApplicationShortcut" Guid="F4231A9F-F6F5-4BFC-A687-BC8AD31E2DE3">
<Shortcut Id="ApplicationDesktopShortcut"
Name="CSTestApp"
Description="CSharp Test Application"
Target="[INSTALLFOLDER]CSWinApp.exe"
WorkingDirectory="INSTALLFOLDER"/>
<RegistryValue
Root="HKCU"
Key="Software\RGA\CSWinApp"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
コマンドラインでのビルド方法
インストーラーのビルドは、Visual StudioのIDE上では単純にソリューション全体をビルドするか、インストーラーのプロジェクトを指定してビルドすればよいのですが、コマンドライン上からビルドする場合は、(C/C++やC#などのコマンドラインビルドと同様に)msbuild.exeで行います。
今回はアプリ本体はC#で作成された"Any CPU"プラットフォーム向けアプリであるのに対して、インストーラーはx86プラットフォーム向けにビルドすることと、インストーラーのデバッグビルドが不要(デバッグビルドにする意味がほとんどないため)ということもあり、アプリ本体とインストーラーは別々にビルドすることにします。
このため、ビルドコマンドは次のようになります。
msbuild SetupProject.wixproj -p:Configuration=Release -p:Platform=x86 -fl -flp:logfile=installer_release.log
GitLab-CIへの組み込み
さて、今度はGitLab側の設定です。まずはGitLab Runnerにインストーラーのソースコードを開発したのと同じバージョンのWiX Toolsetをインストールしておきます。
続いて.gitlab-ci.ymlの作成ですが、以下がその例で、3つ目のmsbuild実行までがアプリのビルドで、4つ目のmsbuildがインストーラーのビルドになります。
stages:
- build
msbuild:
stage: build
script:
- 'cmd /C msbuild CSWinApp\CSWinApp.sln -t:restore'
- 'cmd /C msbuild CSWinApp\CSWinApp.sln -p:Configuration=Release -p:Platform=''Any CPU'' -p:RunCodeAnalysis=true -fl -flp:logfile=build_release.log'
- 'cmd /C msbuild CSWinApp\CSWinApp.sln -p:Configuration=Debug -p:Platform=''Any CPU'' -p:RunCodeAnalysis=true -fl -flp:logfile=build_debug.log'
- 'cmd /C msbuild CSWinApp\SetupProject\SetupProject.wixproj -p:Configuration=Release -p:Platform=x86 -fl -flp:logfile=installer_release.log'
artifacts:
paths:
- CSWinApp/CSWinApp/bin/
- CSWinApp/SetupProject/bin/
サイレントインストール
Windowsインストーラーファイル(拡張子.msi)は、msiexec.exeに関連付けられており、GUIが表示されます。
CI/CDでデプロイする場合は、msiexec.exeを/qbオプションで実行することでサイレントインストールができます。
msixexec.exe /qb /i C:\App\CSWinApp.msi
また、サイレントアンインストールは以下のようになります。
msixexec.exe /qb /x C:\App\CSWinApp.msi
注意点としては、これらのコマンドはRunnerではなく、デプロイ先のマシンで実行する必要があることです。
今回のCI/CDでは、以下のPowershellスクリプトをRunner上でリモート実行することでこれを実現しました(事前にリモート実行のための設定が必要)。
# create credential for WinRM
$password = ConvertTo-SecureString <administrator password> -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential ("<ip address>\\Administrator", $password)
# terminate if running
Invoke-Command <ip address> -Credential $Cred -ScriptBlock { (get-process | ? name -like "CSWinApp").Kill() }
# uninstall if installed
Invoke-Command <ip address> -Credential $Cred -ScriptBlock { if (Test-Path 'C:/Program Files/CSTestApp') { msiexec.exe /qb /x C:\App\CSWinApp.msi } }
# copy the latest build from S3
Invoke-Command <ip address> -Credential $Cred -ScriptBlock { aws s3 cp "s3://<backetname>/CSWinApp.msi" C:\App\CSWinApp.msi } -ArgumentList "$($Args[0])"
# install the latest build
Invoke-Command <ip address> -Credential $Cred -ScriptBlock { msiexec /qb /i C:\App\CSWinApp.msi }
ちょっと手間がかかりますが、これで期待通りのCI/CDが動くと思います。
――――――――――――――――――――――――――――――――――
【ご案内】
ITシステム開発やITインフラ運用の効率化、高速化、品質向上、その他、情シス部門の働き方改革など、IT自動化導入がもたらすメリットは様々ございます。
IT業務の自動化にご興味・ご関心ございましたら、まずは一度、IT自動化の専門家リアルグローブ・オートメーティッド(RGA)にご相談ください!
お問合せは以下の窓口までお願いいたします。
【お問い合わせ窓口】
代表窓口:info@rg-automated.jp
URL: https://rg-automated.jp