From 13583514aebd7d0f06c1debb83795b3db9d13f5b Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 10 Jun 2021 13:06:38 -0600 Subject: [PATCH] Gets embedded package.xml resources able to install from package migration. --- src/Umbraco.Core/Migrations/MigrationPlan.cs | 2 +- .../RuntimeUnattendedUpgradeNotification.cs | 5 -- .../Packaging/CompiledPackageXmlParser.cs | 6 +-- .../Packaging/InstallationSummary.cs | 45 +++++++++++++++- .../Install/UnattendedUpgrader.cs | 39 +++++++++++--- .../Packaging/IImportPackageBuilder.cs | 5 +- .../Packaging/ImportPackageBuilder.cs | 5 +- .../ImportPackageBuilderExpression.cs | 54 ++++++++++--------- .../Packaging/PackageDataInstallation.cs | 1 + .../Packaging/PackageInstallation.cs | 2 +- .../Runtime/CoreRuntime.cs | 24 ++------- .../Document_Type_Picker_1.1.package.xml | 25 ++------- .../TestData/Packages/Hello_1.0.0.package.xml | 25 +++------ .../Umbraco.Core/RuntimeStateTests.cs | 2 +- 14 files changed, 129 insertions(+), 111 deletions(-) diff --git a/src/Umbraco.Core/Migrations/MigrationPlan.cs b/src/Umbraco.Core/Migrations/MigrationPlan.cs index c7ad2da895..48cc50e1c4 100644 --- a/src/Umbraco.Core/Migrations/MigrationPlan.cs +++ b/src/Umbraco.Core/Migrations/MigrationPlan.cs @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.Migrations /// public class MigrationPlan { - private readonly Dictionary _transitions = new Dictionary(); + private readonly Dictionary _transitions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); private readonly List _postMigrationTypes = new List(); private string _prevState; diff --git a/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs b/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs index dd2fc5a77d..50c02cbad5 100644 --- a/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs +++ b/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace Umbraco.Cms.Core.Notifications { /// @@ -17,8 +14,6 @@ namespace Umbraco.Cms.Core.Notifications /// public UpgradeResult UnattendedUpgradeResult { get; set; } = UpgradeResult.NotRequired; - public List UpgradeExceptions { get; } = new List(); - public enum UpgradeResult { NotRequired = 0, diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index d7e2bccf7f..2a4929930e 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -30,11 +30,7 @@ namespace Umbraco.Cms.Core.Packaging if (info == null) throw new FormatException("The xml document is invalid"); var package = info.Element("package"); if (package == null) throw new FormatException("The xml document is invalid"); - var author = info.Element("author"); - if (author == null) throw new FormatException("The xml document is invalid"); - var requirements = package.Element("requirements"); - if (requirements == null) throw new FormatException("The xml document is invalid"); - + var def = new CompiledPackage { // will be null because we don't know where this data is coming from and diff --git a/src/Umbraco.Core/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs index 2c6be59e0f..fd2b60ccee 100644 --- a/src/Umbraco.Core/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/InstallationSummary.cs @@ -12,6 +12,8 @@ namespace Umbraco.Cms.Core.Packaging [DataContract(IsReference = true)] public class InstallationSummary { + public InstallWarnings Warnings { get; set; } = new InstallWarnings(); + public IEnumerable DataTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable LanguagesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable DictionaryItemsInstalled { get; set; } = Enumerable.Empty(); @@ -26,23 +28,64 @@ namespace Umbraco.Cms.Core.Packaging public override string ToString() { var sb = new StringBuilder(); + var macroConflicts = Warnings.ConflictingMacros.ToList(); + if (macroConflicts.Count > 0) + { + sb.Append("Conflicting macros found, they will be overwritten:"); + foreach(IMacro m in macroConflicts) + { + sb.Append(m.Alias); + sb.Append(','); + } + sb.AppendLine(". "); + } + var templateConflicts = Warnings.ConflictingTemplates.ToList(); + if (templateConflicts.Count > 0) + { + sb.Append("Conflicting templates found, they will be overwritten:"); + foreach (IMacro m in templateConflicts) + { + sb.Append(m.Alias); + sb.Append(','); + } + sb.AppendLine(". "); + } + var stylesheetConflicts = Warnings.ConflictingStylesheets.ToList(); + if (stylesheetConflicts.Count > 0) + { + sb.Append("Conflicting stylesheets found, they will be overwritten:"); + foreach (IMacro m in stylesheetConflicts) + { + sb.Append(m.Alias); + sb.Append(','); + } + sb.AppendLine(". "); + } + sb.Append("Content items installed: "); sb.Append(ContentInstalled.Count()); + sb.AppendLine(". "); sb.Append("Media items installed: "); sb.Append(MediaInstalled.Count()); + sb.AppendLine(". "); sb.Append("Dictionary items installed: "); sb.Append(DictionaryItemsInstalled.Count()); + sb.AppendLine(". "); sb.Append("Macros installed: "); sb.Append(MacrosInstalled.Count()); + sb.AppendLine(". "); sb.Append("Stylesheets installed: "); sb.Append(StylesheetsInstalled.Count()); + sb.AppendLine(". "); sb.Append("Templates installed: "); sb.Append(TemplatesInstalled.Count()); - sb.Append("Templates installed: "); + sb.AppendLine(); sb.Append("Document types installed: "); sb.Append(DocumentTypesInstalled.Count()); + sb.AppendLine(". "); sb.Append("Media types installed: "); sb.Append(MediaTypesInstalled.Count()); + sb.AppendLine(". "); sb.Append("Data types items installed: "); sb.Append(DataTypesInstalled.Count()); return sb.ToString(); diff --git a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs index 1bafa92c12..eeae566cc9 100644 --- a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs +++ b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs @@ -16,6 +16,7 @@ using Umbraco.Cms.Infrastructure.Runtime; using Umbraco.Extensions; using Umbraco.Cms.Core.Migrations; using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core; namespace Umbraco.Cms.Infrastructure.Install { @@ -58,7 +59,7 @@ namespace Umbraco.Cms.Infrastructure.Install switch (_runtimeState.Reason) { - case Core.RuntimeLevelReason.UpgradeMigrations: + case RuntimeLevelReason.UpgradeMigrations: { var plan = new UmbracoPlan(_umbracoVersion); using (_profilingLogger.TraceDuration( @@ -77,7 +78,7 @@ namespace Umbraco.Cms.Infrastructure.Install } } break; - case Core.RuntimeLevelReason.UpgradePackageMigrations: + case RuntimeLevelReason.UpgradePackageMigrations: { if (!_runtimeState.StartupState.TryGetValue(RuntimeState.PendingPacakgeMigrationsStateKey, out var pm) || pm is not IReadOnlyList pendingMigrations) @@ -90,6 +91,7 @@ namespace Umbraco.Cms.Infrastructure.Install throw new InvalidOperationException("No pending migrations found but the runtime level reason is " + Core.RuntimeLevelReason.UpgradePackageMigrations); } + var exceptions = new List(); var packageMigrationsPlans = _packageMigrationPlans.ToDictionary(x => x.Name); foreach (var migrationName in pendingMigrations) @@ -108,25 +110,48 @@ namespace Umbraco.Cms.Infrastructure.Install try { upgrader.Execute(_migrationPlanExecutor, _scopeProvider, _keyValueService); - notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete; } catch (Exception ex) { - notification.UpgradeExceptions.Add(new UnattendedInstallException("Unattended package migration failed for " + migrationName, ex)); + exceptions.Add(new UnattendedInstallException("Unattended package migration failed for " + migrationName, ex)); } } } + + if (exceptions.Count > 0) + { + notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors; + SetRuntimeErrors(exceptions); + } + else + { + notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.PackageMigrationComplete; + } } break; default: throw new InvalidOperationException("Invalid reason " + _runtimeState.Reason); } + } + return Task.CompletedTask; + } - - + private void SetRuntimeErrors(List exception) + { + Exception innerException; + if (exception.Count == 1) + { + innerException = exception[0]; + } + else + { + innerException = new AggregateException(exception); } - return Task.CompletedTask; + _runtimeState.Configure( + RuntimeLevel.BootFailed, + RuntimeLevelReason.BootFailedOnException, + innerException); } } } diff --git a/src/Umbraco.Infrastructure/Packaging/IImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/IImportPackageBuilder.cs index 99617d698b..c9657d2ba9 100644 --- a/src/Umbraco.Infrastructure/Packaging/IImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/IImportPackageBuilder.cs @@ -1,10 +1,11 @@ -using Umbraco.Cms.Infrastructure.Migrations.Expressions; +using Umbraco.Cms.Infrastructure.Migrations.Expressions; using Umbraco.Cms.Infrastructure.Migrations.Expressions.Common; namespace Umbraco.Cms.Core.Packaging { public interface IImportPackageBuilder : IFluentBuilder { - IExecutableBuilder FromEmbeddedResource(); + IExecutableBuilder FromEmbeddedResource() + where TPackageMigration : PackageMigrationBase; } } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs index 7cec1df5ec..005a80e861 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs @@ -14,9 +14,10 @@ namespace Umbraco.Cms.Core.Packaging public void Do() => Expression.Execute(); - public IExecutableBuilder FromEmbeddedResource() + public IExecutableBuilder FromEmbeddedResource() + where TPackageMigration : PackageMigrationBase { - Expression.FromEmbeddedResource = true; + Expression.EmbeddedResourceMigrationType = typeof(TPackageMigration); return this; } } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs index e51c617f0a..06bba25876 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs @@ -13,46 +13,48 @@ namespace Umbraco.Cms.Core.Packaging internal class ImportPackageBuilderExpression : MigrationExpressionBase { private readonly IPackagingService _packagingService; + private bool _executed; public ImportPackageBuilderExpression(IPackagingService packagingService, IMigrationContext context) : base(context) => _packagingService = packagingService; - public bool FromEmbeddedResource { get; set; } + /// + /// The type of the migration which dictates the namespace of the embedded resource + /// + public Type EmbeddedResourceMigrationType { get; set; } public override void Execute() { - if (!FromEmbeddedResource) + if (_executed) { - throw new InvalidOperationException($"Nothing to execute, {nameof(FromEmbeddedResource)} has not been called."); + throw new InvalidOperationException("This expression has already been executed."); } - try + _executed = true; + Context.BuildingExpression = false; + + if (EmbeddedResourceMigrationType == null) { - // lookup the embedded resource by convention - Type currentType = GetType(); - Assembly currentAssembly = currentType.Assembly; - var fileName = $"{currentType.Namespace}.package.xml"; - Stream stream = currentAssembly.GetManifestResourceStream(fileName); - if (stream == null) - { - throw new FileNotFoundException("Cannot find the embedded file.", fileName); - } - XDocument xml; - using (stream) - { - xml = XDocument.Load(stream); - } - - InstallationSummary installationSummary = _packagingService.InstallCompiledPackageData(xml); - - Logger.LogInformation($"Package migration executed. Summary: {installationSummary}"); + throw new InvalidOperationException($"Nothing to execute, {nameof(EmbeddedResourceMigrationType)} has not been set."); } - catch (Exception ex) + + // lookup the embedded resource by convention + Assembly currentAssembly = EmbeddedResourceMigrationType.Assembly; + var fileName = $"{EmbeddedResourceMigrationType.Namespace}.package.xml"; + Stream stream = currentAssembly.GetManifestResourceStream(fileName); + if (stream == null) { - Logger.LogError(ex, "Package migration failed."); - - // TODO: We need to exit with a status + throw new FileNotFoundException("Cannot find the embedded file.", fileName); } + XDocument xml; + using (stream) + { + xml = XDocument.Load(stream); + } + + InstallationSummary installationSummary = _packagingService.InstallCompiledPackageData(xml); + + Logger.LogInformation($"Package migration executed. Summary: {installationSummary}"); } } } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index bec92b1fd5..0a56dfa913 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -85,6 +85,7 @@ namespace Umbraco.Cms.Core.Packaging { var installationSummary = new InstallationSummary { + Warnings = compiledPackage.Warnings, DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), LanguagesInstalled = ImportLanguages(compiledPackage.Languages, userId), DictionaryItemsInstalled = ImportDictionaryItems(compiledPackage.DictionaryItems, userId), diff --git a/src/Umbraco.Infrastructure/Packaging/PackageInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageInstallation.cs index 1d65552281..d7394582ce 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageInstallation.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Core.Packaging Name = compiledPackage.Name }; - var installationSummary = _packageDataInstallation.InstallPackageData(compiledPackage, userId); + InstallationSummary installationSummary = _packageDataInstallation.InstallPackageData(compiledPackage, userId); //make sure the definition is up to date with everything foreach (var x in installationSummary.DataTypesInstalled) packageDefinition.DataTypes.Add(x.Id.ToInvariantString()); diff --git a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs index d847ba1e4d..ecdc17f97d 100644 --- a/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Infrastructure/Runtime/CoreRuntime.cs @@ -122,28 +122,12 @@ namespace Umbraco.Cms.Infrastructure.Runtime switch (unattendedUpgradeNotification.UnattendedUpgradeResult) { case RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors: - if (unattendedUpgradeNotification.UpgradeExceptions.Count == 0) + if (State.BootFailedException == null) { - throw new InvalidOperationException("The upgrade result was " + RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors + " but no exceptions have been registered"); + throw new InvalidOperationException($"Unattended upgrade result was {RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors} but no {nameof(BootFailedException)} was registered"); } - BootFailedException bootFailedException; - if (unattendedUpgradeNotification.UpgradeExceptions.Count == 1) - { - bootFailedException = new BootFailedException( - unattendedUpgradeNotification.UpgradeExceptions[0].Message, - unattendedUpgradeNotification.UpgradeExceptions[0]); - } - else - { - bootFailedException = new BootFailedException( - "Several package migrations failed" , - new AggregateException(unattendedUpgradeNotification.UpgradeExceptions)); - } - State.Configure( - RuntimeLevel.BootFailed, - RuntimeLevelReason.BootFailedOnException, - bootFailedException); - break; + // we cannot continue here, the exception will be rethrown by BootFailedMiddelware + return; case RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete: case RuntimeUnattendedUpgradeNotification.UpgradeResult.PackageMigrationComplete: // upgrade is done, set reason to Run diff --git a/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.package.xml b/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.package.xml index 33cb39c948..25cc51dd69 100644 --- a/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.package.xml +++ b/src/Umbraco.Tests.Integration/TestData/Packages/Document_Type_Picker_1.1.package.xml @@ -1,26 +1,9 @@ - - - - 095e064b-ba4d-442d-9006-3050983c13d8.dll/binAuros.DocumentTypePicker.dll + + - Document Type Picker - 1.1 - MIT - http://www.auros.co.uk - - 3 - 0 - 0 - + Document Type Picker - - @tentonipete - auros.co.uk - - - - @@ -31,4 +14,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.package.xml b/src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.package.xml index a84887bdba..e652c33702 100644 --- a/src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.package.xml +++ b/src/Umbraco.Tests.Integration/TestData/Packages/Hello_1.0.0.package.xml @@ -1,24 +1,9 @@ - + - Hello - 1.0.0 - - MIT License - http://hello.com - - 8 - 0 - 0 - - - asdf - http://hello.com - - @@ -85,11 +70,13 @@ @@ -100,4 +87,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs index b764deb1b2..317c0aa474 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs @@ -94,7 +94,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core public override void Migrate() { - ImportPackage.FromEmbeddedResource().Do(); + ImportPackage.FromEmbeddedResource().Do(); } } }