Skip to content

Commit 570005f

Browse files
Run both cms and package migrations in upgrader (#17575)
* Run both cms and package migrations in upgrader * Use correct setting
1 parent e662468 commit 570005f

2 files changed

Lines changed: 100 additions & 48 deletions

File tree

src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs

Lines changed: 96 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Options;
13
using Umbraco.Cms.Core;
24
using Umbraco.Cms.Core.Configuration;
5+
using Umbraco.Cms.Core.Configuration.Models;
6+
using Umbraco.Cms.Core.DependencyInjection;
37
using Umbraco.Cms.Core.Events;
48
using Umbraco.Cms.Core.Exceptions;
59
using Umbraco.Cms.Core.Logging;
@@ -23,19 +27,39 @@ public class UnattendedUpgrader : INotificationAsyncHandler<RuntimeUnattendedUpg
2327
private readonly IProfilingLogger _profilingLogger;
2428
private readonly IRuntimeState _runtimeState;
2529
private readonly IUmbracoVersion _umbracoVersion;
30+
private readonly UnattendedSettings _unattendedSettings;
2631

2732
public UnattendedUpgrader(
2833
IProfilingLogger profilingLogger,
2934
IUmbracoVersion umbracoVersion,
3035
DatabaseBuilder databaseBuilder,
3136
IRuntimeState runtimeState,
32-
PackageMigrationRunner packageMigrationRunner)
37+
PackageMigrationRunner packageMigrationRunner,
38+
IOptions<UnattendedSettings> unattendedSettings)
3339
{
3440
_profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger));
3541
_umbracoVersion = umbracoVersion ?? throw new ArgumentNullException(nameof(umbracoVersion));
3642
_databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder));
3743
_runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState));
3844
_packageMigrationRunner = packageMigrationRunner;
45+
_unattendedSettings = unattendedSettings.Value;
46+
}
47+
48+
[Obsolete("Use constructor that takes IOptions<UnattendedSettings>, this will be removed in V16")]
49+
public UnattendedUpgrader(
50+
IProfilingLogger profilingLogger,
51+
IUmbracoVersion umbracoVersion,
52+
DatabaseBuilder databaseBuilder,
53+
IRuntimeState runtimeState,
54+
PackageMigrationRunner packageMigrationRunner)
55+
: this(
56+
profilingLogger,
57+
umbracoVersion,
58+
databaseBuilder,
59+
runtimeState,
60+
packageMigrationRunner,
61+
StaticServiceProvider.Instance.GetRequiredService<IOptions<UnattendedSettings>>())
62+
{
3963
}
4064

4165
public Task HandleAsync(RuntimeUnattendedUpgradeNotification notification, CancellationToken cancellationToken)
@@ -46,55 +70,26 @@ public Task HandleAsync(RuntimeUnattendedUpgradeNotification notification, Cance
4670
{
4771
case RuntimeLevelReason.UpgradeMigrations:
4872
{
49-
var plan = new UmbracoPlan(_umbracoVersion);
50-
using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Verbose) ? null : _profilingLogger.TraceDuration<UnattendedUpgrader>(
51-
"Starting unattended upgrade.",
52-
"Unattended upgrade completed."))
53-
{
54-
DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan);
55-
if (result?.Success == false)
56-
{
57-
var innerException = new UnattendedInstallException(
58-
"An error occurred while running the unattended upgrade.\n" + result.Message);
59-
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
60-
}
61-
62-
notification.UnattendedUpgradeResult =
63-
RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete;
64-
}
65-
}
73+
RunUpgrade(notification);
6674

67-
break;
68-
case RuntimeLevelReason.UpgradePackageMigrations:
69-
{
70-
if (!_runtimeState.StartupState.TryGetValue(
71-
RuntimeState.PendingPackageMigrationsStateKey,
72-
out var pm)
73-
|| pm is not IReadOnlyList<string> pendingMigrations)
75+
// If we errored out when upgrading don't do anything.
76+
if (notification.UnattendedUpgradeResult is RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors)
7477
{
75-
throw new InvalidOperationException(
76-
$"The required key {RuntimeState.PendingPackageMigrationsStateKey} does not exist in startup state");
78+
return Task.CompletedTask;
7779
}
7880

79-
if (pendingMigrations.Count == 0)
81+
// It's entirely possible that there's both a core upgrade and package migrations to run, so try and run package migrations too.
82+
// but only if upgrade unattended is enabled.
83+
if (_unattendedSettings.PackageMigrationsUnattended)
8084
{
81-
throw new InvalidOperationException(
82-
"No pending migrations found but the runtime level reason is " +
83-
RuntimeLevelReason.UpgradePackageMigrations);
85+
RunPackageMigrations(notification);
8486
}
87+
}
8588

