Giter Site home page Giter Site logo

onova's Introduction

Onova

Status Made in Ukraine Build Coverage Version Downloads Discord Fuck Russia

Development of this project is entirely funded by the community. Consider donating to support!

Icon

Onova is a lightweight auto-update framework for desktop applications. It's primarily designed for performing in-place updates for portable applications distributed via archive files (as opposed to installers or packages). The library requires minimal configuration, doesn't impose any changes to the CI/CD process, and doesn't affect the life cycle of your application.

✨ See also Onova.Publisher β€” community project that provides an integrated installation experience based on Onova.

Terms of use[?]

By using this project or its source code, for any purpose and in any shape or form, you grant your implicit agreement to all the following statements:

  • You condemn Russia and its military aggression against Ukraine
  • You recognize that Russia is an occupant that unlawfully invaded a sovereign state
  • You support Ukraine's territorial integrity, including its claims over temporarily occupied territories of Crimea and Donbas
  • You reject false narratives perpetuated by Russian state propaganda

To learn more about the war and how you can help, click here. Glory to Ukraine! πŸ‡ΊπŸ‡¦

Install

  • πŸ“¦ NuGet: dotnet add package Onova

Features

  • Requires minimal configuration
  • Supports the following package resolvers:
    • LocalPackageResolver - file system
    • GithubPackageResolver - GitHub releases
    • WebPackageResolver - web version manifest
    • NugetPackageResolver - NuGet feed
    • AggregatePackageResolver - aggregates multiple resolvers
  • Supports the following package extractors:
    • ZipPackageExtractor - zip archives
    • NugetPackageExtractor - NuGet packages
  • Extendable with custom resolvers and extractors
  • Can report progress and supports cancellation
  • Allows updating to any available version, not necessarily latest
  • Overwrites files in-place using an external executable
  • Works with multiple running instances of an application
  • Automatically prompts for elevated privileges if necessary
  • Fully self-contained and doesn't require additional files
  • Supports desktop apps built with .NET Core 3.0+
  • Targets .NET Framework 4.6.1+ and .NET Standard 2.0+ (Windows only)
  • No external dependencies

Workflow

Package resolving

Packages and their versions are resolved using an implementation of IPackageResolver. Currently there are 5 built-in implementations:

LocalPackageResolver

This implementation looks for files in the specified directory using a predefined pattern. Package versions are extracted from file names, e.g. file named MyProject-v2.1.5.zip corresponds to package version 2.1.5.

GithubPackageResolver

This implementation looks for assets in releases of specified GitHub repository using a predefined pattern. Package versions are extracted from release names, e.g. release named v1.0 corresponds to package version 1.0.

Since .NET assemblies do not support semantic versions, pre-releases are ignored.

WebPackageResolver

This implementation requests a version manifest using the specified URL. The server is expected to respond with a plain-text list of package versions and their URLs, separated by space, one line per package. E.g.:

1.0 https://my.server.com/1.0.zip
2.0 https://my.server.com/2.0.zip

NugetPackageResolver

This implementation resolves packages from the specified NuGet feed.

Since .NET assemblies do not support semantic versions, pre-releases are ignored.

AggregatePackageResolver

This implementation provides aggregation over multiple other IPackageResolver instances. It allows resolving and downloading packages from more than one source.

Package extraction

Downloaded packages are extracted using an implementation of IPackageExtractor. Currently there are 2 built-in implementations:

ZipPackageExtractor

This implementation extracts files from zip-archived packages.

NugetPackageExtractor

This implementation extracts files from NuGet packages, from the specified root directory.

Usage

Basic usage example

The following code checks for updates and installs them if they are available, in a single operation.

// Configure to look for packages in specified directory and treat them as zips
using (var manager = new UpdateManager(
    new LocalPackageResolver("c:\\test\\packages", "*.zip"),
    new ZipPackageExtractor()))
{
    // Check for new version and, if available, perform full update and restart
    await manager.CheckPerformUpdateAsync();
}

Handling intermediate steps manually

To provide users with the most optimal experience, you will probably want to handle intermediate steps manually.

// Check for updates
var result = await manager.CheckForUpdatesAsync();
if (result.CanUpdate)
{
    // Prepare an update by downloading and extracting the package
    // (supports progress reporting and cancellation)
    await manager.PrepareUpdateAsync(result.LastVersion);

    // Launch an executable that will apply the update
    // (can be instructed to restart the application afterwards)
    manager.LaunchUpdater(result.LastVersion);

    // Terminate the running application so that the updater can overwrite files
    Environment.Exit(0);
}

