Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 4, 2025

Fixes an issue where user-modified values in .env files were being overwritten during Docker Compose publish operations.

Changes Made

Core Fix

  • SaveKeysOnly Behavior: Modified EnvFile.SaveKeysOnly() to preserve existing non-empty values from disk instead of always writing empty values
  • Path Storage: Refactored EnvFile to store the file path internally, eliminating the need to pass path parameters to Save() methods
  • Entries Property: Changed _entries from a private field to an internal Entries property for better accessibility within the assembly

Logging Enhancement

  • Overwrite Logging: Added logging at Information level when an existing environment file will be overwritten during save operations
  • Logger Integration: Added ILogger parameter to EnvFile.Load() and Create() methods to enable logging throughout the workflow

PrepareAsync Improvement

  • Fresh File Creation: Added EnvFile.Create() method to create new env files without loading existing content
  • Updated PrepareAsync: Changed PrepareAsync to use Create() instead of Load() to ensure clean environment files during the prepare step

Cleanup

  • Removed SharedEnvFile: Removed unused SharedEnvFile property as EnvFile is now loaded fresh on each operation

Testing

  • Test Coverage: Added two new tests (PrepareStep_OverwritesExistingEnvFileAndLogsWarning and PrepareStep_OverwritesExistingEnvFileWithCustomEnvironmentName) to validate env file overwriting behavior and log message emission
  • Test Snapshots: Updated test snapshots to reflect the new expected behavior where user-modified values like PARAM1=changed are preserved

Test Results

  • ✅ All 60 tests pass (58 original + 2 new)
  • ✅ Code review completed
  • ✅ Security scan completed
  • ✅ Log messages properly emitted during file overwrites

Fixes #12650

Original prompt

This section details on the original issue you should resolve

<issue_title>.env files shouldn't be overriden by Docker Compose publisher</issue_title>
<issue_description>Are these changes intentional?

Originally posted by @eerhardt in #12629 (comment)
</issue_description>

<agent_instructions>Update the EnvFile implementation so that if a value that was loaded for an entry is different from the one that is going to be saved, then we favor the value that is on disk. This behavior should only apply for scenarios where we save the keys of the env file only. Verify that the DockerComposeAppendsNewKeysToEnvFileOnPublish works correctly after this change. The value that was replaced PARAM1=changed should not be removed.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

When SaveKeysOnly is called, it now checks if keys already exist on disk with non-empty values and preserves those values instead of overwriting them. This ensures that user modifications to .env files are not lost during subsequent publish operations.

Co-authored-by: captainsafia <[email protected]>
Copilot AI changed the title [WIP] Fix .env files override issue in Docker Compose Fix .env files being overwritten by Docker Compose publisher Nov 4, 2025
Copilot AI requested a review from captainsafia November 4, 2025 04:12
Copilot finished work on behalf of captainsafia November 4, 2025 04:12
@captainsafia
Copy link
Member

@copilot Make the following changes to the implementation:

  • Store the path associated with the EnvFile on load as a private field.
  • Remove the path parameter from the Save and SaveKeysOnly command and use the value in the private field instead.
  • Don't reload the keys from disk in the save command. Use the values that are already populated in entires when the file is loaded.
  • Change _entries from a private field to an internal property.
  • Update the PrepareAsync method to initialize its own EnvFile.

- Store path on Load as private field
- Remove path parameter from Save/SaveKeysOnly methods
- Use existing values in Entries instead of reloading from disk
- Change _entries to internal Entries property
- Update PrepareAsync to initialize its own EnvFile
- Update all test usages to call Save() without path parameter

Co-authored-by: captainsafia <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 4, 2025

@copilot Make the following changes to the implementation:

  • Store the path associated with the EnvFile on load as a private field.
    ...

Done in bd3a719. All changes have been implemented:

  • ✅ Path is now stored as a private field _path when Load() is called
  • Save() and SaveKeysOnly() no longer take a path parameter - they use the stored _path
  • SaveKeysOnly() now uses existing values in Entries instead of reloading from disk
  • _entries is now an internal property called Entries
  • PrepareAsync now initializes its own EnvFile by calling EnvFile.Load(envFilePath)

All 58 tests in Aspire.Hosting.Docker.Tests pass.

Copilot finished work on behalf of captainsafia November 4, 2025 04:34
@github-actions
Copy link
Contributor

github-actions bot commented Nov 4, 2025

🚀 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 -- 12656

Or

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

@captainsafia captainsafia marked this pull request as ready for review November 4, 2025 05:01
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 refactors the EnvFile class to track the file path internally and removes the need to pass the path as a parameter to Save() methods. This change addresses an issue where user-modified values in environment files were being overwritten during publish operations.

Key changes:

  • The EnvFile class now stores the file path internally (set during Load())
  • Save() methods no longer require a path parameter
  • The SaveKeysOnly() method now preserves existing non-empty values instead of always writing empty values
  • SharedEnvFile property is removed from the publishing workflow in favor of loading fresh instances

Reviewed Changes

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

