Skip to content

Commit 4c7966c

Browse files
committed
Refactor Docker volume mappings and add deduplication (25-12-15)
1 parent 8bbfc8c commit 4c7966c

File tree

6 files changed

+113
-83
lines changed

6 files changed

+113
-83
lines changed

DockerBuild.ps1

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -315,10 +315,6 @@ if ($Claude -and -not $NoMcp)
315315
if (-not $env:IS_TEAMCITY_AGENT -and -not $NoClean)
316316
{
317317
Write-Host "Cleaning up." -ForegroundColor Green
318-
if (Test-Path "artifacts")
319-
{
320-
Remove-Item artifacts -Force -Recurse -ErrorAction SilentlyContinue
321-
}
322318
Get-ChildItem "bin" -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
323319
Get-ChildItem "obj" -Recurse | Remove-Item -Force -Recurse -ErrorAction SilentlyContinue
324320
}
@@ -375,8 +371,8 @@ if (-not (Test-Path $dockerContextDirectory))
375371
}
376372

377373

378-
# Prepare volume mappings
379-
$VolumeMappings = @("-v", "${SourceDirName}:${SourceDirName}")
374+
# Prepare volume mappings (stored as mapping strings, "-v" flags added later)
375+
$VolumeMappings = @("${SourceDirName}:${SourceDirName}")
380376
$MountPoints = @($SourceDirName, "c:\packages")
381377
$GitDirectories = @($SourceDirName)
382378

@@ -385,7 +381,7 @@ $gitSystemDir = "$BuildAgentPath\system\git"
385381

386382
if (Test-Path $gitSystemDir)
387383
{
388-
$VolumeMappings += @("-v", "${gitSystemDir}:${gitSystemDir}:ro")
384+
$VolumeMappings += "${gitSystemDir}:${gitSystemDir}:ro"
389385
$MountPoints += $gitSystemDir
390386
}
391387

@@ -400,7 +396,7 @@ if (-not $NoNuGetCache)
400396
New-Item -ItemType Directory -Force -Path $nugetCacheDir | Out-Null
401397
}
402398

403-
$VolumeMappings += @("-v", "${nugetCacheDir}:c:\packages")
399+
$VolumeMappings += "${nugetCacheDir}:c:\packages"
404400
}
405401

406402
# Mount VS Remote Debugger
@@ -420,7 +416,7 @@ if ($StartVsmon)
420416
}
421417

422418
$remoteDebuggerContainerDir = "C:\msvsmon"
423-
$VolumeMappings += @("-v", "${remoteDebuggerHostDir}:${remoteDebuggerContainerDir}:ro")
419+
$VolumeMappings += "${remoteDebuggerHostDir}:${remoteDebuggerContainerDir}:ro"
424420
$MountPoints += $remoteDebuggerContainerDir
425421

