aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server/Migrations/Stages/CodeMigration.cs
diff options
context:
space:
mode:
authorJoshua M. Boniface <joshua@boniface.me>2025-08-03 17:27:17 -0400
committerGitHub <noreply@github.com>2025-08-03 17:27:17 -0400
commit4b6fb6c4bb2478badad068ce18aabe0c2955db48 (patch)
tree15f986ee62327cceb8f5c8f009bcf08d10cfaa66 /Jellyfin.Server/Migrations/Stages/CodeMigration.cs
parente7bc86ebb8496615e0b3f73eb4f13ab4c0913dc8 (diff)
parentdb7465e83d9cc07134a0bffad7ed17b1c7b873da (diff)
Merge branch 'master' into master
Diffstat (limited to 'Jellyfin.Server/Migrations/Stages/CodeMigration.cs')
-rw-r--r--Jellyfin.Server/Migrations/Stages/CodeMigration.cs91
1 files changed, 91 insertions, 0 deletions
diff --git a/Jellyfin.Server/Migrations/Stages/CodeMigration.cs b/Jellyfin.Server/Migrations/Stages/CodeMigration.cs
new file mode 100644
index 000000000..264710bce
--- /dev/null
+++ b/Jellyfin.Server/Migrations/Stages/CodeMigration.cs
@@ -0,0 +1,91 @@
+using System;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+using Jellyfin.Server.ServerSetupApp;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations.Stages;
+
+internal class CodeMigration(Type migrationType, JellyfinMigrationAttribute metadata, JellyfinMigrationBackupAttribute? migrationBackupAttribute)
+{
+ public Type MigrationType { get; } = migrationType;
+
+ public JellyfinMigrationAttribute Metadata { get; } = metadata;
+
+ public JellyfinMigrationBackupAttribute? BackupRequirements { get; set; } = migrationBackupAttribute;
+
+ public string BuildCodeMigrationId()
+ {
+ return Metadata.Order.ToString("yyyyMMddHHmmsss", CultureInfo.InvariantCulture) + "_" + Metadata.Name!;
+ }
+
+ private IServiceCollection MigrationServices(IServiceProvider serviceProvider, IStartupLogger logger)
+ {
+ var childServiceCollection = new ServiceCollection()
+ .AddSingleton(serviceProvider)
+ .AddSingleton(logger)
+ .AddSingleton(typeof(IStartupLogger<>), typeof(NestedStartupLogger<>))
+ .AddSingleton<StartupLogTopic>(logger.Topic!);
+
+ foreach (ServiceDescriptor service in serviceProvider.GetRequiredService<IServiceCollection>())
+ {
+ if (service.Lifetime == ServiceLifetime.Singleton && !service.ServiceType.IsGenericTypeDefinition)
+ {
+ object? serviceInstance = serviceProvider.GetService(service.ServiceType);
+ if (serviceInstance != null)
+ {
+ childServiceCollection.AddSingleton(service.ServiceType, serviceInstance);
+ continue;
+ }
+ }
+
+ childServiceCollection.Add(service);
+ }
+
+ return childServiceCollection;
+ }
+
+ public async Task Perform(IServiceProvider? serviceProvider, IStartupLogger logger, CancellationToken cancellationToken)
+ {
+#pragma warning disable CS0618 // Type or member is obsolete
+ if (typeof(IMigrationRoutine).IsAssignableFrom(MigrationType))
+ {
+ if (serviceProvider is null)
+ {
+ ((IMigrationRoutine)Activator.CreateInstance(MigrationType)!).Perform();
+ }
+ else
+ {
+ using var migrationServices = MigrationServices(serviceProvider, logger).BuildServiceProvider();
+ ((IMigrationRoutine)ActivatorUtilities.CreateInstance(migrationServices, MigrationType)).Perform();
+#pragma warning restore CS0618 // Type or member is obsolete
+ }
+ }
+ else if (typeof(IAsyncMigrationRoutine).IsAssignableFrom(MigrationType))
+ {
+ if (serviceProvider is null)
+ {
+ await ((IAsyncMigrationRoutine)Activator.CreateInstance(MigrationType)!).PerformAsync(cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ using var migrationServices = MigrationServices(serviceProvider, logger).BuildServiceProvider();
+ await ((IAsyncMigrationRoutine)ActivatorUtilities.CreateInstance(migrationServices, MigrationType)).PerformAsync(cancellationToken).ConfigureAwait(false);
+ }
+ }
+ else
+ {
+ throw new InvalidOperationException($"The type {MigrationType} does not implement either IMigrationRoutine or IAsyncMigrationRoutine and is not a valid migration type");
+ }
+ }
+
+ private class NestedStartupLogger<TCategory> : StartupLogger<TCategory>
+ {
+ public NestedStartupLogger(ILogger logger, StartupLogTopic topic) : base(logger, topic)
+ {
+ }
+ }
+}