automated framework update enhancement
This commit is contained in:
		@ -94,8 +94,9 @@ else
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void Upgrade()
 | 
			
		||||
    private async Task Upgrade()
 | 
			
		||||
    {
 | 
			
		||||
        await InstallationService.Upgrade();
 | 
			
		||||
        NavigationManager.NavigateTo(NavigateUrl(Reload.Application));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -279,7 +279,7 @@ namespace Oqtane.Controllers
 | 
			
		||||
        public GenericResponse Upgrade()
 | 
			
		||||
        {
 | 
			
		||||
            var response = new GenericResponse { Success = true, Message = "" };
 | 
			
		||||
            InstallationManager.InstallPackages("Framework", true);
 | 
			
		||||
            InstallationManager.UpgradeFramework();
 | 
			
		||||
            return response;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
    public interface IInstallationManager
 | 
			
		||||
    {
 | 
			
		||||
        void InstallPackages(string Folders, bool Restart);
 | 
			
		||||
        void UpgradeFramework();
 | 
			
		||||
        void RestartApplication();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using Oqtane.Shared;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Oqtane.Infrastructure
 | 
			
		||||
{
 | 
			
		||||
@ -110,6 +111,98 @@ namespace Oqtane.Infrastructure
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void UpgradeFramework()
 | 
			
		||||
        {
 | 
			
		||||
            string folder = Path.Combine(environment.WebRootPath, "Framework");
 | 
			
		||||
            if (Directory.Exists(folder))
 | 
			
		||||
            {
 | 
			
		||||
                string upgradepackage = "";
 | 
			
		||||
 | 
			
		||||
                // iterate through packages
 | 
			
		||||
                foreach (string packagename in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg"))
 | 
			
		||||
                {
 | 
			
		||||
                    using (ZipArchive archive = ZipFile.OpenRead(packagename))
 | 
			
		||||
                    {
 | 
			
		||||
                        string frameworkversion = "";
 | 
			
		||||
                        // locate nuspec
 | 
			
		||||
                        foreach (ZipArchiveEntry entry in archive.Entries)
 | 
			
		||||
                        {
 | 
			
		||||
                            if (entry.FullName.ToLower().EndsWith(".nuspec"))
 | 
			
		||||
                            {
 | 
			
		||||
                                // open nuspec
 | 
			
		||||
                                XmlTextReader reader = new XmlTextReader(entry.Open());
 | 
			
		||||
                                reader.Namespaces = false; // remove namespace
 | 
			
		||||
                                XmlDocument doc = new XmlDocument();
 | 
			
		||||
                                doc.Load(reader);
 | 
			
		||||
                                // get framework version
 | 
			
		||||
                                XmlNode node = doc.SelectSingleNode("/package/metadata/version");
 | 
			
		||||
                                if (node != null)
 | 
			
		||||
                                {
 | 
			
		||||
                                    frameworkversion = node.InnerText;
 | 
			
		||||
                                }
 | 
			
		||||
                                reader.Close();
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // ensure package version is higher than current framework
 | 
			
		||||
                        if (frameworkversion != "" && Version.Parse(Constants.Version).CompareTo(Version.Parse(frameworkversion)) < 0)
 | 
			
		||||
                        {
 | 
			
		||||
                            upgradepackage = packagename;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            File.Delete(packagename);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (upgradepackage != "")
 | 
			
		||||
                {
 | 
			
		||||
                    FinishUpgrade(upgradepackage);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void FinishUpgrade(string packagename)
 | 
			
		||||
        {
 | 
			
		||||
            string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
 | 
			
		||||
 | 
			
		||||
            // check if upgrade application exists
 | 
			
		||||
            if (File.Exists(Path.Combine(folder, "Oqtane.Upgrade.exe")))
 | 
			
		||||
            {
 | 
			
		||||
                // unzip package
 | 
			
		||||
                using (ZipArchive archive = ZipFile.OpenRead(packagename))
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (ZipArchiveEntry entry in archive.Entries)
 | 
			
		||||
                    {
 | 
			
		||||
                        string filename = Path.GetFileName(entry.FullName);
 | 
			
		||||
                        if (Path.GetExtension(filename) == ".dll")
 | 
			
		||||
                        {
 | 
			
		||||
                            entry.ExtractToFile(Path.Combine(Path.Combine(environment.WebRootPath, "Framework"), filename), true);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // delete package
 | 
			
		||||
                File.Delete(packagename);
 | 
			
		||||
 | 
			
		||||
                // run upgrade application
 | 
			
		||||
                var process = new Process();
 | 
			
		||||
                process.StartInfo.FileName = Path.Combine(folder, "Oqtane.Upgrade.exe");
 | 
			
		||||
                process.StartInfo.Arguments = "";
 | 
			
		||||
                process.StartInfo.ErrorDialog = false;
 | 
			
		||||
                process.StartInfo.UseShellExecute = false;
 | 
			
		||||
                process.StartInfo.CreateNoWindow = true;
 | 
			
		||||
                process.StartInfo.RedirectStandardOutput = false;
 | 
			
		||||
                process.StartInfo.RedirectStandardError = false;
 | 
			
		||||
                process.Start();
 | 
			
		||||
                process.Dispose();
 | 
			
		||||
 | 
			
		||||
                // stop application so upgrade application can proceed
 | 
			
		||||
                RestartApplication();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void RestartApplication()
 | 
			
		||||
        {
 | 
			
		||||
            HostApplicationLifetime.StopApplication();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								Oqtane.Server/app_offline.bak
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Oqtane.Server/app_offline.bak
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="utf-8" />
 | 
			
		||||
    <meta name="viewport" content="width=device-width">
 | 
			
		||||
    <title>Oqtane</title>
 | 
			
		||||
    <base href="/" />
 | 
			
		||||
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <div>
 | 
			
		||||
	<br /><br /><h1 align="center">Please Wait... Upgrade In Progress....</h1>
 | 
			
		||||
        <img src="https://www.oqtane.org/Portals/0/oqtane.png" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);" />
 | 
			
		||||
    </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										15
									
								
								Oqtane.Upgrade/Oqtane.Upgrade.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Oqtane.Upgrade/Oqtane.Upgrade.csproj
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <OutputType>Exe</OutputType>
 | 
			
		||||
    <TargetFramework>netcoreapp3.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
 | 
			
		||||
    <OutputPath>..\Oqtane.Server\bin\Release\</OutputPath>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
 | 
			
		||||
    <OutputPath>..\Oqtane.Server\bin\Debug\</OutputPath>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										129
									
								
								Oqtane.Upgrade/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								Oqtane.Upgrade/Program.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,129 @@
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
namespace Oqtane.Upgrade
 | 
			
		||||
{
 | 
			
		||||
    class Program
 | 
			
		||||
    {
 | 
			
		||||
        static void Main(string[] args)
 | 
			
		||||
        {
 | 
			
		||||
            string binfolder = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
 | 
			
		||||
            // assumes that the application executable must be deployed to the /bin of the Oqtane.Server project
 | 
			
		||||
            if (binfolder.Contains("Oqtane.Server\\bin"))
 | 
			
		||||
            {
 | 
			
		||||
                // ie. Oqtane.Server\bin\Debug\netcoreapp3.0\Oqtane.Upgrade.exe
 | 
			
		||||
                string rootfolder = Directory.GetParent(binfolder).Parent.Parent.FullName;
 | 
			
		||||
                string deployfolder = Path.Combine(rootfolder, "wwwroot\\Framework");
 | 
			
		||||
 | 
			
		||||
                // take the app offline
 | 
			
		||||
                if (File.Exists(Path.Combine(rootfolder, "app_offline.bak")))
 | 
			
		||||
                {
 | 
			
		||||
                    File.Move(Path.Combine(rootfolder, "app_offline.bak"), Path.Combine(rootfolder, "app_offline.htm"));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (Directory.Exists(deployfolder))
 | 
			
		||||
                {
 | 
			
		||||
                    string filename;
 | 
			
		||||
                    string[] files = Directory.GetFiles(deployfolder);
 | 
			
		||||
                    if (CanAccessFiles(files, binfolder))
 | 
			
		||||
                    {
 | 
			
		||||
                        // backup the files 
 | 
			
		||||
                        foreach (string file in files)
 | 
			
		||||
                        {
 | 
			
		||||
                            filename = Path.Combine(binfolder, Path.GetFileName(file));
 | 
			
		||||
                            if (File.Exists(filename))
 | 
			
		||||
                            {
 | 
			
		||||
                                File.Move(filename, filename.Replace(".dll", ".bak"));
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // copy the new files
 | 
			
		||||
                        bool success = true;
 | 
			
		||||
                        try
 | 
			
		||||
                        {
 | 
			
		||||
                            foreach (string file in files)
 | 
			
		||||
                            {
 | 
			
		||||
                                filename = Path.Combine(binfolder, Path.GetFileName(file));
 | 
			
		||||
                                // delete the file from the /bin if it exists
 | 
			
		||||
                                if (File.Exists(filename))
 | 
			
		||||
                                {
 | 
			
		||||
                                    File.Delete(filename);
 | 
			
		||||
                                }
 | 
			
		||||
                                // copy the new file to the /bin
 | 
			
		||||
                                File.Move(Path.Combine(deployfolder, Path.GetFileName(file)), filename);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        catch
 | 
			
		||||
                        {
 | 
			
		||||
                            // an error occurred deleting or moving a file
 | 
			
		||||
                            success = false;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // restore on failure
 | 
			
		||||
                        if (!success)
 | 
			
		||||
                        {
 | 
			
		||||
                            foreach (string file in files)
 | 
			
		||||
                            {
 | 
			
		||||
                                filename = Path.Combine(binfolder, Path.GetFileName(file));
 | 
			
		||||
                                if (File.Exists(filename))
 | 
			
		||||
                                {
 | 
			
		||||
                                    File.Move(filename, filename.Replace(".bak", ".dll"));
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // bring the app back online
 | 
			
		||||
                if (File.Exists(Path.Combine(rootfolder, "app_offline.htm")))
 | 
			
		||||
                {
 | 
			
		||||
                    File.Move(Path.Combine(rootfolder, "app_offline.htm"), Path.Combine(rootfolder, "app_offline.bak"));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static bool CanAccessFiles(string[] files, string folder)
 | 
			
		||||
        {
 | 
			
		||||
            // ensure files are not locked by another process - the shutdownTimeLimit defines the duration for app shutdown
 | 
			
		||||
            bool canaccess = true;
 | 
			
		||||
            FileStream stream = null;
 | 
			
		||||
            int i = 0;
 | 
			
		||||
            while (i < (files.Length - 1) && canaccess)
 | 
			
		||||
            {
 | 
			
		||||
                string filepath = Path.Combine(folder, Path.GetFileName(files[i]));
 | 
			
		||||
                int attempts = 0;
 | 
			
		||||
                bool locked = true;
 | 
			
		||||
                // try up to 30 times
 | 
			
		||||
                while (attempts < 30 && locked == true)
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        stream = System.IO.File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.None);
 | 
			
		||||
                        locked = false;
 | 
			
		||||
                    }
 | 
			
		||||
                    catch // file is locked by another process
 | 
			
		||||
                    {
 | 
			
		||||
                        Thread.Sleep(1000); // wait 1 second
 | 
			
		||||
                    }
 | 
			
		||||
                    finally
 | 
			
		||||
                    {
 | 
			
		||||
                        if (stream != null)
 | 
			
		||||
                        {
 | 
			
		||||
                            stream.Close();
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    attempts += 1;
 | 
			
		||||
                }
 | 
			
		||||
                if (locked && canaccess)
 | 
			
		||||
                {
 | 
			
		||||
                    canaccess = false;
 | 
			
		||||
                }
 | 
			
		||||
                i += 1;
 | 
			
		||||
            }
 | 
			
		||||
            return canaccess;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Client", "Oqtane.Cli
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Shared", "Oqtane.Shared\Oqtane.Shared.csproj", "{19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Oqtane.Upgrade", "Oqtane.Upgrade\Oqtane.Upgrade.csproj", "{2E8C6889-37CF-4C8D-88B1-505547F25098}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
@ -34,6 +36,12 @@ Global
 | 
			
		||||
		{19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Wasm|Any CPU.ActiveCfg = Wasm|Any CPU
 | 
			
		||||
		{19D67A9D-3F2E-41BD-80E6-0B50CA83C3AE}.Wasm|Any CPU.Build.0 = Wasm|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Wasm|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{2E8C6889-37CF-4C8D-88B1-505547F25098}.Wasm|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user