Skip to content

Conversation

@vhvb1989
Copy link
Contributor

Description

Fixes #13102 - a case sensitivity bug in deployment state file caching on Linux and macOS systems that prevented aspire deploy from loading cached deployment state unless an explicit --environment flag with the exact casing was provided.

Problem

When running aspire deploy on Linux/macOS, the deployment state cache was not being loaded properly due to a case mismatch in environment name handling:

  • Saving: FileDeploymentStateManager saves state files with lowercase environment names (e.g., production.json)
  • Loading: DistributedApplicationBuilder.LoadDeploymentState was using the original casing from EnvironmentName (e.g., Production.json)

Since Linux and macOS filesystems are case-sensitive, this caused cache loading to fail silently. The deployment would work only when explicitly specifying aspire deploy --environment production (lowercase).

Solution

Normalized the environment name to lowercase in LoadDeploymentState method using .ToLowerInvariant() to match the behavior in FileDeploymentStateManager.GetStatePath().

Changes

  • src/Aspire.Hosting/DistributedApplicationBuilder.cs: Added .ToLowerInvariant() to environment name when constructing deployment state path
  • tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs: Updated test to use lowercase production.json instead of Production.json to reflect actual behavior

Testing

  • ✅ Existing test DeployAsync_WithCachedDeploymentState_LoadsFromCache passes with updated filename
  • ✅ Verified consistency with FileDeploymentStateManager.GetStatePath() implementation
  • ✅ Verified DeployAsync_WithStagingEnvironment_UsesStagingStateFile already uses lowercase staging.json

Microsoft Reviewers: Open in CodeFlow

When loading deployment state, the environment name was not normalized to lowercase, causing a mismatch with the saved state file on case-sensitive file systems (Linux/macOS).

FileDeploymentStateManager saves files with lowercase environment names (e.g., production.json), but LoadDeploymentState in DistributedApplicationBuilder was using the original casing (e.g., Production.json), causing cache loading to fail unless the exact casing was specified with --environment flag.

Changes:
- Normalized environment name to lowercase in LoadDeploymentState method
- Updated test to use lowercase filename to match actual behavior

Fixes the issue where aspire deploy would only work with explicit --environment production flag on Linux.
Copilot AI review requested due to automatic review settings November 21, 2025 22:25
@github-actions
Copy link
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 13103

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 13103"

@vhvb1989 vhvb1989 self-assigned this Nov 21, 2025
Copilot finished reviewing on behalf of vhvb1989 November 21, 2025 22:28
Copy link
Member

@davidfowl davidfowl left a comment

Choose a reason for hiding this comment

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

This is a breaking change right?

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a case sensitivity bug in deployment state file caching on Linux and macOS systems. The bug occurred because FileDeploymentStateManager saves state files with lowercase environment names (using .ToLowerInvariant()), but DistributedApplicationBuilder.LoadDeploymentState was using the original casing from EnvironmentName, causing file lookups to fail on case-sensitive filesystems.

Key Changes

  • Fixed: Added .ToLowerInvariant() normalization in LoadDeploymentState to match FileDeploymentStateManager behavior
  • Corrected test: Updated test filename from Production.json to production.json to reflect actual runtime behavior

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/Aspire.Hosting/DistributedApplicationBuilder.cs Applied .ToLowerInvariant() to environment name when constructing deployment state path, ensuring consistency with file save logic
tests/Aspire.Hosting.Azure.Tests/AzureDeployerTests.cs Corrected test filename to use lowercase production.json matching the actual file system behavior

@vhvb1989
Copy link
Contributor Author

This is a breaking change right?

I don't think so. Looks more like a bug-fix to me 🤔 (for linux). In windows you can't have Foo.json and foo.json in the same path. So this was invisible to users there.
In linux, the env state file was saved as production.json and then Aspire would look for Production.json

@vhvb1989 vhvb1989 merged commit 0a0e2ca into main Nov 21, 2025
301 of 302 checks passed
@vhvb1989 vhvb1989 deleted the fix/deployment-state-case-sensitivity branch November 21, 2025 22:48
@dotnet-policy-service dotnet-policy-service bot added this to the 13.1 milestone Nov 21, 2025
@sliekens
Copy link
Contributor

@vhvb1989 I suspect there is another place where this is an issue.

I could reproduce the problem on Debian 12 and workaround with --environment production (lowercase) but Aspire still prompts for a connection secret which is also in the production.json. The workaround did resolve my issue with other parameters.

[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout: info: Aspire.Hosting.Publishing.PipelineExecutor[0]
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:       Initializing deployment for environment 'production'
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout: info: Aspire.Hosting.Publishing.PipelineExecutor[0]
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:       Deployment state will be loaded from: /home/vscode/.aspire/deployments/6478E03D48B9146AA38A1193F8C8B94F76A278EB033735B071D130EB25D34554/production.json
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout: info: Aspire.Hosting.Publishing.PipelineExecutor[807050189]
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:       Generating Compose output
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout: warn: Aspire.Hosting.ParameterProcessor[0]
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:       Failed to save parameter values to deployment state.
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:       Aspire.Hosting.MissingParameterValueException: Connection string parameter resource could not be used because connection string 'seq' is missing.
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ParameterResourceBuilderExtensions.<>c__DisplayClass9_0.<AddConnectionString>b__0(ParameterDefault _) in /_/src/Aspire.Hosting/ParameterResourceBuilderExtensions.cs:line 249
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ApplicationModel.ParameterResource.<.ctor>b__3_0() in /_/src/Aspire.Hosting/ApplicationModel/ParameterResource.cs:line 30
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at System.Lazy`1.ViaFactory(LazyThreadSafetyMode mode)
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at System.Lazy`1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at System.Lazy`1.CreateValue()
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ApplicationModel.ParameterResource.get_ValueInternal() in /_/src/Aspire.Hosting/ApplicationModel/ParameterResource.cs:line 43
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ParameterProcessor.ProcessParameterAsync(ParameterResource parameterResource) in /_/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs:line 172
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ApplicationModel.ParameterResource.GetValueAsync(CancellationToken cancellationToken) in /_/src/Aspire.Hosting/ApplicationModel/ParameterResource.cs:line 90
[17:32:01] [dbug] DotNetCliRunner: dotnet(35929) stdout:          at Aspire.Hosting.ParameterProcessor.SaveParametersToDeploymentStateAsync(IEnumerable`1 parameters, CancellationToken cancellationToken) in /_/src/Aspire.Hosting/Orchestrator/ParameterProcessor.cs:line 386

@davidfowl
Copy link
Member

cc @captainsafia

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.

Deployment state cache fails to load on Linux due to case sensitivity

4 participants