426422
}
@@ -437,7 +433,7 @@ if (Test-Path $sourceDependenciesDir)
437433
if (-not [string]::IsNullOrEmpty($targetPath) -and (Test-Path $targetPath))
438434
{
439435
Write-Host "Found symbolic link '$( $link.Name )' -> '$targetPath'" -ForegroundColor Cyan
440-
$VolumeMappings += @("-v", "${targetPath}:${targetPath}:ro")
436+
$VolumeMappings += "${targetPath}:${targetPath}:ro"
441437
$MountPoints += $targetPath
442438
$GitDirectories += $targetPath
443439
}
@@ -467,15 +463,10 @@ if ($parentDir -and (Test-Path $parentDir) -and ($parentDirName -like "PostSharp
467463
foreach ($sibling in $siblingDirs)
468464
{
469465
$siblingPath = $sibling.FullName
470-
# Skip if already mounted
471-
$alreadyMounted = $VolumeMappings | Where-Object { $_ -like "*${siblingPath}:*" }
472-
if (-not $alreadyMounted)
473-
{
474-
Write-Host "Mounting product family sibling: $siblingPath" -ForegroundColor Cyan
475-
$VolumeMappings += @("-v", "${siblingPath}:${siblingPath}:ro")
476-
$MountPoints += $siblingPath
477-
$GitDirectories += $siblingPath
478-
}
466+
Write-Host "Mounting product family sibling: $siblingPath" -ForegroundColor Cyan
467+
$VolumeMappings += "${siblingPath}:${siblingPath}:ro"
468+
$MountPoints += $siblingPath
469+
$GitDirectories += $siblingPath
479470
}
480471
}
481472

@@ -489,16 +480,11 @@ if ($grandparentDir -and (Test-Path $grandparentDir))
489480

490481
foreach ($engDir in $engineeringDirs)
491482
{
492-
$engPath = $engDir.FullName
493-
# Skip if already mounted
494-
$alreadyMounted = $VolumeMappings | Where-Object { $_ -like "*${engPath}:*" }
495-
if (-not $alreadyMounted)
496-
{
497-
Write-Host "Mounting engineering repo: $engPath" -ForegroundColor Cyan
498-
$VolumeMappings += @("-v", "${engPath}:${engPath}:ro")
499-
$MountPoints += $engPath
500-
$GitDirectories += $engPath
501-
}
483+
$engDirPath = $engDir.FullName
484+
Write-Host "Mounting engineering repo: $engDirPath" -ForegroundColor Cyan
485+
$VolumeMappings += "${engDirPath}:${engDirPath}:ro"
486+
$MountPoints += $engDirPath
487+
$GitDirectories += $engDirPath
502488
}
503489
}
504490

@@ -509,6 +495,11 @@ if (Test-Path $dockerMountsScript)
509495
Write-Host "Importing Docker mount points from $dockerMountsScript" -ForegroundColor Cyan
510496
. $dockerMountsScript
511497
}
498+
elseif (-not $env:IS_TEAMCITY_AGENT)
499+
{
500+
Write-Error "DockerMounts.g.ps1 not found at '$dockerMountsScript'. Run './Build.ps1 prepare' or './Build.ps1 dependencies update' to generate it."
501+
exit 1
502+
}
512503

513504
# Handle non-C: drive letters for Docker (Windows containers only have C: by default)
514505
# We mount X:\foo to C:\X\foo in the container, then use subst to create the X: drive
@@ -531,23 +522,20 @@ function Get-ContainerPath($hostPath)
531522

532523
# Transform all volume mappings to use container paths
533524
$transformedVolumeMappings = @()
534-
for ($i = 0; $i -lt $VolumeMappings.Count; $i += 2)
525+
foreach ($mapping in $VolumeMappings)
535526
{
536-
$flag = $VolumeMappings[$i]
537-
$mapping = $VolumeMappings[$i + 1]
538-
539527
# Parse volume mapping: hostPath:containerPath[:options]
540528
if ($mapping -match '^([A-Za-z]:\\[^:]*):([A-Za-z]:\\[^:]*)(:.+)?$')
541529
{
542530
$hostPath = $Matches[1]
543531
$containerPath = $Matches[2]
544532
$options = $Matches[3]
545533
$newContainerPath = Get-ContainerPath $containerPath
546-
$transformedVolumeMappings += @($flag, "${hostPath}:${newContainerPath}${options}")
534+
$transformedVolumeMappings += "${hostPath}:${newContainerPath}${options}"
547535
}
548536
else
549537
{
550-
$transformedVolumeMappings += @($flag, $mapping)
538+
$transformedVolumeMappings += $mapping
551539
}
552540
}
553541
$VolumeMappings = $transformedVolumeMappings
@@ -573,6 +561,11 @@ foreach ($dir in $GitDirectories)
573561
}
574562
$GitDirectories = $expandedGitDirectories
575563

