Skip to content

Conversation

@AgentEnder
Copy link
Member

@AgentEnder AgentEnder commented Oct 20, 2025

Current Behavior

There's not an automated way to move from @nx-dotnet/core to @nx/dotnet, as well as missing docs

Expected Behavior

There is a semi-automated migration, and additional docs

Related Issue(s)

Fixes #

@netlify
Copy link

netlify bot commented Oct 20, 2025

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit 3c5205c
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/68f5c07b539e0b00089f34f2
😎 Deploy Preview https://deploy-preview-33133--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@vercel
Copy link

vercel bot commented Oct 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
nx-dev Ready Ready Preview Oct 20, 2025 5:06am

💡 Enable Vercel Agent with $100 free credit for automated AI reviews

@nx-cloud
Copy link
Contributor

nx-cloud bot commented Oct 20, 2025

View your CI Pipeline Execution ↗ for commit 3c5205c

Command Status Duration Result
nx affected --targets=lint,test,test-kt,build,e... ❌ Failed 1h 17m 16s View ↗
nx run-many -t check-imports check-commit check... ✅ Succeeded 2m 28s View ↗
nx-cloud record -- nx-cloud conformance:check ✅ Succeeded 3s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 3s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-20 06:25:48 UTC

Copy link
Contributor

@nx-cloud nx-cloud bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nx Cloud is proposing a fix for your failed CI:

We've fixed the migration generator implementation to resolve the test failures. The changes ensure that the @nx/dotnet plugin is always created even when there's no configuration to migrate, properly format the summary markdown with correct list structures, and correctly merge targetDefaults with inferredTargets configurations without losing important properties.

