Skip to content

Stop publishing SHA256SUMS checksum files with releases #49

Stop publishing SHA256SUMS checksum files with releases

Stop publishing SHA256SUMS checksum files with releases #49

Workflow file for this run

# 260405Cl Version.cs の History 先頭行が変わったら自動でインストーラをビルド・リリースする
# 260602Cl portable ZIP も同じリリースに添付。MSI を主配布のまま (管理 PC 向けの代替として self-contained ZIP を追加)
name: Build And Release Installer And Portable ZIP
on:
push:
branches: [master]
paths:
- "ReciPro/Version.cs"
# 260530Cl force input を廃止: v* tag 削除禁止 ruleset と非整合で、既存 release を壊す危険があるため。
# 手動再実行は引数なし workflow_dispatch で可能 (ビルド失敗時の再試行用)。
# 旧:
# workflow_dispatch:
# inputs:
# force:
# description: "既存リリースを削除してリビルドする"
# type: boolean
# default: false
workflow_dispatch:
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"
# 同じバージョンのリリースが既にあれば常にスキップ
# 260530Cl force モードを廃止: v* tag 削除禁止 ruleset と非整合で、
# 既存 release を消した後にタグを再作成できず壊すため。
# リリースに失敗したら Version.cs を進めて新しい tag で出し直す運用とする。
gh release view $tag 2>&1 | Out-Null
$releaseExists = ($LASTEXITCODE -eq 0)
$global:LASTEXITCODE = 0
if ($releaseExists) {
Write-Host "Release $tag already exists. Skipping."
"needed=false" >> $env:GITHUB_OUTPUT
return
}
# 旧 force 処理 (260530Cl 廃止):
# if ($releaseExists) {
# if ("(inputs.force)" -eq "true") { # 260530Cl 旧コードでは inputs.force を式参照していた
# Write-Host "Force mode: deleting existing release $tag..."
# gh release delete $tag --yes
# git push origin :refs/tags/$tag 2>&1 | Out-Null
# $global:LASTEXITCODE = 0
# } else {
# 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 # 260602Cl 旧: 変更履歴 1 行のみ
@( # 260602Cl MSI を第一候補、portable ZIP を管理 PC 向け代替として release notes に明記
# 260613Cl SHA256SUMS.txt の行を削除 (checksum 公開廃止: 同一チャネル配布では改竄対策にならず、初心者を惑わすため)
$historyLine
""
"Assets:"
"- ReciProSetup.msi: recommended installer."
"- ReciPro-v.$ver-x64.zip: no-install, self-contained portable alternative for managed Windows PCs."
) | 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)." }
# 260613Cl vdproj→WiX 移行 (Phase B、計画書 .project-guidance/ReciPro_WiX移行計画.md):
# 旧「Build managed application」(dotnet build → bin\Release を vdproj が直接参照) を、
# MSI 専用 staging への framework-dependent publish に置換。bin\Release 共有による
# 「publish は MSI 後」順序制約はこれで解消 (vdproj の SourcePath 参照が消えたため)。
# ★FileVersion 注入は必須: 欠くと csproj 静的値が旧 release より古くなり、MSI の
# ファイルバージョン規則で自前アセンブリの FileCopy が costing 段階で省略され、
# アップグレード時に REP 削除後ファイル欠落する (validate-wix-msi.yml run 27405216245 で実証)。
- name: Stage MSI payload (framework-dependent publish)
if: steps.version.outputs.needed == 'true'
shell: pwsh
run: |
$s = "artifacts\staging-msi-x64"
dotnet publish ReciPro\ReciPro.csproj -c Release -p:Platform=x64 --self-contained false -o $s --no-restore `
-p:AssemblyVersion=${{ steps.version.outputs.assembly_version }} -p:FileVersion=${{ steps.version.outputs.assembly_version }}
if ($LASTEXITCODE -ne 0) { throw "x64 publish failed." }
# Crystallography.Native 3 flavor は publish グラフ外 (vcxproj が bin\Release へ後置き) → 明示コピー
$native = Get-ChildItem "ReciPro\bin\Release" -Filter "Crystallography.Native*.dll" -File
if ($native.Count -ne 3) { throw "native DLL count != 3: $($native.Count)" }
$native | Copy-Item -Destination $s
Copy-Item ReciProSetup\LICENSE.rtf, ReciProSetup\REQUIREMENT.rtf $s
# 旧 vdproj MSI と内容を揃える: 参照プロジェクトの分離 pdb は同梱しない (portable ZIP と同方針)
Get-ChildItem $s -Filter *.pdb | Remove-Item
# 出荷前検査 (計画書 §3 D3)
if (Test-Path "$s\README-PORTABLE.txt") { throw "README-PORTABLE.txt leaked (portable marker must not be in MSI)" }
if (Test-Path "$s\coreclr.dll") { throw "self-contained runtime leaked into framework-dependent staging" }
if (-not (Test-Path "$s\ReciPro.exe")) { throw "ReciPro.exe missing from staging" }
Write-Host "MSI staging OK: $((Get-ChildItem $s -Recurse -File).Count) files"
# 260613Cl vdproj→WiX 移行 (Phase B) で以下 4 ステップを廃止 (本文は git 履歴 b6340c74 以前を参照):
# - Update installer version in vdproj (regex で ProductVersion/ProductCode/PackageCode 書換)
# → WiX では -p:ProductVersion 注入 + ProductCode 自動生成で代替 (UpgradeCode は Package.wxs に固定)
# - Disable out-of-proc setup project build (HKCU レジストリハック) → 不要
# - Create installer-only solution (ReciProSetup.CI.sln 動的生成) → 不要
# - Build installer with Visual Studio (devenv.com) → dotnet build (WixToolset.Sdk) に置換
# devenv.com は Crystallography.Native (vcxproj) ビルドのため引き続き必要。
# vdproj 一式 (ReciProSetup\) は WiX 版リリース 1 回成功後に削除予定 (計画書 §6 Phase B-3)。
- name: Build installer with WiX
id: build_installer
if: steps.version.outputs.needed == 'true'
timeout-minutes: 10
shell: pwsh
run: |
dotnet build ReciProSetup.Wix\ReciProSetup.wixproj -c Release -p:InstallerPlatform=x64 `
-p:ProductVersion=${{ steps.version.outputs.product_version }} `
-p:StagingDir="$PWD\artifacts\staging-msi-x64"
if ($LASTEXITCODE -ne 0) { throw "WiX build failed (exit $LASTEXITCODE)." }
# MSI の存在確認 (出力名 ReciProSetup.msi は自動更新の固定 URL なので不変)
$msi = Get-Item "ReciProSetup.Wix\bin\Release\ReciProSetup.msi" -ErrorAction SilentlyContinue
if (-not $msi) { throw "ReciProSetup.msi was not found." }
Write-Host "MSI built: $($msi.FullName) ($($msi.Length) bytes)"
"msi_path=$($msi.FullName)" >> $env:GITHUB_OUTPUT
- name: Publish portable ZIP
if: steps.version.outputs.needed == 'true'
id: portable
shell: pwsh
run: |
# 260602Cl 管理された Windows PC 向けの代替配布。MSI が第一候補、ZIP は管理者権限や .NET Runtime 導入が難しい環境用。
# 260602Cl このステップは installer (MSI) ビルドより後に置く。self-contained publish は bin\Release を
# ランタイム同梱の内容で汚染し (AppendRuntimeIdentifierToOutputPath=false で publish と build が
# bin\Release を共有するため)、MSI ビルドより前に走らせると framework-dependent であるべき MSI に
# ランタイムが紛れ込む。詳細は .project-guidance\ReciPro_MSI+PortableZip構築方針.md §4。
# 260613Cl ↑の順序制約は WiX 移行で解消済み (MSI は専用 staging から作り bin\Release を参照しない)。
# 位置は当面維持 (変更を最小にするため)。
$publishDir = Join-Path $env:RUNNER_TEMP "ReciPro-portable\ReciPro"
# 260613Cl 作者指示で命名簡素化 (旧: ReciPro-v.X-win-x64-portable.zip)。自動更新は MSI 固定 URL のみで ZIP 名に依存しない
$zipPath = Join-Path (Get-Location) "ReciPro-v.${{ steps.version.outputs.version }}-x64.zip"
if (Test-Path $publishDir) { Remove-Item -LiteralPath $publishDir -Recurse -Force }
if (Test-Path $zipPath) { Remove-Item -LiteralPath $zipPath -Force }
# 260602Cl DebugType は csproj の既定 (ReciPro 本体は embedded) を使うため、PDIndexer のような
# 別 pdb 欠落による MSI ビルド失敗 (方針 §4-a) は起こらない。参照ライブラリ
# (Crystallography 等) は分離 pdb を出すので、配布前に publish 出力から除去する (後述)。
# 260611Cl SatelliteResourceLanguages=ja: self-contained publish が同梱する .NET ランタイムの
# ローカライズ 13 言語 (~18MB) を、アプリが提供する neutral(en)+ja に合わせて ja のみに絞る。
# publish 引数に留める (csproj に入れない) ことで MSI ビルド経路には影響させない。
# 260611Cl PublishSingleFile=true: フラット ~290 ファイルでは ReciPro.exe が探しにくいという作者要望
# (arm64 Surface 検証) を受け、ランタイム・managed DLL・ja サテライト・libxrl を exe に埋め込む。
# Crystallography.Native*.dll (3 flavors) は従来どおり後コピーで exe 横に外置き
# (NativeWrapper の BaseDirectory 解決と CPU 判定 flavor 選択をそのまま使うため)。
# ビルド出力 (bin\Release) や MSI には影響しない (PublishSingleFile は publish 専用)。
dotnet publish ReciPro\ReciPro.csproj `
-c Release `
-r win-x64 `
--self-contained true `
-p:Platform=x64 `
-p:AssemblyVersion=${{ steps.version.outputs.assembly_version }} `
-p:FileVersion=${{ steps.version.outputs.assembly_version }} `
-p:PublishSingleFile=true `
-p:PublishTrimmed=false `
-p:SatelliteResourceLanguages=ja `
-o $publishDir
# 260602Cl ★ReciPro 固有: Crystallography.Native*.dll は ProjectReference でも Content でも PackageReference でもなく、
# vcxproj の OutDir 経由で ReciPro\bin\Release に置かれるだけなので publish 出力には含まれない。
# generic / avx2 / avx512 の 3 種を明示コピーする (NativeWrapper が起動時に CPU 判定で選択する)。
$nativeDir = "ReciPro\bin\Release"
$natives = Get-ChildItem -Path $nativeDir -Filter "Crystallography.Native*.dll" -ErrorAction SilentlyContinue
if (-not $natives) { throw "Native DLLs (Crystallography.Native*.dll) were not found in $nativeDir." }
foreach ($n in $natives) { Copy-Item $n.FullName $publishDir -Force; Write-Host "Bundled native: $($n.Name)" }
Copy-Item "LICENSE.md" $publishDir -Force
Copy-Item "README-PORTABLE.txt" $publishDir -Force
# 260602Cl 参照ライブラリの分離 pdb を配布物から除去 (ReciPro 本体は embedded のため元々 pdb なし)。
Get-ChildItem $publishDir -Filter "*.pdb" | Remove-Item -Force
Compress-Archive -Path (Join-Path (Split-Path $publishDir -Parent) "ReciPro") -DestinationPath $zipPath -Force
Write-Host "Portable ZIP built: $zipPath"
"zip_path=$zipPath" >> $env:GITHUB_OUTPUT
# 260613Cl 「Generate release checksums」ステップを廃止 (SHA256SUMS.txt の公開をやめる)。
# 理由: 同一チャネル (同じ release ページ) に置く checksum は改竄対策にならず、
# リリースページで初心者を惑わす悪影響の方が大きい (作者決定)。真正性保証は SignPath 署名 (Phase D) で行う。
# 本文は git 履歴 b3c35d22 以前を参照。
- name: Upload workflow artifacts
if: steps.version.outputs.needed == 'true'
# 260425Cl Node.js 20 廃止対応 (v6 で Node.js 24 がデフォルト)
uses: actions/upload-artifact@v6
with:
name: ReciPro-release-v.${{ steps.version.outputs.version }}
path: |
${{ steps.build_installer.outputs.msi_path }}
${{ steps.portable.outputs.zip_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 }}"
# 260530Cl タグとリリースを gh release create で原子的に作成 (--target で対象コミットを固定)
# 旧実装は git tag -a + git push の2段階で、push 成功後に release 作成が失敗すると
# タグだけ remote に残り、v* tag 削除禁止 ruleset により同一バージョンで復旧不能だった。
# gh release create は tag 未存在時に --target のコミットへ lightweight tag を作成し、
# release と一括で作るため orphan 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
# 260602Cl 第一候補の MSI に加え、portable ZIP も添付する。
# 260613Cl SHA256SUMS.txt の添付を廃止 (checksum 公開廃止)。
gh release create $tag `
"${{ steps.build_installer.outputs.msi_path }}" `
"${{ steps.portable.outputs.zip_path }}" `
--target $env:GITHUB_SHA `
--title "ReciPro $tag" `
--notes-file "${{ steps.version.outputs.notes_path }}"