564+
# Deduplicate again after transformations and expansions (case-insensitive for Windows paths)
565+
$VolumeMappings = $VolumeMappings | Group-Object { $_.ToLower() } | ForEach-Object { $_.Group[0] }
566+
$MountPoints = $MountPoints | Group-Object { $_.ToLower() } | ForEach-Object { $_.Group[0] }
567+
$GitDirectories = $GitDirectories | Group-Object { $_.ToLower() } | ForEach-Object { $_.Group[0] }
568+
576569
# Build subst commands string for inline execution in docker run
577570
$substCommandsInline = ""
578571
foreach ($letter in $driveLetters.Keys | Sort-Object)
@@ -774,7 +767,7 @@ if (-not $BuildImage)
774767
# Mount .claude directory (settings and credentials)
775768
if (Test-Path "$hostUserProfile\.claude")
776769
{
777-
$VolumeMappings += @("-v", "${hostUserProfile}\.claude:${containerUserProfile}\.claude")
770+
$VolumeMappings += "${hostUserProfile}\.claude:${containerUserProfile}\.claude"
778771
}
779772

780773
# Copy .claude.json to docker-context (cannot mount files on Windows Docker)
@@ -798,10 +791,16 @@ if (-not $BuildImage)
798791
# Mount .cache\claude (cache)
799792
if (Test-Path "$hostUserProfile\.cache\claude")
800793
{
801-
$VolumeMappings += @("-v", "${hostUserProfile}\.cache\claude:${containerUserProfile}\.cache\claude")
794+
$VolumeMappings += "${hostUserProfile}\.cache\claude:${containerUserProfile}\.cache\claude"
802795
}
803796

804-
$VolumeMappingsAsString = $VolumeMappings -join " "
797+
# Convert volume mappings to docker args format (interleave "-v" flags)
798+
$volumeArgs = @()
799+
foreach ($mapping in $VolumeMappings)
800+
{
801+
$volumeArgs += @("-v", $mapping)
802+
}
803+
$VolumeMappingsAsString = ($VolumeMappings | ForEach-Object { "-v $_" }) -join " "
805804

806805
# Extract Claude prompt from remaining arguments if present
807806
# Usage: -Claude for interactive, -Claude "prompt" for non-interactive
@@ -844,7 +843,7 @@ if (-not $BuildImage)
844843
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -e HOME=$containerUserProfile -e USERPROFILE=$containerUserProfile -w $ContainerSourceDir $ImageTag `"$pwshPath`" -Command `"$inlineScript`"" -ForegroundColor Cyan
845844

846845
try {
847-
docker run --rm --memory=12g $dockerArgs @VolumeMappings @envArgs -w $ContainerSourceDir $ImageTag $pwshPath -Command $inlineScript
846+
docker run --rm --memory=12g $dockerArgs @volumeArgs @envArgs -w $ContainerSourceDir $ImageTag $pwshPath -Command $inlineScript
848847
$dockerExitCode = $LASTEXITCODE
849848
}
850849
finally {
@@ -927,7 +926,14 @@ if (-not $BuildImage)
927926
}
928927

929928
$buildArgsString = $BuildArgs -join " "
930-
$VolumeMappingsAsString = $VolumeMappings -join " "
929+
930+
# Convert volume mappings to docker args format (interleave "-v" flags)
931+
$volumeArgs = @()
932+
foreach ($mapping in $VolumeMappings)
933+
{
934+
$volumeArgs += @("-v", $mapping)
935+
}
936+
$VolumeMappingsAsString = ($VolumeMappings | ForEach-Object { "-v $_" }) -join " "
931937
$dockerArgsAsString = $dockerArgs -join " "
932938

933939
# Build inline script: subst drives, run init, cd to source, run build
@@ -936,7 +942,7 @@ if (-not $BuildImage)
936942
$pwshPath = 'C:\Program Files\PowerShell\7\pwsh.exe'
937943
Write-Host "Executing: ``docker run --rm --memory=12g $dockerArgsAsString $VolumeMappingsAsString -w $ContainerSourceDir $ImageTag `"$pwshPath`" $pwshArgs -Command `"$inlineScript`"" -ForegroundColor Cyan
938944

