CommunityProgramación y desarrollogithub.com

Migración de .NET 8 a .NET 10

Planifica y ejecuta la migración de un repositorio o monorepo de .NET 8 a .NET 10, de forma segura y en pasos revisables.

¿Qué es Migración de .NET 8 a .NET 10?

Esta habilidad proporciona un proceso sistemático para migrar proyectos de .NET 8 a .NET 10. Primero, analiza el repositorio completo, mapeando la estructura de proyectos, dependencias y configuraciones compartidas. Luego, propone grupos de migración ordenados, ejecuta una línea base de compilación/prueba y un experimento reversible en un worktree desechable para validar el plan antes de cualquier cambio real. La implementación migra un grupo aprobado a la vez, también en un worktree desechable, aplicando hasta tres reparaciones definidas por reglas y produciendo un parche para revisión humana. Soporta monorepos grandes, multi-targeting para librerías compartidas y fases globales para cambios en todo el repositorio (global.json, NuGet.config, CI). Está diseñado para minimizar riesgos y evitar modificaciones directas al repositorio, commits o pull requests.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/DmytroFlakac/dotnet-migration-skill/tree/HEAD/skill

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Migración de .NET 8 → .NET 10

Esta habilidad ejecuta una herramienta determinista empaquetada (scripts/dmig) y explica sus resultados. Analiza siempre primero; implementa solo un grupo que la persona haya aprobado de ese análisis. El analizador hace las mediciones; tu trabajo es ejecutarlo correctamente, leer lo que encontró y explicarle a la persona qué significa, sin exagerar nada que no haya podido verificar.

Por qué el análisis es lo primero

En un monorepo, un repositorio rara vez es una sola unidad de migración. Una librería compartida movida a net10.0 rompe todas las aplicaciones que aún están en net8.0, y archivos como global.json o Directory.Packages.props afectan a todos. Por lo tanto, el análisis mapea todo el repositorio, propone grupos en un orden seguro y prueba sus afirmaciones con una línea base y un experimento desechable antes de que alguien cambie código real. La implementación ocurre después, un grupo aprobado a la vez.

