diff --git a/src/Aspire.Dashboard/Model/ExportHelpers.cs b/src/Aspire.Dashboard/Model/ExportHelpers.cs
index 12d5e277cab..785acc8684c 100644
--- a/src/Aspire.Dashboard/Model/ExportHelpers.cs
+++ b/src/Aspire.Dashboard/Model/ExportHelpers.cs
@@ -79,7 +79,7 @@ public static ExportResult GetResourceAsJson(ResourceViewModel resource, IDictio
/// A result containing the .env file content and suggested file name.
public static ExportResult GetEnvironmentVariablesAsEnvFile(ResourceViewModel resource, IDictionary resourceByName)
{
- var envContent = EnvHelpers.ConvertToEnvFormat(resource.Environment.Select(e => new KeyValuePair(e.Name, e.Value)));
+ var envContent = EnvHelpers.ConvertToEnvFormat(resource.Environment.Where(e => e.FromSpec).Select(e => new KeyValuePair(e.Name, e.Value)));
var fileName = $"{ResourceViewModel.GetResourceName(resource, resourceByName)}.env";
return new ExportResult(envContent, fileName);
}
diff --git a/src/Aspire.Dashboard/Model/ResourceMenuBuilder.cs b/src/Aspire.Dashboard/Model/ResourceMenuBuilder.cs
index 325386ac05e..b8412e42c29 100644
--- a/src/Aspire.Dashboard/Model/ResourceMenuBuilder.cs
+++ b/src/Aspire.Dashboard/Model/ResourceMenuBuilder.cs
@@ -124,7 +124,7 @@ await TextVisualizerDialog.OpenDialogAsync(new OpenTextVisualizerDialogOptions
}
});
- if (resource.Environment.Length > 0)
+ if (resource.Environment.Any(e => e.FromSpec))
{
menuItems.Add(new MenuButtonItem
{
diff --git a/tests/Aspire.Dashboard.Tests/Model/ExportHelpersTests.cs b/tests/Aspire.Dashboard.Tests/Model/ExportHelpersTests.cs
index 500242982aa..6e7a3d9f3c8 100644
--- a/tests/Aspire.Dashboard.Tests/Model/ExportHelpersTests.cs
+++ b/tests/Aspire.Dashboard.Tests/Model/ExportHelpersTests.cs
@@ -42,7 +42,8 @@ public void GetEnvironmentVariablesAsEnvFile_ReturnsExpectedResult()
resourceType: "Container",
state: KnownResourceState.Running,
environment: [
- new EnvironmentVariableViewModel("MY_VAR", "my-value", fromSpec: false)
+ new EnvironmentVariableViewModel("MY_VAR", "my-value", fromSpec: true),
+ new EnvironmentVariableViewModel("RUNTIME_VAR", "runtime-value", fromSpec: false)
]);
var resourceByName = new Dictionary(StringComparer.OrdinalIgnoreCase) { [resource.Name] = resource };
@@ -52,6 +53,12 @@ public void GetEnvironmentVariablesAsEnvFile_ReturnsExpectedResult()
// Assert
Assert.Equal("Test Resource.env", result.FileName);
- Assert.Contains("MY_VAR=my-value", result.Content);
+ Assert.Equal(
+ """
+ MY_VAR=my-value
+
+ """,
+ result.Content,
+ ignoreLineEndingDifferences: true);
}
}
diff --git a/tests/Aspire.Dashboard.Tests/Model/ResourceMenuBuilderTests.cs b/tests/Aspire.Dashboard.Tests/Model/ResourceMenuBuilderTests.cs
index 3decb32518c..62d1ce01296 100644
--- a/tests/Aspire.Dashboard.Tests/Model/ResourceMenuBuilderTests.cs
+++ b/tests/Aspire.Dashboard.Tests/Model/ResourceMenuBuilderTests.cs
@@ -183,6 +183,73 @@ public void AddMenuItems_HasTelemetry_TelemetryItems()
e => Assert.Equal("Localized:ResourceActionMetricsText", e.Text));
}
+ [Fact]
+ public void AddMenuItems_WithFromSpecEnvVars_ExportEnvMenuItemShown()
+ {
+ // Arrange
+ var resource = ModelTestHelpers.CreateResource(
+ environment: [
+ new EnvironmentVariableViewModel("SPEC_VAR", "spec-value", fromSpec: true),
+ new EnvironmentVariableViewModel("RUNTIME_VAR", "runtime-value", fromSpec: false)
+ ]);
+ var repository = TelemetryTestHelpers.CreateRepository();
+ var aiContextProvider = new TestAIContextProvider();
+ var resourceMenuBuilder = CreateResourceMenuBuilder(repository, aiContextProvider);
+
+ // Act
+ var menuItems = new List();
+ resourceMenuBuilder.AddMenuItems(
+ menuItems,
+ resource,
+ new Dictionary(StringComparer.OrdinalIgnoreCase) { [resource.Name] = resource },
+ EventCallback.Empty,
+ EventCallback.Empty,
+ (_, _) => false,
+ showViewDetails: true,
+ showConsoleLogsItem: true,
+ showUrls: true);
+
+ // Assert
+ Assert.Collection(menuItems,
+ e => Assert.Equal("Localized:ActionViewDetailsText", e.Text),
+ e => Assert.Equal("Localized:ResourceActionConsoleLogsText", e.Text),
+ e => Assert.Equal("Localized:ExportJson", e.Text),
+ e => Assert.Equal("Localized:ExportEnv", e.Text));
+ }
+
+ [Fact]
+ public void AddMenuItems_WithoutFromSpecEnvVars_ExportEnvMenuItemNotShown()
+ {
+ // Arrange - only runtime env vars (fromSpec: false), no spec env vars
+ var resource = ModelTestHelpers.CreateResource(
+ environment: [
+ new EnvironmentVariableViewModel("RUNTIME_VAR1", "value1", fromSpec: false),
+ new EnvironmentVariableViewModel("RUNTIME_VAR2", "value2", fromSpec: false)
+ ]);
+ var repository = TelemetryTestHelpers.CreateRepository();
+ var aiContextProvider = new TestAIContextProvider();
+ var resourceMenuBuilder = CreateResourceMenuBuilder(repository, aiContextProvider);
+
+ // Act
+ var menuItems = new List();
+ resourceMenuBuilder.AddMenuItems(
+ menuItems,
+ resource,
+ new Dictionary(StringComparer.OrdinalIgnoreCase) { [resource.Name] = resource },
+ EventCallback.Empty,
+ EventCallback.Empty,
+ (_, _) => false,
+ showViewDetails: true,
+ showConsoleLogsItem: true,
+ showUrls: true);
+
+ // Assert - ExportEnv should NOT be in the menu
+ Assert.Collection(menuItems,
+ e => Assert.Equal("Localized:ActionViewDetailsText", e.Text),
+ e => Assert.Equal("Localized:ResourceActionConsoleLogsText", e.Text),
+ e => Assert.Equal("Localized:ExportJson", e.Text));
+ }
+
private sealed class TestNavigationManager : NavigationManager
{
}