939-
docker run --rm --memory=12g $dockerArgs @VolumeMappings -w $ContainerSourceDir $ImageTag $pwshPath $pwshArgs -Command $inlineScript
945+
docker run --rm --memory=12g $dockerArgs @volumeArgs -w $ContainerSourceDir $ImageTag $pwshPath $pwshArgs -Command $inlineScript
940946
if ($LASTEXITCODE -ne 0)
941947
{
942948
Write-Host "Docker run (build) failed with exit code $LASTEXITCODE" -ForegroundColor Red

Dockerfile.claude

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ RUN C:\\nodejs\\npm.cmd install --global @anthropic-ai/claude-code
9090
# Set HOME/USERPROFILE so Claude CLI finds credentials during build
9191
ENV HOME=C:\\Users\\ContainerUser
9292
ENV USERPROFILE=C:\\Users\\ContainerUser
93+
ENV CLAUDE_CODE_SHELL=pwsh
9394

9495
# Create Claude config directory (credentials are mounted at runtime)
9596
RUN mkdir C:\Users\ContainerUser\.claude

src/PostSharp.Engineering.BuildTools/ContinuousIntegration/GenerateScriptsCommand.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
using JetBrains.Annotations;
44
using PostSharp.Engineering.BuildTools.Build;
5+
using PostSharp.Engineering.BuildTools.Build.Model;
56
using PostSharp.Engineering.BuildTools.ContinuousIntegration.TeamCity.Generation;
7+
using PostSharp.Engineering.BuildTools.Dependencies.Model;
68
using PostSharp.Engineering.BuildTools.Docker;
79
using PostSharp.Engineering.BuildTools.Utilities;
810

@@ -46,6 +48,20 @@ public static bool Execute( BuildContext context, CommonCommandSettings settings
4648
{
4749
return false;
4850
}
51+
52+
// Generate DockerMounts.g.ps1 to define additional mount points for dependencies
53+
var buildSettings = new BuildSettings { BuildConfiguration = BuildConfiguration.Debug };
54+
buildSettings.Initialize( context );
55+
56+
if ( !DependenciesConfigurationFile.TryLoad( context, buildSettings, buildSettings.BuildConfiguration, out var dependenciesOverrideFile ) )
57+
{
58+
return false;
59+
}
60+
61+
if ( !dependenciesOverrideFile.TryWrite( context ) )
62+
{
63+
return false;
64+
}
4965
}
5066

5167
context.Console.WriteSuccess( "Generating build scripts was successful." );

src/PostSharp.Engineering.BuildTools/Dependencies/Model/DependenciesConfigurationFile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ void AddImport( string file, bool required = true, string? label = null, bool ad
350350
{
351351
var mountPoint = Path.GetDirectoryName( file )!;
352352
dockerMountsWriter!.WriteLine( $"# {file}" );
353-
dockerMountsWriter.WriteLine( $"$VolumeMappings += @(\"-v\", \"{mountPoint}:{mountPoint}:ro\")" );
353+
dockerMountsWriter.WriteLine( $"$VolumeMappings += \"{mountPoint}:{mountPoint}:ro\"" );
354354
dockerMountsWriter.WriteLine( $"$MountPoints += \"{mountPoint}\"" );
355355
dockerMountsWriter.WriteLine();
356356
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public override void WriteDockerfile( TextWriter writer )
5151
# Set HOME/USERPROFILE so Claude CLI finds credentials during build
5252
ENV HOME=C:\\Users\\ContainerUser
5353
ENV USERPROFILE=C:\\Users\\ContainerUser
54+
ENV CLAUDE_CODE_SHELL=pwsh
5455
5556
# Create Claude config directory (credentials are mounted at runtime)
5657
RUN mkdir C:\Users\ContainerUser\.claude

0 commit comments

Comments
 (0)