Skip to content

Disable VS telemetry in CI to prevent devenv hang #15

Disable VS telemetry in CI to prevent devenv hang

Disable VS telemetry in CI to prevent devenv hang #15

Workflow file for this run

# (260405Ch) Build ReciProSetup on v* tag push and publish a GitHub Release using the latest Version.cs history line
name: Build And Release Installer
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
release:
runs-on: windows-2025-vs2026 # (260405Ch) net10.0-windows + WinForms setup build needs Visual Studio 18 on GitHub-hosted runners.
timeout-minutes: 30 # 260405Cl 追加: devenv ハング時に無限待ちしないようジョブ全体にタイムアウト設定
env: # 260405Cl 追加: CI で VS テレメトリを無効化し devenv ハングを防止
VSCMD_SKIP_SENDTELEMETRY: "1"
DOTNET_CLI_TELEMETRY_OPTOUT: "1"
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Setup .NET 10
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"
- name: Restore managed dependencies
shell: pwsh
run: dotnet restore ReciPro\ReciPro.csproj
- name: Confirm installer project sources
shell: pwsh
run: |
# (260405Ch) Fail early with a clear message if installer project sources were not committed to GitHub.
$requiredSources = @(
"ReciProSetup\ReciProSetup.vdproj",
"ReciProSetup\EnableLaunchApplication.js",
"LICENSE.rtf",
"REQUIREMENT.rtf"
)
$missingSources = $requiredSources | Where-Object { -not (Test-Path $_) }
if ($missingSources.Count -gt 0) {
throw "Installer source files are missing from the checkout: $($missingSources -join ', ')"
}
- name: Read release info from Version.cs
id: version_info
shell: pwsh
run: |
$versionFile = "ReciPro\Version.cs"
$content = Get-Content $versionFile -Raw
$historyRegex = [regex]::Matches($content, 'ver[^\r\n"]+')
if ($historyRegex.Count -eq 0) {
throw "Could not find a release history line in $versionFile."
}
$latestHistory = $historyRegex[0].Value.Trim()
if ($latestHistory -notmatch '^ver(?<Version>[0-9][0-9A-Za-z\.\-_]*)') {
throw "Could not parse version number from history line: $latestHistory"
}
$historyTag = "v$($Matches.Version)"
$tagName = "${{ github.ref_name }}"
if ($historyTag -ne $tagName) {
throw "Tag '$tagName' does not match Version.cs latest history '$historyTag'."
}
$releaseTitle = "ReciPro $tagName"
$notesPath = Join-Path $env:RUNNER_TEMP "release-notes.txt"
@(
$latestHistory
""
"Built automatically from tag $tagName."
) | Set-Content -Path $notesPath -Encoding UTF8
"latest_history=$latestHistory" >> $env:GITHUB_OUTPUT
"release_title=$releaseTitle" >> $env:GITHUB_OUTPUT
"notes_path=$notesPath" >> $env:GITHUB_OUTPUT
- name: Disable out-of-proc setup project build
shell: pwsh
run: |
# (260405Ch) Force installer builds to run in-proc so devenv does not return before MSI generation completes.
$visualStudioRoot = "HKCU:\Software\Microsoft\VisualStudio"
$configKeys = Get-ChildItem $visualStudioRoot -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -like '17.0*_Config' -or $_.PSChildName -like '18.0*_Config' }
if ($configKeys.Count -eq 0) {
Write-Warning "No Visual Studio *_Config registry keys were found under $visualStudioRoot."
}
foreach ($configKey in $configKeys) {
$msbuildKey = Join-Path $configKey.PSPath "MSBuild"
if (-not (Test-Path $msbuildKey)) {
New-Item -Path $msbuildKey -Force | Out-Null
}
New-ItemProperty -Path $msbuildKey -Name DisableOutOfProcBuild -PropertyType DWord -Value 1 -Force | Out-Null
Write-Host "Set DisableOutOfProcBuild=1 at $msbuildKey"
}
- name: Build native runtime binaries
shell: pwsh
run: |
# (260405Ch) Build Crystallography.Native separately because the main solution build does not include it in Release|x64.
$devenvExe = (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property productPath).Trim()
if (-not $devenvExe) {
throw "Visual Studio was not found."
}
$devenvCli = [System.IO.Path]::ChangeExtension($devenvExe, ".com")
if (-not (Test-Path $devenvCli)) {
throw "devenv.com was not found next to $devenvExe."
}
$buildLogPath = Join-Path $env:RUNNER_TEMP "devenv-native-build.log"
& $devenvCli "ReciPro.sln" /Build "Release|x64" /Project "Crystallography.Native" /Out $buildLogPath
$buildExitCode = $LASTEXITCODE
if (Test-Path $buildLogPath) {
Get-Content $buildLogPath
}
if ($buildExitCode -ne 0) {
throw "Crystallography.Native build failed with exit code $buildExitCode."
}
- name: Build managed application
shell: pwsh
run: |
# (260405Ch) Build managed projects with dotnet to avoid devenv-triggered file locking inside obj during CI.
dotnet build ReciPro\ReciPro.csproj -c Release -p:Platform=x64 --no-restore
- name: Confirm packaging inputs
shell: pwsh
run: |
# (260405Ch) Validate that setup packaging prerequisites exist before running the setup project.
$requiredOutputs = @(
"ReciPro\bin\Release\ReciPro.exe",
"ReciPro\bin\Release\Crystallography.Native.dll",
"ReciPro\bin\Release\Crystallography.Native.avx2.dll",
"ReciPro\bin\Release\Crystallography.Native.avx512.dll"
)
$missingOutputs = $requiredOutputs | Where-Object { -not (Test-Path $_) }
if ($missingOutputs.Count -gt 0) {
throw "Setup packaging inputs are missing: $($missingOutputs -join ', ')"
}
- name: Create installer-only solution
id: pack_solution
shell: pwsh
run: |
# (260405Ch) Generate a temporary solution that keeps project output metadata for vdproj while skipping managed rebuilds.
$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
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D01883A1-458D-4127-8E9B-EEC176201483}.Debug|x64.ActiveCfg = Debug|x64
{D01883A1-458D-4127-8E9B-EEC176201483}.Release|x64.ActiveCfg = Release|x64
{3AABEFB0-E84E-4AF6-8496-4D1F33C8F517}.Debug|x64.ActiveCfg = Debug|x64
{3AABEFB0-E84E-4AF6-8496-4D1F33C8F517}.Release|x64.ActiveCfg = Release|x64
{FD96FE43-6DEE-4520-BB26-CFF7976DE27D}.Debug|x64.ActiveCfg = Debug|x64
{FD96FE43-6DEE-4520-BB26-CFF7976DE27D}.Release|x64.ActiveCfg = Release|x64
{BE7CC301-70E0-4BAA-9349-FDA49B38CC93}.Debug|x64.ActiveCfg = Debug|x64
{BE7CC301-70E0-4BAA-9349-FDA49B38CC93}.Release|x64.ActiveCfg = Release|x64
{0A6070E5-8843-4C4B-A4ED-4ABC55BFDBDD}.Debug|x64.ActiveCfg = Debug|x64
{0A6070E5-8843-4C4B-A4ED-4ABC55BFDBDD}.Release|x64.ActiveCfg = Release|x64
{8083E96B-786B-4351-87EF-8546C9C8B297}.Debug|x64.ActiveCfg = Debug
{8083E96B-786B-4351-87EF-8546C9C8B297}.Debug|x64.Build.0 = Debug
{8083E96B-786B-4351-87EF-8546C9C8B297}.Release|x64.ActiveCfg = Release
{8083E96B-786B-4351-87EF-8546C9C8B297}.Release|x64.Build.0 = Release
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
'@ | Set-Content -Path $solutionPath -Encoding ASCII
"solution_path=$solutionPath" >> $env:GITHUB_OUTPUT
- name: Build installer with Visual Studio
id: build_installer
timeout-minutes: 10
shell: pwsh
run: |
# 260405Cl devenv.com はテレメトリ(Chrome/GCM)が終了せずハングするため、
# バックグラウンドで起動し MSI 出現を監視して強制終了する。
$devenvExe = (& "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property productPath).Trim()
if (-not $devenvExe) { throw "Visual Studio was not found." }
$devenvCli = [System.IO.Path]::ChangeExtension($devenvExe, ".com")
if (-not (Test-Path $devenvCli)) { throw "devenv.com was not found next to $devenvExe." }
$buildLogPath = Join-Path $env:RUNNER_TEMP "devenv-build.log"
$msiPath = "ReciProSetup\Release\ReciProSetup.msi"
# devenv をバックグラウンド起動
$proc = Start-Process -FilePath $devenvCli -ArgumentList "`"${{ steps.pack_solution.outputs.solution_path }}`" /Build `"Release|x64`" /Project `"ReciProSetup`" /Out `"$buildLogPath`"" -PassThru -NoNewWindow
# MSI が生成されるか、devenv が自力で終了するまで待機 (最大5分)
$deadline = (Get-Date).AddMinutes(5)
while (-not $proc.HasExited -and (Get-Date) -lt $deadline) {
if (Test-Path $msiPath) {
Write-Host "MSI detected, waiting 10s for post-build events..."
Start-Sleep -Seconds 10
break
}
Start-Sleep -Seconds 2
}
# devenv がまだ生きていたら強制終了
if (-not $proc.HasExited) {
Write-Host "Stopping devenv (pid $($proc.Id)) to avoid telemetry hang..."
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
}
if (Test-Path $buildLogPath) {
Get-Content $buildLogPath
}
if (-not (Test-Path $msiPath)) {
throw "MSI was not produced within the time limit."
}
Write-Host "MSI built successfully: $msiPath"
"build_exit_code=0" >> $env:GITHUB_OUTPUT
"build_log_path=$buildLogPath" >> $env:GITHUB_OUTPUT
- name: Confirm installer outputs
id: installer_outputs
shell: pwsh
run: |
# (260405Ch) Some environments materialize setup project outputs slightly after devenv returns, so wait and locate them dynamically.
$requiredNames = @("ReciProSetup.msi")
$foundFiles = @{}
$deadline = (Get-Date).AddMinutes(2)
do {
Get-ChildItem -Path . -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $requiredNames -contains $_.Name } |
ForEach-Object { $foundFiles[$_.Name] = $_.FullName }
$missingFiles = $requiredNames | Where-Object { -not $foundFiles.ContainsKey($_) }
if ($missingFiles.Count -eq 0) {
break
}
Start-Sleep -Seconds 2
} while ((Get-Date) -lt $deadline)
$missingFiles = $requiredNames | Where-Object { -not $foundFiles.ContainsKey($_) }
if ($missingFiles.Count -gt 0) {
Write-Host "Located installer-like files:"
Get-ChildItem -Path . -Recurse -File -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -eq ".msi" -or $_.Name -eq "setup.exe" } |
Select-Object FullName, Length, LastWriteTime
if (Test-Path "${{ steps.build_installer.outputs.build_log_path }}") {
Write-Host "devenv build log:"
Get-Content "${{ steps.build_installer.outputs.build_log_path }}"
}
throw "Required output was not found: $($missingFiles -join ', ')"
}
if ("${{ steps.build_installer.outputs.build_exit_code }}" -ne "0") {
Write-Warning "devenv returned exit code ${{ steps.build_installer.outputs.build_exit_code }}, but the MSI output was found. Continuing with the generated file."
}
"msi_path=$($foundFiles['ReciProSetup.msi'])" >> $env:GITHUB_OUTPUT
- name: Upload workflow artifacts
uses: actions/upload-artifact@v4
with:
name: ReciPro-installer-${{ github.ref_name }}
path: |
${{ steps.installer_outputs.outputs.msi_path }}
- name: Create GitHub Release
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "${{ github.ref_name }}" `
"${{ steps.installer_outputs.outputs.msi_path }}" `
--verify-tag `
--title "${{ steps.version_info.outputs.release_title }}" `
--notes-file "${{ steps.version_info.outputs.notes_path }}"