diff --git a/Directory.Build.targets b/Directory.Build.targets index 4596aa08441..97b496c4561 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -5,10 +5,10 @@ Condition="'$(OutputType)' == 'Library' and '$(CopyLocalLockFileAssemblies)' == 'true' and $(AssemblyName.EndsWith('Tests')) == 'false' "> - false + true - false + true diff --git a/Scripts/flowlauncher.nuspec b/Scripts/flowlauncher.nuspec index fa12150cc7c..3b662ec34fa 100644 --- a/Scripts/flowlauncher.nuspec +++ b/Scripts/flowlauncher.nuspec @@ -6,11 +6,18 @@ $version$ Flow-Launcher Team https://github.com/Flow-Launcher/Flow.Launcher - https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher/master/Flow.Launcher/Images/app.png + images\app.png + https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher/master/Flow.Launcher/Resources/app.ico + README.md false Flow Launcher - Quick file search and app launcher for Windows with community-made plugins + + + + + diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 8d2d14a8054..27698495799 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -7,7 +7,8 @@ Write-Host "Config: $config" function Build-Version { if ([string]::IsNullOrEmpty($env:flowVersion)) { $targetPath = Join-Path $solution "Output/Release/Flow.Launcher.dll" -Resolve - $v = (Get-Command ${targetPath}).FileVersionInfo.FileVersion + # Use Get-Item for reliability and ProductVersion to align with AssemblyInformationalVersion. + $v = (Get-Item $targetPath).VersionInfo.ProductVersion } else { $v = $env:flowVersion } @@ -32,17 +33,25 @@ function Build-Path { } function Copy-Resources ($path) { - # making version static as multiple versions can exist in the nuget folder and in the case a breaking change is introduced. - Copy-Item -Force $env:USERPROFILE\.nuget\packages\squirrel.windows\1.5.2\tools\Squirrel.exe $path\Output\Update.exe + $squirrelExe = (Get-ChildItem -Path "$env:USERPROFILE\.nuget\packages\squirrel.windows\*" -Directory | Sort-Object Name -Descending | Select-Object -First 1).FullName + "\tools\Squirrel.exe" + if (Test-Path $squirrelExe) { + Copy-Item -Force $squirrelExe $path\Output\Update.exe + } else { + Write-Host "Warning: Squirrel.exe could not be found in the NuGet cache." -ForegroundColor Yellow + } } -function Delete-Unused ($path, $config) { +function Remove-UnusedFiles ($path, $config) { $target = "$path\Output\$config" $included = Get-ChildItem $target -Filter "*.dll" foreach ($i in $included){ - $deleteList = Get-ChildItem $target\Plugins -Include $i -Recurse | Where { $_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion -And $_.Name -eq "$i" } - $deleteList | ForEach-Object{ Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName } - $deleteList | Remove-Item + $deleteList = Get-ChildItem $target\Plugins -Include $i.Name -Recurse | Where-Object { $_.VersionInfo.FileVersion -eq $i.VersionInfo.FileVersion } + foreach ($fileToDelete in $deleteList) { + # A plugin's main DLL has the same name as its parent directory. We must not delete it. + if ($fileToDelete.Directory.Name -ne $fileToDelete.BaseName) { + Remove-Item $fileToDelete.FullName + } + } } Remove-Item -Path $target -Include "*.xml" -Recurse } @@ -50,38 +59,47 @@ function Delete-Unused ($path, $config) { function Remove-CreateDumpExe ($path, $config) { $target = "$path\Output\$config" - $depjson = Get-Content $target\Flow.Launcher.deps.json -raw - $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $target\Flow.Launcher.deps.json -Encoding UTF8 + $depjsonPath = (Get-Item "$target\Flow.Launcher.deps.json").FullName + if (Test-Path $depjsonPath) { + $depjson = Get-Content $depjsonPath -raw + $depjson -replace '(?s)(.createdump.exe": {.*?}.*?\n)\s*', "" | Out-File $depjsonPath -Encoding UTF8 + } Remove-Item -Path $target -Include "*createdump.exe" -Recurse } -function Validate-Directory ($output) { - New-Item $output -ItemType Directory -Force +function Initialize-Directory ($output) { + if (Test-Path $output) { + Remove-Item -Recurse -Force $output + } + New-Item $output -ItemType Directory -Force | Out-Null } -function Pack-Squirrel-Installer ($path, $version, $output) { +function New-SquirrelInstallerPackage ($path, $version, $output) { # msbuild based installer generation is not working in appveyor, not sure why Write-Host "Begin pack squirrel installer" $spec = "$path\Scripts\flowlauncher.nuspec" - $input = "$path\Output\Release" + $inputPath = "$path\Output\Release" Write-Host "Packing: $spec" - Write-Host "Input path: $input" + Write-Host "Input path: $inputPath" - # dotnet pack is not used because ran into issues, need to test installation and starting up if to use it. - nuget pack $spec -Version $version -BasePath $input -OutputDirectory $output -Properties Configuration=Release + nuget pack $spec -Version $version -BasePath $inputPath -OutputDirectory $output -Properties Configuration=Release $nupkg = "$output\FlowLauncher.$version.nupkg" Write-Host "nupkg path: $nupkg" $icon = "$path\Flow.Launcher\Resources\app.ico" Write-Host "icon: $icon" - # Squirrel.com: https://github.com/Squirrel/Squirrel.Windows/issues/369 - New-Alias Squirrel $env:USERPROFILE\.nuget\packages\squirrel.windows\1.5.2\tools\Squirrel.exe -Force - # why we need Write-Output: https://github.com/Squirrel/Squirrel.Windows/issues/489#issuecomment-156039327 - # directory of releaseDir in squirrel can't be same as directory ($nupkg) in releasify + + $squirrelExe = (Get-ChildItem -Path "$env:USERPROFILE\.nuget\packages\squirrel.windows\*" -Directory | Sort-Object Name -Descending | Select-Object -First 1).FullName + "\tools\Squirrel.exe" + if (-not (Test-Path $squirrelExe)) { + Write-Host "FATAL: Squirrel.exe could not be found, aborting installer creation." -ForegroundColor Red + exit 1 + } + New-Alias Squirrel $squirrelExe -Force + $temp = "$output\Temp" Squirrel --releasify $nupkg --releaseDir $temp --setupIcon $icon --no-msi | Write-Output @@ -96,41 +114,127 @@ function Pack-Squirrel-Installer ($path, $version, $output) { Write-Host "End pack squirrel installer" } -function Publish-Self-Contained ($p) { +function Build-Solution ($p) { + Write-Host "Building solution..." + $solutionFile = Join-Path $p "Flow.Launcher.sln" + dotnet build $solutionFile -c Release + if ($LASTEXITCODE -ne 0) { return $false } + return $true +} +function Publish-SelfContainedToTemp($p, $outputPath) { + Write-Host "Publishing self-contained application to temporary directory..." $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve - $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net9.0-SelfContained.pubxml" -Resolve + + # We publish to a temporary directory first to ensure a clean, self-contained build + # without interfering with the main /Output/Release folder, which contains plugins. + # Let publish do its own build to ensure all self-contained dependencies are correctly resolved. + dotnet publish -c Release $csproj -r win-x64 --self-contained true -o $outputPath + if ($LASTEXITCODE -ne 0) { return $false } + return $true +} - # we call dotnet publish on the main project. - # The other projects should have been built in Release at this point. - dotnet publish -c Release $csproj /p:PublishProfile=$profile +function Merge-PublishToRelease($publishPath, $releasePath) { + Write-Host "Merging published files into release directory..." + Copy-Item -Path "$publishPath\*" -Destination $releasePath -Recurse -Force + Remove-Item -Recurse -Force $publishPath } -function Publish-Portable ($outputLocation, $version) { +function Publish-Portable ($outputLocation, $version, $path) { + # The portable version is created by silently running the installer to a temporary location, + # then packaging the result. This ensures the structure is identical to a real installation + # and can be updated by Squirrel. + & "$outputLocation\Flow-Launcher-Setup.exe" --silent | Out-Null + + $installRoot = Join-Path $env:LocalAppData "FlowLauncher" + $appPath = Join-Path $installRoot "app-$version" + $appExePath = Join-Path $appPath "Flow.Launcher.exe" + + # Wait for silent installation to complete + $waitTime = 0 + $maxWaitTime = 60 # 60 seconds timeout + while (-not (Test-Path $appExePath) -and $waitTime -lt $maxWaitTime) { + Start-Sleep -Seconds 1 + $waitTime++ + } + + if (-not (Test-Path $appExePath)) { + Write-Host "Error: Timed out waiting for silent installation to complete." -ForegroundColor Red + return + } - & $outputLocation\Flow-Launcher-Setup.exe --silent | Out-Null - mkdir "$env:LocalAppData\FlowLauncher\app-$version\UserData" - Compress-Archive -Path $env:LocalAppData\FlowLauncher -DestinationPath $outputLocation\Flow-Launcher-Portable.zip + # Create a temporary staging directory for the portable package + $stagingDir = Join-Path $outputLocation "PortableStaging" + Initialize-Directory $stagingDir + + try { + # Copy installed files to staging directory + Copy-Item -Path "$installRoot\*" -Destination $stagingDir -Recurse -Force + + # Create the UserData folder inside app- to enable portable mode. + New-Item -Path (Join-Path $stagingDir "app-$version" "UserData") -ItemType Directory -Force | Out-Null + + # Remove the unnecessary 'packages' directory before creating the archive + $packagesPath = Join-Path $stagingDir "packages" + if (Test-Path $packagesPath) { + Remove-Item -Path $packagesPath -Recurse -Force + } + + # Create the zip from the staging directory's contents + Compress-Archive -Path "$stagingDir\*" -DestinationPath "$outputLocation\Flow-Launcher-Portable.zip" -Force + } + finally { + if (Test-Path $stagingDir) { + Remove-Item -Recurse -Force $stagingDir + } + } + + # Uninstall after packaging + $uninstallExe = Join-Path $installRoot "Update.exe" + if (Test-Path $uninstallExe) { + Write-Host "Uninstalling temporary application..." + Start-Process -FilePath $uninstallExe -ArgumentList "--uninstall -s" -Wait + } } function Main { $p = Build-Path - $v = Build-Version - Copy-Resources $p if ($config -eq "Release"){ - Delete-Unused $p $config - - Publish-Self-Contained $p - + if (-not (Build-Solution $p)) { + Write-Host "dotnet build failed. Aborting post-build script." -ForegroundColor Red + exit 1 + } + + $tempPublishPath = Join-Path $p "Output\PublishTemp" + Initialize-Directory $tempPublishPath + if (-not (Publish-SelfContainedToTemp $p $tempPublishPath)) { + Write-Host "dotnet publish failed. Aborting." -ForegroundColor Red + exit 1 + } + + Merge-PublishToRelease $tempPublishPath (Join-Path $p "Output\Release") + + $v = Build-Version + if ([string]::IsNullOrEmpty($v)) { + Write-Host "Could not determine build version. Aborting." -ForegroundColor Red + exit 1 + } + + Copy-Resources $p + Remove-UnusedFiles $p $config Remove-CreateDumpExe $p $config $o = "$p\Output\Packages" - Validate-Directory $o - Pack-Squirrel-Installer $p $v $o - - Publish-Portable $o $v + Initialize-Directory $o + New-SquirrelInstallerPackage $p $v $o + if ($LASTEXITCODE -ne 0) { + Write-Host "Squirrel packaging failed. Aborting." -ForegroundColor Red + exit 1 + } + + Publish-Portable $o $v $p } }