Skip to content

Commit 1816797

Browse files
committed
Improve Docker build and MCP server experience
- Skip cleanup by default, add -Clean flag - Map NuGet packages to same path in host/container - Add distinct beep sounds for MCP auto-approve vs manual
1 parent 046aa59 commit 1816797

File tree

6 files changed

+90
-38
lines changed

6 files changed

+90
-38
lines changed

DockerBuild.ps1

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ param(
66
[switch]$Interactive, # Opens an interactive PowerShell session
77
[switch]$BuildImage, # Only builds the image, but does not build the product.
88
[switch]$NoBuildImage, # Does not build the image.
9-
[switch]$NoClean, # Does not clean up.
9+
[switch]$Clean, # Performs cleanup of bin and obj directories.
1010
[switch]$NoNuGetCache, # Does not mount the host nuget cache in the container.
1111
[switch]$KeepEnv, # Does not override the env.g.json file.
1212
[switch]$Claude, # Run Claude CLI instead of Build.ps1. Use -Claude for interactive, -Claude "prompt" for non-interactive.
@@ -52,6 +52,17 @@ function New-EnvJson
5252
}
5353
}
5454

55+
# Add NUGET_PACKAGES with default if not set
56+
if (-not $envVariables.ContainsKey("NUGET_PACKAGES"))
57+
{
58+
$nugetPackages = $env:NUGET_PACKAGES
59+
if ([string]::IsNullOrEmpty($nugetPackages))
60+
{
61+
$nugetPackages = Join-Path $env:USERPROFILE ".nuget\packages"
62+
}
63+
$envVariables["NUGET_PACKAGES"] = $nugetPackages
64+
}
65+
5566
# Add secrets from the PostSharpBuildEnv key vault, on our development machines.
5667
# On CI agents, these environment variables are supposed to be set by the host.
5768
if ($LoadEnvFromKeyVault -or ($env:IS_POSTSHARP_OWNED -and -not $env:IS_TEAMCITY_AGENT))
@@ -140,6 +151,14 @@ function New-ClaudeEnvJson
140151
$claudeEnv["GIT_USER_EMAIL"] = $gitUserEmail
141152
}
142153

154+
# Add NUGET_PACKAGES with default if not set
155+
$nugetPackages = $env:NUGET_PACKAGES
156+
if ([string]::IsNullOrEmpty($nugetPackages))
157+
{
158+
$nugetPackages = Join-Path $env:USERPROFILE ".nuget\packages"
159+
}
160+
$claudeEnv["NUGET_PACKAGES"] = $nugetPackages
161+
143162
# Convert to JSON and save
144163
$jsonPath = Join-Path $dockerContextDirectory "env.g.json"
145164

@@ -310,9 +329,9 @@ if ($Claude -and -not $NoMcp)
310329
}
311330
}
312331

