From bd44bc302cbe45e7a3352bb6700a75464df0326d Mon Sep 17 00:00:00 2001 From: Caelan Sayler Date: Sun, 18 May 2025 22:54:28 +0100 Subject: [PATCH] Fix tests and add size validation to rust --- src/lib-csharp/UpdateManager.cs | 18 ++++++---------- src/lib-rust/src/lib.rs | 6 ++++-- src/lib-rust/src/manager.rs | 8 +++++++- test/Velopack.Tests/UpdateManagerTests.cs | 25 +++++++++++++++++++++++ test/fixtures/testfeed.json | 4 ++-- 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/src/lib-csharp/UpdateManager.cs b/src/lib-csharp/UpdateManager.cs index cbb130d8..60790347 100644 --- a/src/lib-csharp/UpdateManager.cs +++ b/src/lib-csharp/UpdateManager.cs @@ -235,20 +235,14 @@ namespace Velopack var completeFile = Locator.GetLocalPackagePath(targetRelease); var incompleteFile = completeFile + ".partial"; + + // if the package already exists on disk, we can skip the download. + if (File.Exists(completeFile)) { + Log.Info($"Package already exists on disk: '{completeFile}', nothing to do."); + return; + } try { - // if the package already exists on disk, we can skip the download. - if (File.Exists(completeFile)) { - Log.Info($"Package already exists on disk: '{completeFile}', verifying checksum..."); - try { - VerifyPackageChecksum(targetRelease, completeFile); - Log.Info("Package checksum verified, skipping download."); - return; - } catch (ChecksumFailedException ex) { - Log.Warn(ex, $"Checksum failed for file '{completeFile}'. Deleting and starting over."); - } - } - var deltasSize = updates.DeltasToTarget.Sum(x => x.Size); var deltasCount = updates.DeltasToTarget.Count(); diff --git a/src/lib-rust/src/lib.rs b/src/lib-rust/src/lib.rs index fe9cce17..5f2a9104 100644 --- a/src/lib-rust/src/lib.rs +++ b/src/lib-rust/src/lib.rs @@ -127,8 +127,10 @@ pub enum Error FileNotFound(String), #[error("IO error: {0}")] Io(#[from] std::io::Error), - #[error("Checusum did not match for {0} (expected {1}, actual {2})")] - Checksum(String, String, String), + #[error("Checksum did not match for {0} (expected {1}, actual {2})")] + ChecksumInvalid(String, String, String), + #[error("Size did not match for {0} (expected {1}, actual {2})")] + SizeInvalid(String, u64, u64), #[error("Zip error: {0}")] Zip(#[from] zip::result::ZipError), #[error("Network error: {0}")] diff --git a/src/lib-rust/src/manager.rs b/src/lib-rust/src/manager.rs index 9a75aea0..b90a4abe 100644 --- a/src/lib-rust/src/manager.rs +++ b/src/lib-rust/src/manager.rs @@ -519,10 +519,16 @@ impl UpdateManager { } fn verify_package_checksum(&self, file: &PathBuf, asset: &VelopackAsset) -> Result<(), Error> { + let file_size = file.metadata()?.len(); + if file_size != asset.Size { + error!("File size mismatch for file '{}': expected {}, got {}", file.to_string_lossy(), asset.Size, file_size); + return Err(Error::SizeInvalid(file.to_string_lossy().to_string(), asset.Size, file_size)); + } + let sha1 = util::calculate_file_sha1(file)?; if !sha1.eq_ignore_ascii_case(&asset.SHA1) { error!("SHA1 checksum mismatch for file '{}': expected '{}', got '{}'", file.to_string_lossy(), asset.SHA1, sha1); - return Err(Error::Checksum(file.to_string_lossy().to_string(), asset.SHA1.clone(), sha1)); + return Err(Error::ChecksumInvalid(file.to_string_lossy().to_string(), asset.SHA1.clone(), sha1)); } Ok(()) } diff --git a/test/Velopack.Tests/UpdateManagerTests.cs b/test/Velopack.Tests/UpdateManagerTests.cs index 8816c322..f3fd4a6e 100644 --- a/test/Velopack.Tests/UpdateManagerTests.cs +++ b/test/Velopack.Tests/UpdateManagerTests.cs @@ -144,6 +144,31 @@ public class UpdateManagerTests Assert.Equal("https://mysite.com/releases/AvaloniaCrossPlat$-1.1.0.nupkg", dl.LastUrl); } + [Fact] + public void DownlaodFullUpdateFromFixtures() + { + using var logger = _output.BuildLoggerFor(); + using var _1 = TempUtil.GetTempDirectory(out var packagesPath); + using var _2 = TempUtil.GetTempDirectory(out var feedPath); + + var locator = new TestVelopackLocator("MyCoolApp", "1.0.0", packagesPath, logger.ToVelopackLogger()); + + File.Copy(PathHelper.GetFixture("testfeed.json"), Path.Combine(feedPath, "releases.beta.json"), true); + File.Copy(PathHelper.GetFixture("AvaloniaCrossPlat-1.0.11-win-full.nupkg"), Path.Combine(feedPath, "AvaloniaCrossPlat-1.0.11-full.nupkg"), true); + + var options = new UpdateOptions() { + ExplicitChannel = "beta", + AllowVersionDowngrade = false, + MaximumDeltasBeforeFallback = 10, + }; + + var um = new UpdateManager(feedPath, options, locator); + var updateInfo = um.CheckForUpdates(); + Assert.NotNull(updateInfo); + um.DownloadUpdates(updateInfo); + Assert.True(File.Exists(Path.Combine(packagesPath, "AvaloniaCrossPlat-1.0.11-full.nupkg"))); + } + [Fact] public void CheckFromLocal() { diff --git a/test/fixtures/testfeed.json b/test/fixtures/testfeed.json index efd6a87e..e03b2989 100644 --- a/test/fixtures/testfeed.json +++ b/test/fixtures/testfeed.json @@ -6,8 +6,8 @@ "Type": "Full", "UnknownProperty": "To test that serializers will ignore this", "FileName": "AvaloniaCrossPlat-1.0.11-full.nupkg", - "SHA1": "D9F1CE7DE35D9544DF65AE6A5674D1A2D7EE5EAC", - "Size": 14763516, + "SHA1": "E0BEDE4B1329B0E8279A15E7717CE823ABE8367C", + "Size": 13558493, "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" },