Handling updates with multiple running instances of the application

To prevent conflicts when running multiple instances of the same application, only one instance of UpdateManager (across all processes) is able to prepare updates and launch the updater.

In order to correctly handle cases where multiple instances of the application may try to update at the same time, you need to catch these exceptions:

  • LockFileNotAcquiredException - thrown by PrepareUpdateAsync and LaunchUpdater when this instance of UpdateManager cannot acquire a lock file. This means that another instance currently owns the lock file and is probably performing an update.
  • UpdaterAlreadyLaunchedException - thrown by PrepareUpdateAsync and LaunchUpdater when an updater executable has already been launched, either by this instance of UpdateManager or another instance that has released the lock file.

The updater will wait until all instances of the application have exited before applying an update, regardless of which instance launched it.

Etymology

The name "Onova" is derived from the Ukrainian word for "update" (noun).

onova's People

Contributors

0xced avatar derech1e avatar miakh avatar pipe01 avatar tyrrrz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

onova's Issues

Implement a publisher

Introduction

As this project seems to be my main alternative to Squirrel while converting my apps to .NET Core 3.1 / .NET FW 5,
I would like to put out the idea of a dedicated (Windows) installer / publisher in similar fashion to Squirrel.
(I will be using the term .NET 5 for whatever the correct version will be.)

Notes

I guess making the Installer/Uninstaller parts are the hardest, however I will have to replace Squirrel eventually and it's good to have some plan at least. This project does not need to be made under the Onova name, but if I ever start working on this, I will fork Onova. If Squirrel finishes the rework on time, I guess this will not be as needed. But still, I have issues even with the current working Squirrel so creating this would still benefit me (at least).

Publisher

  • Implemented in .NET 5
  • Available NuGet as Onova.Publish
  • Works the same as dotnet publish, except it creates additional files for the Onova update system

Input

  • Config file (e.g. some simple XML, not necessarily NuGet nuspec)
  • CLI (publisher.exe -> read config file from $(SolutionDirectory) -> output to ReleaseDir)