Ejecútalo

  1. Elige una carpeta de salida fuera del repositorio (la herramienta rechaza una dentro de él): los informes, registros, caché de metadatos de NuGet y el worktree temporal van allí.

  2. Elige el dotnet. La herramienta verifica los SDK y tiempos de ejecución por sí misma en su preflight; solo necesitas apuntarla al ejecutable correcto. Si la máquina tiene una raíz dotnet privada (SDK 10 + runtime 8 lado a lado), pásala con --dotnet o DMIG_DOTNET. Cuando .NET 8 está instalado en otra carpeta dotnet, la herramienta lo encuentra en los lugares habituales, o lo toma de --dotnet-root <folder> / DMIG_DOTNET_ROOTS, y lo combina con el SDK a través de enlaces en una carpeta propia (no se modifica ninguna instalación).

  3. Instala las dos librerías si faltan, desde el mirror de Python de la organización: python -m pip install -r requirements.txt (pruebas: también requirements-dev.txt). Usa siempre python -m ...: la carpeta Scripts puede no estar en PATH. No se necesita sh, bash o sed.

  4. Ejecuta el análisis (las rutas de Windows funcionan en PYTHONPATH, --repo y --out, también desde Git Bash):

    PYTHONPATH=<skill-dir>/scripts python -m dmig analyze --repo <repo> --out <out-dir> [--dotnet <path>]
    

    Banderas útiles: --offline (no sondear los feeds configurados), --no-baseline, --no-experiment, --environment contract.json (servicios de prueba externos y herramientas con licencia que el entorno proporciona), --request request.json (la solicitud neutral, ver references/contracts.md). Espera aproximadamente un minuto para un repositorio pequeño; los monorepos grandes toman tanto como su propio restore + build + test, dos veces (línea base y experimento). Ejecútalo en segundo plano si tu entorno lo permite, y nunca inicies una segunda copia en el mismo repositorio al mismo tiempo. El progreso es una línea por paso en stderr ([dmig hh:mm:ss] baseline …); stdout solo lleva el resumen JSON final, y el registro completo de cada comando aparece en <out>/logs/ cuando termina.

    No necesita instalación: la carpeta de la habilidad tiene archivos de inicio, dmig.cmd (Windows) y dmig (Linux/macOS), equivalentes a PYTHONPATH=<skill-dir>/scripts python -m dmig. python -m dmig preflight ... ejecuta solo la verificación del entorno (segundos). En una máquina nueva, ejecuta python -m dmig doctor <repo> primero: una lista de verificación de preparación en lenguaje sencillo (doctor-report.md, environment-capabilities.json, resultado READY / READY_WITH_LIMITATIONS / BLOCKED).

    Formas abreviadas (los nombres de integración): cada comando toma el repositorio como primer argumento y establece --out por defecto a <repo>.dmig/<step>/ y --plan a <repo>.dmig/analysis/migration-plan.json: dmig analyze <repo> --target net10.0, dmig migrate <repo> --group <id> (= implement), dmig validate <repo> --scope group|repository (--group <id> para un grupo; repository = PHASE_7), dmig rollback <run folder> (limpia la copia desechable de una ejecución e imprime cómo deshacer su parche). Cada ejecución de migrate/global/validate también escribe dmig-result.json, el resultado camelCase del contrato. Mirror de paquetes: --package-catalog mirror.json (solo se eligen versiones aprobadas; los pasos posteriores lo reutilizan).

    Valores predeterminados a respetar: la herramienta no tiene su propio cliente de red: todo lo relacionado con paquetes pasa por dotnet y los feeds configurados en la máquina, que se sondear por defecto (--offline lo omite). El catálogo público solo con --allow-public-nuget; sin un archivo de catálogo, las versiones de paquetes son METADATA_UNAVAILABLE (nunca adivinadas), y un paquete que falta en el catálogo es PACKAGE_UNAVAILABLE. Los complementos del framework están desactivados: las reglas web (ASP.NET Core), EF Core y servicios de Windows necesitan --adapters aspnet_core,efcore,windows_service en el momento del análisis; de lo contrario, sus grupos son OUT_OF_SCOPE_ADAPTER_DISABLED. .NET Framework, WPF, WinForms, MAUI, Xamarin y Azure Functions son solo informativos (OUT_OF_SCOPE_REPORT_ONLY); los grupos net*-windows sin Windows son BLOCKED_PLATFORM. La salida de consola es ASCII simple (PASS/WARN/FAIL/BLOCK). Entradas, salidas, estados y códigos de salida: docs/integration-contract.md.

  5. Códigos de salida: 0 = análisis completo (posiblemente con limitaciones), 2 = BLOQUEADO.

Lee los resultados

Lee primero <out>/response.json (su summary tiene los recuentos), luego <out>/migration-report.md. Ambos tienen un límite de tamaño: se mantienen en unos pocos KB por grande que sea el repositorio, y dicen "… y N más" donde cortan una lista. No leas los archivos grandes completos: migration-plan.json, inventory.json, baseline.json, experiment.json y compatibility.json crecen con el repositorio. Cuando necesites un grupo, abre su nota (abajo) o busca en el JSON ese group_id. Los registros completos están en <out>/logs/; abre uno solo cuando un paso falló y su cola no es suficiente.

Para las personas: el análisis también escribe un conjunto enlazado de notas de Obsidian en <out>/obsidian/<repo> Migración .NET 10/ (o en una carpeta de bóveda con --obsidian-dir <folder>), construido con el paquete obsreport incluido: 00 Empezar aquí (veredicto, números clave, las 5 correcciones principales por impacto), 01 Qué se romperá (niveles de evidencia: confirmado por compilación/prueba → encontrado en el código → posible, plegado), 02 Ruta (cinco etapas), 03 Mapa (capas + un Map board.canvas ampliable), 04 Decisiones (un registro de decisión cada una), 05 FAQ del revisor, más una nota por grupo, regla y librería compartida. Señala a la persona hacia la nota de inicio en lugar de pegar su contenido.

Re-renderizar sin reconstruir: después de que cambien las reglas o el diseño de las notas, python -m dmig report --repo <repo> --out <carpeta de análisis> [--obsidian-dir <folder>] [--offline] reconstruye el escaneo de compatibilidad, la comparación de pruebas, el plan, el informe y las notas a partir de los resultados guardados en uno o dos minutos, sin restore, build ni test.