Show a summary per file
File Description
src/Aspire.Hosting.Docker/EnvFile.cs Refactored to store path internally, changed Save() to parameterless, and modified SaveKeysOnly() to preserve existing values
src/Aspire.Hosting.Docker/DockerComposePublishingContext.cs Updated to call Save() without path parameter
src/Aspire.Hosting.Docker/DockerComposeEnvironmentResource.cs Changed to load a fresh EnvFile instance in PrepareAsync() instead of reusing SharedEnvFile
tests/Aspire.Hosting.Docker.Tests/EnvFileTests.cs Updated all test calls to use parameterless Save()
tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeDoesNotOverwriteEnvFileOnPublish#01.verified.env Updated snapshot showing preserved user-modified value
tests/Aspire.Hosting.Docker.Tests/Snapshots/DockerComposePublisherTests.DockerComposeAppendsNewKeysToEnvFileOnPublish#01.verified.env Updated snapshot showing preserved user-modified value when new keys are appended

{
lines.Add($"{entry.Key}=");
}

Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

Trailing whitespace on line 134. The blank line before lines.Add(string.Empty); contains only whitespace characters, which violates coding standards. Remove the trailing whitespace.

Suggested change

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in eb5371f.

private readonly SortedDictionary<string, EnvEntry> _entries = [];
private string? _path;

internal SortedDictionary<string, EnvEntry> Entries { get; } = [];
Copy link

Copilot AI Nov 4, 2025

Choose a reason for hiding this comment

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

The Entries property is marked as internal but was previously a private field _entries. Exposing the internal dictionary directly allows external code within the assembly to modify it without going through the controlled Add() method, potentially bypassing validation logic. Consider keeping this as a private field or exposing it as IReadOnlyDictionary<string, EnvEntry> to prevent uncontrolled mutations.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

Is Entries used outside of this class?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, Entries is only used within the EnvFile class itself. The grep results show it's only accessed internally in EnvFile.cs at line 42 where it assigns values during the Load operation.

@captainsafia
Copy link
Member

@copilot When saving a envfile with keys and values, check if a file with that name already exists and emit an info log indicating it will be overwritten if it does.

- Add ILogger parameter to EnvFile.Load() and constructor
- Log at Information level when saving with values overwrites existing file
- Fix trailing whitespace on line 134
- Update callers to pass logger from context
- All tests pass

Co-authored-by: captainsafia <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 4, 2025

@copilot When saving a envfile with keys and values, check if a file with that name already exists and emit an info log indicating it will be overwritten if it does.

Done in eb5371f. When saving an environment file with keys and values, the code now logs at Information level if a file with that name already exists and will be overwritten:

if (File.Exists(_path))
{
    _logger?.LogInformation("Environment file '{EnvFilePath}' already exists and will be overwritten", _path);
}

The logger is now passed through the EnvFile.Load() method from both PrepareAsync (via context.Logger) and DockerComposePublishingContext (via the logger field).

Copilot finished work on behalf of captainsafia November 4, 2025 16:05
@captainsafia
Copy link
Member

@copilot Add tests to DockerComposePublisherTests that validate that .env.{Environment} files are overwritten when the prepare step is called. Verify that the log is emitted.

- Add EnvFile.Create() method to create new env file without loading existing content
- Update PrepareAsync to use Create() instead of Load() to avoid loading old entries
- Add PrepareStep_OverwritesExistingEnvFileAndLogsWarning test
- Add PrepareStep_OverwritesExistingEnvFileWithCustomEnvironmentName test
- Both tests verify file is overwritten and log message is emitted
- All 60 tests pass

Co-authored-by: captainsafia <[email protected]>
Copy link
Contributor Author

Copilot AI commented Nov 4, 2025

@copilot Add tests to DockerComposePublisherTests that validate that .env.{Environment} files are overwritten when the prepare step is called. Verify that the log is emitted.

Done in 2df4bf4. Added two tests to validate .env.{Environment} file overwriting:

  1. PrepareStep_OverwritesExistingEnvFileAndLogsWarning - Verifies that when a .env.Production file exists, it's completely overwritten (old keys removed) and the log message is emitted.

  2. PrepareStep_OverwritesExistingEnvFileWithCustomEnvironmentName - Same verification but for a custom environment name (.env.Staging).

Both tests:

  • Pre-create an env file with old content
  • Run the prepare step
  • Verify the file contains only new keys (old keys removed)
  • Capture log output showing: "Environment file '{path}' already exists and will be overwritten"

Also fixed PrepareAsync to use the new EnvFile.Create() method instead of Load() to ensure it creates a fresh file without loading old entries.

All 60 tests pass.

- Remove SharedEnvFile property from DockerComposeEnvironmentResource
- Remove SharedEnvFile usage from DockerComposePublishingContext
- EnvFile is now loaded fresh on each publish operation
- All 60 tests pass

Co-authored-by: captainsafia <[email protected]>
Copilot finished work on behalf of captainsafia November 4, 2025 17:48
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.

.env files shouldn't be overriden by Docker Compose publisher

4 participants