@@ -315,10 +315,6 @@ if ($Claude -and -not $NoMcp)
315315if (-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
386382if (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 = " "
578571foreach ($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
0 commit comments