Output

  • Zip package

  • Changelog file

    • Version A - Read config file and append the changelog entry into a unified CHANGELOG file with version tags

      • This would mean that the Resolver would have to fetch just one file to get the whole changelog
      • On the other hand, the Resolver would have to use some sort of regex to find the last entry
      • The changelog could get gigantic and would defeat the purpose of this system
    • Version B - Copy changelog from config and paste to a new .changelog file that will have the same name as the zip package

      • Probably easier to implement and suits my use case better
      • If you want to show the user a list of all changelogs, you have to iterate over the manifest file and fetch all changelogs
    • Reason for this:

      • I need to show the user a changelog before the download happens, currently in Squirrel I just put it into a nuspec and then during update I fetch the whole package, before the user clicks yes/no (which is obviously bad in every aspect I can think of)
    • This feature also needs to implement custom Resolver (probably can reuse WebResolver)

  • Manifest file (publisher scans ReleaseDir for *.zip and creates text file for WebResolver)

  • Installer file (executable installer that embeds the zip for the newest version)

    • Must also contain an uninstaller
  • Hash file

    • Only a suggestion to implement - it is not required but I guess that it would be nice if the updater could also fetch a hash of the update package and check it in similar fashion to Squirrel

    • Why a standalone file? - The manifest file for WebResolver already contains the version number, I guess it could also be appended after the package URL to not break existing Resolver infrastructure (haven't looked into the implementation)

    • Suggestion: If both Changelog and Hash files will be implemented, it is advised to create new Resolver that will get everything under one roof (e. g. AdvancedWebResolver)

Installer

  • This file is generated using Onova.Publish

  • Probably compiled at runtime (during publish) using Roslyn

  • Version A

    • Implemented in .NET 5
    • Uses the trimmed (.NET 5 containing) single package or the single file package with prompt to install if FW is missing, whichever actually works
  • Version B

    • Implemented in .NET 4.6.1
    • Nothing extra should be needed, let's assume every system has this FW
    • We can check if .NET 5 is installed

Properties

  • Executable (.exe) file
  • Contains the newest .zip package from publish
  • Installer and uninstaller have icons (set in the publisher config)

Run

  • Creates a folder in %localappdata% and extracts the embedded zip package
  • Creates entry in Start Menu (also creates app publisher's name folder)
  • Creates Windows registry entry for uninstaller
  • Run the app itself after installation

Uninstaller

  • Do whatever works on Windows to uninstall the correct files and itself (probably the same as Squirrel)

Allow extract and copy content without zip root directory

I use the GithubPackageResolver with ZipPackageExtractor.
Inside zip has a root directory with the same name as zip, up to version 2.4.2 it extracts without root directory and correctly replaces the program.

Imagem_2020-01-03_135209

Above this version it extract and copy with the root (content) directory.

Imagem_2020-01-03_135141

Any way or possibility to configure to ignore/skip the zip root directory and only consider the content as in the first image?

Great job!

Use Onova with dotnet core application

Looking at the documentation it seems that Onova targets only desktop applications built with .NetStandard 2.0. We were wondering if there is any way you can use it to update ASP Net Core 2.0 web applications that use Kestrel as web server and, in case it is possible, how to do it.

Thanks in advance and best regards.

Wrong updatee path in wpf .net core 3.0

the launcher gets started with the wrong updatee path with wpf .net core 3.0.
it's a self-contained .exe

12-Dez-2019 18:58:18.268> Onova Updater v2.4.5.0 started with args: [C:\Users\xxx\AppData\Local\Temp\.net\ApplicationName\gkl4m13w.04h\ApplicationName.dll, C:\Users\xxx\AppData\Local\Onova\ApplicationName\1.0.4, True, ].
12-Dez-2019 18:58:18.275> Waiting for all running updatee instances to exit...
12-Dez-2019 18:58:18.784> Copying package contents from storage to updatee's directory...
12-Dez-2019 18:58:18.842> Restarting updatee [C:\Users\xxx\AppData\Local\Temp\.net\ApplicationName\gkl4m13w.04h\ApplicationName.exe ]...
12-Dez-2019 18:58:18.909> Restarted as pid:40824.
12-Dez-2019 18:58:18.909> Deleting package contents from storage...
12-Dez-2019 18:58:18.919> Update completed successfully.```

Not working with /p:PublishSingleFile=true apps

If a project is built as a single file:

dotnet publish -c Release -r win-x86 /p:PublishSingleFile=true
zip -j $zip_file ./bin/Release/netcoreapp3.1/win-x86/publish/myapp.exe

then the app is not updated with this code:

using (var manager = new UpdateManager(
	new WebPackageResolver(Config.update_manifest_url),
	new ZipPackageExtractor()))
{
	var result = await manager.CheckForUpdatesAsync();
	if (result.CanUpdate)
	{
		// Prepare an update by downloading and extracting the package
		// (supports progress reporting and cancellation)
		await manager.PrepareUpdateAsync(result.LastVersion);

		// Launch an executable that will apply the update
		// (can be instructed to restart the application afterwards)
		manager.LaunchUpdater(result.LastVersion);

		// Terminate the running application so that the updater can overwrite files
		Environment.Exit(0);
	}
}

It correctly detects the new version, downloads it, runs LaunchUpdater, but the actual file is not replaced with the new version. It works fine without /p:PublishSingleFile=true.

Back up files before overwriting

Version

2.6.2

Details

Sometimes overwriting updatee files may fail and, because it's not an atomic operations, the installation may become corrupted. In such cases we need to backup the updatee files and revert the changes if the overwrite fails. Additionally, we probably need some retry mechanism.

image

Steps to reproduce

n/a

Add the ability to specify custom arguments for app restart

Hi @Tyrrrz! I checked out your framework and it looks very good, great job on this!

I have a tiny feature request that I might contribute myself if you're fine with it.

Currently, the app can be restarted with the same arguments that it launched with, and while it is a nice feature, that's not what I'd always want. My use case is this: the app can be run on startup in the background, using a certain cmd-line switch to make sure it starts minimized in the tray. So, if the app was started using this cmd-line switch, but the user presses "Restart & Update" in the app UI, I'd like the app to be started with the main window visible = without this cmd-line switch.

My initial idea for the API is adding a third nullable string optional argument restartArgs to UpdateManager.LaunchUpdater() method.
Let me know what you think ✌️

Does not Update on Windows Store App (WAP Dist) - APPXBUNDLE (.appxbundle)

  • Works great on dev mode.

  • Works great on most Commons Windows Installers

- Does not work on Microsoft Store App (WAP redist) - APPXBUNDLE (.appxbundle)

how to reproduce the problem

  • just create a Microsoft Store packaging project with Onova Test App

  • After administrator permission App.Updater.exe cant apply files. Apparently a permission issue, even with admin elevation permission

Is there way multi version update?

hi
is there way to multi update packages with different package file?
if old version apps run and new packages does not have specific files the user need to download full packages app

thanks

Updater dont work after the last update

Since your latest update the updater download the update, but not corretly install it. When i try to finalize the update and restart the application, it will download the update again. You broke something in the last patch...

GitHub's 60 requests per hour limit is hindering GithubPackageResolver

GitHub allows only up to 60 requests per hour from every IP when using their API without authentication. We can't use authentication because Onova is used in client-side apps, which will expose the private keys.
Currently, Onova makes 1 request to check for update and 1 more to download updates so 60/hr is more or less enough, however this limit is shared among everything coming from one IP so if the user has e.g. Octotree extension installed in their browser, they can very quickly exhaust this limit, making Onova unable to work.

Any plan to update to the nuget package?

Thank you for your great project.
It's really easy to use and simple.

However, I wanted to use githubresolver, but fixed version is not published yet.
#33

Do you have any plan for it?

If not, I would make a fork and publish it to my own nuget.

Thank you.

My Onova-related projects generate too many system*.dlls in the bin directory

  • Issue description
    In the generated bin\Release directory, there exist 16 system*.dlls, such as System.Buffers.dll/System.Buffers.xml, System.Memory.dll/System.Memory.xml, System.ValueTuple.dll/System.ValueTuple.xml, etc.
  • Possible reason
    Refer to why does my net standard nuget package trigger so many dependencies and Using .NET Standard with Full Framework .NET, these redundant output dlls may be the result of that current version of Onova depends on .netframework v4.6.1 and .netstandard v2.0, and .netframework 4.6.1 is just partially .net standard 2.0 compliant. Update to .netframework 4.7.2 or higher may resolve this issue.

"Delayed" restart

First of all thank you so much!
I was looking for a framework to replace clickonce with dot net 3 and this is exactly what I was looking for ... Really a great job!
I used this code:

        public UpdateBarViewModel()
        {
            var appPath = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
            var urlFile = Path.Combine(appPath, "RpositoryUrl");
            Url = File.Exists(urlFile) ? File.ReadAllText(urlFile) : "";
            if (!string.IsNullOrEmpty(Url))
            {
                var timer = new Timer(60000);
                timer.Elapsed += Timer_Elapsed;
                timer.Start();
            }
        }

        private async void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            using (var manager = new UpdateManager(new LocalPackageResolver(Url, "*.zip"), new ZipPackageExtractor()))
            {
                var result = await manager.CheckForUpdatesAsync();
                if (result.CanUpdate)
                {
                    UpdateAvailable = true;
                    // Prepare an update by downloading and extracting the package
                    // (supports progress reporting and cancellation)
                    var progress = new Progress<double>(NotifyDownoading);
                    await manager.PrepareUpdateAsync(result.LastVersion, progress);

                    // Launch an executable that will apply the update
                    // (can be instructed to restart the application afterwards)
                    manager.LaunchUpdater(result.LastVersion, false);

                    // Terminate the running application so that the updater can overwrite files
                    //Environment.Exit(0);
                }
            }
        }

As you can see I don't want it to be closed independently ... I could lose a lot of user data.
If I set LaunchUpdater with restart = true, however, the application restarts even if the user is closed (let's say to go home :))
If the amount is false, I no longer have the possibility to restart it.
The behavior I would like is the following:
1-The new version is downloaded and installed
2-On gui I have a key that is activated with the words "a new version is present: Restart"
3-If the user clicks on the button the app restarts
4-if the user closes the app the new version is still installed but the app is not reopened

Currently I don't think I can get similar behavior.

Version 2.6.8 does not work

Version

2.6.8

Details

I was using Onova 2.6.7 with no issues, but after upgrading to 2.6.8 I am experiencing a problem.

The issue is:

I can see through Windows Task Manager that {appname}.Update.exe launched, indicating the update started.

However, {appname}.Update.exe disappeared quickly, showing the update ended.

But Onova did not automatically restart, and the version also did not update.

When I manually double click to launch Onova, the version number is still the old one, the update was not successful.

I hope you can investigate this issue and find out possible reasons. Currently it seems like the update process for 2.6.8 has some problems.

Thank you for providing the great Onova app, I hope this bug can be fixed to let the update work smoothly again.

Steps to reproduce

  1. I packaged App.exe, with AssemblyVersion = 1.0.0.0, into App-v1.0.zip
  2. I packaged App.exe, with AssemblyVersion = 2.0.0.0, into App-v2.0.zip
  3. I launched App.exe of v1.0 and it detected an update, prompting me to update
  4. I closed the program, and through Windows Task Manager I saw the App.Update.exe process
  5. The App.Update.exe process ended, but it did not restart App.exe
  6. When I double clicked App.exe again it again prompted me of an update, and checking the exe's AssemblyVersion it was still 1.0.0.0

The issue here seems to be that although the updater process (App.Update.exe) runs, it is not actually performing the update and replacing the older App.exe with the new version. So when the updater process ends, the old App.exe is still there and runs as normal.

Updater reuses the same hidden console window with the updatee when it restarts

I am using Onova with the WebPackageResolver with the ZipPackageExtractor.
Everything works fine, until the updater restarts my console application.
The process successfully starts, but the console window is not visible.

I am publishing the app as .net core 3.1 standalone package (win-64)

Is there something i can do?

Explicit Specification of Update Directory/Path

Proposal

Allow for explicit definition of where content is to be extracted by the Onova updater.

The specific purpose of the proposal is to extend Onova to be able to update more than just the currently running application itself.

Potential Use Case

  • Application consists of multiple sub-applications/components integrating with each other.
    • Each sub-application/component may be maintained and updated independently e.g. in a different repository.
    • Initial setup may bundle multiple of these applications/components together.
    • Integrating Onova in each of these applications is technically a possibility but would make no logic/sense if, for example, the individual component is a plugin as opposed to a full separate application.

Existing API

public class UpdateManager : IUpdateManager
{
    public void LaunchUpdater(Version version, bool restart = true)
}

Proposed API

public class UpdateManager : IUpdateManager
{
    public void LaunchUpdater(Version version, bool restart = true) // Calls other overload with directory = null
    public void LaunchUpdater(Version version, string directory, bool restart = false)
}

If directory is null, Onova.Updater should be ran with existing default exe path.

Additional Changes Required

It may be necessary to make a simple change to Onova.Updater to allow for the specification of a file to launch (restart) on completion of the update explicitly rather than assuming folder from a file path.

Get the list of prepared updates

Sometimes a prepared update may fail to install, for any reason that could cause LaunchUpdaterAsync to not execute. It's nice to be able to install such updates next time the application runs -- having it already prepared means it shouldn't take long.

Add WebPackageResolver that uses a predefined URL to query package list

Initialized with a URL, which is queried, expecting a plain-text response like this one:

1.0 https://some.website.org/files/package-1.0.zip
1.1 https://some.website.org/files/package-1.1.zip
2.0 https://other.resource.com/files/package-2.0.zip

This allows developers to use their own web server for providing updates.

Restart with Avalonia

Automatic restarting does not work with Avalonia Applications.
It is probably because I don't have a .exe file.
In order to start it again it would need to do
dotnet MyApp.dll
It would be nice to have an option to support this.

Onova.Updater doesn't run on fresh installation of Windows 7

Version

v2.6.2

Details

Because Onova.Updater targets net46 instead of net35, it will not run on clean installation of Windows 7. We can do the same thing as with DotnetRuntimeBootstrapper which allows the executable to run on net35 or roll forward to net4x on newer operating systems (where net35 is not installed by default anymore):

https://github.com/Tyrrrz/DotnetRuntimeBootstrapper/blob/841ca5df0a31ee50725a7ffcfe3d09bfaf16a1d9/DotnetRuntimeBootstrapper.Executable/App.config#L1-L11

Steps to reproduce

  • Install Windows 7 SP1 (without any updates)
  • Run an application that uses Onova
  • Find an update to install, get it to run Onova.Updater
  • Observe that the update wasn't installed because Onova.Updater failed to run

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.