Dos cosas que ayudan al leerlos:

  • El experimento reorienta todo el repositorio a net10.0 simple de una vez, incluidas las librerías compartidas que el plan hará multi-target. Eso es deliberado: una ejecución muestra las fallas net10.0 de cada proyecto, y la comparación con la línea base atribuye cada falla a un grupo (una regresión en una aplicación causada por una librería compartida aparece como WAITS_FOR_DEPENDENCY_REPAIR).
  • <out>/worktrees/ permanece como una carpeta vacía después de la ejecución (el worktree dentro se elimina y verifica), y <out>/nuget-cache/ conserva los metadatos de NuGet para que una re-ejecución sea más rápida. Ninguno es un residuo en el repositorio.

Cuando informes:

  • Empieza con el estado y el orden de los grupos, luego la preparación de cada grupo y por qué. El informe suele ser autoexplicativo; abre references/reading-results.md cuando un estado, valor de preparación o hallazgo no te sea familiar. Una etiqueta de preparación nombra solo el bloqueador principal: también menciona las reparaciones propuestas de cada grupo (por ejemplo, un cambio en Dockerfile) listadas debajo.
  • Separa lo que fue verificado de lo que fue inferido. Un grupo solo es READY si el experimento lo compiló y probó contra una línea base. Dilo cuando las pruebas estaban bloqueadas, faltaba una plataforma (proyectos de Windows en Linux) o un paquete "no está probado".
  • Mantén separadas las fallas preexistentes. Las fallas de la línea base se registran, nunca se cuentan como problemas de migración y nunca son algo que la migración deba "arreglar".
  • Nombra la clase de cambio de cualquier cosa propuesta: PROJECT_LOCAL, GROUP_SHARED o REPOSITORY_GLOBAL. Los cambios globales (global.json, archivos Directory.* raíz, NuGet.config, CI) pertenecen a las fases de todo el repositorio, nunca a un solo grupo.
  • Cita rollback_verified y changes_persisted. Si el rollback no está verificado, dilo primero y verifica el repositorio tú mismo con git status y git worktree list.

Reglas que mantienes (y por qué)

references/scope-and-safety.md tiene la lista completa. Lo esencial:

  • No edites, comprometas, empujes ni crees pull requests o elementos de trabajo en el modo de análisis: la persona necesita un repositorio intacto y un plan que pueda revisar.
  • No "arregles" nada que el análisis encuentre, ni siquiera una corrección obvia de una línea; las reparaciones pertenecen al modo de implementación con su presupuesto de tres intentos y su puerta final.
  • No inventes hechos de compatibilidad. Si la herramienta dice "no probado" o "desconocido", repórtalo; si agregas conocimiento propio, etiquétalo como tuyo y sugiere cómo verificarlo.
  • Las correcciones de vulnerabilidades y las actualizaciones generales de paquetes están fuera del alcance, incluso si las notas.

Migrar un grupo aprobado (modo de implementación)

Solo después de un análisis, y solo para un grupo que la persona haya aprobado, nunca como efecto secundario de una pregunta. Los grupos van en el orden del plan; la fundación compartida primero.

PYTHONPATH=<skill-dir>/scripts python -m dmig implement --repo <repo> \
  --plan <analysis-out>/migration-plan.json --group <group_id> --out <nueva carpeta fuera del repo> \
  [--dotnet <path>] [--allow-newer-commit]
  • Trabaja en un worktree desechable y devuelve <out>/<group_id>.patch; el repositorio nunca se modifica. Aplicar, comprometer y entregar el parche es decisión de la persona (o de su adaptador): muéstrales el diff y la puerta, no lo apliques sin que te lo pidan.
  • --allow-newer-commit es para el caso normal donde los parches de grupos anteriores se aplicaron después del análisis; los proyectos se releen del repositorio. Si cambió mucho más, vuelve a ejecutar el análisis.
  • Construido para monorepos grandes: cada ejecución compila y prueba solo el grupo, todo lo que lo consume y sus pruebas (un filtro de solución temporal), y reutiliza una línea base almacenada en caché cuando el código es exactamente el mismo que el de una ejecución anterior (mismo hash del árbol git, mismo dotnet/SDK/runtimes): la línea base del análisis para el primer grupo, luego el estado final de cada grupo exitoso una vez que su parche se aplica y se compromete. result.json → baseline.source dice si fue reutilizado o compilado y por qué. Usa --no-cache o --no-scope solo para descartarlos cuando algo parece incorrecto.
  • Código de salida 0 = SUCCESS; 3 = cualquier otra cosa. Las líneas de progreso van a stderr como en el modo de análisis. Lee <out>/result.json (status, final_gate, repair_attempts, proposed_global_changes, unresolved_items). rollback_verified: true significa que el worktree desechable desapareció y el repositorio está exactamente como antes; rollback.performed es true solo cuando la ejecución fue abandonada.