Suggested Fix changes
diff --git a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrate-from-nx-dotnet.ts b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrate-from-nx-dotnet.ts
index a42ecdaf26..a8562f0e92 100644
--- a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrate-from-nx-dotnet.ts
+++ b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrate-from-nx-dotnet.ts
@@ -78,6 +78,9 @@ export async function migrateFromNxDotnetGenerator(
   const allCompleted: string[] = [];
   const allManualSteps: string[] = [];
 
+  // Re-read nx.json after disabling old plugin
+  let nxJson = readNxJson(tree);
+
   if (hasRcFile || oldPluginConfig) {
     logger.info('');
     if (hasRcFile && oldPluginConfig) {
@@ -92,8 +95,6 @@ export async function migrateFromNxDotnetGenerator(
       );
     }
 
-    // Re-read nx.json after disabling old plugin
-    const nxJson = readNxJson(tree);
     const dotnetPlugin = ensureDotnetPlugin(nxJson);
 
     // Merge RC config with old plugin config (old plugin config takes precedence)
@@ -124,6 +125,10 @@ export async function migrateFromNxDotnetGenerator(
   } else {
     logger.info('');
     logger.info('Step 2: No configuration found to migrate');
+
+    // Even if there's no configuration to migrate, ensure @nx/dotnet plugin exists
+    const dotnetPlugin = ensureDotnetPlugin(nxJson);
+    updateNxJson(tree, nxJson);
   }
 
   // Remove @nx-dotnet/core packages
@@ -312,21 +317,19 @@ function generateMigrationSummary(
 
   summaryLines.push(
     h1('Migration from @nx-dotnet/core to @nx/dotnet'),
-    h2('✅ Completed', unorderedList(...allCompleted)),
-    h2(
-      'Next Steps',
-      orderedList(
-        'Verify your project graph: ' + code('nx graph'),
-        'Test your builds: ' + code('nx run-many -t build,test'),
-        'Review and complete any manual migration steps below'
-      )
+    h2('✅ Completed'),
+    unorderedList(...allCompleted),
+    h2('Next Steps'),
+    orderedList(
+      'Verify your project graph: ' + code('nx graph'),
+      'Test your builds: ' + code('nx run-many -t build,test'),
+      'Review and complete any manual migration steps below'
     )
   );
 
   if (allManualSteps.length > 0) {
-    summaryLines.push(
-      h2('⚠️  Manual Migration Required', unorderedList(...allManualSteps))
-    );
+    summaryLines.push(h2('⚠️  Manual Migration Required'));
+    summaryLines.push(unorderedList(...allManualSteps));
   }
 
   summaryLines.push(
diff --git a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-inferred-targets.ts b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-inferred-targets.ts
index a5b6787519..1e39c133ef 100644
--- a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-inferred-targets.ts
+++ b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-inferred-targets.ts
@@ -23,20 +23,51 @@ export function migrateInferredTargets(
   for (const [targetType, targetConfig] of Object.entries(
     rcConfig.inferredTargets
   )) {
+    const existingConfig = dotnetPlugin.options[targetType];
+
     if (targetConfig === false) {
-      // Target is disabled
-      dotnetPlugin.options[targetType] = false;
-      logger.info(`    - Disabled ${targetType} target`);
+      // Target is disabled - but only if there's no existing targetDefaults config
+      if (existingConfig && typeof existingConfig === 'object') {
+        // Keep the targetDefaults config, don't override with false
+        logger.info(
+          `    - Kept existing targetDefaults configuration for ${targetType} target (inferredTargets had false)`
+        );
+      } else {
+        dotnetPlugin.options[targetType] = false;
+        logger.info(`    - Disabled ${targetType} target`);
+      }
     } else if (typeof targetConfig === 'string') {
       // Simple rename
-      dotnetPlugin.options[targetType] = { targetName: targetConfig };
-      logger.info(`    - Renamed ${targetType} target to "${targetConfig}"`);
+      if (existingConfig && typeof existingConfig === 'object') {
+        // Merge: add targetName to existing config
+        dotnetPlugin.options[targetType] = {
+          ...existingConfig,
+          targetName: targetConfig,
+        };
+        logger.info(
+          `    - Added targetName "${targetConfig}" to existing ${targetType} configuration`
+        );
+      } else {
+        dotnetPlugin.options[targetType] = { targetName: targetConfig };
+        logger.info(`    - Renamed ${targetType} target to "${targetConfig}"`);
+      }
     } else if (typeof targetConfig === 'object') {
       // Complex configuration
-      dotnetPlugin.options[targetType] = targetConfig;
-      logger.info(
-        `    - Migrated ${targetType} target with custom configuration`
-      );
+      if (existingConfig && typeof existingConfig === 'object') {
+        // Merge: inferredTargets properties override targetDefaults properties
+        dotnetPlugin.options[targetType] = {
+          ...existingConfig,
+          ...targetConfig,
+        };
+        logger.info(
+          `    - Merged ${targetType} inferredTargets with existing targetDefaults configuration`
+        );
+      } else {
+        dotnetPlugin.options[targetType] = targetConfig;
+        logger.info(
+          `    - Migrated ${targetType} target with custom configuration`
+        );
+      }
     }
   }
 
diff --git a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-target-defaults.ts b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-target-defaults.ts
index afa169769a..7717502960 100644
--- a/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-target-defaults.ts
+++ b/packages/dotnet/src/generators/migrate-from-nx-dotnet/migrations/migrate-target-defaults.ts
@@ -42,14 +42,24 @@ export function migrateTargetDefaults(
           `    - Migrated ${executorName} configuration to ${targetType} target`
         );
       } else {
-        // Merge configurations
-        dotnetPlugin.options[targetType] = {
-          ...dotnetPlugin.options[targetType],
-          ...(config as any),
-        };
-        logger.info(
-          `    - Merged ${executorName} configuration with existing ${targetType} target`
-        );
+        const existing = dotnetPlugin.options[targetType];
+        // Merge configurations - targetDefaults properties go first, then inferredTargets properties
+        // If inferredTargets is false or a string, keep it as is, otherwise merge objects
+        if (typeof existing === 'boolean' || typeof existing === 'string') {
+          // inferredTargets had a simple value, keep it
+          logger.info(
+            `    - Kept inferredTargets ${targetType} configuration (${existing}), skipping targetDefaults merge`
+          );
+        } else {
+          // Both are objects, merge with inferredTargets taking precedence for conflicts
+          dotnetPlugin.options[targetType] = {
+            ...(config as any),
+            ...existing,
+          };
+          logger.info(
+            `    - Merged ${executorName} configuration with existing ${targetType} target`
+          );
+        }
       }
 
       // Remove the old executor-based targetDefault

Apply fix via Nx Cloud  Reject fix via Nx Cloud

Or Apply changes locally with:

npx nx-cloud apply-locally AEk8-KyvO

Apply fix locally with your editor ↗  View interactive diff ↗


🎓 To learn more about Self Healing CI, please visit nx.dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant