| | | 1 | | using System; |
| | | 2 | | using System.Globalization; |
| | | 3 | | using System.Threading; |
| | | 4 | | using System.Threading.Tasks; |
| | | 5 | | using Jellyfin.Server.ServerSetupApp; |
| | | 6 | | using Microsoft.Extensions.DependencyInjection; |
| | | 7 | | |
| | | 8 | | namespace Jellyfin.Server.Migrations.Stages; |
| | | 9 | | |
| | | 10 | | internal class CodeMigration(Type migrationType, JellyfinMigrationAttribute metadata, JellyfinMigrationBackupAttribute? |
| | | 11 | | { |
| | | 12 | | public Type MigrationType { get; } = migrationType; |
| | | 13 | | |
| | | 14 | | public JellyfinMigrationAttribute Metadata { get; } = metadata; |
| | | 15 | | |
| | | 16 | | public JellyfinMigrationBackupAttribute? BackupRequirements { get; set; } = migrationBackupAttribute; |
| | | 17 | | |
| | | 18 | | public string BuildCodeMigrationId() |
| | | 19 | | { |
| | 630727 | 20 | | return Metadata.Order.ToString("yyyyMMddHHmmsss", CultureInfo.InvariantCulture) + "_" + Metadata.Name!; |
| | | 21 | | } |
| | | 22 | | |
| | | 23 | | public async Task Perform(IServiceProvider serviceProvider, IStartupLogger logger, CancellationToken cancellationTok |
| | | 24 | | { |
| | 137 | 25 | | if (!IsMigrationRoutine(MigrationType)) |
| | | 26 | | { |
| | 0 | 27 | | throw new InvalidOperationException($"The type {MigrationType} does not implement either IMigrationRoutine o |
| | | 28 | | } |
| | | 29 | | |
| | | 30 | | // The routine runs against a scope of the applications own container. Copying the application service |
| | | 31 | | // descriptors into a child container instead would make that child container the owner of every singleton it |
| | | 32 | | // forwards, so disposing it after the migration would also dispose the applications own instance of services |
| | | 33 | | // like the ProviderManager and leave the server broken until the next restart. |
| | 137 | 34 | | var scope = serviceProvider.CreateAsyncScope(); |
| | 137 | 35 | | await using (scope.ConfigureAwait(false)) |
| | | 36 | | { |
| | | 37 | | // Nests everything the routine logs through an injected IStartupLogger under the migrations own topic. |
| | 137 | 38 | | using (StartupLogger.BeginAmbientTopic(logger.Topic)) |
| | | 39 | | { |
| | 137 | 40 | | await RunAsync(ActivatorUtilities.CreateInstance(scope.ServiceProvider, MigrationType), cancellationToke |
| | 137 | 41 | | } |
| | | 42 | | } |
| | 137 | 43 | | } |
| | | 44 | | |
| | | 45 | | // The obsolete IMigrationRoutine is still implemented by every routine that predates the async interface, so |
| | | 46 | | // the members that have to touch it are grouped here behind a single suppression. |
| | | 47 | | #pragma warning disable CS0618 // Type or member is obsolete |
| | | 48 | | private static bool IsMigrationRoutine(Type migrationType) |
| | | 49 | | { |
| | 137 | 50 | | return typeof(IMigrationRoutine).IsAssignableFrom(migrationType) || typeof(IAsyncMigrationRoutine).IsAssignableF |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | private static async Task RunAsync(object routine, CancellationToken cancellationToken) |
| | | 54 | | { |
| | 137 | 55 | | if (routine is IMigrationRoutine migrationRoutine) |
| | | 56 | | { |
| | 135 | 57 | | migrationRoutine.Perform(); |
| | 135 | 58 | | return; |
| | | 59 | | } |
| | | 60 | | |
| | 2 | 61 | | await ((IAsyncMigrationRoutine)routine).PerformAsync(cancellationToken).ConfigureAwait(false); |
| | 137 | 62 | | } |
| | | 63 | | #pragma warning restore CS0618 // Type or member is obsolete |
| | | 64 | | } |