umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
MIT License
4.48k stars 2.69k forks source link

V12.1.2 - Unable to specify order of PackageMigrations for execution #14813

Closed warrenbuckley closed 1 year ago

warrenbuckley commented 1 year ago

Which Umbraco version are you using? (Please write the exact version, example: 10.1.0)

12.1.2

Bug summary

Unable to specify an order that package migrations execute in.

Why is this a problem?

This can be problematic if you are building a starter kit or starting point site that uses other packages which have their own Package Migrations,

For example I may use the Umbraco 'The Starter Kit' which has it's own package migrations to install content & other things, I then as a developer wish to run a Package Migration AFTER The Starter Kit, so that I can modify templates/views with some additional updates.

There is no guarantee in the order of execution my Package Migration may run before The Starter Kit's one and my modifications to a template

Specifics

No response

Steps to reproduce

With a clean Umbraco Solution 12.1.2

using Umbraco.Cms.Core.Packaging;
using Umbraco.Cms.Infrastructure.Packaging;

namespace MySite.StarterKit.PackageMigrations;

public class AaaCustomPackageMigrationPlan : PackageMigrationPlan
{
    public AaaCustomPackageMigrationPlan() : base("Aaa.The-Starter-Kit")
    {
    }

    protected override void DefinePlan()
    {
        // TODO: Check how do we know our Package Migration plan is gonna run
        // After TheStarterKit packages?

        From(InitialState)
            .To<UpdateMasterTemplate>(new Guid("16F3CA63-E362-416E-8F20-5CDFEFCFA43F"));
    }
}

UpdateMasterTemplate.cs

using System.Text;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.IO;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Strings;
using Umbraco.Cms.Infrastructure.Migrations;
using Umbraco.Cms.Infrastructure.Packaging;

namespace MySite.StarterKit.PackageMigrations;

public class UpdateMasterTemplate : PackageMigrationBase
{
    private readonly IFileService _fileService;
    private readonly ILogger<UpdateMasterTemplate> _logger;

    public UpdateMasterTemplate(
        IPackagingService packagingService, 
        IMediaService mediaService, 
        MediaFileManager mediaFileManager, 
        MediaUrlGeneratorCollection mediaUrlGenerators, 
        IShortStringHelper shortStringHelper, 
        IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, 
        IMigrationContext context, 
        IOptions<PackageMigrationSettings> packageMigrationsSettings,
        IFileService fileService,
        ILogger<UpdateMasterTemplate> logger) 
        : base(packagingService, mediaService, mediaFileManager, mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, context, packageMigrationsSettings)
    {
        _fileService = fileService;
        _logger = logger;
    }

    protected override void Migrate()
    {
        // Try & find the 'master' template installed from TheStarterKit
        var masterTemplate = _fileService.GetTemplate("master");
        if (masterTemplate == null)
        {
            _logger.LogError("Unable to find the 'master' template. Please ensure you have installed Umbraco.TheStarterKit nuget package.");
            return;
        }

        // Get the existing template file contents
        var fileContents = masterTemplate.Content;

        if (string.IsNullOrWhiteSpace(fileContents))
        {
            _logger.LogError("The template with alias master is empty");
            return;
        }

        // Find the position of the closing </body> tag
        var positionOfBodyTag = fileContents.IndexOf("</body>", StringComparison.InvariantCultureIgnoreCase);

        // Only update the template if we found the closing </body> tag
        if (positionOfBodyTag < 0)
        {
            _logger.LogError("Unable to find the closing </body> tag in the template with alias master");
            return;
        }

        // Includes the TAB character as well to line up the HTML with the rest of the template
        var newContent = new StringBuilder();
        newContent.AppendLine("");
        newContent.AppendLine("    @* Update to template *@");
        newContent.AppendLine("");

        // Update the existing content and insert our updates before closing </body> tag
        masterTemplate.Content = fileContents.Insert(positionOfBodyTag , newContent.ToString());    

        // Update the file contents with our new content
        // Save the template with updated content
        _fileService.SaveTemplate(masterTemplate);
    }
}

Expected result / actual result

To be able to specify a specific set of order that PackageMigrations execute.

Suggestion

To perhaps make the PackageMigrationPlans Collection a Weighted or Ordered Collection

Notes

For reference the same issue discussed with @ronaldbarendse on Discord https://discord.com/channels/869656431308189746/1151161117939335179/1151279287610118185

Zeegaan commented 1 year ago

Heyo! 👋 As this is a feature request, I will convert this into a discussion 👍