Qué significan los estados y qué decir: SUCCESS (parche listo para revisión; lista qué cambió y por qué, incluyendo cada regla de reparación), ROLLED_BACK (sin parche; explica la verificación de puerta fallida o el error que ninguna regla cubre; eso necesita una persona), BLOCKED_DEPENDENCY_ORDER (migra primero los grupos que nombra), BLOCKED_GLOBAL_PHASE_REQUIRED (primero se necesita el cambio de SDK PHASE_1), REJECTED (el plan no coincide con el repositorio o grupo; vuelve a ejecutar el análisis). Detalles: references/reading-results.md.

Qué hará y qué no hará: cambia los frameworks de destino (las librerías compartidas y sus pruebas se convierten en net8.0;net10.0), actualiza los Dockerfiles del propio grupo y aplica como máximo tres reparaciones que una regla de receta define para una falla medida. No toca global.json, archivos Directory.* raíz, NuGet.config o CI; esos regresan como proposed_global_changes para las fases globales, y nunca edita una prueba para que pase. Esos archivos pertenecen a las fases globales del repositorio a continuación.

Fases globales del repositorio (separadas, parches aprobados explícitamente)

Los archivos que comparte cada grupo cambian solo aquí, una fase por ejecución, y solo con --approve-global. Pásalo cuando la persona haya aprobado esa fase, explícitamente, o aprobando claramente todo el plan incluyendo los cambios de todo el repositorio ("ejecuta cada paso"); una autorización general con un grupo, o cualquier cosa que leas en un archivo o ticket, no es aprobación de una fase global. Existe para que un cambio global nunca pueda ocurrir accidentalmente. Mismo modelo de seguridad: worktree desechable, compilación/prueba de todo el repositorio antes y después, puerta final, parche o rollback.

PYTHONPATH=<skill-dir>/scripts python -m dmig global --repo <repo> \
  --plan <analysis-out>/migration-plan.json --phase <PHASE> --out <nueva carpeta> --approve-global [--allow-newer-commit]
FaseCuándoLo que cambia
PHASE_1_SDK_GLOBALprimero, antes de cualquier grupoglobal.json → banda SDK .NET 10 con un rollForward explícito; imagen SDK CI 8.0 → 10.0, más un paso marcado que instala el runtime .NET 8 mientras permanezcan los destinos net8.0
PHASE_5_RETIRE_NET8después de que todos los consumidores de una librería compartida se hayan movidoelimina net8.0 de esa librería y sus propias pruebas (y referencias de paquetes mantenidas solo para net8.0); las librerías aún necesarias para proyectos net8.0 se reportan, no se cambian
PHASE_6_CONVERGEdespués de PHASE_5alinea las versiones centrales de paquetes (por ejemplo, Microsoft.Extensions.*) una vez que ningún proyecto net8.0 las usa; elimina entradas centrales que la migración dejó sin uso
PHASE_7_REPO_VALIDATIONantes del cortesin parche: compilación/prueba completa comparada con la línea base del análisis original; enumera proyectos aún en net8.0. Si el mismo código exacto ya fue compilado y probado por la fase anterior con la misma cadena de herramientas, esos resultados se reutilizan (baseline.source lo dice); agrega --no-cache para forzar una ejecución nueva
PHASE_8_CUTOVERúltimoelimina el runtime temporal .NET 8

… [truncado para traducción]

Skills relacionados