86-
try
87-
{
88-
_packageMigrationRunner.RunPackagePlans(pendingMigrations);
89-
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult
90-
.PackageMigrationComplete;
91-
}
92-
catch (Exception ex)
93-
{
94-
SetRuntimeError(ex);
95-
notification.UnattendedUpgradeResult =
96-
RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors;
97-
}
89+
break;
90+
case RuntimeLevelReason.UpgradePackageMigrations:
91+
{
92+
RunPackageMigrations(notification);
9893
}
9994

10095
break;
@@ -106,6 +101,64 @@ public Task HandleAsync(RuntimeUnattendedUpgradeNotification notification, Cance
106101
return Task.CompletedTask;
107102
}
108103

104+
private void RunPackageMigrations(RuntimeUnattendedUpgradeNotification notification)
105+
{
106+
if (_runtimeState.StartupState.TryGetValue(
107+
RuntimeState.PendingPackageMigrationsStateKey,
108+
out var pm) is false
109+
|| pm is not IReadOnlyList<string> pendingMigrations)
110+
{
111+
throw new InvalidOperationException(
112+
$"The required key {RuntimeState.PendingPackageMigrationsStateKey} does not exist in startup state");
113+
}
114+
115+
if (pendingMigrations.Count == 0)
116+
{
117+
// If we determined we needed to run package migrations but there are none, this is an error
118+
if (_runtimeState.Reason is RuntimeLevelReason.UpgradePackageMigrations)
119+
{
120+
throw new InvalidOperationException(
121+
"No pending migrations found but the runtime level reason is " +
122+
RuntimeLevelReason.UpgradePackageMigrations);
123+
}
124+
125+
return;
126+
}
127+
128+
try
129+
{
130+
_packageMigrationRunner.RunPackagePlans(pendingMigrations);
131+
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult
132+
.PackageMigrationComplete;
133+
}
134+
catch (Exception ex)
135+
{
136+
SetRuntimeError(ex);
137+
notification.UnattendedUpgradeResult =
138+
RuntimeUnattendedUpgradeNotification.UpgradeResult.HasErrors;
139+
}
140+
}
141+
142+
private void RunUpgrade(RuntimeUnattendedUpgradeNotification notification)
143+
{
144+
var plan = new UmbracoPlan(_umbracoVersion);
145+
using (!_profilingLogger.IsEnabled(Core.Logging.LogLevel.Verbose) ? null : _profilingLogger.TraceDuration<UnattendedUpgrader>(
146+
"Starting unattended upgrade.",
147+
"Unattended upgrade completed."))
148+
{
149+
DatabaseBuilder.Result? result = _databaseBuilder.UpgradeSchemaAndData(plan);
150+
if (result?.Success == false)
151+
{
152+
var innerException = new UnattendedInstallException(
153+
"An error occurred while running the unattended upgrade.\n" + result.Message);
154+
_runtimeState.Configure(RuntimeLevel.BootFailed, RuntimeLevelReason.BootFailedOnException, innerException);
155+
}
156+
157+
notification.UnattendedUpgradeResult =
158+
RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete;
159+
}
160+
}
161+
109162
private void SetRuntimeError(Exception exception)
110163
=> _runtimeState.Configure(
111164
RuntimeLevel.BootFailed,

src/Umbraco.Infrastructure/Runtime/RuntimeState.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,18 +325,17 @@ private UmbracoDatabaseState GetUmbracoDatabaseState(IUmbracoDatabaseFactory dat
325325
// All will be prefixed with the same key.
326326
IReadOnlyDictionary<string, string?>? keyValues = database.GetFromKeyValueTable(Constants.Conventions.Migrations.KeyValuePrefix);
327327

328-
// This could need both an upgrade AND package migrations to execute but
329-
// we will process them one at a time, first the upgrade, then the package migrations.
328+
// This could need both an upgrade AND package migrations to execute, so always add any pending package migrations
329+
IReadOnlyList<string> packagesRequiringMigration = _packageMigrationState.GetPendingPackageMigrations(keyValues);
330+
_startupState[PendingPackageMigrationsStateKey] = packagesRequiringMigration;
331+
330332
if (DoesUmbracoRequireUpgrade(keyValues))
331333
{
332334
return UmbracoDatabaseState.NeedsUpgrade;
333335
}
334336

335-
IReadOnlyList<string> packagesRequiringMigration = _packageMigrationState.GetPendingPackageMigrations(keyValues);
336337
if (packagesRequiringMigration.Count > 0)
337338
{
338-
_startupState[PendingPackageMigrationsStateKey] = packagesRequiringMigration;
339-
340339
return UmbracoDatabaseState.NeedsPackageMigration;
341340
}
342341
}

0 commit comments

Comments
 (0)