From ef3a62bf7217fce45fda1795d36fffcd4f8eb863 Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Sun, 17 Aug 2014 15:45:44 +0200 Subject: [PATCH] Changed the default for auto-update to be notify instead of install. Added the --auto-update option to the commandline to allow automatic updates. Added a new Duplicati.Library.AutoUpdater.exe tool to work with updates, especially handy if the update breaks the install and for manual handling of updates. --- .../CommandLine/Duplicati.CommandLine.csproj | 4 + Duplicati/CommandLine/Program.cs | 32 ++++- .../Library/AutoUpdater/AutoUpdateSettings.cs | 3 +- .../Duplicati.Library.AutoUpdater.csproj | 4 +- Duplicati/Library/AutoUpdater/Program.cs | 123 ++++++++++++++++++ .../Library/AutoUpdater/UpdaterManager.cs | 18 ++- Duplicati/Server/Duplicati.Server.csproj | 8 +- .../Duplicati.Service.csproj | 4 + 8 files changed, 181 insertions(+), 15 deletions(-) create mode 100644 Duplicati/Library/AutoUpdater/Program.cs diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj index d975d891b..09615be3d 100644 --- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj +++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj @@ -157,6 +157,10 @@ {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} Duplicati.Library.Backend.OneDrive + + {B68F2214-951F-4F78-8488-66E1ED3F50BF} + Duplicati.Library.Localization + diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/Program.cs index fe66826fb..68a42aad4 100644 --- a/Duplicati/CommandLine/Program.cs +++ b/Duplicati/CommandLine/Program.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Text; using System.Linq; +using Duplicati.Library.Localization.Short; namespace Duplicati.CommandLine { @@ -230,10 +231,36 @@ namespace Duplicati.CommandLine Console.WriteLine(string.Format("Failed to delete temp file: {0}", path)); }); - + var autoupdate = Library.Utility.Utility.ParseBoolOption(options, "auto-update"); + options.Remove("auto-update"); + if (knownCommands.ContainsKey(command)) { - return knownCommands[command](cargs, options, filter); + var res = knownCommands[command](cargs, options, filter); + + if (autoupdate) + { + var update = Library.AutoUpdater.UpdaterManager.LastUpdateCheckVersion; + if (update == null) + update = Library.AutoUpdater.UpdaterManager.CheckForUpdate(); + + if (update != null && update.Version != Library.AutoUpdater.UpdaterManager.SelfVersion.Version) + { + Console.WriteLine("Found update \"{0}\", downloading ...", update.Displayname); + long lastpg = 0; + Library.AutoUpdater.UpdaterManager.DownloadAndUnpackUpdate(update, f => { + var npg = (long)(f*100); + if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100)) + { + lastpg = npg; + Console.WriteLine("Downloading {0}% ...", npg); + } + }); + Console.WriteLine("Update \"{0}\" ({1}) installed, using on next launch", update.Displayname, update.Version); + } + } + + return res; } else { @@ -281,6 +308,7 @@ namespace Duplicati.CommandLine new Library.Interface.CommandLineArgument("exclude", Library.Interface.CommandLineArgument.ArgumentType.String, Strings.Program.ExcludeShort, Strings.Program.ExcludeLong), new Library.Interface.CommandLineArgument("control-files", Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.ControlFilesOptionShort, Strings.Program.ControlFilesOptionLong, "false"), new Library.Interface.CommandLineArgument("quiet-console", Library.Interface.CommandLineArgument.ArgumentType.Boolean, Strings.Program.QuietConsoleOptionShort, Strings.Program.QuietConsoleOptionLong, "false"), + new Library.Interface.CommandLineArgument("auto-update", Library.Interface.CommandLineArgument.ArgumentType.Boolean, LC.L("Toggle automatic updates"), LC.L("Set this option if you prefer to have the commandline version automatically update"), "false"), }); } } diff --git a/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs b/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs index da4d4c202..19f05f95e 100644 --- a/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs +++ b/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs @@ -29,8 +29,7 @@ namespace Duplicati.Library.AutoUpdater private const string UPDATE_README = "AutoUpdateFolderReadme.txt"; private const string UPDATE_INSTALL_FILE = "AutoUpdateInstallIDTemplate.txt"; - private const string UPDATEURL_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_URLS"; - + internal const string UPDATEURL_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_URLS"; static AutoUpdateSettings() { diff --git a/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj b/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj index 120717548..04c6ff09b 100644 --- a/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj +++ b/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj @@ -6,11 +6,12 @@ 8.0.30703 2.0 {7E119745-1F62-43F0-936C-F312A1912C0B} - Library + Exe Duplicati.Library.AutoUpdater Duplicati.Library.AutoUpdater true ..\..\GUI\Duplicati.GUI.TrayIcon\Duplicati.snk + Duplicati.Library.AutoUpdater.Program true @@ -43,6 +44,7 @@ + diff --git a/Duplicati/Library/AutoUpdater/Program.cs b/Duplicati/Library/AutoUpdater/Program.cs new file mode 100644 index 000000000..91101c48d --- /dev/null +++ b/Duplicati/Library/AutoUpdater/Program.cs @@ -0,0 +1,123 @@ +// Copyright (C) 2014, Kenneth Skovhede + +// http://www.hexad.dk, opensource@hexad.dk +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +using System; +using System.Linq; +using System.Collections.Generic; + +namespace Duplicati.Library.AutoUpdater +{ + public static class Program + { + public static int Main(string[] args) + { + return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args, AutoUpdateStrategy.Never); + } + + public static int RealMain(string[] _args) + { + var args = new List(_args); + Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args); + + if (args.Count == 0) + { + WriteUsage(); + return 100; + } + else if (args.Count != 1) + { + Console.WriteLine("Invalid number of arguments, got {0}:{1}{1}{2}{1}{1}", args.Count, Environment.NewLine, string.Join(Environment.NewLine, args)); + return 100; + } + + var cmd = args[0].ToLowerInvariant().Trim(); + switch (cmd) + { + case "help": + WriteUsage(); + return 0; + case "list": + { + var versions = UpdaterManager.FindInstalledVersions(); + var selfdir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + if (string.Equals(Library.Utility.Utility.AppendDirSeparator(selfdir), Library.Utility.Utility.AppendDirSeparator(UpdaterManager.InstalledBaseDir))) + versions = versions.Union(new KeyValuePair[] { new KeyValuePair(selfdir, UpdaterManager.SelfVersion) }); + Console.WriteLine(string.Join(Environment.NewLine, versions.Select(x => string.Format(" {0} {1} ({2})", (x.Value.Version == UpdaterManager.SelfVersion.Version ? "*" : "-"), x.Value.Displayname, x.Value.Version)))); + return 0; + } + case "check": + { + var update = UpdaterManager.CheckForUpdate(); + if (update == null) + Console.WriteLine("No updates found"); + else if (update.Version == UpdaterManager.SelfVersion.Version) + Console.WriteLine("You are running the latest version: {0}", update.Displayname); + else + Console.WriteLine("New version is available: {0}", update.Displayname); + + return 0; + } + case "install": + { + var update = UpdaterManager.CheckForUpdate(); + if (update == null || update.Version == UpdaterManager.SelfVersion.Version) + { + Console.WriteLine("You are running the latest version: {0} ({1})", UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version); + return 0; + } + Console.WriteLine("Downloading update \"{0}\" ...", update.Displayname); + + long lastpg = 0; + UpdaterManager.DownloadAndUnpackUpdate(update, f => { + var npg = (long)(f*100); + if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100)) + { + lastpg = npg; + Console.WriteLine("Downloading {0}% ...", npg); + } + }); + + Console.WriteLine("New version \"{0}\" installed!", update.Displayname); + return 0; + } + default: + Console.WriteLine("Unknown command: \"{0}\"", args[0]); + Console.WriteLine(); + Console.WriteLine("Try the command \"{0}\" instead", "help"); + return 100; + } + } + + private static void WriteUsage() + { + Console.WriteLine("Usage:{0}\t{1}{2} [LIST|CHECK|INSTALL|HELP]", Environment.NewLine, Duplicati.Library.Utility.Utility.IsMono ? "mono " : "", System.IO.Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location)); + Console.WriteLine(); + Console.WriteLine("Environment variables:"); + Console.WriteLine(); + Console.WriteLine("{0} - Disables updates completely", string.Format(UpdaterManager.SKIPUPDATE_ENVNAME_TEMPLATE, AutoUpdateSettings.AppName)); + Console.WriteLine("{0} - Choose how to handle updates, valid settings: {1}", string.Format(UpdaterManager.UPDATE_STRATEGY_ENVNAME_TEMPLATE, AutoUpdateSettings.AppName), string.Join(", ", Enum.GetNames(typeof(AutoUpdateStrategy)))); + Console.WriteLine("{0} - Use alternate updates urls", string.Format(AutoUpdateSettings.UPDATEURL_ENVNAME_TEMPLATE, AutoUpdateSettings.AppName)); + Console.WriteLine(); + Console.WriteLine("Updates are downloaded from: {0}", string.Join(";", AutoUpdateSettings.URLs)); + Console.WriteLine("Updates are installed in: {0}", UpdaterManager.INSTALLDIR); + Console.WriteLine("The base install dir is: {0}", UpdaterManager.InstalledBaseDir); + Console.WriteLine("This version is \"{0}\" ({1}) and is installed in: {2}", UpdaterManager.SelfVersion.Displayname, System.Reflection.Assembly.GetExecutingAssembly().GetName().Version, System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); + Console.WriteLine(); + } + } +} + diff --git a/Duplicati/Library/AutoUpdater/UpdaterManager.cs b/Duplicati/Library/AutoUpdater/UpdaterManager.cs index aed65f43c..d78ed24d8 100644 --- a/Duplicati/Library/AutoUpdater/UpdaterManager.cs +++ b/Duplicati/Library/AutoUpdater/UpdaterManager.cs @@ -38,7 +38,7 @@ namespace Duplicati.Library.AutoUpdater private static readonly string[] MANIFEST_URLS = AutoUpdateSettings.URLs; private static readonly string APPNAME = AutoUpdateSettings.AppName; - private static readonly string INSTALLDIR; + internal static readonly string INSTALLDIR; private static readonly string INSTALLED_BASE_DIR = string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable(string.Format(INSTALLDIR_ENVNAME_TEMPLATE, APPNAME))) ? System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) @@ -50,16 +50,16 @@ namespace Duplicati.Library.AutoUpdater private static KeyValuePair? m_hasUpdateInstalled; - private static readonly UpdateInfo SelfVersion; + public static readonly UpdateInfo SelfVersion; public static event Action OnError; private const string DATETIME_FORMAT = "yyyymmddhhMMss"; private const string INSTALLDIR_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_INSTALL_ROOT"; - private const string SKIPUPDATE_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_SKIP_UPDATE"; + internal const string SKIPUPDATE_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_SKIP_UPDATE"; private const string RUN_UPDATED_FOLDER_PATH = "AUTOUPDATER_LOAD_UPDATE"; private const string SLEEP_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_SLEEP"; - private const string UPDATE_STRATEGY_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_POLICY"; + internal const string UPDATE_STRATEGY_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_POLICY"; private const string UPDATE_MANIFEST_FILENAME = "autoupdate.manifest"; private const string README_FILE = "README.txt"; private const string INSTALL_FILE = "installation.txt"; @@ -71,6 +71,11 @@ namespace Duplicati.Library.AutoUpdater /// The original directory that this application was installed into public static string InstalledBaseDir { get { return INSTALLED_BASE_DIR; } } + /// + /// Gets the last version found from an update + /// + public static UpdateInfo LastUpdateCheckVersion { get; private set; } + static UpdaterManager() { string installdir = null; @@ -197,7 +202,7 @@ namespace Duplicati.Library.AutoUpdater using(var tmpfile = new Library.Utility.TempFile()) { System.Net.WebClient wc = new System.Net.WebClient(); - wc.Headers.Add(System.Net.HttpRequestHeader.UserAgent, string.Format("{0} v{1}", APPNAME, SelfVersion.Version)); + wc.Headers.Add(System.Net.HttpRequestHeader.UserAgent, string.Format("{0} v{1}{2}", APPNAME, SelfVersion.Version, string.IsNullOrWhiteSpace(InstallID) ? "" : " -" + InstallID)); wc.Headers.Add("X-Install-ID", InstallID); wc.DownloadFile(url, tmpfile); @@ -214,6 +219,7 @@ namespace Duplicati.Library.AutoUpdater if (string.Equals(SelfVersion.ReleaseType, "Debug", StringComparison.InvariantCultureIgnoreCase) && !string.Equals(update.ReleaseType, SelfVersion.ReleaseType, StringComparison.CurrentCultureIgnoreCase)) return null; + LastUpdateCheckVersion = update; return update; } } @@ -856,7 +862,7 @@ namespace Duplicati.Library.AutoUpdater } } - public static int RunFromMostRecent(System.Reflection.MethodInfo method, string[] cmdargs, AutoUpdateStrategy defaultstrategy = AutoUpdateStrategy.InstallDuring) + public static int RunFromMostRecent(System.Reflection.MethodInfo method, string[] cmdargs, AutoUpdateStrategy defaultstrategy = AutoUpdateStrategy.CheckDuring) { // If the update is disabled, go straight in if (DISABLE_UPDATE_DOMAIN) diff --git a/Duplicati/Server/Duplicati.Server.csproj b/Duplicati/Server/Duplicati.Server.csproj index a9e8ed29d..4040104a0 100644 --- a/Duplicati/Server/Duplicati.Server.csproj +++ b/Duplicati/Server/Duplicati.Server.csproj @@ -226,10 +226,6 @@ {2C838169-B187-4B09-8768-1C24C2521C8D} Duplicati.Library.SQLiteHelper - - {7E119745-1F62-43F0-936C-F312A1912C0B} - Duplicati.Library.AutoUpdater - {CCD76347-7DC7-4B42-B7E1-E500E624CAC3} Duplicati.Library.Backend.OneDrive @@ -242,6 +238,10 @@ {B68F2214-951F-4F78-8488-66E1ED3F50BF} Duplicati.Library.Localization + + {7E119745-1F62-43F0-936C-F312A1912C0B} + Duplicati.Library.AutoUpdater + diff --git a/Duplicati/Service/Duplicati.Service/Duplicati.Service.csproj b/Duplicati/Service/Duplicati.Service/Duplicati.Service.csproj index 9b0e827d2..c56de1d68 100644 --- a/Duplicati/Service/Duplicati.Service/Duplicati.Service.csproj +++ b/Duplicati/Service/Duplicati.Service/Duplicati.Service.csproj @@ -42,5 +42,9 @@ {19E661D2-C5DA-4F35-B3EE-7586E5734B5F} Duplicati.Server + + {7E119745-1F62-43F0-936C-F312A1912C0B} + Duplicati.Library.AutoUpdater + \ No newline at end of file