| File: Migrations\IMigration.cs | Web Access |
| Project: src\src\Aspire.Cli\Aspire.Cli.csproj (aspire) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. namespace Aspire.Cli.Migrations; /// <summary> /// Identifies the AppHost a migration should inspect or mutate. /// </summary> /// <param name="AppHostFile"> /// The AppHost file explicitly selected by the command, or <see langword="null"/> when the migration /// should resolve the current AppHost from the working directory. /// </param> internal sealed record MigrationContext(FileInfo? AppHostFile) { /// <summary> /// A context with no explicitly selected AppHost, so the migration resolves the current AppHost /// from the working directory. /// </summary> public static MigrationContext CurrentDirectory { get; } = new((FileInfo?)null); } /// <summary> /// A single, self-contained migration that can detect whether it applies to the current project /// and, when it does, bring the project up to the latest recommended Aspire conventions. /// </summary> /// <remarks> /// Migrations are the shared unit of work behind both <c>aspire update --migrate</c> (which applies them) /// and the <c>aspire doctor</c> pending-migrations check (which only detects them). New kinds of /// migration — a future Java AppHost layout change, a C# version bump, or an integration breaking /// change — are added by implementing this interface and registering it in DI; neither the /// <c>migrate</c> command nor the doctor check need to change. /// See: https://github.com/microsoft/aspire/issues/17842 /// </remarks> internal interface IMigration { /// <summary> /// A stable, machine-readable identifier for this migration (e.g. <c>typescript-apphost-mts</c>). /// Surfaced as the check name in <c>aspire doctor --format json</c>, so treat it as a contract. /// </summary> string Id { get; } /// <summary> /// Relative execution order. Lower values run first. Use this when one migration must be applied /// before another (e.g. a layout move before a dependent rewrite). /// </summary> int Order { get; } /// <summary> /// Detects whether this migration applies to the selected project. /// </summary> /// <param name="context">The AppHost to inspect, or the current working directory when no AppHost is specified.</param> /// <param name="cancellationToken">Cancellation token.</param> /// <returns> /// A <see cref="MigrationDescriptor"/> describing what would change, or <see langword="null"/> /// when there is nothing to migrate. Detection must be side-effect free so it is safe to run /// repeatedly (e.g. from <c>aspire doctor</c>). /// </returns> Task<MigrationDescriptor?> DetectAsync(MigrationContext context, CancellationToken cancellationToken); /// <summary> /// Applies the migration to the selected project. Implementations own their own progress, /// success, and best-effort failure messaging via <c>IInteractionService</c>. Applying must be /// idempotent: if there is nothing to migrate (for example because a previous run already /// completed), this should be a no-op. /// </summary> /// <param name="context">The AppHost to mutate, or the current working directory when no AppHost is specified.</param> /// <param name="cancellationToken">Cancellation token.</param> Task ApplyAsync(MigrationContext context, CancellationToken cancellationToken); }