diff --git a/Aspire.slnx b/Aspire.slnx
index 03ee4cce7bd..a3e9e9ad130 100644
--- a/Aspire.slnx
+++ b/Aspire.slnx
@@ -48,6 +48,7 @@
+
diff --git a/src/Aspire.Hosting.Analyzers/AnalyzerReleases.Unshipped.md b/src/Aspire.Hosting.Analyzers/AnalyzerReleases.Unshipped.md
index ab70a35f04b..62bfdc2112c 100644
--- a/src/Aspire.Hosting.Analyzers/AnalyzerReleases.Unshipped.md
+++ b/src/Aspire.Hosting.Analyzers/AnalyzerReleases.Unshipped.md
@@ -1,17 +1,2 @@
; Unshipped analyzer release
; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
-
-### New Rules
-
-Rule ID | Category | Severity | Notes
---------|----------|----------|-------
-ASPIREEXPORT001 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT001)
-ASPIREEXPORT002 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT002)
-ASPIREEXPORT003 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT003)
-ASPIREEXPORT004 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT004)
-ASPIREEXPORT005 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT005)
-ASPIREEXPORT006 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT006)
-ASPIREEXPORT007 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT007)
-ASPIREEXPORT008 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT008)
-ASPIREEXPORT009 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT009)
-ASPIREEXPORT010 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT010)
diff --git a/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Shipped.md b/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Shipped.md
new file mode 100644
index 00000000000..d027c512cb9
--- /dev/null
+++ b/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Shipped.md
@@ -0,0 +1,3 @@
+; Shipped analyzer releases
+; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
diff --git a/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Unshipped.md b/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Unshipped.md
new file mode 100644
index 00000000000..ab70a35f04b
--- /dev/null
+++ b/src/Aspire.Hosting.Integration.Analyzers/AnalyzerReleases.Unshipped.md
@@ -0,0 +1,17 @@
+; Unshipped analyzer release
+; https://github.com/dotnet/roslyn/blob/main/src/RoslynAnalyzers/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+ASPIREEXPORT001 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT001)
+ASPIREEXPORT002 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT002)
+ASPIREEXPORT003 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT003)
+ASPIREEXPORT004 | Design | Error | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT004)
+ASPIREEXPORT005 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT005)
+ASPIREEXPORT006 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT006)
+ASPIREEXPORT007 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT007)
+ASPIREEXPORT008 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT008)
+ASPIREEXPORT009 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT009)
+ASPIREEXPORT010 | Design | Warning | AspireExportAnalyzer, [Documentation](https://aka.ms/aspire/diagnostics/ASPIREEXPORT010)
diff --git a/src/Aspire.Hosting.Integration.Analyzers/Aspire.Hosting.Integration.Analyzers.csproj b/src/Aspire.Hosting.Integration.Analyzers/Aspire.Hosting.Integration.Analyzers.csproj
new file mode 100644
index 00000000000..430dfb5802d
--- /dev/null
+++ b/src/Aspire.Hosting.Integration.Analyzers/Aspire.Hosting.Integration.Analyzers.csproj
@@ -0,0 +1,44 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ false
+ false
+ false
+ 12.0
+
+ $(NoWarn),1573,1591,1712
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Aspire.Hosting.Analyzers/AspireExportAnalyzer.Diagnostics.cs b/src/Aspire.Hosting.Integration.Analyzers/AspireExportAnalyzer.Diagnostics.cs
similarity index 100%
rename from src/Aspire.Hosting.Analyzers/AspireExportAnalyzer.Diagnostics.cs
rename to src/Aspire.Hosting.Integration.Analyzers/AspireExportAnalyzer.Diagnostics.cs
diff --git a/src/Aspire.Hosting.Analyzers/AspireExportAnalyzer.cs b/src/Aspire.Hosting.Integration.Analyzers/AspireExportAnalyzer.cs
similarity index 100%
rename from src/Aspire.Hosting.Analyzers/AspireExportAnalyzer.cs
rename to src/Aspire.Hosting.Integration.Analyzers/AspireExportAnalyzer.cs
diff --git a/src/Aspire.Hosting/Aspire.Hosting.csproj b/src/Aspire.Hosting/Aspire.Hosting.csproj
index bbfb120f7fc..cc7c588988c 100644
--- a/src/Aspire.Hosting/Aspire.Hosting.csproj
+++ b/src/Aspire.Hosting/Aspire.Hosting.csproj
@@ -10,6 +10,10 @@
Core abstractions for the Aspire application model.
+
+
+
+
@@ -138,4 +142,26 @@
+
+
+ $(BeforePack);IncludeIntegrationAnalyzerInPackage
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Aspire.Hosting/buildTransitive/Aspire.Hosting.targets b/src/Aspire.Hosting/buildTransitive/Aspire.Hosting.targets
new file mode 100644
index 00000000000..39a2d820fb0
--- /dev/null
+++ b/src/Aspire.Hosting/buildTransitive/Aspire.Hosting.targets
@@ -0,0 +1,12 @@
+
+
+
+ false
+ <_AspireIntegrationAnalyzerAssembly Condition="'$(_AspireIntegrationAnalyzerAssembly)' == ''">$(MSBuildThisFileDirectory)Aspire.Hosting.Integration.Analyzers.dll
+
+
+
+
+
+
+
diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets
index 828c0f7d33d..d1bbc9062e9 100644
--- a/src/Directory.Build.targets
+++ b/src/Directory.Build.targets
@@ -18,8 +18,12 @@
-
+
+
diff --git a/tests/Aspire.Hosting.Tests/MSBuildTests.cs b/tests/Aspire.Hosting.Tests/MSBuildTests.cs
index 7100f876562..8ccefd334d4 100644
--- a/tests/Aspire.Hosting.Tests/MSBuildTests.cs
+++ b/tests/Aspire.Hosting.Tests/MSBuildTests.cs
@@ -372,6 +372,133 @@ the Aspire.AppHost.SDK targets that will automatically add these references to p
Assert.Contains("warning ASPIRE004", output);
}
+ [Fact]
+ public void AspireExportAnalyzersAreDisabledByDefault()
+ {
+ var repoRoot = MSBuildUtils.GetRepoRoot();
+ using var tempDirectory = new TestTempDirectory();
+
+ var projectDirectory = Path.Combine(tempDirectory.Path, "MyHostingExtension");
+ Directory.CreateDirectory(projectDirectory);
+
+ File.WriteAllText(Path.Combine(projectDirectory, "MyHostingExtension.csproj"),
+ $"""
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+ """);
+
+ File.WriteAllText(Path.Combine(projectDirectory, "Extensions.cs"),
+ """
+ using Aspire.Hosting;
+ using Aspire.Hosting.ApplicationModel;
+
+ namespace MyHostingExtension;
+
+ public static class CustomResourceExtensions
+ {
+ public static IResourceBuilder AddCustomContainer(this IDistributedApplicationBuilder builder)
+ {
+ return builder.AddContainer("custom", "custom-image");
+ }
+ }
+ """);
+
+ CreateExportAnalyzerDirectoryBuildFiles(projectDirectory, repoRoot);
+
+ var output = BuildProject(projectDirectory);
+
+ Assert.DoesNotContain("warning ASPIREEXPORT008", output);
+ }
+
+ [Fact]
+ public void AspireExportAnalyzersCanBeEnabledWithMsBuildProperty()
+ {
+ var repoRoot = MSBuildUtils.GetRepoRoot();
+ using var tempDirectory = new TestTempDirectory();
+
+ var projectDirectory = Path.Combine(tempDirectory.Path, "MyHostingExtension");
+ Directory.CreateDirectory(projectDirectory);
+
+ File.WriteAllText(Path.Combine(projectDirectory, "MyHostingExtension.csproj"),
+ $"""
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+ """);
+
+ File.WriteAllText(Path.Combine(projectDirectory, "Extensions.cs"),
+ """
+ using Aspire.Hosting;
+ using Aspire.Hosting.ApplicationModel;
+
+ namespace MyHostingExtension;
+
+ public static class CustomResourceExtensions
+ {
+ public static IResourceBuilder AddCustomContainer(this IDistributedApplicationBuilder builder)
+ {
+ return builder.AddContainer("custom", "custom-image");
+ }
+ }
+ """);
+
+ CreateExportAnalyzerDirectoryBuildFiles(projectDirectory, repoRoot, enableAspireIntegrationAnalyzers: true);
+
+ var output = BuildProject(projectDirectory);
+
+ Assert.Contains("warning ASPIREEXPORT008", output);
+ }
+
+ private static void CreateExportAnalyzerDirectoryBuildFiles(
+ string basePath,
+ string repoRoot,
+ bool enableAspireIntegrationAnalyzers = false)
+ {
+ File.WriteAllText(Path.Combine(basePath, "Directory.Build.props"),
+ $"""
+
+
+ {enableAspireIntegrationAnalyzers.ToString().ToLowerInvariant()}
+
+
+ """);
+ File.WriteAllText(Path.Combine(basePath, "Directory.Build.targets"),
+ $"""
+
+
+
+
+
+
+
+ """);
+ }
+
///
/// Tests that when GenerateAssemblyInfo is set to false, a build error is emitted.
///