From 708ba46bee69dc8ecbe232cfbeda2aa68db760bc Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Tue, 16 Jan 2024 11:55:18 +0000 Subject: [PATCH] Fix tests --- src/Velopack/Internal/SimpleJson.cs | 22 ++- src/Velopack/Internal/Utility.cs | 2 +- src/Velopack/Locators/VelopackLocator.cs | 18 +- src/Velopack/Sources/SimpleFileSource.cs | 22 ++- .../DeploymentTests.cs | 2 +- .../WindowsPackTests.cs | 16 +- test/Velopack.Tests/SimpleJsonTests.cs | 9 + .../TestHelpers/FakeFixtureRepository.cs | 44 ++++- test/Velopack.Tests/UpdateManagerTests.cs | 180 +++++++++++------- test/fixtures/releases.win.json | 176 +++++++++++++++++ 10 files changed, 384 insertions(+), 107 deletions(-) create mode 100644 test/fixtures/releases.win.json diff --git a/src/Velopack/Internal/SimpleJson.cs b/src/Velopack/Internal/SimpleJson.cs index 160108ab..6bb9b4de 100644 --- a/src/Velopack/Internal/SimpleJson.cs +++ b/src/Velopack/Internal/SimpleJson.cs @@ -38,6 +38,11 @@ namespace Velopack.Json { return JsonSerializer.Deserialize(json, Options); } + + public static string SerializeObject(T obj) + { + return JsonSerializer.Serialize(obj, Options); + } } internal class SemanticVersionConverter : JsonConverter @@ -65,13 +70,20 @@ namespace Velopack.Json internal static class SimpleJson { + private static readonly JsonSerializerSettings Options = new JsonSerializerSettings { + Converters = { new StringEnumConverter(), new SemanticVersionConverter() }, + ContractResolver = new JsonNameContractResolver(), + NullValueHandling = NullValueHandling.Ignore, + }; + public static T DeserializeObject(string json) { - var options = new JsonSerializerSettings { - Converters = { new StringEnumConverter(), new SemanticVersionConverter() }, - ContractResolver = new JsonNameContractResolver(), - }; - return JsonConvert.DeserializeObject(json, options); + return JsonConvert.DeserializeObject(json, Options); + } + + public static string SerializeObject(T obj) + { + return JsonConvert.SerializeObject(obj, Formatting.Indented, Options); } } diff --git a/src/Velopack/Internal/Utility.cs b/src/Velopack/Internal/Utility.cs index d6fb7a08..62212e47 100644 --- a/src/Velopack/Internal/Utility.cs +++ b/src/Velopack/Internal/Utility.cs @@ -185,7 +185,7 @@ namespace Velopack public static string GetVeloReleaseIndexName(string channel) { - return $"releases.{channel}.json"; + return $"releases.{channel ?? VelopackRuntimeInfo.SystemOs.GetOsShortName()}.json"; } [Obsolete] diff --git a/src/Velopack/Locators/VelopackLocator.cs b/src/Velopack/Locators/VelopackLocator.cs index 3fe5bd50..5d1dfca9 100644 --- a/src/Velopack/Locators/VelopackLocator.cs +++ b/src/Velopack/Locators/VelopackLocator.cs @@ -6,6 +6,7 @@ using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using NuGet.Versioning; +using Velopack.Sources; namespace Velopack.Locators { @@ -89,10 +90,19 @@ namespace Velopack.Locators try { if (CurrentlyInstalledVersion == null) return new List(0); - return Directory.EnumerateFiles(PackagesDir, "*.nupkg") - .Select(x => VelopackAsset.FromNupkg(x)) - .Where(x => x?.Version != null) - .ToList(); + + var list = new List(); + foreach (var pkg in Directory.EnumerateFiles(PackagesDir, "*.nupkg")) { + try { + var asset = VelopackAsset.FromNupkg(pkg); + if (asset?.Version != null) { + list.Add(asset); + } + } catch (Exception ex) { + Log.Warn(ex, $"Error while reading local package '{pkg}'."); + } + } + return list; } catch (Exception ex) { Log.Error(ex, "Error while reading local packages."); return new List(0); diff --git a/src/Velopack/Sources/SimpleFileSource.cs b/src/Velopack/Sources/SimpleFileSource.cs index 348b4a30..813f6ac0 100644 --- a/src/Velopack/Sources/SimpleFileSource.cs +++ b/src/Velopack/Sources/SimpleFileSource.cs @@ -1,11 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Velopack.NuGet; namespace Velopack.Sources { @@ -32,14 +31,19 @@ namespace Velopack.Sources return Task.FromResult(new VelopackAssetFeed()); } - var assets = Directory.EnumerateFiles(BaseDirectory.FullName, "*.nupkg") - .Select(x => new ZipPackage(x)) - .Where(x => x?.Version != null) - .Where(x => x.Channel == null || x.Channel == channel) - .Select(x => VelopackAsset.FromZipPackage(x)) - .ToArray(); + var list = new List(); + foreach (var pkg in Directory.EnumerateFiles(BaseDirectory.FullName, "*.nupkg")) { + try { + var asset = VelopackAsset.FromNupkg(pkg); + if (asset?.Version != null) { + list.Add(asset); + } + } catch (Exception ex) { + logger.Warn(ex, $"Error while reading local package '{pkg}'."); + } + } - return Task.FromResult(new VelopackAssetFeed { Assets = assets }); + return Task.FromResult(new VelopackAssetFeed { Assets = list.ToArray() }); } /// diff --git a/test/Velopack.Packaging.Tests/DeploymentTests.cs b/test/Velopack.Packaging.Tests/DeploymentTests.cs index f5f7557b..dc954639 100644 --- a/test/Velopack.Packaging.Tests/DeploymentTests.cs +++ b/test/Velopack.Packaging.Tests/DeploymentTests.cs @@ -166,7 +166,7 @@ This is just a _test_! foreach (var r in ghrel) { Assert.Equal(releaseName, r.Release.Name); Assert.Equal(id, r.PackageId); - Assert.Equal(newVer + "-" + uniqueSuffix, r.Version.ToNormalizedString()); + Assert.Equal(newVer, r.Version.ToNormalizedString()); } using var _2 = Utility.GetTempDirectory(out var releaseDirNew); diff --git a/test/Velopack.Packaging.Tests/WindowsPackTests.cs b/test/Velopack.Packaging.Tests/WindowsPackTests.cs index 32bcbfc6..25a405b0 100644 --- a/test/Velopack.Packaging.Tests/WindowsPackTests.cs +++ b/test/Velopack.Packaging.Tests/WindowsPackTests.cs @@ -64,8 +64,10 @@ public class WindowsPackTests var setupPath = Path.Combine(tmpReleaseDir, $"{id}-asd123-Setup.exe"); Assert.True(File.Exists(setupPath)); - var releasesPath = Path.Combine(tmpReleaseDir, $"RELEASES-asd123"); - Assert.True(File.Exists(releasesPath)); + //var releasesPath = Path.Combine(tmpReleaseDir, $"RELEASES-asd123"); + //Assert.True(File.Exists(releasesPath)); + var releasesPath2 = Path.Combine(tmpReleaseDir, $"releases.asd123.json"); + Assert.True(File.Exists(releasesPath2)); EasyZip.ExtractZipToDirectory(logger, nupkgPath, unzipDir); @@ -75,7 +77,7 @@ public class WindowsPackTests var xml = XDocument.Load(nuspecPath); Assert.Equal(id, xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("id").Single().Value); - Assert.Equal(version + "-asd123", xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("version").Single().Value); + Assert.Equal(version, xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("version").Single().Value); Assert.Equal(exe, xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("mainExe").Single().Value); Assert.Equal("Test Squirrel App", xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("title").Single().Value); Assert.Equal("author", xml.Root.ElementsNoNamespace("metadata").Single().ElementsNoNamespace("authors").Single().Value); @@ -297,7 +299,7 @@ public class WindowsPackTests PackTestApp(id, "2.0.0", "version 2 test", releaseDir, logger); // move package into local packages dir - var fileName = $"{id}-2.0.0-win-full.nupkg"; + var fileName = $"{id}-2.0.0-full.nupkg"; var mvFrom = Path.Combine(releaseDir, fileName); var mvTo = Path.Combine(installDir, "packages", fileName); File.Copy(mvFrom, mvTo); @@ -321,7 +323,7 @@ public class WindowsPackTests PackTestApp(id, "2.0.0", "version 2 test", releaseDir, logger); // did a zsdiff get created for our v2 update? - var deltaPath = Path.Combine(releaseDir, $"{id}-2.0.0-win-delta.nupkg"); + var deltaPath = Path.Combine(releaseDir, $"{id}-2.0.0-delta.nupkg"); Assert.True(File.Exists(deltaPath)); using var _2 = Utility.GetTempDirectory(out var extractDir); EasyZip.ExtractZipToDirectory(logger, deltaPath, extractDir); @@ -335,14 +337,14 @@ public class WindowsPackTests // apply delta and check package var output = Path.Combine(releaseDir, "delta.patched"); new DeltaPatchCommandRunner(logger).Run(new DeltaPatchOptions { - BasePackage = Path.Combine(releaseDir, $"{id}-1.0.0-win-full.nupkg"), + BasePackage = Path.Combine(releaseDir, $"{id}-1.0.0-full.nupkg"), OutputFile = output, PatchFiles = new[] { new FileInfo(deltaPath) }, }).GetAwaiterResult(); // are the packages the same? Assert.True(File.Exists(output)); - var v2 = Path.Combine(releaseDir, $"{id}-2.0.0-win-full.nupkg"); + var v2 = Path.Combine(releaseDir, $"{id}-2.0.0-full.nupkg"); var f1 = File.ReadAllBytes(output); var f2 = File.ReadAllBytes(v2); Assert.True(new ReadOnlySpan(f1).SequenceEqual(new ReadOnlySpan(f2))); diff --git a/test/Velopack.Tests/SimpleJsonTests.cs b/test/Velopack.Tests/SimpleJsonTests.cs index 19f65b28..e24abce5 100644 --- a/test/Velopack.Tests/SimpleJsonTests.cs +++ b/test/Velopack.Tests/SimpleJsonTests.cs @@ -79,6 +79,15 @@ namespace Velopack.Tests Assert.Equal(obj.Greetings, dez.greetings); } + [Fact] + public void JsonCanParseReleasesJson() + { + var json = File.ReadAllText(PathHelper.GetFixture("releases.win.json")); + var feed = SimpleJson.DeserializeObject(json); + Assert.Equal(21, feed.Assets.Length); + Assert.True(feed.Assets.First().Version == new SemanticVersion(1, 0, 11)); + } + public class TestGithubReleaseAsset { /// diff --git a/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs b/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs index 33aa4725..c61aea8b 100644 --- a/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs +++ b/test/Velopack.Tests/TestHelpers/FakeFixtureRepository.cs @@ -1,4 +1,7 @@ -using System.Text; +#pragma warning disable CS0618 // Type or member is obsolete +#pragma warning disable CS0612 // Type or member is obsolete +using System.Text; +using System.Text.Json; using Velopack.Sources; namespace Velopack.Tests.TestHelpers @@ -7,16 +10,24 @@ namespace Velopack.Tests.TestHelpers { private readonly string _pkgId; private readonly IEnumerable _releases; + private readonly VelopackAssetFeed _releasesNew; private readonly string _releasesName; + private readonly string _releasesNameNew; public FakeFixtureRepository(string pkgId, bool mockLatestFullVer, string channel = null) { _releasesName = Utility.GetReleasesFileName(channel); + _releasesNameNew = Utility.GetVeloReleaseIndexName(channel); _pkgId = pkgId; var releases = ReleaseEntry.BuildReleasesFile(PathHelper.GetFixturesDir(), false) .Where(r => r.OriginalFilename.StartsWith(_pkgId)) .ToList(); + var releasesNew = new SimpleFileSource(new DirectoryInfo(PathHelper.GetFixturesDir())) + .GetReleaseFeed(NullLogger.Instance, null).GetAwaiterResult().Assets + .Where(r => r.FileName.StartsWith(_pkgId)) + .ToList(); + if (mockLatestFullVer) { var minFullVer = releases.Where(r => !r.IsDelta).OrderBy(r => r.Version).First(); var maxfullVer = releases.Where(r => !r.IsDelta).OrderByDescending(r => r.Version).First(); @@ -26,9 +37,19 @@ namespace Velopack.Tests.TestHelpers if (maxfullVer.Version < maxDeltaVer.Version) { var name = new ReleaseEntryName(maxfullVer.PackageId, maxDeltaVer.Version, false); releases.Add(new ReleaseEntry("0000000000000000000000000000000000000000", name.ToFileName(), maxfullVer.Filesize)); + + releasesNew.Add(new VelopackAsset { + PackageId = maxfullVer.PackageId, + Version = maxDeltaVer.Version, + Type = VelopackAssetType.Full, + FileName = $"{maxfullVer.PackageId}-{maxDeltaVer.Version}-full.nupkg", + }); } } + _releasesNew = new VelopackAssetFeed { + Assets = releasesNew.ToArray(), + }; _releases = releases; } @@ -40,6 +61,11 @@ namespace Velopack.Tests.TestHelpers return Task.FromResult(ms.ToArray()); } + if (url.Contains($"/{_releasesNameNew}?")) { + var json = JsonSerializer.Serialize(_releasesNew, SimpleJsonTests.Options); + return Task.FromResult(Encoding.UTF8.GetBytes(json)); + } + var rel = _releases.FirstOrDefault(r => url.EndsWith(r.OriginalFilename)); if (rel == null) throw new Exception("Fake release not found: " + url); @@ -70,12 +96,18 @@ namespace Velopack.Tests.TestHelpers public Task DownloadString(string url, string authorization = null, string accept = null) { - if (!url.Contains("/RELEASES?")) { - throw new NotImplementedException(); + if (url.Contains($"/{_releasesName}?")) { + MemoryStream ms = new MemoryStream(); + ReleaseEntry.WriteReleaseFile(_releases, ms); + return Task.FromResult(Encoding.UTF8.GetString(ms.ToArray())); } - MemoryStream ms = new MemoryStream(); - ReleaseEntry.WriteReleaseFile(_releases, ms); - return Task.FromResult(Encoding.UTF8.GetString(ms.ToArray())); + + if (url.Contains($"/{_releasesNameNew}?")) { + var json = JsonSerializer.Serialize(_releasesNew, SimpleJsonTests.Options); + return Task.FromResult(json); + } + + throw new NotSupportedException("FakeFixtureRepository doesn't have: " + url); } } } diff --git a/test/Velopack.Tests/UpdateManagerTests.cs b/test/Velopack.Tests/UpdateManagerTests.cs index 6b1f6f64..7be49cd2 100644 --- a/test/Velopack.Tests/UpdateManagerTests.cs +++ b/test/Velopack.Tests/UpdateManagerTests.cs @@ -1,5 +1,7 @@ using System.Text; +using System.Text.Json; using NuGet.Versioning; +using Velopack.Json; using Velopack.Locators; using Velopack.Sources; using Velopack.Tests.TestHelpers; @@ -15,25 +17,99 @@ namespace Velopack.Tests _output = output; } + private FakeDownloader GetMockDownloaderNoDelta() + { + var feed = new VelopackAssetFeed() { + Assets = new VelopackAsset[] { + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 1, 0), + Type = VelopackAssetType.Full, + FileName = $"MyCoolApp-1.1.0.nupkg", + SHA1 = "3a2eadd15dd984e4559f2b4d790ec8badaeb6a39", + Size = 1040561, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 0, 0), + Type = VelopackAssetType.Full, + FileName = $"MyCoolApp-1.0.0.nupkg", + SHA1 = "94689fede03fed7ab59c24337673a27837f0c3ec", + Size = 1004502, + }, + } + }; + var json = JsonSerializer.Serialize(feed, SimpleJsonTests.Options); + return new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(json) }; + } + + private FakeDownloader GetMockDownloaderWith2Delta() + { + var feed = new VelopackAssetFeed { + Assets = new VelopackAsset[] { + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 1, 0), + Type = VelopackAssetType.Full, + FileName = $"MyCoolApp-1.1.0.nupkg", + SHA1 = "3a2eadd15dd984e4559f2b4d790ec8badaeb6a39", + Size = 1040561, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 0, 0), + Type = VelopackAssetType.Full, + FileName = $"MyCoolApp-1.0.0.nupkg", + SHA1 = "94689fede03fed7ab59c24337673a27837f0c3ec", + Size = 1004502, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 1, 0), + Type = VelopackAssetType.Delta, + FileName = $"MyCoolApp-1.1.0-delta.nupkg", + SHA1 = "14db31d2647c6d2284882a2e101924a9c409ee67", + Size = 80396, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 0, 0), + Type = VelopackAssetType.Delta, + FileName = $"MyCoolApp-1.0.0-delta.nupkg", + SHA1 = "14db31d2647c6d2284882a2e101924a9c409ee67", + Size = 80396, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 2, 0), + Type = VelopackAssetType.Delta, + FileName = $"MyCoolApp-1.2.0-delta.nupkg", + SHA1 = "14db31d2647c6d2284882a2e101924a9c409ee67", + Size = 80396, + }, + new VelopackAsset() { + PackageId = "MyCoolApp", + Version = new SemanticVersion(1, 2, 0), + Type = VelopackAssetType.Full, + FileName = $"MyCoolApp-1.2.0.nupkg", + SHA1 = "3a2eadd15dd984e4559f2b4d790ec8badaeb6a39", + Size = 1040561, + }, + } + }; + var json = JsonSerializer.Serialize(feed, SimpleJsonTests.Options); + return new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(json) }; + } + [Fact] public void CheckForUpdatesFromLocal() { using var logger = _output.BuildLoggerFor(); using var _1 = Utility.GetTempDirectory(out var tempPath); - - string releasesSuffix = VelopackRuntimeInfo.SystemOs switch { - RuntimeOs.Windows => "", - RuntimeOs.Linux => "-linux", - RuntimeOs.OSX => "-osx", - _ => throw new ArgumentOutOfRangeException() - }; - - File.WriteAllText(Path.Combine(tempPath, "RELEASES" + releasesSuffix), """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""); + var dl = GetMockDownloaderNoDelta(); + var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); - var um = new UpdateManager(tempPath, null, logger, locator); + var um = new UpdateManager(source, null, logger, locator); var info = um.CheckForUpdates(); Assert.NotNull(info); Assert.True(new SemanticVersion(1, 1, 0) == info.TargetFullRelease.Version); @@ -45,35 +121,15 @@ namespace Velopack.Tests { using var logger = _output.BuildLoggerFor(); using var _1 = Utility.GetTempDirectory(out var tempPath); - File.WriteAllText(Path.Combine(tempPath, "RELEASES-experimental"), """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""); + var dl = GetMockDownloaderNoDelta(); + var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); - var um = new UpdateManager(tempPath, "experimental", logger, locator); + var um = new UpdateManager(source, "experimental", logger, locator); var info = um.CheckForUpdates(); Assert.NotNull(info); Assert.True(new SemanticVersion(1, 1, 0) == info.TargetFullRelease.Version); Assert.Equal(0, info.DeltasToTarget.Count()); - } - - [Fact] - public void CheckForUpdatesFromRemote() - { - using var logger = _output.BuildLoggerFor(); - using var _1 = Utility.GetTempDirectory(out var tempPath); - var releases = """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""; - var downloader = new FakeDownloader() { MockedResponseBytes = Encoding.UTF8.GetBytes(releases) }; - var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); - var um = new UpdateManager(new SimpleWebSource("http://any.com", downloader), "hello", logger, locator); - var info = um.CheckForUpdates(); - Assert.NotNull(info); - Assert.True(new SemanticVersion(1, 1, 0) == info.TargetFullRelease.Version); - Assert.Equal(0, info.DeltasToTarget.Count()); - Assert.Contains("/RELEASES-hello?", downloader.LastUrl); + Assert.StartsWith("http://any.com/releases.experimental.json?", dl.LastUrl); } [Fact] @@ -81,21 +137,10 @@ namespace Velopack.Tests { using var logger = _output.BuildLoggerFor(); using var _1 = Utility.GetTempDirectory(out var tempPath); - string releasesSuffix = VelopackRuntimeInfo.SystemOs switch { - RuntimeOs.Windows => "", - RuntimeOs.Linux => "-linux", - RuntimeOs.OSX => "-osx", - _ => throw new ArgumentOutOfRangeException() - }; - File.WriteAllText(Path.Combine(tempPath, "RELEASES" + releasesSuffix), """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.2.0.nupkg 1040561 -14db31d2647c6d2284882a2e101924a9c409ee67 MyCoolApp-1.2.0-delta.nupkg 80396 -14db31d2647c6d2284882a2e101924a9c409ee67 MyCoolApp-1.1.0-delta.nupkg 80396 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""); + var dl = GetMockDownloaderWith2Delta(); + var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", tempPath, logger); - var um = new UpdateManager(tempPath, null, logger, locator); + var um = new UpdateManager(source, null, logger, locator); var info = um.CheckForUpdates(); Assert.NotNull(info); Assert.True(new SemanticVersion(1, 2, 0) == info.TargetFullRelease.Version); @@ -134,18 +179,10 @@ namespace Velopack.Tests { using var logger = _output.BuildLoggerFor(); using var _1 = Utility.GetTempDirectory(out var tempPath); - string releasesSuffix = VelopackRuntimeInfo.SystemOs switch { - RuntimeOs.Windows => "", - RuntimeOs.Linux => "-linux", - RuntimeOs.OSX => "-osx", - _ => throw new ArgumentOutOfRangeException() - }; - File.WriteAllText(Path.Combine(tempPath, "RELEASES" + releasesSuffix), """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""); + var dl = GetMockDownloaderNoDelta(); + var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.1.0", tempPath, logger); - var um = new UpdateManager(tempPath, null, logger, locator); + var um = new UpdateManager(source, null, logger, locator); var info = um.CheckForUpdates(); Assert.Null(info); } @@ -155,18 +192,10 @@ namespace Velopack.Tests { using var logger = _output.BuildLoggerFor(); using var _1 = Utility.GetTempDirectory(out var tempPath); - string releasesSuffix = VelopackRuntimeInfo.SystemOs switch { - RuntimeOs.Windows => "", - RuntimeOs.Linux => "-linux", - RuntimeOs.OSX => "-osx", - _ => throw new ArgumentOutOfRangeException() - }; - File.WriteAllText(Path.Combine(tempPath, "RELEASES" + releasesSuffix), """ -3a2eadd15dd984e4559f2b4d790ec8badaeb6a39 MyCoolApp-1.1.0.nupkg 1040561 -94689fede03fed7ab59c24337673a27837f0c3ec MyCoolApp-1.0.0.nupkg 1004502 -"""); + var dl = GetMockDownloaderNoDelta(); + var source = new SimpleWebSource("http://any.com", dl); var locator = new TestVelopackLocator("MyCoolApp", "1.2.0", tempPath, logger); - var um = new UpdateManager(tempPath, null, logger, locator); + var um = new UpdateManager(source, null, logger, locator); var info = um.CheckForUpdates(); Assert.Null(info); } @@ -206,7 +235,10 @@ namespace Velopack.Tests var repo = new FakeFixtureRepository(id, true); var source = new SimpleWebSource("http://any.com", repo); - var basePkg = (await source.GetReleaseFeed(logger)).Assets.Single(x => x.Version == SemanticVersion.Parse(fromVersion)); + var feed = await source.GetReleaseFeed(logger); + var basePkg = feed.Assets + .Where(x => x.Type == VelopackAssetType.Full) + .Single(x => x.Version == SemanticVersion.Parse(fromVersion)); var basePkgFixturePath = PathHelper.GetFixture(basePkg.FileName); var basePkgPath = Path.Combine(packagesDir, basePkg.FileName); File.Copy(basePkgFixturePath, basePkgPath); diff --git a/test/fixtures/releases.win.json b/test/fixtures/releases.win.json new file mode 100644 index 00000000..1fa90fb1 --- /dev/null +++ b/test/fixtures/releases.win.json @@ -0,0 +1,176 @@ +{ + "Assets": [ + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.11", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.11-full.nupkg", + "SHA1": "D9F1CE7DE35D9544DF65AE6A5674D1A2D7EE5EAC", + "Size": 14763516, + "NotesMarkdown": "\u003Cpicture\u003E\n \u003Csource media=\u0022(prefers-color-scheme: dark)\u0022 srcset=\u0022docs/artwork/velopack-white.svg\u0022\u003E\n \u003Cimg alt=\u0022Velopack Logo\u0022 src=\u0022docs/artwork/velopack-black.svg\u0022 width=\u0022400\u0022\u003E\n\u003C/picture\u003E\n\n---\n\n[![Nuget](https://img.shields.io/nuget/v/Velopack?style=flat-square)](https://www.nuget.org/packages/Velopack/)\n[![Discord](https://img.shields.io/discord/767856501477343282?style=flat-square\u0026color=purple)](https://discord.gg/CjrCrNzd3F)\n[![Build](https://img.shields.io/github/actions/workflow/status/velopack/velopack/build.yml?branch=develop\u0026style=flat-square)](https://github.com/velopack/velopack/actions)\n[![Codecov](https://img.shields.io/codecov/c/github/velopack/velopack?style=flat-square)](https://app.codecov.io/gh/velopack/velopack)\n[![License](https://img.shields.io/github/license/velopack/velopack?style=flat-square)](https://github.com/velopack/velopack/blob/develop/LICENSE)\n\nVelopack is a setup / installation framework for cross-platform dotnet applications. Great out-of-the-box development experience, with zero configuration or setup needed. Lightning fast to use, and lightning fast for your users, too.\n\n## Features\n\n- \uD83D\uDE0D **Zero config** \u2013 Velopack takes your dotnet build output (eg. \u0060dotnet publish\u0060), and generates an installer, and update package in a single command.\n- \uD83C\uDFAF **Cross platform** \u2013 Velopack supports building packages for **Windows**, **OSX**, and **Linux**. No matter your target, Velopack can create a release in just one command.\n- \uD83D\uDE80 **Automatic migrations** - If you are coming from [Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows) or [Clowd.Squirrel](https://github.com/clowd/Clowd.Squirrel), Velopack will automatically migrate your application. Just build your Velopack release and deploy! [Read more.](docs/migrating.md)\n- \u26A1\uFE0F **Lightning fast** \u2013 Velopack is written in Rust for native performance. Creating releases is multi-threaded, and produces delta packages for ultra fast app updates. Applying update packages is highly optimised, and often can be done in the background.\n\n## Getting Started\nThis is a very simple example of the steps you would take to generate an installer and update packages for your application. Be sure to [read the documentation](docs) for an overview of more features!\n\n1. Install the command line tool \u0060vpk\u0060:\n \u0060\u0060\u0060cmd\n dotnet tool install -g vpk\n \u0060\u0060\u0060\n2. Install the [Velopack NuGet Package](https://www.nuget.org/packages/velopack) in your main project:\n \u0060\u0060\u0060cmd\n dotnet add package Velopack\n \u0060\u0060\u0060\n3. Configure your Velopack app at the beginning of \u0060Program.Main\u0060:\n \u0060\u0060\u0060cs\n static void Main(string[] args)\n {\n VelopackApp.Build().Run();\n // ... your other startup code below\n }\n \u0060\u0060\u0060\n4. Publish dotnet and build your first Velopack release! \uD83C\uDF89\n \u0060\u0060\u0060cmd\n dotnet publish -c Release --self-contained -r win-x64 -o .\\publish\n vpk pack -u YourAppId -v 1.0.0 -p .\\publish\n \u0060\u0060\u0060\n5. Add automatic updating to your app:\n \u0060\u0060\u0060cs\n private static async Task UpdateMyApp()\n {\n var mgr = new UpdateManager(\u0022https://the.place/you-host/updates\u0022);\n\n // check for new version\n var newVersion = await mgr.CheckForUpdatesAsync();\n if (newVersion == null)\n return; // no update available\n\n // download new version\n await mgr.DownloadUpdatesAsync(newVersion);\n\n // install new version and restart app\n mgr.ApplyUpdatesAndRestart();\n }\n \u0060\u0060\u0060\n\nIf you\u0027re not sure how these instructions fit into your app, check the example apps for common scenarios such as WPF or Avalonia.\n\n## Documentation\n- \uD83D\uDCD6 [Read the docs](docs)\n- \uD83D\uDD76\uFE0F [View example apps](examples)\n\n## Community\n- \u2753 Ask questions, get support, or discuss ideas on [our Discord server](https://discord.gg/CjrCrNzd3F)\n- \uD83D\uDDE3\uFE0F Report bugs on [GitHub Issues](https://github.com/velopack/velopack/issues)\n\n\n## Contributing\n- \uD83D\uDCAC Join us on [Discord](https://discord.gg/CjrCrNzd3F) to get involved in dev discussions\n- \uD83D\uDEA6 Read our [compiling guide](docs/compiling.md)", + "NotesHTML": "\u003Cp\u003E\u003Cpicture\u003E\n \u003Csource media=\u0022(prefers-color-scheme: dark)\u0022 srcset=\u0022docs/artwork/velopack-white.svg\u0022\u003E\n \u003Cimg alt=\u0022Velopack Logo\u0022 src=\u0022docs/artwork/velopack-black.svg\u0022 width=\u0022400\u0022\u003E\n\u003C/picture\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Chr /\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Ca href=\u0022https://www.nuget.org/packages/Velopack/\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/nuget/v/Velopack?style=flat-square\u0022 alt=\u0022Nuget\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/discord/767856501477343282?style=flat-square\u0026amp;color=purple\u0022 alt=\u0022Discord\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://github.com/velopack/velopack/actions\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/github/actions/workflow/status/velopack/velopack/build.yml?branch=develop\u0026amp;style=flat-square\u0022 alt=\u0022Build\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://app.codecov.io/gh/velopack/velopack\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/codecov/c/github/velopack/velopack?style=flat-square\u0022 alt=\u0022Codecov\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://github.com/velopack/velopack/blob/develop/LICENSE\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/github/license/velopack/velopack?style=flat-square\u0022 alt=\u0022License\u0022 /\u003E\u003C/a\u003E\u003C/p\u003E\n\n\u003Cp\u003EVelopack is a setup / installation framework for cross-platform dotnet applications. Great out-of-the-box development experience, with zero configuration or setup needed. Lightning fast to use, and lightning fast for your users, too.\u003C/p\u003E\n\n\u003Cp\u003E\u003Ch2\u003EFeatures\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDE0D \u003Cstrong\u003EZero config\u003C/strong\u003E \u2013 Velopack takes your dotnet build output (eg. \u003Ccode\u003Edotnet publish\u003C/code\u003E), and generates an installer, and update package in a single command.\u003C/li\u003E\n\u003Cli\u003E\uD83C\uDFAF \u003Cstrong\u003ECross platform\u003C/strong\u003E \u2013 Velopack supports building packages for \u003Cstrong\u003EWindows\u003C/strong\u003E, \u003Cstrong\u003EOSX\u003C/strong\u003E, and \u003Cstrong\u003ELinux\u003C/strong\u003E. No matter your target, Velopack can create a release in just one command.\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDE80 \u003Cstrong\u003EAutomatic migrations\u003C/strong\u003E - If you are coming from \u003Ca href=\u0022https://github.com/Squirrel/Squirrel.Windows\u0022\u003ESquirrel.Windows\u003C/a\u003E or \u003Ca href=\u0022https://github.com/clowd/Clowd.Squirrel\u0022\u003EClowd.Squirrel\u003C/a\u003E, Velopack will automatically migrate your application. Just build your Velopack release and deploy! \u003Ca href=\u0022docs/migrating.md\u0022\u003ERead more.\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\u26A1\uFE0F \u003Cstrong\u003ELightning fast\u003C/strong\u003E \u2013 Velopack is written in Rust for native performance. Creating releases is multi-threaded, and produces delta packages for ultra fast app updates. Applying update packages is highly optimised, and often can be done in the background.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003EGetting Started\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003EThis is a very simple example of the steps you would take to generate an installer and update packages for your application. Be sure to \u003Ca href=\u0022docs\u0022\u003Eread the documentation\u003C/a\u003E for an overview of more features!\u003C/p\u003E\n\n\u003Cp\u003E\u003Col\u003E\n\u003Cli\u003EInstall the command line tool \u003Ccode\u003Evpk\u003C/code\u003E:\n\u003Ccode\u003Ecmd\ndotnet tool install -g vpk\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EInstall the \u003Ca href=\u0022https://www.nuget.org/packages/velopack\u0022\u003EVelopack NuGet Package\u003C/a\u003E in your main project:\n\u003Ccode\u003Ecmd\ndotnet add package Velopack\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EConfigure your Velopack app at the beginning of \u003Ccode\u003EProgram.Main\u003C/code\u003E:\n\u003Ccode\u003Ecs\nstatic void Main(string[] args)\n{\n VelopackApp.Build().Run();\n // ... your other startup code below\n}\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPublish dotnet and build your first Velopack release! \uD83C\uDF89\n\u003Ccode\u003Ecmd\ndotnet publish -c Release --self-contained -r win-x64 -o .\\publish\nvpk pack -u YourAppId -v 1.0.0 -p .\\publish\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAdd automatic updating to your app:\n\u0060\u0060\u0060cs\nprivate static async Task UpdateMyApp()\n{\n var mgr = new UpdateManager(\u0022https://the.place/you-host/updates\u0022);\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// check for new version\n var newVersion = await mgr.CheckForUpdatesAsync();\n if (newVersion == null)\n return; // no update available\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// download new version\n await mgr.DownloadUpdatesAsync(newVersion);\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// install new version and restart app\n mgr.ApplyUpdatesAndRestart();\n}\n\u0060\u0060\u0060\u003C/p\u003E\u003C/li\u003E\n\u003C/ol\u003E\nIf you\u0027re not sure how these instructions fit into your app, check the example apps for common scenarios such as WPF or Avalonia.\u003C/p\u003E\n\n\u003Cp\u003E\u003Ch2\u003EDocumentation\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDCD6 \u003Ca href=\u0022docs\u0022\u003ERead the docs\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDD76\uFE0F \u003Ca href=\u0022examples\u0022\u003EView example apps\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003ECommunity\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\u2753 Ask questions, get support, or discuss ideas on \u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003Eour Discord server\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDDE3\uFE0F Report bugs on \u003Ca href=\u0022https://github.com/velopack/velopack/issues\u0022\u003EGitHub Issues\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003EContributing\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDCAC Join us on \u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003EDiscord\u003C/a\u003E to get involved in dev discussions\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDEA6 Read our \u003Ca href=\u0022docs/compiling.md\u0022\u003Ecompiling guide\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\u003C/p\u003E" + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.11", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.11-delta.nupkg", + "SHA1": "1E2618E5B8A22D9BF930AAEEEBC4626255FE0368", + "Size": 86578, + "NotesMarkdown": "\u003Cpicture\u003E\n \u003Csource media=\u0022(prefers-color-scheme: dark)\u0022 srcset=\u0022docs/artwork/velopack-white.svg\u0022\u003E\n \u003Cimg alt=\u0022Velopack Logo\u0022 src=\u0022docs/artwork/velopack-black.svg\u0022 width=\u0022400\u0022\u003E\n\u003C/picture\u003E\n\n---\n\n[![Nuget](https://img.shields.io/nuget/v/Velopack?style=flat-square)](https://www.nuget.org/packages/Velopack/)\n[![Discord](https://img.shields.io/discord/767856501477343282?style=flat-square\u0026color=purple)](https://discord.gg/CjrCrNzd3F)\n[![Build](https://img.shields.io/github/actions/workflow/status/velopack/velopack/build.yml?branch=develop\u0026style=flat-square)](https://github.com/velopack/velopack/actions)\n[![Codecov](https://img.shields.io/codecov/c/github/velopack/velopack?style=flat-square)](https://app.codecov.io/gh/velopack/velopack)\n[![License](https://img.shields.io/github/license/velopack/velopack?style=flat-square)](https://github.com/velopack/velopack/blob/develop/LICENSE)\n\nVelopack is a setup / installation framework for cross-platform dotnet applications. Great out-of-the-box development experience, with zero configuration or setup needed. Lightning fast to use, and lightning fast for your users, too.\n\n## Features\n\n- \uD83D\uDE0D **Zero config** \u2013 Velopack takes your dotnet build output (eg. \u0060dotnet publish\u0060), and generates an installer, and update package in a single command.\n- \uD83C\uDFAF **Cross platform** \u2013 Velopack supports building packages for **Windows**, **OSX**, and **Linux**. No matter your target, Velopack can create a release in just one command.\n- \uD83D\uDE80 **Automatic migrations** - If you are coming from [Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows) or [Clowd.Squirrel](https://github.com/clowd/Clowd.Squirrel), Velopack will automatically migrate your application. Just build your Velopack release and deploy! [Read more.](docs/migrating.md)\n- \u26A1\uFE0F **Lightning fast** \u2013 Velopack is written in Rust for native performance. Creating releases is multi-threaded, and produces delta packages for ultra fast app updates. Applying update packages is highly optimised, and often can be done in the background.\n\n## Getting Started\nThis is a very simple example of the steps you would take to generate an installer and update packages for your application. Be sure to [read the documentation](docs) for an overview of more features!\n\n1. Install the command line tool \u0060vpk\u0060:\n \u0060\u0060\u0060cmd\n dotnet tool install -g vpk\n \u0060\u0060\u0060\n2. Install the [Velopack NuGet Package](https://www.nuget.org/packages/velopack) in your main project:\n \u0060\u0060\u0060cmd\n dotnet add package Velopack\n \u0060\u0060\u0060\n3. Configure your Velopack app at the beginning of \u0060Program.Main\u0060:\n \u0060\u0060\u0060cs\n static void Main(string[] args)\n {\n VelopackApp.Build().Run();\n // ... your other startup code below\n }\n \u0060\u0060\u0060\n4. Publish dotnet and build your first Velopack release! \uD83C\uDF89\n \u0060\u0060\u0060cmd\n dotnet publish -c Release --self-contained -r win-x64 -o .\\publish\n vpk pack -u YourAppId -v 1.0.0 -p .\\publish\n \u0060\u0060\u0060\n5. Add automatic updating to your app:\n \u0060\u0060\u0060cs\n private static async Task UpdateMyApp()\n {\n var mgr = new UpdateManager(\u0022https://the.place/you-host/updates\u0022);\n\n // check for new version\n var newVersion = await mgr.CheckForUpdatesAsync();\n if (newVersion == null)\n return; // no update available\n\n // download new version\n await mgr.DownloadUpdatesAsync(newVersion);\n\n // install new version and restart app\n mgr.ApplyUpdatesAndRestart();\n }\n \u0060\u0060\u0060\n\nIf you\u0027re not sure how these instructions fit into your app, check the example apps for common scenarios such as WPF or Avalonia.\n\n## Documentation\n- \uD83D\uDCD6 [Read the docs](docs)\n- \uD83D\uDD76\uFE0F [View example apps](examples)\n\n## Community\n- \u2753 Ask questions, get support, or discuss ideas on [our Discord server](https://discord.gg/CjrCrNzd3F)\n- \uD83D\uDDE3\uFE0F Report bugs on [GitHub Issues](https://github.com/velopack/velopack/issues)\n\n\n## Contributing\n- \uD83D\uDCAC Join us on [Discord](https://discord.gg/CjrCrNzd3F) to get involved in dev discussions\n- \uD83D\uDEA6 Read our [compiling guide](docs/compiling.md)", + "NotesHTML": "\u003Cp\u003E\u003Cpicture\u003E\n \u003Csource media=\u0022(prefers-color-scheme: dark)\u0022 srcset=\u0022docs/artwork/velopack-white.svg\u0022\u003E\n \u003Cimg alt=\u0022Velopack Logo\u0022 src=\u0022docs/artwork/velopack-black.svg\u0022 width=\u0022400\u0022\u003E\n\u003C/picture\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Chr /\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Ca href=\u0022https://www.nuget.org/packages/Velopack/\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/nuget/v/Velopack?style=flat-square\u0022 alt=\u0022Nuget\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/discord/767856501477343282?style=flat-square\u0026amp;color=purple\u0022 alt=\u0022Discord\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://github.com/velopack/velopack/actions\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/github/actions/workflow/status/velopack/velopack/build.yml?branch=develop\u0026amp;style=flat-square\u0022 alt=\u0022Build\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://app.codecov.io/gh/velopack/velopack\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/codecov/c/github/velopack/velopack?style=flat-square\u0022 alt=\u0022Codecov\u0022 /\u003E\u003C/a\u003E\n\u003Ca href=\u0022https://github.com/velopack/velopack/blob/develop/LICENSE\u0022\u003E\u003Cimg src=\u0022https://img.shields.io/github/license/velopack/velopack?style=flat-square\u0022 alt=\u0022License\u0022 /\u003E\u003C/a\u003E\u003C/p\u003E\n\n\u003Cp\u003EVelopack is a setup / installation framework for cross-platform dotnet applications. Great out-of-the-box development experience, with zero configuration or setup needed. Lightning fast to use, and lightning fast for your users, too.\u003C/p\u003E\n\n\u003Cp\u003E\u003Ch2\u003EFeatures\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDE0D \u003Cstrong\u003EZero config\u003C/strong\u003E \u2013 Velopack takes your dotnet build output (eg. \u003Ccode\u003Edotnet publish\u003C/code\u003E), and generates an installer, and update package in a single command.\u003C/li\u003E\n\u003Cli\u003E\uD83C\uDFAF \u003Cstrong\u003ECross platform\u003C/strong\u003E \u2013 Velopack supports building packages for \u003Cstrong\u003EWindows\u003C/strong\u003E, \u003Cstrong\u003EOSX\u003C/strong\u003E, and \u003Cstrong\u003ELinux\u003C/strong\u003E. No matter your target, Velopack can create a release in just one command.\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDE80 \u003Cstrong\u003EAutomatic migrations\u003C/strong\u003E - If you are coming from \u003Ca href=\u0022https://github.com/Squirrel/Squirrel.Windows\u0022\u003ESquirrel.Windows\u003C/a\u003E or \u003Ca href=\u0022https://github.com/clowd/Clowd.Squirrel\u0022\u003EClowd.Squirrel\u003C/a\u003E, Velopack will automatically migrate your application. Just build your Velopack release and deploy! \u003Ca href=\u0022docs/migrating.md\u0022\u003ERead more.\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\u26A1\uFE0F \u003Cstrong\u003ELightning fast\u003C/strong\u003E \u2013 Velopack is written in Rust for native performance. Creating releases is multi-threaded, and produces delta packages for ultra fast app updates. Applying update packages is highly optimised, and often can be done in the background.\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003EGetting Started\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003EThis is a very simple example of the steps you would take to generate an installer and update packages for your application. Be sure to \u003Ca href=\u0022docs\u0022\u003Eread the documentation\u003C/a\u003E for an overview of more features!\u003C/p\u003E\n\n\u003Cp\u003E\u003Col\u003E\n\u003Cli\u003EInstall the command line tool \u003Ccode\u003Evpk\u003C/code\u003E:\n\u003Ccode\u003Ecmd\ndotnet tool install -g vpk\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EInstall the \u003Ca href=\u0022https://www.nuget.org/packages/velopack\u0022\u003EVelopack NuGet Package\u003C/a\u003E in your main project:\n\u003Ccode\u003Ecmd\ndotnet add package Velopack\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EConfigure your Velopack app at the beginning of \u003Ccode\u003EProgram.Main\u003C/code\u003E:\n\u003Ccode\u003Ecs\nstatic void Main(string[] args)\n{\n VelopackApp.Build().Run();\n // ... your other startup code below\n}\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003EPublish dotnet and build your first Velopack release! \uD83C\uDF89\n\u003Ccode\u003Ecmd\ndotnet publish -c Release --self-contained -r win-x64 -o .\\publish\nvpk pack -u YourAppId -v 1.0.0 -p .\\publish\n\u003C/code\u003E\u003C/li\u003E\n\u003Cli\u003E\u003Cp\u003EAdd automatic updating to your app:\n\u0060\u0060\u0060cs\nprivate static async Task UpdateMyApp()\n{\n var mgr = new UpdateManager(\u0022https://the.place/you-host/updates\u0022);\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// check for new version\n var newVersion = await mgr.CheckForUpdatesAsync();\n if (newVersion == null)\n return; // no update available\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// download new version\n await mgr.DownloadUpdatesAsync(newVersion);\u003C/p\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cp\u003E// install new version and restart app\n mgr.ApplyUpdatesAndRestart();\n}\n\u0060\u0060\u0060\u003C/p\u003E\u003C/li\u003E\n\u003C/ol\u003E\nIf you\u0027re not sure how these instructions fit into your app, check the example apps for common scenarios such as WPF or Avalonia.\u003C/p\u003E\n\n\u003Cp\u003E\u003Ch2\u003EDocumentation\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDCD6 \u003Ca href=\u0022docs\u0022\u003ERead the docs\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDD76\uFE0F \u003Ca href=\u0022examples\u0022\u003EView example apps\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003ECommunity\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\u2753 Ask questions, get support, or discuss ideas on \u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003Eour Discord server\u003C/a\u003E\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDDE3\uFE0F Report bugs on \u003Ca href=\u0022https://github.com/velopack/velopack/issues\u0022\u003EGitHub Issues\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\n\u003Ch2\u003EContributing\u003C/h2\u003E\u003C/p\u003E\n\n\u003Cp\u003E\u003Cul\u003E\n\u003Cli\u003E\uD83D\uDCAC Join us on \u003Ca href=\u0022https://discord.gg/CjrCrNzd3F\u0022\u003EDiscord\u003C/a\u003E to get involved in dev discussions\u003C/li\u003E\n\u003Cli\u003E\uD83D\uDEA6 Read our \u003Ca href=\u0022docs/compiling.md\u0022\u003Ecompiling guide\u003C/a\u003E\u003C/li\u003E\n\u003C/ul\u003E\u003C/p\u003E" + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.10", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.10-full.nupkg", + "SHA1": "384AB80ADF4E70A3AE82703F866DC0A20D5C7104", + "Size": 14758787 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.10", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.10-delta.nupkg", + "SHA1": "A1013517AAA7132469733D831508F8AC83E76D1F", + "Size": 15011 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.9", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.9-full.nupkg", + "SHA1": "78B9E3F207EA610889665EA493D3A9CD77FE3536", + "Size": 14758781 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.9", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.9-delta.nupkg", + "SHA1": "BAF9B715B7A234BA09E27EB269CD2C2077A01FB4", + "Size": 14985 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.6-build.30\u002Bmetadata", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.6-build.30-full.nupkg", + "SHA1": "FC4D23995E30D74082BFE8F186BBF6D0FB27F313", + "Size": 14758866 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.6-build.30\u002Bmetadata", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.6-build.30-delta.nupkg", + "SHA1": "7B12DAB98FCE5FCCD427B916F3D7BC94BB366B32", + "Size": 14736 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.6-build.29\u002Bmetadata", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.6-build.29\u002Bmetadata-full.nupkg", + "SHA1": "4B7E42D72408DA2D833C849D10F516FBD6B3D7B2", + "Size": 14758867 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.6-build.29\u002Bmetadata", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.6-build.29\u002Bmetadata-delta.nupkg", + "SHA1": "B766FC5FA848A936D5CFB60A0582A4B8EA891B3D", + "Size": 15103 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.5", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.5-full.nupkg", + "SHA1": "A9D9421EDD1664BB805BA773503E5CFC60ECCCB2", + "Size": 14758780 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.5", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.5-delta.nupkg", + "SHA1": "BF2B72E2E0A40A0CFD25BCC64DE24CC69B33E0EA", + "Size": 73654 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.4", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.4-full.nupkg", + "SHA1": "B5B0E458771D59108990D87CBCEAC00170887FF3", + "Size": 14758707 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.4", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.4-delta.nupkg", + "SHA1": "C707A3644E062EF35DA8BA707A503A1455C6AFA2", + "Size": 92120 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.3", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.3-full.nupkg", + "SHA1": "75776415ECB1C5A53281B1B9AED77E8AD9D40191", + "Size": 14759065 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.3", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.3-delta.nupkg", + "SHA1": "57D14DCB1D0FB178F195D598B93076F3A5B3988E", + "Size": 14752 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.2", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.2-full.nupkg", + "SHA1": "4E564CBCF30BA03B446D16CF5A678928925B780E", + "Size": 14759070 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.2", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.2-delta.nupkg", + "SHA1": "F49C40A76A2607E8A60256E2013A65C9D654F9F6", + "Size": 14752 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.1", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.1-full.nupkg", + "SHA1": "501C5AC6860E1AD8DF2ED619ED73D9A8C507B031", + "Size": 14759057 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.1", + "Type": "Delta", + "FileName": "AvaloniaCrossPlat-1.0.1-delta.nupkg", + "SHA1": "6789E1F78C113155CB7B9E083CD6DF7F86D5988D", + "Size": 14753 + }, + { + "PackageId": "AvaloniaCrossPlat", + "Version": "1.0.0", + "Type": "Full", + "FileName": "AvaloniaCrossPlat-1.0.0-full.nupkg", + "SHA1": "F8C2848BAF1791DEF4138157DB269C58B311EAFA", + "Size": 14759050 + } + ] +} \ No newline at end of file