name: CI on: # Triggers the workflow on push or pull request events but only for the master branch push: branches: [ master ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: # This workflow contains a single job called "build" build: permissions: contents: write runs-on: windows-2025-vs2026 # Steps represent a sequence of tasks that will be executed as part of the job steps: # Step to check if the commit message contains #GITBUILD - name: Check Commit Message shell: powershell run: | # Get the commit message $strVal ='${{ github.event. head_commit.message }}' # Convert commit message to a single line if multiline $singleLineStrVal = $strVal -replace "`r`n", " " -replace "`n", " " if ($singleLineStrVal -match '#GITBUILD') { Write-Host 'Build commit detected. Proceeding with build...' echo "build_trigger=true" >> $env:GITHUB_ENV } else { Write-Host 'No build commit. Skipping build steps...' echo "build_trigger=false" >> $env:GITHUB_ENV } # Step to ensure the repository is checked out - uses: actions/checkout@v4 with: fetch-depth: 0 fetch-tags: true # Inform if build steps are skipped - name: Inform Skipped Build Steps if: env.build_trigger != 'true' shell: powershell run: | Write-Host "Skipping build steps because the commit message does not contain #GITBUILD." # Install 7Zip PowerShell module - name: Install 7Zip PowerShell Module if: env.build_trigger == 'true' shell: powershell run: Install-Module 7Zip4PowerShell -Force -Verbose # Restore NuGet packages - name: Restore NuGet packages if: env.build_trigger == 'true' run: nuget restore UnityLauncherPro.sln # Build the binary - name: Build Binary if: env.build_trigger == 'true' shell: cmd run: call .\Build.cmd # Build the artifact - name: Build Artifact if: env.build_trigger == 'true' shell: cmd run: call .\ArtifactBuild.cmd # Check that output exists - name: Validate UnityLauncherPro.exe exists if: env.build_trigger == 'true' shell: cmd run: | if not exist "UnityLauncherPro\bin\Release\UnityLauncherPro.exe" ( echo ERROR: UnityLauncherPro.exe not found. exit /b 1 ) echo Found UnityLauncherPro.exe # 1) Compute a wrapped major.minor.build from the run number - name: Compute installer version if: env.build_trigger == 'true' shell: bash run: | TOTAL=${{ github.run_number }} BUILD=$(( TOTAL % 65536 )) MINOR_TOTAL=$(( TOTAL / 65536 )) MINOR=$(( MINOR_TOTAL % 256 )) MAJOR=$(( MINOR_TOTAL / 256 )) echo "INSTALLER_MAJOR=$MAJOR" >> $GITHUB_ENV echo "INSTALLER_MINOR=$MINOR" >> $GITHUB_ENV echo "INSTALLER_BUILD=$BUILD" >> $GITHUB_ENV echo "INSTALLER_VER=$MAJOR.$MINOR.$BUILD" >> $GITHUB_ENV echo "Computed installer version → $MAJOR.$MINOR.$BUILD (run #${{ github.run_number }})" # 2) Patch your .vdproj in place (inject ProductVersion and a new GUID) - name: Patch .vdproj ProductVersion & ProductCode if: env.build_trigger == 'true' shell: pwsh run: | $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' $ver = "${{ env.INSTALLER_VER }}" # e.g. 0.0.118 $guid = [Guid]::NewGuid().ToString("B").ToUpper() # e.g. {E821A3F5-1F84-4A4B-BE9D-115D93E9E6F0} # Read & rewrite line-by-line $fixed = Get-Content $proj | ForEach-Object { if ($_ -match '^(\s*)"ProductVersion"') { # preserve indentation, then inject exactly one pair of braces "$($Matches[1])""ProductVersion"" = ""8:$ver""" } elseif ($_ -match '^(\s*)"ProductCode"') { "$($Matches[1])""ProductCode"" = ""8:$guid""" } else { $_ } } # Overwrite the project Set-Content -Path $proj -Value $fixed Write-Host "→ ProductVersion patched to 8:$ver" Write-Host "→ ProductCode patched to 8:$guid" # 3) **DEBUG**: print out the patched .vdproj so you can inspect it - name: Show patched .vdproj if: env.build_trigger == 'true' shell: pwsh run: | $proj = 'UnityLauncherProInstaller\UnityLauncherProInstaller.vdproj' Write-Host "=== BEGIN .vdproj CONTENT ===" Get-Content $proj Write-Host "=== END .vdproj CONTENT ===" # locate VS 2026 - name: Locate Visual Studio 2026 id: vswhere shell: pwsh run: | $vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" if (-not (Test-Path $vswhere)) { throw "Could not find vswhere.exe at $vswhere" } $installPath = & $vswhere -latest -products * -version "[18.0,19.0)" ` -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath if (-not $installPath) { Write-Host 'VS 2026 (major 18) not found on runner. Falling back to VS 2022 (major 17).' $installPath = & $vswhere -latest -products * -version "[17.0,18.0)" ` -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath } if (-not $installPath) { throw "Could not find a Visual Studio installation" } "VS=$installPath" >> $env:GITHUB_ENV "installPath=$installPath" >> $env:GITHUB_OUTPUT # download vs installer builder (v2.0.1) - name: Download Installer-Projects VSIX shell: pwsh run: | $vsixUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioClient/vsextensions/MicrosoftVisualStudio2026InstallerProjects/2.0.1/vspackage" $fallbackUrl = "https://marketplace.visualstudio.com/_apis/public/gallery/publishers/VisualStudioClient/vsextensions/MicrosoftVisualStudio2022InstallerProjects/2.0.1/vspackage" try { Invoke-WebRequest $vsixUrl -OutFile installerprojects.vsix -ErrorAction Stop } catch { Write-Host "VS2026 Installer Projects package not found, falling back to VS2022 feed." Invoke-WebRequest $fallbackUrl -OutFile installerprojects.vsix -ErrorAction Stop } # install vs installer builder - name: Install Installer-Projects extension shell: pwsh run: | $vsixInstaller = Join-Path "${{ steps.vswhere.outputs.installPath }}" 'Common7\IDE\VSIXInstaller.exe' Write-Host "Running: $vsixInstaller installerprojects.vsix /quiet" & $vsixInstaller installerprojects.vsix /quiet # Build MSI installer project using Visual Studio 2026 workaround - name: Build Installer MSI if: env.build_trigger == 'true' shell: cmd run: | setlocal EnableDelayedExpansion echo === Running BuildInstaller.bat === call .\BuildInstaller.bat || echo WARNING: BuildInstaller.bat exited with %ERRORLEVEL%, continuing... set "ec=!ERRORLEVEL!" if not "!ec!"=="0" echo WARNING: BuildInstaller.bat exited with !ec!, continuing... echo === Verifying MSI === if exist "UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi" ( echo Success: MSI found at UnityLauncherProInstaller\Release\UnityLauncherPro-Installer.msi exit /b 0 ) else ( echo ERROR: MSI not found in UnityLauncherProInstaller\Release exit /b 1 ) # Get the current date and time - name: Get current date and time (Helsinki) if: env.build_trigger == 'true' shell: pwsh run: | $nowUtc = Get-Date -AsUTC $tz = [TimeZoneInfo]::FindSystemTimeZoneById('FLE Standard Time') # Europe/Helsinki $now = [TimeZoneInfo]::ConvertTimeFromUtc($nowUtc, $tz) "current_datetime=$($now.ToString('dd/MM/yyyy HH:mm'))" >> $env:GITHUB_ENV "build_timestamp=$($now.ToString('HH:mm:ss - dd/MM/yyyy'))" >> $env:GITHUB_ENV - name: Echo timestamp (debug) if: env.build_trigger == 'true' shell: pwsh run: echo "build_timestamp=${env:build_timestamp}" # Get commits since last build - name: Get commits since last numeric CI tag id: get_commits if: env.build_trigger == 'true' shell: bash run: | set -euo pipefail # Defensive: fetch tags git fetch --tags --force # Collect numeric-only tags (e.g., 145, 143...) and sort ascending numerically mapfile -t NUM_TAGS < <(git tag --list '[0-9]*' | sort -n) PREV_TAG="" # Walk from newest to oldest and pick the newest tag that is an ancestor of HEAD for (( idx=${#NUM_TAGS[@]}-1 ; idx>=0 ; idx-- )); do t="${NUM_TAGS[$idx]}" if git merge-base --is-ancestor "$t" HEAD; then PREV_TAG="$t" break fi done if [ -z "$PREV_TAG" ]; then echo "No previous numeric CI tag reachable from HEAD → listing all commits" RANGE="--all" else echo "Previous CI tag: $PREV_TAG" RANGE="$PREV_TAG..HEAD" fi # Nicely formatted bullets; add --date=short if you want dates COMMITS=$(git log $RANGE --no-merges --pretty='* %h %s (by %an)') if [ -z "$COMMITS" ]; then COMMITS="* No code changes since last CI tag." fi { echo 'commits<> "$GITHUB_OUTPUT" - name: Create GitHub Release + Upload Assets if: env.build_trigger == 'true' uses: softprops/action-gh-release@v2 with: tag_name: ${{ github.run_number }} name: ${{ env.current_datetime }} (${{ github.run_number }}) body: | Automated Release by GitHub Action CI. **Build time:** ${{ env.build_timestamp }} NOTE: Installer is experimental (and it loses settings from previous version) ### Commits in this release ${{ steps.get_commits.outputs.commits }} draft: false prerelease: false files: | ./UnityLauncherPro.zip ./UnityLauncherProInstaller/Release/UnityLauncherPro-Installer.msi