param( [string]$Version = "0.2.8", [string]$BridgeVersion = "0.2.5", [string]$ObsVersion = "31.1.1" ) $ErrorActionPreference = "Stop" $repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path $project = Join-Path $repoRoot "companion\src\Lumi.Companion.App\Lumi.Companion.App.csproj" $outputRoot = Join-Path $repoRoot "companion\installer\output" $publishRoot = Join-Path $outputRoot "publish" $stageRoot = Join-Path $outputRoot "package" $sourceStageRoot = Join-Path $outputRoot "obs-bridge-source" $archive = Join-Path $outputRoot "Lumi.Companion-win-x64.zip" $installer = Join-Path $outputRoot "Lumi.Companion-Setup.exe" $legalSourceRoot = Join-Path $repoRoot "companion\legal" $publishLegalRoot = Join-Path $publishRoot "legal" function Invoke-Native([string]$FilePath, [string[]]$Arguments) { $argumentLine = ($Arguments | ForEach-Object { '"' + ([string]$_).Replace('"', '\"') + '"' }) -join ' ' $process = Start-Process -FilePath $FilePath -ArgumentList $argumentLine -NoNewWindow -Wait -PassThru return $process.ExitCode } function Compress-ArchiveWithRetry { param( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$DestinationPath, [int]$Attempts = 8 ) for ($attempt = 1; $attempt -le $Attempts; $attempt++) { try { Remove-Item $DestinationPath -Force -ErrorAction SilentlyContinue Compress-Archive -Path $Path -DestinationPath $DestinationPath -CompressionLevel Optimal -ErrorAction Stop return } catch { if ($attempt -eq $Attempts) { throw } Start-Sleep -Seconds 2 } } } & (Join-Path $PSScriptRoot "generate-companion-icon.ps1") & (Join-Path $PSScriptRoot "build-obs-bridge.ps1") -BridgeVersion $BridgeVersion -ObsVersion $ObsVersion Remove-Item $publishRoot, $stageRoot, $sourceStageRoot -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $publishRoot, $stageRoot | Out-Null $localDotnet = Join-Path $HOME ".dotnet-sdk\dotnet.exe" $dotnet = if (Test-Path $localDotnet) { $localDotnet } else { (Get-Command dotnet.exe -ErrorAction Stop).Source } $publishArguments = @("publish", $project, "-c", "Release", "-r", "win-x64", "--self-contained", "true", "--disable-build-servers", "-o", $publishRoot, "-p:PublishSingleFile=true", "-p:IncludeNativeLibrariesForSelfExtract=true", "-p:DebugType=None", "-p:Version=$Version") if ((Invoke-Native $dotnet $publishArguments)) { throw "Companion publish failed." } $bridgeDiagnostic = Join-Path $outputRoot "obs-bridge-package-diagnostic.json" Remove-Item $bridgeDiagnostic -Force -ErrorAction SilentlyContinue if ((Invoke-Native (Join-Path $publishRoot "Lumi.Companion.App.exe") @("--diagnose-obs-bridge", $bridgeDiagnostic))) { $detail = if (Test-Path $bridgeDiagnostic) { Get-Content $bridgeDiagnostic -Raw } else { "No diagnostic result was written." } throw "Published OBS integration payload failed its executable-level check: $detail" } Remove-Item $bridgeDiagnostic -Force -ErrorAction SilentlyContinue # Copy the release legal bundle beside the application so installed and portable # builds carry the same terms, privacy notice, and third-party attributions. New-Item -ItemType Directory -Force -Path $publishLegalRoot | Out-Null foreach ($name in @( "LUMI-COMPANION-LICENCE.txt", "PRIVACY-NOTICE.txt", "THIRD-PARTY-NOTICES.txt", "LEGAL-README.txt" )) { Copy-Item (Join-Path $legalSourceRoot $name) (Join-Path $publishLegalRoot $name) -Force } Copy-Item (Join-Path $legalSourceRoot "third-party") (Join-Path $publishLegalRoot "third-party") -Recurse -Force & (Join-Path $PSScriptRoot "collect-nuget-notices.ps1") -RepoRoot $repoRoot -OutputRoot (Join-Path $publishLegalRoot "third-party\nuget") # The bundled OBS module links with GPL-licensed OBS/libobs. Ship the exact Lumi # bridge source and build materials with every binary distribution. $sourceCompanionRoot = Join-Path $sourceStageRoot "companion" $sourceNativeParent = Join-Path $sourceCompanionRoot "native" $sourceScriptsRoot = Join-Path $sourceCompanionRoot "scripts" New-Item -ItemType Directory -Force -Path $sourceNativeParent, $sourceScriptsRoot | Out-Null Copy-Item (Join-Path $repoRoot "companion\native\obs-bridge") (Join-Path $sourceNativeParent "obs-bridge") -Recurse -Force Copy-Item (Join-Path $repoRoot "companion\scripts\build-obs-bridge.ps1") (Join-Path $sourceScriptsRoot "build-obs-bridge.ps1") -Force $upstreamSourceRoot = Join-Path $sourceStageRoot "upstream-source" New-Item -ItemType Directory -Force -Path $upstreamSourceRoot | Out-Null $bridgeCacheRoot = Join-Path $env:LOCALAPPDATA "LumiCompanionBuild\obs-$ObsVersion" $obsSourceArchive = Join-Path $bridgeCacheRoot "obs-source.zip" $jsonSourceRoot = Join-Path $bridgeCacheRoot "bridge-build\_deps\json-src" if (!(Test-Path $obsSourceArchive)) { throw "The exact OBS source archive used for the bridge build is missing." } if (!(Test-Path $jsonSourceRoot)) { throw "The exact nlohmann/json source used for the bridge build is missing." } Copy-Item $obsSourceArchive (Join-Path $upstreamSourceRoot "OBS-Studio-$ObsVersion-source.zip") -Force Copy-Item $jsonSourceRoot (Join-Path $upstreamSourceRoot "nlohmann-json-3.12.0") -Recurse -Force @" Lumi OBS Bridge corresponding source Release version: $BridgeVersion This archive contains the Lumi-authored source and build script used for the GPL-2.0-or-later OBS bridge distributed with Lumi Companion. The archive includes the Lumi bridge source, its build script, the exact OBS Studio $ObsVersion source archive used for its interfaces, and the exact nlohmann/json 3.12.0 source used in the binary. Checksums and upstream locations remain recorded in the build script. OBS Studio binaries are not distributed by Companion. Licence: companion/native/obs-bridge/COPYING "@ | Set-Content -Encoding UTF8 (Join-Path $sourceStageRoot "SOURCE-README.txt") $bridgeSourceArchive = Join-Path $publishLegalRoot "Lumi-OBS-Bridge-Source.zip" Compress-ArchiveWithRetry -Path (Join-Path $sourceStageRoot "*") -DestinationPath $bridgeSourceArchive Copy-Item (Join-Path $publishRoot "Lumi.Companion.App.exe") $stageRoot Copy-Item (Join-Path $publishRoot "components") $stageRoot -Recurse Copy-Item (Join-Path $publishRoot "legal") $stageRoot -Recurse Remove-Item $archive -Force -ErrorAction SilentlyContinue Compress-ArchiveWithRetry -Path (Join-Path $stageRoot "*") -DestinationPath $archive $isccCandidates = @( (Join-Path $env:LOCALAPPDATA "Programs\Inno Setup 6\ISCC.exe"), (Join-Path ${env:ProgramFiles(x86)} "Inno Setup 6\ISCC.exe"), (Join-Path $env:ProgramFiles "Inno Setup 6\ISCC.exe") ) $iscc = $isccCandidates | Where-Object { $_ -and (Test-Path $_) } | Select-Object -First 1 if (-not $iscc) { throw "Inno Setup 6 is required to build the durable per-user Companion installer." } Remove-Item $installer -Force -ErrorAction SilentlyContinue $compileArguments = @( "/DAppVersion=$Version", "/DSourceRoot=$publishRoot", "/DOutputRoot=$outputRoot", (Join-Path $repoRoot "companion\installer\Lumi.Companion.iss") ) if ((Invoke-Native $iscc $compileArguments) -or -not (Test-Path $installer)) { throw "Companion installer compilation failed." } $file = Get-Item $archive $sha = (Get-FileHash $archive -Algorithm SHA256).Hash.ToLowerInvariant() $setupFile = Get-Item $installer $setupSha = (Get-FileHash $installer -Algorithm SHA256).Hash.ToLowerInvariant() Write-Host "Published Lumi Companion $Version" Write-Host "Artifact: $archive" Write-Host "Bytes: $($file.Length)" Write-Host "SHA256: $sha" Write-Host "Installer: $installer" Write-Host "Installer bytes: $($setupFile.Length)" Write-Host "Installer SHA256: $setupSha"