313-
# When building locally (as opposed as on the build agent), we must do a complete cleanup because
332+
# When building locally (as opposed as on the build agent), we can optionally do a complete cleanup because
314333
# obj files may point to the host filesystem.
315-
if (-not $env:IS_TEAMCITY_AGENT -and -not $NoClean)
334+
if ($Clean)
316335
{
317336
Write-Host "Cleaning up." -ForegroundColor Green
318337
Get-ChildItem "bin" -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
@@ -388,15 +407,23 @@ if (Test-Path $gitSystemDir)
388407
# Mount the host NuGet cache in the container.
389408
if (-not $NoNuGetCache)
390409
{
391-
$nugetCacheDir = Join-Path $env:USERPROFILE ".nuget\packages"
410+
# Use NUGET_PACKAGES from environment or default to user profile
411+
$nugetCacheDir = $env:NUGET_PACKAGES
412+
if ([string]::IsNullOrEmpty($nugetCacheDir))
413+
{
414+
$nugetCacheDir = Join-Path $env:USERPROFILE ".nuget\packages"
415+
}
416+
392417
Write-Host "NuGet cache directory: $nugetCacheDir" -ForegroundColor Cyan
393418
if (-not (Test-Path $nugetCacheDir))
394419
{
395420
Write-Host "Creating NuGet cache directory on host: $nugetCacheDir"
396421
New-Item -ItemType Directory -Force -Path $nugetCacheDir | Out-Null
397422
}
398423

399-
$VolumeMappings += "${nugetCacheDir}:c:\packages"
424+
# Mount to the same path in the container (will be transformed by Get-ContainerPath later)
425+
$VolumeMappings += "${nugetCacheDir}:${nugetCacheDir}"
426+
$MountPoints += $nugetCacheDir
400427
}
401428

402429
# Mount VS Remote Debugger

Dockerfile

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ RUN Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/dow
4242
if ($process.ExitCode -ne 0) { exit $process.ExitCode }; `
4343
Remove-Item PowerShell.msi
4444

45-
# Add PowerShell 7 to PATH using ENV directive (persists across shell switches)
4645
ENV PATH="C:\Program Files\PowerShell\7;${PATH}"
4746

4847

@@ -78,8 +77,5 @@ RUN c:\ReadEnvironmentVariables.ps1 c:\env.g.json
7877
# Copy Init.g.ps1 placeholder (drive mappings handled inline in docker run)
7978
COPY Init.g.ps1 c:\Init.g.ps1
8079

81-
# Configure NuGet
82-
ENV NUGET_PACKAGES=c:\packages
83-
8480
# Configure .NET SDK
8581
ENV DOTNET_NOLOGO=1

Dockerfile.claude

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ RUN Invoke-WebRequest -Uri https://github.com/PowerShell/PowerShell/releases/dow
4242
if ($process.ExitCode -ne 0) { exit $process.ExitCode }; `
4343
Remove-Item PowerShell.msi
4444

45-
# Add PowerShell 7 to PATH using ENV directive (persists across shell switches)
4645
ENV PATH="C:\Program Files\PowerShell\7;${PATH}"
4746

4847

@@ -58,13 +57,11 @@ RUN & .\dotnet-install.ps1 -Version 9.0.305 -InstallDir 'C:\Program Files\dotnet
5857

5958

6059
# Install Node.js
61-
# Install Node.js 22.0.0 directly
6260
RUN Invoke-WebRequest -Uri "https://nodejs.org/dist/v22.0.0/node-v22.0.0-win-x64.zip" -OutFile node.zip; `
6361
Expand-Archive node.zip -DestinationPath C:\; `
6462
Rename-Item "C:\node-v22.0.0-win-x64" "C:\nodejs"; `
6563
Remove-Item node.zip
6664

67-
# Add Node.js to PATH using ENV directive (persists across shell switches)
6865
ENV PATH="C:\nodejs;${PATH}"
6966

7067

@@ -74,7 +71,6 @@ RUN Invoke-WebRequest -Uri https://github.com/cli/cli/releases/download/v2.63.2/
7471
if ($process.ExitCode -ne 0) { exit $process.ExitCode }; `
7572
Remove-Item gh.msi
7673

77-
# Add GitHub CLI to PATH using ENV directive (persists across shell switches)
7874
ENV PATH="C:\Program Files\GitHub CLI;${PATH}"
7975

8076

@@ -120,8 +116,5 @@ RUN c:\ReadEnvironmentVariables.ps1 c:\env.g.json
120116
# Copy Init.g.ps1 placeholder (drive mappings handled inline in docker run)
121117
COPY Init.g.ps1 c:\Init.g.ps1
122118

123-
# Configure NuGet
124-
ENV NUGET_PACKAGES=c:\packages
125-
126119
# Configure .NET SDK
127120
ENV DOTNET_NOLOGO=1

src/PostSharp.Engineering.BuildTools/Docker/EpilogueComponent.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ ARG MOUNTPOINTS
3636
# Copy Init.g.ps1 placeholder (drive mappings handled inline in docker run)
3737
COPY Init.g.ps1 c:\Init.g.ps1
3838
39-
# Configure NuGet
40-
ENV NUGET_PACKAGES=c:\packages
41-
4239
# Configure .NET SDK
4340
ENV DOTNET_NOLOGO=1
4441
""" );

src/PostSharp.Engineering.BuildTools/Mcp/Services/ApprovalPrompter.cs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,32 @@ public Task<bool> RequestApprovalAsync(
2727
RiskAssessment regexAssessment )
2828
#pragma warning restore CA1822
2929
{
30-
// Beep to alert the user
30+
// Auto-approve LOW risk commands when combined assessment recommends approval
31+
if ( combinedAssessment.Level == RiskLevel.Low && combinedAssessment.Recommendation == Recommendation.Approve )
32+
{
33+
// Pleasant single beep for auto-approve
34+
try
35+
{
36+
#pragma warning disable CA1416 // Platform compatibility - we handle non-Windows in catch
37+
Console.Beep( 1200, 150 );
38+
#pragma warning restore CA1416
39+
}
40+
catch
41+
{
42+
// Beep may not be supported on all systems
43+
}
44+
45+
AnsiConsole.WriteLine();
46+
AnsiConsole.MarkupLine( "[green]Auto-approved (LOW risk)[/]" );
47+
AnsiConsole.MarkupLine( $"[dim]Reason: {Markup.Escape( combinedAssessment.Reason )}[/]" );
48+
AnsiConsole.WriteLine();
49+
50+
return Task.FromResult( true );
51+
}
52+
53+
// HIGH/CRITICAL risk commands always require manual approval (no auto-reject)
54+
55+
// Alert beep for user approval required
3156
try
3257
{
3358
#pragma warning disable CA1416 // Platform compatibility - we handle non-Windows in catch
@@ -46,19 +71,6 @@ public Task<bool> RequestApprovalAsync(
4671

4772
try
4873
{
49-
// Auto-approve LOW risk commands when combined assessment recommends approval
50-
if ( combinedAssessment.Level == RiskLevel.Low && combinedAssessment.Recommendation == Recommendation.Approve )
51-
{
52-
AnsiConsole.WriteLine();
53-
AnsiConsole.MarkupLine( "[green]Auto-approved (LOW risk)[/]" );
54-
AnsiConsole.MarkupLine( $"[dim]Reason: {Markup.Escape( combinedAssessment.Reason )}[/]" );
55-
AnsiConsole.WriteLine();
56-
57-
return Task.FromResult( true );
58-
}
59-
60-
// HIGH/CRITICAL risk commands always require manual approval (no auto-reject)
61-
6274
AnsiConsole.WriteLine();
6375
AnsiConsole.Write( new Rule( "[yellow]Command Approval Request[/]" ) );
6476
AnsiConsole.WriteLine();

src/PostSharp.Engineering.BuildTools/Resources/DockerBuild.ps1

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ param(
66
[switch]$Interactive, # Opens an interactive PowerShell session
77
[switch]$BuildImage, # Only builds the image, but does not build the product.
88
[switch]$NoBuildImage, # Does not build the image.
9-
[switch]$NoClean, # Does not clean up.
9+
[switch]$Clean, # Performs cleanup of bin and obj directories.
1010
[switch]$NoNuGetCache, # Does not mount the host nuget cache in the container.
1111
[switch]$KeepEnv, # Does not override the env.g.json file.
1212
[switch]$Claude, # Run Claude CLI instead of Build.ps1. Use -Claude for interactive, -Claude "prompt" for non-interactive.
@@ -52,6 +52,17 @@ function New-EnvJson
5252
}
5353
}
5454

55+
# Add NUGET_PACKAGES with default if not set
56+
if (-not $envVariables.ContainsKey("NUGET_PACKAGES"))
57+
{
58+
$nugetPackages = $env:NUGET_PACKAGES
59+
if ([string]::IsNullOrEmpty($nugetPackages))
60+
{
61+
$nugetPackages = Join-Path $env:USERPROFILE ".nuget\packages"
62+
}
63+
$envVariables["NUGET_PACKAGES"] = $nugetPackages
64+
}
65+
5566
# Add secrets from the PostSharpBuildEnv key vault, on our development machines.
5667
# On CI agents, these environment variables are supposed to be set by the host.
5768
if ($LoadEnvFromKeyVault -or ($env:IS_POSTSHARP_OWNED -and -not $env:IS_TEAMCITY_AGENT))
@@ -140,6 +151,14 @@ function New-ClaudeEnvJson
140151
$claudeEnv["GIT_USER_EMAIL"] = $gitUserEmail
141152
}
142153

154+
# Add NUGET_PACKAGES with default if not set
155+
$nugetPackages = $env:NUGET_PACKAGES
156+
if ([string]::IsNullOrEmpty($nugetPackages))
157+
{
158+
$nugetPackages = Join-Path $env:USERPROFILE ".nuget\packages"
159+
}
160+
$claudeEnv["NUGET_PACKAGES"] = $nugetPackages
161+
143162
# Convert to JSON and save
144163
$jsonPath = Join-Path $dockerContextDirectory "env.g.json"
145164

@@ -310,9 +329,9 @@ if ($Claude -and -not $NoMcp)
310329
}
311330
}
312331

313-
# When building locally (as opposed as on the build agent), we must do a complete cleanup because
332+
# When building locally (as opposed as on the build agent), we can optionally do a complete cleanup because
314333
# obj files may point to the host filesystem.
315-
if (-not $env:IS_TEAMCITY_AGENT -and -not $NoClean)
334+
if ($Clean)
316335
{
317336
Write-Host "Cleaning up." -ForegroundColor Green
318337
Get-ChildItem "bin" -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
@@ -388,15 +407,23 @@ if (Test-Path $gitSystemDir)
388407
# Mount the host NuGet cache in the container.
389408
if (-not $NoNuGetCache)
390409
{
391-
$nugetCacheDir = Join-Path $env:USERPROFILE ".nuget\packages"
410+
# Use NUGET_PACKAGES from environment or default to user profile
411+
$nugetCacheDir = $env:NUGET_PACKAGES
412+
if ([string]::IsNullOrEmpty($nugetCacheDir))
413+
{
414+
$nugetCacheDir = Join-Path $env:USERPROFILE ".nuget\packages"
415+
}
416+
392417
Write-Host "NuGet cache directory: $nugetCacheDir" -ForegroundColor Cyan
393418
if (-not (Test-Path $nugetCacheDir))
394419
{
395420
Write-Host "Creating NuGet cache directory on host: $nugetCacheDir"
396421
New-Item -ItemType Directory -Force -Path $nugetCacheDir | Out-Null
397422
}
398423

399-
$VolumeMappings += "${nugetCacheDir}:c:\packages"
424+
# Mount to the same path in the container (will be transformed by Get-ContainerPath later)
425+
$VolumeMappings += "${nugetCacheDir}:${nugetCacheDir}"
426+
$MountPoints += $nugetCacheDir
400427
}
401428

402429
# Mount VS Remote Debugger

0 commit comments

Comments
 (0)