Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update before Launch??? #1530

Open
samer1977 opened this issue Aug 15, 2019 · 16 comments
Open

Update before Launch??? #1530

samer1977 opened this issue Aug 15, 2019 · 16 comments

Comments

@samer1977
Copy link

1.9.1
Which version(s) of the project are you using?

Description

Can someone provide an example -if possible with squirrel- on how to get the update before launching the application? This seems to be very simple question but I could not find anything to help with that...documentation is very poor. Thanks

Steps to recreate

  1. Replace this
  2. text with
  3. the steps
  4. to recreate

Expected behavior
Explain what it's doing and why it's wrong

Actual behavior
Explain what it should be doing after it's fixed

Additional information
Add any other context about the problem here

@uygary
Copy link

uygary commented Aug 15, 2019

Let me respond by saying: "I'm not sure if this is a bug or a feature request. Also the steps to recreate this issue are quite confusing."

And then explain that response a bit further:

  • For the love of Odin folks, stop opening "issues" with the goal of asking simple questions. This is an issue tracker, not a forum.
  • Please, oh please, stop duplicating punctuation marks. There is no sustainable form of human communication that relies on three question marks.
  • All the necessary information is right there in the code. Just check the UpdateManager class. I swear to you, the answer to your question is right there in the code itself.

@samer1977
Copy link
Author

If you are talking about the RestartApp method, I already have tried that but what happens is when I lunch the app it goes into an infinite loop of updating and restarting the app. , so I tried to put a guard with the CheckForUpdate method - UpdateInfo.ReleasesToApply.Count>0 - and that did not work as well. My apologies if this is the wrong place to ask such question, I did not where else I can ask such question, if you know please let me know and I Will be happy to delete it from here and post it to the new location. Thanks

@uygary
Copy link

uygary commented Aug 15, 2019

First of all, apologies for snapping at you. You obviously have a valid question, but since the "issue" didn't contain any information about the context, it pretty much seemed like an open-ended question that was asked without putting much effort in searching for the answer.
It's now obvious that that's not the case.

Regarding your specific case, I believe you were heading towards the right direction with the CheckForUpdate approach.

If you want to ensure that the application will be updated before making the actual UI available to the user, you should build some sort of a launcher class that would be instantiated before the actual application class is instantiated.
Then you can instantiate UpdateManager within the launcher, run CheckForUpdate, check ReleasesToApply, and if there is a new release, UpdateApp.
Once that's done, you should be able to invoke (if my memory isn't failing me) RestartApp on UpdateManager in order to complete the update and restart the new version of the application.

And on the happy path, when ReleasesToApply doesn't contain any updates, you can quit the launcher instance and instead let the main loop of the application run.

At least, off the top of my head, these should be more or less what needs to be done. I hope I'm not misleading you.
If you could post some code to reproduce the issue, I'm sure someone with more experience with the library than myself can be of more help.

@samer1977
Copy link
Author

No worries. Trust me I have been banging my head against this the whole day so you can imagine the frustration. What makes it worse is when I tried to follow examples used on demo videos like "https://channel9.msdn.com/Events/dotnetConf/2015/Squirrel-for-Windows-installing-NET-apps-the-way-it-should-be" (Minute 40) or blog like "https://madstt.dk/squirrel-the-basic/" that mention the restartApp it doesnt seem to work and the application goes into the infinite loop. I will try to follow what you explained even though it doesnt seem simple. If you find it in yourself to provide a code for that I would really appreciate it and I believe it would be great help for others who want to avoid clickonce. My application is called in batch command and it has to update before lunch. Thanks for the time and the help.

@anaisbetts
Copy link
Contributor

anaisbetts commented Aug 15, 2019

Squirrel is a library, not a wrapper around your application like ClickOnce, there is no way to update the app before your app runs. I think @uygary has the right idea around making your app check for updates and relaunching itself.

@samer1977
Copy link
Author

