diff --git a/Flow.Launcher.Core/Configuration/IPortable.cs b/Flow.Launcher.Core/Configuration/IPortable.cs index e56b0188c..a741a0e9d 100644 --- a/Flow.Launcher.Core/Configuration/IPortable.cs +++ b/Flow.Launcher.Core/Configuration/IPortable.cs @@ -5,12 +5,6 @@ namespace Flow.Launcher.Core.Configuration { void EnablePortableMode(); void DisablePortableMode(); - void RemoveShortcuts(); - void RemoveUninstallerEntry(); - void CreateShortcuts(); - void CreateUninstallerEntry(); - void MoveUserDataFolder(string fromLocation, string toLocation); - void VerifyUserDataAfterMove(string fromLocation, string toLocation); bool CanUpdatePortability(); } -} \ No newline at end of file +} diff --git a/Flow.Launcher.Core/Configuration/Portable.cs b/Flow.Launcher.Core/Configuration/Portable.cs index b58154dcb..8b860b3a0 100644 --- a/Flow.Launcher.Core/Configuration/Portable.cs +++ b/Flow.Launcher.Core/Configuration/Portable.cs @@ -1,5 +1,4 @@ using Microsoft.Win32; -using Squirrel; using System; using System.IO; using System.Reflection; @@ -9,41 +8,26 @@ using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin.SharedCommands; using System.Linq; +using Flow.Launcher.Core.Plugin; +using Velopack; +using Velopack.Locators; +using Velopack.Windows; namespace Flow.Launcher.Core.Configuration { public class Portable : IPortable { - /// - /// As at Squirrel.Windows version 1.5.2, UpdateManager needs to be disposed after finish - /// - /// - private UpdateManager NewUpdateManager() - { - var applicationFolderName = Constant.ApplicationDirectory - .Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.None) - .Last(); - - return new UpdateManager(string.Empty, applicationFolderName, Constant.RootDirectory); - } - public void DisablePortableMode() { try { MoveUserDataFolder(DataLocation.PortableDataPath, DataLocation.RoamingDataPath); -#if !DEBUG - // Create shortcuts and uninstaller are not required in debug mode, - // otherwise will repoint the path of the actual installed production version to the debug version - CreateShortcuts(); - CreateUninstallerEntry(); -#endif IndicateDeletion(DataLocation.PortableDataPath); MessageBox.Show("Flow Launcher needs to restart to finish disabling portable mode, " + - "after the restart your portable data profile will be deleted and roaming data profile kept"); + "after the restart your portable data profile will be deleted and roaming data profile kept"); - UpdateManager.RestartApp(Constant.ApplicationFileName); + PluginManager.API.RestartApp(); } catch (Exception e) { @@ -56,18 +40,12 @@ namespace Flow.Launcher.Core.Configuration try { MoveUserDataFolder(DataLocation.RoamingDataPath, DataLocation.PortableDataPath); -#if !DEBUG - // Remove shortcuts and uninstaller are not required in debug mode, - // otherwise will delete the actual installed production version - RemoveShortcuts(); - RemoveUninstallerEntry(); -#endif IndicateDeletion(DataLocation.RoamingDataPath); MessageBox.Show("Flow Launcher needs to restart to finish enabling portable mode, " + - "after the restart your roaming data profile will be deleted and portable data profile kept"); + "after the restart your roaming data profile will be deleted and portable data profile kept"); - UpdateManager.RestartApp(Constant.ApplicationFileName); + PluginManager.API.RestartApp(); } catch (Exception e) { @@ -75,23 +53,6 @@ namespace Flow.Launcher.Core.Configuration } } - public void RemoveShortcuts() - { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu); - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop); - portabilityUpdater.RemoveShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup); - } - } - - public void RemoveUninstallerEntry() - { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.RemoveUninstallerRegistryEntry(); - } - } public void MoveUserDataFolder(string fromLocation, string toLocation) { @@ -104,32 +65,6 @@ namespace Flow.Launcher.Core.Configuration FilesFolders.VerifyBothFolderFilesEqual(fromLocation, toLocation); } - public void CreateShortcuts() - { - using (var portabilityUpdater = NewUpdateManager()) - { - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.StartMenu, false); - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Desktop, false); - portabilityUpdater.CreateShortcutsForExecutable(Constant.ApplicationFileName, ShortcutLocation.Startup, false); - } - } - - public void CreateUninstallerEntry() - { - var uninstallRegSubKey = @"Software\Microsoft\Windows\CurrentVersion\Uninstall"; - - using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)) - using (var subKey1 = baseKey.CreateSubKey(uninstallRegSubKey, RegistryKeyPermissionCheck.ReadWriteSubTree)) - using (var subKey2 = subKey1.CreateSubKey(Constant.FlowLauncher, RegistryKeyPermissionCheck.ReadWriteSubTree)) - { - subKey2.SetValue("DisplayIcon", Path.Combine(Constant.ApplicationDirectory, "app.ico"), RegistryValueKind.String); - } - - using (var portabilityUpdater = NewUpdateManager()) - { - _ = portabilityUpdater.CreateUninstallerRegistryEntry(); - } - } internal void IndicateDeletion(string filePathTodelete) { @@ -145,9 +80,28 @@ namespace Flow.Launcher.Core.Configuration /// public void PreStartCleanUpAfterPortabilityUpdate() { + var locator = VelopackLocator.GetDefault(null); + + // this would not happen for development environment + if (locator.PackagesDir != null) + { + // check whether the package locate in %localappdata% + // if not create the portable data folder + if (!locator.PackagesDir.IsSubPathOf( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)) + && !DataLocation.PortableDataPath.LocationExists()) + { + Directory.CreateDirectory(DataLocation.PortableDataPath); + } + } + // Specify here so this method does not rely on other environment variables to initialise - var portableDataDir = Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), "UserData"); - var roamingDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "FlowLauncher"); + var portableDataDir = + Path.Combine(Directory.GetParent(Assembly.GetExecutingAssembly().Location.NonNull()).ToString(), + "UserData"); + var roamingDataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "FlowLauncher"); + // Get full path to the .dead files for each case var portableDataDeleteFilePath = Path.Combine(portableDataDir, DataLocation.DeletionIndicatorFile); @@ -161,7 +115,7 @@ namespace Flow.Launcher.Core.Configuration if (MessageBox.Show("Flow Launcher has detected you enabled portable mode, " + "would you like to move it to a different location?", string.Empty, - MessageBoxButton.YesNo) == MessageBoxResult.Yes) + MessageBoxButton.YesNo) == MessageBoxResult.Yes) { FilesFolders.OpenPath(Constant.RootDirectory); @@ -175,7 +129,7 @@ namespace Flow.Launcher.Core.Configuration FilesFolders.RemoveFolderIfExists(portableDataDir); MessageBox.Show("Flow Launcher has detected you disabled portable mode, " + - "the relevant shortcuts and uninstaller entry have been created"); + "the relevant shortcuts and uninstaller entry have been created"); } } @@ -187,8 +141,8 @@ namespace Flow.Launcher.Core.Configuration if (roamingLocationExists && portableLocationExists) { MessageBox.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " + - "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.", - DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); + "{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.", + DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine)); return false; } diff --git a/Flow.Launcher.Core/Updater.cs b/Flow.Launcher.Core/Updater.cs index 3a8e7ec7f..7fc50a02d 100644 --- a/Flow.Launcher.Core/Updater.cs +++ b/Flow.Launcher.Core/Updater.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Net; using System.Net.Http; using System.Net.Sockets; @@ -7,7 +8,6 @@ using System.Linq; using System.Threading.Tasks; using System.Windows; using JetBrains.Annotations; -using Squirrel; using Flow.Launcher.Core.Resource; using Flow.Launcher.Plugin.SharedCommands; using Flow.Launcher.Infrastructure; @@ -17,8 +17,11 @@ using Flow.Launcher.Infrastructure.UserSettings; using Flow.Launcher.Plugin; using System.Text.Json.Serialization; using System.Threading; +using System.Windows.Shapes; using Velopack; +using Velopack.Locators; using Velopack.Sources; +using Path = System.IO.Path; namespace Flow.Launcher.Core { @@ -54,7 +57,7 @@ namespace Flow.Launcher.Core api.GetTranslation("update_flowlauncher_check_connection")); return; } - + var newReleaseVersion = Version.Parse(newUpdateInfo.TargetFullRelease.Version.ToString()); var currentVersion = Version.Parse(Constant.Version); @@ -73,23 +76,18 @@ namespace Flow.Launcher.Core await updateManager.DownloadUpdatesAsync(newUpdateInfo).ConfigureAwait(false); - await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false); - if (DataLocation.PortableDataLocationInUse()) { - var targetDestination = updateManager.RootAppDirectory + - $"\\app-{newReleaseVersion.ToString()}\\{DataLocation.PortableFolderName}"; - FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination); - if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination)) + var targetDestination = Path.Combine(VelopackLocator.GetDefault(null).RootAppDir!, + DataLocation.PortableFolderName); + + DataLocation.PortableDataPath.CopyAll(targetDestination); + if (!DataLocation.PortableDataPath.VerifyBothFolderFilesEqual(targetDestination)) MessageBox.Show(string.Format( api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"), DataLocation.PortableDataPath, targetDestination)); } - else - { - await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false); - } var newVersionTips = NewVersionTips(newReleaseVersion.ToString()); @@ -98,7 +96,11 @@ namespace Flow.Launcher.Core if (MessageBox.Show(newVersionTips, api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes) { - UpdateManager.RestartApp(Constant.ApplicationFileName); + updateManager.ApplyUpdatesAndRestart(newUpdateInfo); + } + else + { + updateManager.WaitExitThenApplyUpdates(newUpdateInfo); } } catch (Exception e) @@ -121,14 +123,19 @@ namespace Flow.Launcher.Core } } - [UsedImplicitly] - private class GithubRelease + public static void RecoverPortableData() { - [JsonPropertyName("prerelease")] public bool Prerelease { get; [UsedImplicitly] set; } + var locator = VelopackLocator.GetDefault(null); - [JsonPropertyName("published_at")] public DateTime PublishedAt { get; [UsedImplicitly] set; } + var portableDataLocation = Path.Combine(VelopackLocator.GetDefault(null).RootAppDir!, + DataLocation.PortableFolderName); - [JsonPropertyName("html_url")] public string HtmlUrl { get; [UsedImplicitly] set; } + if (Path.Exists(portableDataLocation)) + { + portableDataLocation.CopyAll(Path.Combine(locator.AppContentDir!, DataLocation.PortableFolderName)); + } + + Directory.Delete(portableDataLocation); } public string NewVersionTips(string version) diff --git a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs index 4137ca8d0..f25f9fe2a 100644 --- a/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs +++ b/Flow.Launcher.Plugin/SharedCommands/FilesFolders.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; #pragma warning disable IDE0005 using System.Windows; + #pragma warning restore IDE0005 namespace Flow.Launcher.Plugin.SharedCommands @@ -14,6 +15,18 @@ namespace Flow.Launcher.Plugin.SharedCommands { private const string FileExplorerProgramName = "explorer"; + public static bool IsSubPathOf(this string subPath, string basePath) + { + var rel = Path.GetRelativePath( + basePath.Replace('\\', '/'), + subPath.Replace('\\', '/')); + return rel != "." + && rel != ".." + && !rel.StartsWith("../") + && !Path.IsPathRooted(rel); + } + + /// /// Copies the folder and all of its files and folders /// including subfolders to the target location @@ -65,7 +78,6 @@ namespace Flow.Launcher.Plugin.SharedCommands RemoveFolderIfExists(targetPath); #endif } - } /// @@ -82,10 +94,12 @@ namespace Flow.Launcher.Plugin.SharedCommands var fromDir = new DirectoryInfo(fromPath); var toDir = new DirectoryInfo(toPath); - if (fromDir.GetFiles("*", SearchOption.AllDirectories).Length != toDir.GetFiles("*", SearchOption.AllDirectories).Length) + if (fromDir.GetFiles("*", SearchOption.AllDirectories).Length != + toDir.GetFiles("*", SearchOption.AllDirectories).Length) return false; - if (fromDir.GetDirectories("*", SearchOption.AllDirectories).Length != toDir.GetDirectories("*", SearchOption.AllDirectories).Length) + if (fromDir.GetDirectories("*", SearchOption.AllDirectories).Length != + toDir.GetDirectories("*", SearchOption.AllDirectories).Length) return false; return true; @@ -99,7 +113,6 @@ namespace Flow.Launcher.Plugin.SharedCommands return false; #endif } - } /// @@ -151,9 +164,7 @@ namespace Flow.Launcher.Plugin.SharedCommands { var psi = new ProcessStartInfo { - FileName = FileExplorerProgramName, - UseShellExecute = true, - Arguments = '"' + fileOrFolderPath + '"' + FileName = FileExplorerProgramName, UseShellExecute = true, Arguments = '"' + fileOrFolderPath + '"' }; try { @@ -279,7 +290,7 @@ namespace Flow.Launcher.Plugin.SharedCommands && !rel.StartsWith(@"..\") && !Path.IsPathRooted(rel); } - + /// /// Returns path ended with "\" /// diff --git a/Flow.Launcher/App.xaml.cs b/Flow.Launcher/App.xaml.cs index fbc7e7f31..2792fdfff 100644 --- a/Flow.Launcher/App.xaml.cs +++ b/Flow.Launcher/App.xaml.cs @@ -37,7 +37,11 @@ namespace Flow.Launcher [STAThread] public static void Main() { - VelopackApp.Build().Run(); + VelopackApp.Build() + .WithAfterUpdateFastCallback(x => + { + Updater.RecoverPortableData(); + }).Run(); if (SingleInstance.InitializeAsFirstInstance(Unique)) { diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index b5cd67a00..b0b8e84b2 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -1,14 +1,17 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.IO.Pipes; +using System.Reflection; using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; +using Velopack; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // modified to allow single instace restart @@ -226,7 +229,6 @@ namespace Flow.Launcher.Helper public static void Restart() { singleInstanceMutex?.ReleaseMutex(); - StopRemoteServiceTokenSource?.Cancel(); while (RemoteServiceRunning) @@ -235,16 +237,23 @@ namespace Flow.Launcher.Helper Thread.Sleep(10); } - System.Diagnostics.Process.Start(Application.ResourceAssembly.Location); + var info = new ProcessStartInfo + { + Arguments = "/C choice /C Y /N /D Y /T 1 & START \"\" \"" + Environment.ProcessPath + "\"", + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + FileName = "cmd.exe" + }; + Process.Start(info); Application.Current.Shutdown(); } // this is always going to run only once private static CancellationTokenSource StopRemoteServiceTokenSource { get; set; } - private static volatile bool RemoteServiceRunning = false; + private static volatile bool RemoteServiceRunning = true; + - /// /// Checks if the instance of the application attempting to start is the first instance. /// If not, activates the first instance. @@ -252,6 +261,8 @@ namespace Flow.Launcher.Helper /// True if this is the first instance of the application. public static bool InitializeAsFirstInstance(string uniqueName) { + StopRemoteServiceTokenSource = new CancellationTokenSource(); + // Build unique application Id and the IPC channel name. string applicationIdentifier = uniqueName + Environment.UserName; @@ -261,12 +272,12 @@ namespace Flow.Launcher.Helper singleInstanceMutex = new Mutex(true, applicationIdentifier, out var firstInstance); if (firstInstance) { - _ = StartRemoteServiceAsync(channelName, StopRemoteServiceTokenSource.Token); + _ = Task.Run(() => StartRemoteServiceAsync(channelName, StopRemoteServiceTokenSource.Token)); return true; } else { - _ = SignalFirstInstanceAsync(channelName); + _ = Task.Run(() => SignalFirstInstanceAsync(channelName)); return false; } } @@ -336,27 +347,32 @@ namespace Flow.Launcher.Helper /// Cancellation token private static async Task StartRemoteServiceAsync(string channelName, CancellationToken token = default) { - await using NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In); - while (true) + try { - if (token.IsCancellationRequested) + await using NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In); + while (true) { - break; - } + if (token.IsCancellationRequested) + { + break; + } - // Wait for connection to the pipe - await pipeServer.WaitForConnectionAsync(token); - if (Application.Current != null) - { - // Do an asynchronous call to ActivateFirstInstance function - Application.Current.Dispatcher.Invoke(ActivateFirstInstance); - } + // Wait for connection to the pipe + await pipeServer.WaitForConnectionAsync(token); + if (Application.Current != null) + { + // Do an asynchronous call to ActivateFirstInstance function + Application.Current.Dispatcher.Invoke(ActivateFirstInstance); + } - // Disconnect client - pipeServer.Disconnect(); + // Disconnect client + pipeServer.Disconnect(); + } + } + finally + { + RemoteServiceRunning = false; } - - RemoteServiceRunning = true; } /// @@ -366,7 +382,8 @@ namespace Flow.Launcher.Helper private static async Task SignalFirstInstanceAsync(string channelName) { // Create a client pipe connected to server - await using NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out); + await using NamedPipeClientStream pipeClient = + new NamedPipeClientStream(".", channelName, PipeDirection.Out); // Connect to the available pipe await pipeClient.ConnectAsync(0); } diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 00e7df3d0..6d5d4e1ba 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; using System.Windows; -using Squirrel; using Flow.Launcher.Core.Plugin; using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; diff --git a/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs index 792891ad1..7a7496ece 100644 --- a/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs +++ b/Plugins/Flow.Launcher.Plugin.PluginsManager/Utilities.cs @@ -1,5 +1,5 @@ -using ICSharpCode.SharpZipLib.Zip; -using System.IO; +using System.IO; +using System.IO.Compression; namespace Flow.Launcher.Plugin.PluginsManager { @@ -13,29 +13,7 @@ namespace Flow.Launcher.Plugin.PluginsManager /// overwrite internal static void UnZip(string zipFilePath, string strDirectory, bool overwrite) { - if (strDirectory == "") - strDirectory = Directory.GetCurrentDirectory(); - - using var zipStream = new ZipInputStream(File.OpenRead(zipFilePath)); - - ZipEntry theEntry; - - while ((theEntry = zipStream.GetNextEntry()) != null) - { - var pathToZip = theEntry.Name; - var directoryName = string.IsNullOrEmpty(pathToZip) ? "" : Path.GetDirectoryName(pathToZip); - var fileName = Path.GetFileName(pathToZip); - var destinationDir = Path.Combine(strDirectory, directoryName); - var destinationFile = Path.Combine(destinationDir, fileName); - - Directory.CreateDirectory(destinationDir); - - if (string.IsNullOrEmpty(fileName) || (File.Exists(destinationFile) && !overwrite)) - continue; - - using var streamWriter = File.Create(destinationFile); - zipStream.CopyTo(streamWriter); - } + ZipFile.ExtractToDirectory(zipFilePath, strDirectory, overwrite); } internal static string GetContainingFolderPathAfterUnzip(string unzippedParentFolderPath) diff --git a/Scripts/post_build.ps1 b/Scripts/post_build.ps1 index 1757ed99e..b1f566abf 100644 --- a/Scripts/post_build.ps1 +++ b/Scripts/post_build.ps1 @@ -1,14 +1,19 @@ param( - [string]$config = "Release", - [string]$solution = (Join-Path $PSScriptRoot ".." -Resolve) + [string]$config = "Release", + [string]$solution = (Join-Path $PSScriptRoot ".." -Resolve), + [string]$channel = "prerelease" ) Write-Host "Config: $config" -function Build-Version { - if ([string]::IsNullOrEmpty($env:flowVersion)) { +function Build-Version +{ + if ( [string]::IsNullOrEmpty($env:flowVersion)) + { $targetPath = Join-Path $solution "Output/Release/Flow.Launcher.dll" -Resolve $v = (Get-Command ${targetPath}).FileVersionInfo.FileVersion - } else { + } + else + { $v = $env:flowVersion } @@ -16,12 +21,18 @@ function Build-Version { return $v } -function Build-Path { - if (![string]::IsNullOrEmpty($env:APPVEYOR_BUILD_FOLDER)) { +function Build-Path +{ + if (![string]::IsNullOrEmpty($env:APPVEYOR_BUILD_FOLDER)) + { $p = $env:APPVEYOR_BUILD_FOLDER - } elseif (![string]::IsNullOrEmpty($solution)) { + } + elseif (![string]::IsNullOrEmpty($solution)) + { $p = $solution - } else { + } + else + { $p = Get-Location } @@ -31,23 +42,38 @@ function Build-Path { return $p } -function Copy-Resources ($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 } -function Delete-Unused ($path, $config) { +function Delete-Unused($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 + $included = @{ } + Get-ChildItem $target -Filter "*.dll" | Get-FileHash | ForEach-Object { + $hash = $_.hash + $filename = $_.Path | Split-Path -Leaf + $included.Add([tuple]::Create($filename, $hash), $true) } - Remove-Item -Path $target -Include "*.xml" -Recurse + + $deleteList = Get-ChildItem $target\Plugins -Filter "*.dll" -Recurse | + Select-Object Name, VersionInfo, Directory, @{ name = "hash"; expression = { (Get-FileHash $_.FullName) } } | + Where-Object { + $included.Contains([tuple]::Create($_.Name, $_.hash)) + } + + $deleteList | ForEach-Object { + Write-Host Deleting duplicated $_.Name with version $_.VersionInfo.FileVersion at location $_.Directory.FullName + } + $deleteList | Remove-Item -Path { $_.FullName } + + Remove-Item -Path $target -Include "*.xml" -Recurse } -function Remove-CreateDumpExe ($path, $config) { +function Remove-CreateDumpExe($path, $config) +{ $target = "$path\Output\$config" $depjson = Get-Content $target\Flow.Launcher.deps.json -raw @@ -56,12 +82,14 @@ function Remove-CreateDumpExe ($path, $config) { } -function Validate-Directory ($output) { +function Validate-Directory($output) +{ New-Item $output -ItemType Directory -Force } -function Pack-Squirrel-Installer ($path, $version, $output) { +function Pack-Squirrel-Installer($path, $version, $output) +{ # msbuild based installer generation is not working in appveyor, not sure why Write-Host "Begin pack squirrel installer" @@ -71,34 +99,20 @@ function Pack-Squirrel-Installer ($path, $version, $output) { Write-Host "Packing: $spec" Write-Host "Input path: $input" - # 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 - - $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 - $temp = "$output\Temp" - - Squirrel --releasify $nupkg --releaseDir $temp --setupIcon $icon --no-msi | Write-Output - Move-Item $temp\* $output -Force - Remove-Item $temp + $repoUrl = "https://github.com/Flow-Launcher/Prereleases" - $file = "$output\Flow-Launcher-Setup.exe" - Write-Host "Filename: $file" - - Move-Item "$output\Setup.exe" $file -Force - - Write-Host "End pack squirrel installer" + if ($channel -eq "stable") + { + $repoUrl = "https://github.com/Flow-Launcher/Flow.Launcher" + } + + vpk pack --packVersion $version --packDir $input --packId FlowLauncher --mainExe Flow.Launcher.exe --channel $channel } -function Publish-Self-Contained ($p) { +function Publish-Self-Contained($p) +{ - $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve + $csproj = Join-Path "$p" "Flow.Launcher/Flow.Launcher.csproj" -Resolve $profile = Join-Path "$p" "Flow.Launcher/Properties/PublishProfiles/Net7.0-SelfContained.pubxml" -Resolve # we call dotnet publish on the main project. @@ -106,31 +120,32 @@ function Publish-Self-Contained ($p) { dotnet publish -c Release $csproj /p:PublishProfile=$profile } -function Publish-Portable ($outputLocation, $version) { - +function Publish-Portable($outputLocation, $version) +{ + & $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 } -function Main { +function Main +{ $p = Build-Path $v = Build-Version Copy-Resources $p - if ($config -eq "Release"){ - + if ($config -eq "Release") + { + Delete-Unused $p $config Publish-Self-Contained $p - Remove-CreateDumpExe $p $config - $o = "$p\Output\Packages" Validate-Directory $o Pack-Squirrel-Installer $p $v $o - Publish-Portable $o $v + # Publish-Portable $o $v } } diff --git a/appveyor.yml b/appveyor.yml index b84230c43..6a96f88d8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,6 +7,9 @@ init: if ($env:APPVEYOR_REPO_BRANCH -eq "dev") { $env:prereleaseTag = "{0}.{1}.{2}.{3}" -f $version.Major, $version.Minor, $version.Build, $version.Revision + $env:channel = "prerelease" + } else { + $env:channel = "stable" } - sc config WSearch start= auto # Starts Windows Search service- Needed for running ExplorerTest - net start WSearch @@ -40,12 +43,12 @@ artifacts: name: Plugin nupkg - path: 'Output\Packages\Flow-Launcher-*.exe' name: Squirrel Installer -- path: Output\Packages\Flow-Launcher-Portable.zip +- path: Release\Flow-Launcher-Portable.zip name: Portable Version -- path: 'Output\Packages\FlowLauncher-*-full.nupkg' +- path: 'Release\FlowLauncher-*-full.nupkg' name: Squirrel nupkg -- path: 'Output\Packages\RELEASES' - name: Squirrel RELEASES +- path: 'Release\RELEASES*' + name: Velopack RELEASES deploy: - provider: NuGet