Fixes packager to ensure the GUIDs are used for all entities where possible (#10477)

* Clean up and changes to backoffice for the nuget only packages

* temp commit of package logic removal

* Lots of package code cleanup and removal

* Removes old package data from the test package xml

* Updates packaging code to take in XDocument instead of a file since we'll not be dealing with files, starts creating expressions for the package migrations scripting.

* fixing tests

* Fixes runtime state and boot failed middleware so that it actually runs. Separates out unattended install/upgrade into notification handlers.

* Gets unattended package migrations working and running

* Gets embedded package.xml resources able to install from package migration.

* Implements automatic package migrations for package that just declare an xml data manifest.

* fix build

* small cleanups

* fix build

* adds some tests

* Fix export test

* Fix newlines in test for linux

* Typo

* removes old todos and updates AutomaticPackgeMigrationPlan to use getter with backing field.

* Update dictionary package data to use GUID

* Ensures macros are packaged and used with their GUID

* Ensures the GUID for doc types and media types remains consistent for package installation based on what is in the xml.

* fix automatic migrations to not validate initial state, fixes packaging GUIDs for multiple entities.

* Added guids to embedded test packages (Some tests are still failing)

* Fix one more test

* Fixes up Key vs Id, moves tests to correct namespace, fix tests

* Fixes Dictionary packaging to ensure an xml hierarchy

* Fixes tests

* fixes package xml

Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
Shannon Deminick
2021-06-17 16:15:38 +10:00
committed by GitHub
parent de2fb47b78
commit ca72fb01ec
28 changed files with 955 additions and 703 deletions

View File

@@ -49,7 +49,7 @@ namespace Umbraco.Cms.Core.Packaging
if (templateConflicts.Count > 0)
{
sb.Append("Conflicting templates found, they will be overwritten:");
foreach (IMacro m in templateConflicts)
foreach (ITemplate m in templateConflicts)
{
sb.Append(m.Alias);
sb.Append(',');
@@ -60,7 +60,7 @@ namespace Umbraco.Cms.Core.Packaging
if (stylesheetConflicts.Count > 0)
{
sb.Append("Conflicting stylesheets found, they will be overwritten:");
foreach (IMacro m in stylesheetConflicts)
foreach (IFile m in stylesheetConflicts)
{
sb.Append(m.Alias);
sb.Append(',');

View File

@@ -19,6 +19,12 @@ namespace Umbraco.Cms.Core.Packaging
DefinePlan();
}
/// <summary>
/// Inform the plan executor to ignore all saved package state and
/// run the migration from initial state to it's end state.
/// </summary>
public override bool IgnoreCurrentState => true;
protected abstract void DefinePlan();
}

View File

@@ -71,7 +71,8 @@ namespace Umbraco.Cms.Core.Packaging
string packagesFolderPath = null,
string mediaFolderPath = null)
{
if (string.IsNullOrWhiteSpace(packageRepositoryFileName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName));
if (string.IsNullOrWhiteSpace(packageRepositoryFileName))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName));
_contentService = contentService;
_contentTypeService = contentTypeService;
_dataTypeService = dataTypeService;
@@ -114,7 +115,8 @@ namespace Umbraco.Cms.Core.Packaging
{
var packagesXml = EnsureStorage(out var packagesFile);
var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue<int>("id") == id);
if (packageXml == null) return;
if (packageXml == null)
return;
packageXml.Remove();
@@ -123,7 +125,8 @@ namespace Umbraco.Cms.Core.Packaging
public bool SavePackage(PackageDefinition definition)
{
if (definition == null) throw new ArgumentNullException(nameof(definition));
if (definition == null)
throw new ArgumentNullException(nameof(definition));
var packagesXml = EnsureStorage(out var packagesFile);
@@ -162,8 +165,10 @@ namespace Umbraco.Cms.Core.Packaging
public string ExportPackage(PackageDefinition definition)
{
if (definition.Id == default) throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported");
if (definition.PackageId == default) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported");
if (definition.Id == default)
throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported");
if (definition.PackageId == default)
throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported");
//ensure it's valid
ValidatePackage(definition);
@@ -248,9 +253,11 @@ namespace Umbraco.Cms.Core.Packaging
var dataTypes = new XElement("DataTypes");
foreach (var dtId in definition.DataTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
if (!int.TryParse(dtId, out var outInt))
continue;
var dataType = _dataTypeService.GetDataType(outInt);
if (dataType == null) continue;
if (dataType == null)
continue;
dataTypes.Add(_serializer.Serialize(dataType));
}
root.Add(dataTypes);
@@ -261,9 +268,11 @@ namespace Umbraco.Cms.Core.Packaging
var languages = new XElement("Languages");
foreach (var langId in definition.Languages)
{
if (!int.TryParse(langId, out var outInt)) continue;
if (!int.TryParse(langId, out var outInt))
continue;
var lang = _languageService.GetLanguageById(outInt);
if (lang == null) continue;
if (lang == null)
continue;
languages.Add(_serializer.Serialize(lang));
}
root.Add(languages);
@@ -271,15 +280,74 @@ namespace Umbraco.Cms.Core.Packaging
private void PackageDictionaryItems(PackageDefinition definition, XContainer root)
{
var dictionaryItems = new XElement("DictionaryItems");
var rootDictionaryItems = new XElement("DictionaryItems");
var items = new Dictionary<Guid, (IDictionaryItem dictionaryItem, XElement serializedDictionaryValue)>();
foreach (var dictionaryId in definition.DictionaryItems)
{
if (!int.TryParse(dictionaryId, out var outInt)) continue;
var di = _languageService.GetDictionaryItemById(outInt);
if (di == null) continue;
dictionaryItems.Add(_serializer.Serialize(di, false));
if (!int.TryParse(dictionaryId, out var outInt))
{
continue;
}
IDictionaryItem di = _languageService.GetDictionaryItemById(outInt);
if (di == null)
{
continue;
}
items[di.Key] = (di, _serializer.Serialize(di, false));
}
// organize them in hierarchy ...
var itemCount = items.Count;
var processed = new Dictionary<Guid, XElement>();
while (processed.Count < itemCount)
{
foreach(Guid key in items.Keys.ToList())
{
(IDictionaryItem dictionaryItem, XElement serializedDictionaryValue) = items[key];
if (!dictionaryItem.ParentId.HasValue)
{
// if it has no parent, its definitely just at the root
AppendDictionaryElement(rootDictionaryItems, items, processed, key, serializedDictionaryValue);
}
else
{
if (processed.ContainsKey(dictionaryItem.ParentId.Value))
{
// we've processed this parent element already so we can just append this xml child to it
AppendDictionaryElement(processed[dictionaryItem.ParentId.Value], items, processed, key, serializedDictionaryValue);
}
else if (items.ContainsKey(dictionaryItem.ParentId.Value))
{
// we know the parent exists in the dictionary but
// we haven't processed it yet so we'll leave it for the next loop
continue;
}
else
{
// in this case, the parent of this item doesn't exist in our collection, we have no
// choice but to add it to the root.
AppendDictionaryElement(rootDictionaryItems, items, processed, key, serializedDictionaryValue);
}
}
}
}
root.Add(rootDictionaryItems);
static void AppendDictionaryElement(XElement rootDictionaryItems, Dictionary<Guid, (IDictionaryItem dictionaryItem, XElement serializedDictionaryValue)> items, Dictionary<Guid, XElement> processed, Guid key, XElement serializedDictionaryValue)
{
// track it
processed.Add(key, serializedDictionaryValue);
// append it
rootDictionaryItems.Add(serializedDictionaryValue);
// remove it so its not re-processed
items.Remove(key);
}
root.Add(dictionaryItems);
}
private void PackageMacros(PackageDefinition definition, XContainer root)
@@ -287,11 +355,13 @@ namespace Umbraco.Cms.Core.Packaging
var macros = new XElement("Macros");
foreach (var macroId in definition.Macros)
{
if (!int.TryParse(macroId, out var outInt)) continue;
if (!int.TryParse(macroId, out var outInt))
continue;
var macroXml = GetMacroXml(outInt, out var macro);
if (macroXml == null) continue;
macros.Add(macroXml);
if (macroXml == null)
continue;
macros.Add(macroXml);
}
root.Add(macros);
}
@@ -301,7 +371,8 @@ namespace Umbraco.Cms.Core.Packaging
var stylesheetsXml = new XElement("Stylesheets");
foreach (var stylesheetName in definition.Stylesheets)
{
if (stylesheetName.IsNullOrWhiteSpace()) continue;
if (stylesheetName.IsNullOrWhiteSpace())
continue;
var xml = GetStylesheetXml(stylesheetName, true);
if (xml != null)
stylesheetsXml.Add(xml);
@@ -314,9 +385,11 @@ namespace Umbraco.Cms.Core.Packaging
var templatesXml = new XElement("Templates");
foreach (var templateId in definition.Templates)
{
if (!int.TryParse(templateId, out var outInt)) continue;
if (!int.TryParse(templateId, out var outInt))
continue;
var template = _fileService.GetTemplate(outInt);
if (template == null) continue;
if (template == null)
continue;
templatesXml.Add(_serializer.Serialize(template));
}
root.Add(templatesXml);
@@ -328,9 +401,11 @@ namespace Umbraco.Cms.Core.Packaging
var docTypesXml = new XElement("DocumentTypes");
foreach (var dtId in definition.DocumentTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
if (!int.TryParse(dtId, out var outInt))
continue;
var contentType = _contentTypeService.Get(outInt);
if (contentType == null) continue;
if (contentType == null)
continue;
AddDocumentType(contentType, contentTypes);
}
foreach (var contentType in contentTypes)
@@ -345,9 +420,11 @@ namespace Umbraco.Cms.Core.Packaging
var mediaTypesXml = new XElement("MediaTypes");
foreach (var mediaTypeId in definition.MediaTypes)
{
if (!int.TryParse(mediaTypeId, out var outInt)) continue;
if (!int.TryParse(mediaTypeId, out var outInt))
continue;
var mediaType = _mediaTypeService.Get(outInt);
if (mediaType == null) continue;
if (mediaType == null)
continue;
AddMediaType(mediaType, mediaTypes);
}
foreach (var mediaType in mediaTypes)
@@ -456,7 +533,8 @@ namespace Umbraco.Cms.Core.Packaging
private XElement GetMacroXml(int macroId, out IMacro macro)
{
macro = _macroService.GetById(macroId);
if (macro == null) return null;
if (macro == null)
return null;
var xml = _serializer.Serialize(macro);
return xml;
}
@@ -469,15 +547,18 @@ namespace Umbraco.Cms.Core.Packaging
/// <returns></returns>
private XElement GetStylesheetXml(string name, bool includeProperties)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
var sts = _fileService.GetStylesheetByName(name);
if (sts == null) return null;
if (sts == null)
return null;
var stylesheetXml = new XElement("Stylesheet");
stylesheetXml.Add(new XElement("Name", sts.Alias));
stylesheetXml.Add(new XElement("FileName", sts.Name));
stylesheetXml.Add(new XElement("Content", new XCData(sts.Content)));
if (!includeProperties) return stylesheetXml;
if (!includeProperties)
return stylesheetXml;
var properties = new XElement("Properties");
foreach (var ssP in sts.Properties)