Thanks anaisbett for your post. There are some post out there that makes it look as if its possible to accomplish through the RestartApp method as I referenced in my previous post. Not sure if I'm missing something or those posts are misleading. I think it would be worth if someone can provide code on how to do this because I would believe that most people who want to switch from clickonce to squirrel would expect that the app would update itself before launching or at least this option is available. I have been struggling to do so wither within the app itself or through a wrapper. Thanks

@anaisbetts
Copy link
Contributor

anaisbetts commented Aug 15, 2019

I actually don't want to encourage people to do that, it's pretty user-hostile and against the fundamental design of Squirrel, which is to never have updates take precedence over what the user actually wants to do with their computer.

If RestartApp isn't working you should try to debug into what's going on, or possibly just not use it and restart yourself via running the stub executable (the executable installed above the app-xyz directory)

@samer1977
Copy link
Author

That is really unfortunate! My application runs on auto mode via the taskscheduler and there is no user interaction when that happens.

@uygary
Copy link

uygary commented Aug 16, 2019

@anaisbetts's comment regarding this being against the design philosophy of the library is obviously valid. But I still believe Squirrel is simply better than so many alternatives even in this particular use-case that it's worth the effort of handling the update flow in the application code and using Squirrel for apps that inherently go against the design of the library.
That doesn't mean I'd like to see Squirrel bake this use-case into itself, I can clearly see the reasons for this design, and I agree that the library itself should be kept as simple and bare-bones as possible as well.

That being said, what @samer1977 has been asking about is something I had done in the past in an old pet project of mine. The app was a networked app, and it would use semantic versioning. If the major version was bumped, that would mean a protocol/contract change, so the launcher would force an update/restart cycle. If the minor version was bumped, the launcher would instead update but not force a restart.

However, it's been years, so many changes might have happened in the interim. I'll see if I can wire up a simple example for @samer1977 when I have a bit of time.

@anaisbetts
Copy link
Contributor

@samer1977 how are you creating a Scheduled Task without UAC elevation?

@jsobell
Copy link

jsobell commented Aug 19, 2019

@anaisbetts, what @samer1977 is asking for is a very valid and not-unusual requirement.
Sometimes auto-updating is required to keep a paired server and client system in sync, so there is no point providing the user with any client access if the API has a breaking change requiring an update.
I'm keen to see @uygary's suggested solution too.

@samer1977
Copy link
Author

@anaisbetts we use an ETL server that is logged in with service account and we schedule/run the task using this account.
@uygary I appreciate you trying to create a code sample for this. I spend more time trying to get this to work but with no success. It seems to have worked at some point because there are some blogs talk about this but when I follow the examples I dont get the same result so Im not sure if things have changed in latest release. Here is the latest code I have implemented on simple winform app. Debgging this through visual studio crashes, and when I deploy it and run it after releasing new update it hangs.
public Form1()
{
try
{

            InitializeComponent();

            label1.Text = "Version: " + typeof(Form1).Assembly.GetName().Version;

            CheckAndApplyUpdates().Start();


            
        }
        catch (Exception ex)
        {

        }
    }

    private async Task CheckAndApplyUpdates()
    {
        try
        {
            
            bool shouldRestart = false;
            using (var mgr = new UpdateManager(@"C:\Users\samer.alisaleh\source\repos\WindowsFormsApp2\Releases"))
            {
                var updateInfo = await mgr.CheckForUpdate();

                var currentVersion = "";
                if (updateInfo.CurrentlyInstalledVersion == null)
                    currentVersion = "1.0.0.0";
                else
                    currentVersion = updateInfo.CurrentlyInstalledVersion.Version.Version.ToString();

                listBox1.Items.Add("Current Installed Version from UpdateInf: " + currentVersion);
                listBox1.Items.Add("New Version from from UpdateInf: " + updateInfo.FutureReleaseEntry.Version.Version.ToString());

                if (currentVersion != updateInfo.FutureReleaseEntry.Version.Version.ToString())
                {
                    shouldRestart = true;
                    mgr.DownloadReleases(updateInfo.ReleasesToApply).Wait();
                    mgr.ApplyReleases(updateInfo).Wait();
                    mgr.CreateUninstallerRegistryEntry().Wait();
                }
            }

            if (shouldRestart)
            {
                UpdateManager.RestartApp();
            }
        }
        catch (Exception e)
        {
            listBox1.Items.Add("Exception Occure while updating to current revision: " + e.Message);
        }
    }

