Build And Release Installer #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 260405Cl Version.cs の History 先頭行が変わったら自動でインストーラをビルド・リリースする | |
| name: Build And Release Installer | |
| on: | |
| push: | |
| branches: [master] | |
| paths: | |
| - "ReciPro/Version.cs" | |
| workflow_dispatch: # GitHub Actions UI から手動トリガー可能 | |
| permissions: | |
| contents: write | |
| jobs: | |
| release: | |
| runs-on: windows-2025-vs2026 | |
| timeout-minutes: 30 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Parse version and check if release needed | |
| id: version | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $content = Get-Content "ReciPro\Version.cs" -Raw | |
| if ($content -notmatch 'ver(\d+\.\d+)\(') { | |
| throw "Could not parse version from Version.cs History." | |
| } | |
| $ver = $Matches[1] | |
| $tag = "v.$ver" | |
| # 同じバージョンのリリースが既にあればスキップ | |
| gh release view $tag 2>&1 | Out-Null | |
| $releaseExists = ($LASTEXITCODE -eq 0) | |
| $global:LASTEXITCODE = 0 # gh の exit code をリセット | |
| if ($releaseExists) { | |
| Write-Host "Release $tag already exists. Skipping." | |
| "needed=false" >> $env:GITHUB_OUTPUT | |
| return | |
| } | |
| # History の先頭行をリリースノートに使う | |
| $historyLine = ([regex]::Match($content, 'ver[^\r\n"]+').Value).Trim() | |
| $notesPath = Join-Path $env:RUNNER_TEMP "release-notes.txt" | |
| $historyLine | Set-Content -Path $notesPath -Encoding UTF8 | |
| # AssemblyVersion / FileVersion 用のタイムスタンプ (yyyy.M.d.HHmm UTC) | |
| $now = [System.DateTime]::UtcNow | |
| $assemblyVer = "$($now.Year).$($now.Month).$($now.Day).$($now.ToString('HHmm'))" | |
| Write-Host "New version detected: $ver (tag: $tag, assembly: $assemblyVer)" | |
| "needed=true" >> $env:GITHUB_OUTPUT | |
| "version=$ver" >> $env:GITHUB_OUTPUT | |
| "tag=$tag" >> $env:GITHUB_OUTPUT | |
| "product_version=0.$ver" >> $env:GITHUB_OUTPUT | |
| "notes_path=$notesPath" >> $env:GITHUB_OUTPUT | |
| "assembly_version=$assemblyVer" >> $env:GITHUB_OUTPUT | |
| # ---- 以降、新バージョンが検出されたときのみ実行 ---- | |
| - name: Setup .NET 10 | |
| if: steps.version.outputs.needed == 'true' | |
| uses: actions/setup-dotnet@v5 | |
| with: | |
| dotnet-version: "10.0.x" | |
| - name: Restore managed dependencies | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| run: dotnet restore ReciPro\ReciPro.csproj | |
| - name: Find devenv.com | |
| if: steps.version.outputs.needed == 'true' | |
| id: devenv | |
| shell: pwsh | |
| run: | | |
| $devenvExe = (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property productPath).Trim() | |
| if (-not $devenvExe) { throw "Visual Studio was not found." } | |
| $cli = [System.IO.Path]::ChangeExtension($devenvExe, ".com") | |
| if (-not (Test-Path $cli)) { throw "devenv.com was not found." } | |
| Write-Host "devenv.com: $cli" | |
| "cli=$cli" >> $env:GITHUB_OUTPUT | |
| - name: Build native runtime binaries | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| run: | | |
| $log = Join-Path $env:RUNNER_TEMP "devenv-native.log" | |
| & "${{ steps.devenv.outputs.cli }}" "ReciPro.sln" /Build "Release|x64" /Project "Crystallography.Native" /Out $log | |
| if (Test-Path $log) { Get-Content $log } | |
| if ($LASTEXITCODE -ne 0) { throw "Crystallography.Native build failed (exit $LASTEXITCODE)." } | |
| - name: Build managed application | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| run: dotnet build ReciPro\ReciPro.csproj -c Release -p:Platform=x64 -p:AssemblyVersion=${{ steps.version.outputs.assembly_version }} -p:FileVersion=${{ steps.version.outputs.assembly_version }} --no-restore | |
| - name: Update installer version in vdproj | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| run: | | |
| $vdproj = Get-Content "ReciProSetup\ReciProSetup.vdproj" -Raw | |
| # ProductVersion を更新 (例: "8:0.4.922") | |
| $vdproj = $vdproj -replace '"ProductVersion" = "8:[^"]*"', '"ProductVersion" = "8:${{ steps.version.outputs.product_version }}"' | |
| # ProductCode と PackageCode を新しい GUID に更新 (UpgradeCode は変えない) | |
| $newProductCode = ([guid]::NewGuid()).ToString("B").ToUpper() | |
| $newPackageCode = ([guid]::NewGuid()).ToString("B").ToUpper() | |
| $vdproj = $vdproj -replace '"ProductCode" = "8:\{[^}]+\}"', "`"ProductCode`" = `"8:$newProductCode`"" | |
| $vdproj = $vdproj -replace '"PackageCode" = "8:\{[^}]+\}"', "`"PackageCode`" = `"8:$newPackageCode`"" | |
| Set-Content "ReciProSetup\ReciProSetup.vdproj" $vdproj -NoNewline | |
| Write-Host "ProductVersion = 0.${{ steps.version.outputs.version }}" | |
| Write-Host "ProductCode = $newProductCode" | |
| Write-Host "PackageCode = $newPackageCode" | |
| - name: Disable out-of-proc setup project build | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| run: | | |
| $visualStudioRoot = "HKCU:\Software\Microsoft\VisualStudio" | |
| Get-ChildItem $visualStudioRoot -ErrorAction SilentlyContinue | | |
| Where-Object { $_.PSChildName -like '17.0*_Config' -or $_.PSChildName -like '18.0*_Config' } | | |
| ForEach-Object { | |
| $key = Join-Path $_.PSPath "MSBuild" | |
| if (-not (Test-Path $key)) { New-Item -Path $key -Force | Out-Null } | |
| New-ItemProperty -Path $key -Name DisableOutOfProcBuild -PropertyType DWord -Value 1 -Force | Out-Null | |
| Write-Host "Set DisableOutOfProcBuild=1 at $key" | |
| } | |
| - name: Create installer-only solution | |
| if: steps.version.outputs.needed == 'true' | |
| id: pack_solution | |
| shell: pwsh | |
| run: | | |
| $solutionPath = Join-Path (Get-Location) "ReciProSetup.CI.sln" | |
| @' | |
| Microsoft Visual Studio Solution File, Format Version 12.00 | |
| # Visual Studio Version 18 | |
| VisualStudioVersion = 18.0.11205.157 | |
| MinimumVisualStudioVersion = 10.0.40219.1 | |
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReciPro", "ReciPro\ReciPro.csproj", "{D01883A1-458D-4127-8E9B-EEC176201483}" | |
| EndProject | |
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crystallography", "Crystallography\Crystallography.csproj", "{3AABEFB0-E84E-4AF6-8496-4D1F33C8F517}" | |
| EndProject | |
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crystallography.Controls", "Crystallography.Controls\Crystallography.Controls.csproj", "{FD96FE43-6DEE-4520-BB26-CFF7976DE27D}" | |
| EndProject | |
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crystallography.OpenGL", "Crystallography.OpenGL\Crystallography.OpenGL.csproj", "{BE7CC301-70E0-4BAA-9349-FDA49B38CC93}" | |
| EndProject | |
| Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Crystallography.Native", "Crystallography.Native\Crystallography.Native.vcxproj", "{0A6070E5-8843-4C4B-A4ED-4ABC55BFDBDD}" | |
| EndProject | |
| Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "ReciProSetup", "ReciProSetup\ReciProSetup.vdproj", "{8083E96B-786B-4351-87EF-8546C9C8B297}" | |
| EndProject | |
| Global | |
| GlobalSection(SolutionConfigurationPlatforms) = preSolution | |
| Release|x64 = Release|x64 | |
| EndGlobalSection | |
| GlobalSection(ProjectConfigurationPlatforms) = postSolution | |
| {D01883A1-458D-4127-8E9B-EEC176201483}.Release|x64.ActiveCfg = Release|x64 | |
| {3AABEFB0-E84E-4AF6-8496-4D1F33C8F517}.Release|x64.ActiveCfg = Release|x64 | |
| {FD96FE43-6DEE-4520-BB26-CFF7976DE27D}.Release|x64.ActiveCfg = Release|x64 | |
| {BE7CC301-70E0-4BAA-9349-FDA49B38CC93}.Release|x64.ActiveCfg = Release|x64 | |
| {0A6070E5-8843-4C4B-A4ED-4ABC55BFDBDD}.Release|x64.ActiveCfg = Release|x64 | |
| {8083E96B-786B-4351-87EF-8546C9C8B297}.Release|x64.ActiveCfg = Release | |
| {8083E96B-786B-4351-87EF-8546C9C8B297}.Release|x64.Build.0 = Release | |
| EndGlobalSection | |
| EndGlobal | |
| '@ | Set-Content -Path $solutionPath -Encoding ASCII | |
| "path=$solutionPath" >> $env:GITHUB_OUTPUT | |
| - name: Build installer with Visual Studio | |
| id: build_installer | |
| if: steps.version.outputs.needed == 'true' | |
| timeout-minutes: 10 | |
| shell: pwsh | |
| run: | | |
| $log = Join-Path $env:RUNNER_TEMP "devenv-setup.log" | |
| & "${{ steps.devenv.outputs.cli }}" "${{ steps.pack_solution.outputs.path }}" /Build "Release|x64" /Project "ReciProSetup" /Out $log | |
| $exitCode = $LASTEXITCODE | |
| if (Test-Path $log) { Get-Content $log } | |
| if ($exitCode -ne 0) { throw "ReciProSetup build failed (exit $exitCode)." } | |
| # MSI の存在確認 | |
| $msi = Get-ChildItem -Path "ReciProSetup" -Filter "ReciProSetup.msi" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 | |
| if (-not $msi) { throw "ReciProSetup.msi was not found." } | |
| Write-Host "MSI built: $($msi.FullName)" | |
| "msi_path=$($msi.FullName)" >> $env:GITHUB_OUTPUT | |
| - name: Upload workflow artifacts | |
| if: steps.version.outputs.needed == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ReciPro-installer-v.${{ steps.version.outputs.version }} | |
| path: ${{ steps.build_installer.outputs.msi_path }} | |
| - name: Create tag and GitHub Release | |
| if: steps.version.outputs.needed == 'true' | |
| shell: pwsh | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| $tag = "${{ steps.version.outputs.tag }}" | |
| # タグを作成してプッシュ | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git tag -a $tag -m "ReciPro ${{ steps.version.outputs.version }}" | |
| git push origin $tag | |
| # リリースを作成して MSI を添付 | |
| gh release create $tag ` | |
| "${{ steps.build_installer.outputs.msi_path }}" ` | |
| --title "ReciPro $tag" ` | |
| --notes-file "${{ steps.version.outputs.notes_path }}" |