diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..2d85910 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,132 @@ +name: Build Release Installer +run-name: Build LTOG ${{ inputs.version }} installer + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version, for example 1.0.0' + required: true + type: string + +permissions: + contents: write + +jobs: + build-release: + name: Build and publish installer + runs-on: windows-latest + + env: + WINFSP_MSI_URL: https://github.com/winfsp/winfsp/releases/download/v2.1/winfsp-2.1.25156.msi + WINFSP_MSI_SHA256: 073A70E00F77423E34BED98B86E600DEF93393BA5822204FAC57A29324DB9F7A + + steps: + - name: Validate version + shell: pwsh + env: + INPUT_VERSION: ${{ inputs.version }} + run: | + $version = $env:INPUT_VERSION + if ($version -notmatch '^\d+\.\d+\.\d+(\.\d+)?$') { + throw "Version must look like 1.0.0 or 1.0.0.0. Got '$version'." + } + "RELEASE_VERSION=$version" >> $env:GITHUB_ENV + "RELEASE_TAG=v$version" >> $env:GITHUB_ENV + + - name: Configure Git line endings + shell: pwsh + run: git config --global core.autocrlf input + + - name: Check out repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Set up .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Set up MSYS2 + id: msys2 + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + update: true + install: >- + git + autoconf + automake + libtool + make + pkgconf + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-libxml2 + mingw-w64-x86_64-icu + mingw-w64-x86_64-pkgconf + + - name: Extract WinFsp developer files + shell: pwsh + run: | + $msi = Join-Path $env:RUNNER_TEMP 'winfsp.msi' + Invoke-WebRequest -Uri $env:WINFSP_MSI_URL -OutFile $msi + $actual = (Get-FileHash $msi -Algorithm SHA256).Hash + if ($actual -ne $env:WINFSP_MSI_SHA256) { + throw "WinFsp MSI SHA256 mismatch: got $actual, expected $env:WINFSP_MSI_SHA256" + } + + $extractRoot = Join-Path $env:RUNNER_TEMP 'winfsp' + New-Item -ItemType Directory -Force -Path $extractRoot | Out-Null + $msiArgs = @( + '/a', "`"$msi`"", + "TARGETDIR=`"$extractRoot`"", + '/qn', + '/norestart' + ) + $process = Start-Process msiexec.exe -Wait -PassThru -ArgumentList $msiArgs + if ($process.ExitCode -ne 0) { + throw "WinFsp MSI extraction failed with exit code $($process.ExitCode)." + } + + $winfspRoot = Join-Path $extractRoot 'DYNAMIC' + $lib = Join-Path $winfspRoot 'lib\winfsp-x64.lib' + $dll = Join-Path $winfspRoot 'SxS\DYNAMIC\bin\winfsp-x64.dll' + if (-not (Test-Path $lib)) { + throw "WinFsp MSI extraction did not produce '$lib'." + } + if (-not (Test-Path $dll)) { + throw "WinFsp MSI extraction did not produce '$dll'." + } + New-Item -ItemType Directory -Force -Path (Join-Path $winfspRoot 'bin') | Out-Null + Copy-Item $dll (Join-Path $winfspRoot 'bin\winfsp-x64.dll') -Force + "WINFSP=$winfspRoot" >> $env:GITHUB_ENV + + - name: Build installer + shell: pwsh + env: + MSYS2_ROOT: ${{ steps.msys2.outputs['msys2-location'] }} + run: pwsh -NoProfile -ExecutionPolicy Bypass -File .\build.ps1 -Version $env:RELEASE_VERSION -Msys2Root $env:MSYS2_ROOT + + - name: Locate installer + id: installer + shell: pwsh + run: | + $installer = Get-ChildItem .\installer\Output -Filter "LTOG-$env:RELEASE_VERSION-setup.exe" | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + if (-not $installer) { + throw "Installer not found in installer\Output." + } + "path=$($installer.FullName)" >> $env:GITHUB_OUTPUT + + - name: Create GitHub Release + shell: pwsh + env: + GH_TOKEN: ${{ github.token }} + run: | + gh release create $env:RELEASE_TAG "${{ steps.installer.outputs.path }}" ` + --target "${{ github.sha }}" ` + --title "LTOG $env:RELEASE_VERSION" ` + --notes "Installer build for LTOG $env:RELEASE_VERSION." ` + --latest diff --git a/build.ps1 b/build.ps1 index 73e76b4..bb4f076 100644 --- a/build.ps1 +++ b/build.ps1 @@ -30,6 +30,9 @@ .PARAMETER Msys2Root Path to the MSYS2 installation. Default: C:\msys64. +.PARAMETER Version + Version embedded into the GUI assembly and installer. Default: 1.0.0. + .EXAMPLE pwsh -File build.ps1 # full clean build of everything @@ -40,7 +43,9 @@ param( [switch]$SkipNative, [switch]$NoInstaller, - [string]$Msys2Root = 'C:\msys64' + [string]$Msys2Root = 'C:\msys64', + [ValidatePattern('^\d+\.\d+\.\d+(\.\d+)?$')] + [string]$Version = '1.0.0' ) Set-StrictMode -Version Latest @@ -132,9 +137,12 @@ Step 'Building self-contained WinUI 3 GUI (dotnet)' if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { throw "dotnet not found. Install the .NET 8 SDK (winget install Microsoft.DotNet.SDK.8)." } +$assemblyVersion = if (($Version.Split('.')).Count -eq 3) { "$Version.0" } else { $Version } Push-Location $GuiDir try { - & dotnet build 'LTOG.Gui.csproj' -c Release -p:Platform=x64 --nologo + & dotnet build 'LTOG.Gui.csproj' -c Release -p:Platform=x64 ` + -p:Version=$Version -p:AssemblyVersion=$assemblyVersion -p:FileVersion=$assemblyVersion ` + --nologo if ($LASTEXITCODE -ne 0) { throw "dotnet build failed (exit $LASTEXITCODE)." } } finally { Pop-Location @@ -154,7 +162,7 @@ if ($NoInstaller) { } else { Step 'Building Windows installer' & pwsh -NoProfile -ExecutionPolicy Bypass ` - -File (Join-Path $InstallerDir 'build-installer.ps1') -AutoInstallInnoSetup + -File (Join-Path $InstallerDir 'build-installer.ps1') -AutoInstallInnoSetup -Version $Version if ($LASTEXITCODE -ne 0) { throw "installer build failed (exit $LASTEXITCODE)." } } diff --git a/installer/LTOG.iss b/installer/LTOG.iss index cf53145..dcda664 100644 --- a/installer/LTOG.iss +++ b/installer/LTOG.iss @@ -23,7 +23,9 @@ ; =========================================================================== #define MyAppName "LTOG" +#ifndef MyAppVersion #define MyAppVersion "1.0.0" +#endif #define MyAppId "{6E9D2B4A-1C3F-4E58-9A7D-2F5B8C1E4D0A}" #define MyAppPublisher "rlaphoenix" #define MyAppURL "https://github.com/rlaphoenix/LTOG" diff --git a/installer/build-installer.ps1 b/installer/build-installer.ps1 index 1818d47..f35d8cc 100644 --- a/installer/build-installer.ps1 +++ b/installer/build-installer.ps1 @@ -12,11 +12,18 @@ .EXAMPLE pwsh -ExecutionPolicy Bypass -File .\build-installer.ps1 + +.PARAMETER Version + Version embedded into the installer metadata and output filename. Default: 1.0.0. #> [CmdletBinding()] param( # Compile even if ISCC has to be installed via winget without prompting. - [switch]$AutoInstallInnoSetup + [switch]$AutoInstallInnoSetup, + + # Installer/application version. Passed through to LTOG.iss. + [ValidatePattern('^\d+\.\d+\.\d+(\.\d+)?$')] + [string]$Version = '1.0.0' ) Set-StrictMode -Version Latest @@ -131,7 +138,7 @@ Write-Host " using $iscc" # --- 4. compile ----------------------------------------------------------- Write-Step 'Compiling installer' -& $iscc $IssFile +& $iscc "/DMyAppVersion=$Version" $IssFile if ($LASTEXITCODE -ne 0) { throw "ISCC failed with exit code $LASTEXITCODE." } $out = Get-ChildItem (Join-Path $Root 'Output') -Filter '*.exe' |