@uygary
Copy link

uygary commented Aug 21, 2019

@samer1977 I had a bit of time to have a quick look and mock something up last night. This example should be good enough to give you an idea.
Check it out and let me know if it makes sense for your use case.

The main logic is on line 44 in UpdateService.cs and on line 51 in App.xaml.cs.

@samer1977
Copy link
Author

@uygary Thanks for taking the time to do the application. I have downloaded the code and ran it locally and created the squirrel nuget package for app to install. First time the app installed successfully and launched the current version ( 1.0.0.0). I have pumped the version to 1.0.1.0 and when I created the new release under the local releases folder, I went and launched the application but still showed previous verison (1.0.0.0) only when I close and reopen it launches the new version. I tried it again and pumped to (2.0.0.0) this time but when I launch the app from the short cut it gets stuck on "Checking for Updates..." so I closed the app and re launch again and it was showing the latest version. So is it suppose to work this way?! because I thought it should restart the app and relaunch to latest version by itself, unless Im doing something wrong. Thanks again for the time.

@uygary
Copy link

uygary commented Aug 23, 2019

@samer1977 definitely not. The RestartApp call is supposed to restart the application on the bump to v2, so I'm at a bit of a loss here.

In fact, I've just tried this code again believing that it did not work at all and I've just failed to test it properly, but I can say it is actually working for me. Can you try debugging UpdateApp and see what's going on in there?

@weweilep
Copy link

weweilep commented Nov 8, 2019

@samer1977 here is the code (loosely modified) I use for my application where the client must be running the latest communication contract:

private async Task CheckForUpdates()
{
	var installerId = "MyUpgradableClient";
	var exeName = installerId + ".exe";
	var websiteReleasePath = "https://myupgradableclient.com/myupgradableclient";
	
	var tryCount = 1;

	while (tryCount <= 3)
	{
		// Optional ClickOnceMigration, remove if not needed.
		try
		{
			// Remove any previous ClickOnce installed versions.
			var migrator = new InSquirrelAppMigrator(installerId);
			await migrator.Execute();
		}
		catch
		{
			// ignored
		}

		var restart = false;
		var latestExecutable = string.Empty;

		// Wrap the update process in a try/catch in case the website is inaccessible.
		try
		{
			// Look for updates.
			using (var updateManager = new UpdateManager(websiteReleasePath, installerId))
			{
				var updates = await updateManager.CheckForUpdate();

				if (updates.ReleasesToApply.Count > 0)
				{
					// TODO: Change client to show update in progress here.

					restart = true;
					await updateManager.DownloadReleases(updates.ReleasesToApply);
					latestExecutable = $"\"{Path.Combine(await updateManager.ApplyReleases(updates), exeName)}\"";
				}
			}
		}
		catch (WebException webException) when (webException.Message.Contains("Unable to connect to the remote server"))
		{
			// If we can't connect to the web server, don't display any messages.
			return;
		}
		catch
		{
			tryCount++;

			// Continue so we don't catch the restart/success logic.
			continue;
		}

		// Restart the app with the new version if there was an update.
		if (restart)
		{
			UpdateManager.RestartApp(latestExecutable);
		}

		// If we made it here without throwing, we're done.
		return;
	}

	// TODO: If checking for an update fails, give them full download instructions via a dialog that will hold until closed, then close this application.

	Close();
}

Edit: Note, this will not update before launch, but it will give you a chance to put the launching application into an updating state, and re-launch with the updated executable when complete.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants