Skip to content

Commit 70f2fc5

Browse files
gfraiteurclaude
andcommitted
Add -Mount parameter to DockerBuild.ps1 for additional directory mounts
Supports glob patterns (* and **), readonly by default, append :w for writable. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f87b2a0 commit 70f2fc5

2 files changed

Lines changed: 226 additions & 2 deletions

File tree

DockerBuild.ps1

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ param(
2222
[string]$Isolation = 'process', # Docker isolation mode (process or hyperv).
2323
[string]$Memory = '16g', # Docker memory limit.
2424
[int]$Cpus = [Environment]::ProcessorCount, # Docker CPU limit (defaults to host's CPU count).
25+
[string[]]$Mount, # Additional directories to mount from host (readonly by default, append :w for writable). Supports * and ** glob patterns.
2526
[Parameter(ValueFromRemainingArguments)]
2627
[string[]]$BuildArgs # Arguments passed to `Build.ps1` within the container (or Claude prompt if -Claude is specified).
2728
)
@@ -610,6 +611,117 @@ if ($grandparentDir -and (Test-Path $grandparentDir))
610611
}
611612
}
612613

614+
# Process -Mount parameter for additional directory mounts
615+
if ($Mount -and $Mount.Count -gt 0)
616+
{
617+
foreach ($mountSpec in $Mount)
618+
{
619+
# Check if writable (ends with :w)
620+
$isWritable = $false
621+
$pattern = $mountSpec
622+
if ($mountSpec -match ':w$')
623+
{
624+
$isWritable = $true
625+
$pattern = $mountSpec -replace ':w$', ''
626+
}
627+
628+
# Trim trailing slashes
629+
$pattern = $pattern.TrimEnd('\', '/')
630+
631+
$mountOption = if ($isWritable) { "" } else { ":ro" }
632+
633+
# Check if pattern contains glob characters
634+
if ($pattern -match '\*')
635+
{
636+
# Expand glob pattern to match directories only
637+
# Get the base directory (everything before the first glob)
638+
$patternParts = $pattern -split '[\\/]'
639+
$basePathParts = @()
640+
$globStartIndex = -1
641+
642+
for ($i = 0; $i -lt $patternParts.Count; $i++)
643+
{
644+
if ($patternParts[$i] -match '\*')
645+
{
646+
$globStartIndex = $i
647+
break
648+
}
649+
$basePathParts += $patternParts[$i]
650+
}
651+
652+
if ($basePathParts.Count -gt 0)
653+
{
654+
$basePath = $basePathParts -join [System.IO.Path]::DirectorySeparatorChar
655+
}
656+
else
657+
{
658+
$basePath = "."
659+
}
660+
661+
if (Test-Path $basePath)
662+
{
663+
# Determine if recursive search is needed (pattern contains **)
664+
$isRecursive = $pattern -match '\*\*'
665+
666+
# Build the glob pattern for the part after the base path
667+
$globPart = ($patternParts[$globStartIndex..($patternParts.Count - 1)]) -join [System.IO.Path]::DirectorySeparatorChar
668+
669+
# Get matching directories
670+
$matchingDirs = @()
671+
if ($isRecursive)
672+
{
673+
# For ** patterns, recurse and convert ** to * for -like matching
674+
# Replace ** with a regex-friendly pattern for matching
675+
$likePattern = $pattern -replace '\*\*', '*'
676+
$matchingDirs = Get-ChildItem -Path $basePath -Directory -Recurse -ErrorAction SilentlyContinue |
677+
Where-Object { $_.FullName -like $likePattern }
678+
}
679+
else
680+
{
681+
# For single * patterns, use direct matching without recursion
682+
$matchingDirs = Get-ChildItem -Path $basePath -Directory -ErrorAction SilentlyContinue |
683+
Where-Object { $_.FullName -like $pattern }
684+
}
685+
686+
if ($matchingDirs.Count -eq 0)
687+
{
688+
Write-Host "Warning: No directories matched pattern '$pattern'" -ForegroundColor Yellow
689+
}
690+
else
691+
{
692+
foreach ($dir in $matchingDirs)
693+
{
694+
$dirPath = $dir.FullName
695+
$rwStatus = if ($isWritable) { "writable" } else { "readonly" }
696+
Write-Host "Mounting from -Mount pattern '$pattern': $dirPath ($rwStatus)" -ForegroundColor Cyan
697+
$VolumeMappings += "${dirPath}:${dirPath}${mountOption}"
698+
$MountPoints += $dirPath
699+
}
700+
}
701+
}
702+
else
703+
{
704+
Write-Host "Warning: Base path '$basePath' for pattern '$pattern' does not exist" -ForegroundColor Yellow
705+
}
706+
}
707+
else
708+
{
709+
# No glob - mount directly if it's a directory
710+
if (Test-Path $pattern -PathType Container)
711+
{
712+
$rwStatus = if ($isWritable) { "writable" } else { "readonly" }
713+
Write-Host "Mounting from -Mount: $pattern ($rwStatus)" -ForegroundColor Cyan
714+
$VolumeMappings += "${pattern}:${pattern}${mountOption}"
715+
$MountPoints += $pattern
716+
}
717+
else
718+
{
719+
Write-Host "Warning: Mount path '$pattern' does not exist or is not a directory" -ForegroundColor Yellow
720+
}
721+
}
722+
}
723+
}
724+
613725
# Execute auto-generated DockerMounts.g.ps1 script to add more directory mounts.
614726
$dockerMountsScript = Join-Path $EngPath 'DockerMounts.g.ps1'
615727
if (Test-Path $dockerMountsScript)
@@ -1094,7 +1206,7 @@ if (-not $BuildImage)
10941206
{
10951207
# Run standard build mode
10961208
# Delete now and not in the container because it's much faster and lock error messages are more relevant.
1097-
Write-Host "Building the product in the container." -ForegroundColor Green
1209+
Write-Host "Running the script in the container." -ForegroundColor Green
10981210

10991211
# Prepare Build.ps1 arguments
11001212
if ($StartVsmon)

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

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ param(
2222
[string]$Isolation = 'process', # Docker isolation mode (process or hyperv).
2323
[string]$Memory = '16g', # Docker memory limit.
2424
[int]$Cpus = [Environment]::ProcessorCount, # Docker CPU limit (defaults to host's CPU count).
25+
[string[]]$Mount, # Additional directories to mount from host (readonly by default, append :w for writable). Supports * and ** glob patterns.
2526
[Parameter(ValueFromRemainingArguments)]
2627
[string[]]$BuildArgs # Arguments passed to `Build.ps1` within the container (or Claude prompt if -Claude is specified).
2728
)
@@ -610,6 +611,117 @@ if ($grandparentDir -and (Test-Path $grandparentDir))
610611
}
611612
}
612613

614+
# Process -Mount parameter for additional directory mounts
615+
if ($Mount -and $Mount.Count -gt 0)
616+
{
617+
foreach ($mountSpec in $Mount)
618+
{
619+
# Check if writable (ends with :w)
620+
$isWritable = $false
621+
$pattern = $mountSpec
622+
if ($mountSpec -match ':w$')
623+
{
624+
$isWritable = $true
625+
$pattern = $mountSpec -replace ':w$', ''
626+
}
627+
628+
# Trim trailing slashes
629+
$pattern = $pattern.TrimEnd('\', '/')
630+
631+
$mountOption = if ($isWritable) { "" } else { ":ro" }
632+
633+
# Check if pattern contains glob characters
634+
if ($pattern -match '\*')
635+
{
636+
# Expand glob pattern to match directories only
637+
# Get the base directory (everything before the first glob)
638+
$patternParts = $pattern -split '[\\/]'
639+
$basePathParts = @()
640+
$globStartIndex = -1
641+
642+
for ($i = 0; $i -lt $patternParts.Count; $i++)
643+
{
644+
if ($patternParts[$i] -match '\*')
645+
{
646+
$globStartIndex = $i
647+
break
648+
}
649+
$basePathParts += $patternParts[$i]
650+
}
651+
652+
if ($basePathParts.Count -gt 0)
653+
{
654+
$basePath = $basePathParts -join [System.IO.Path]::DirectorySeparatorChar
655+
}
656+
else
657+
{
658+
$basePath = "."
659+
}
660+
661+
if (Test-Path $basePath)
662+
{
663+
# Determine if recursive search is needed (pattern contains **)
664+
$isRecursive = $pattern -match '\*\*'
665+
666+
# Build the glob pattern for the part after the base path
667+
$globPart = ($patternParts[$globStartIndex..($patternParts.Count - 1)]) -join [System.IO.Path]::DirectorySeparatorChar
668+
669+
# Get matching directories
670+
$matchingDirs = @()
671+
if ($isRecursive)
672+
{
673+
# For ** patterns, recurse and convert ** to * for -like matching
674+
# Replace ** with a regex-friendly pattern for matching
675+
$likePattern = $pattern -replace '\*\*', '*'
676+
$matchingDirs = Get-ChildItem -Path $basePath -Directory -Recurse -ErrorAction SilentlyContinue |
677+
Where-Object { $_.FullName -like $likePattern }
678+
}
679+
else
680+
{
681+
# For single * patterns, use direct matching without recursion
682+
$matchingDirs = Get-ChildItem -Path $basePath -Directory -ErrorAction SilentlyContinue |
683+
Where-Object { $_.FullName -like $pattern }
684+
}
685+
686+
if ($matchingDirs.Count -eq 0)
687+
{
688+
Write-Host "Warning: No directories matched pattern '$pattern'" -ForegroundColor Yellow
689+
}
690+
else
691+
{
692+
foreach ($dir in $matchingDirs)
693+
{
694+
$dirPath = $dir.FullName
695+
$rwStatus = if ($isWritable) { "writable" } else { "readonly" }
696+
Write-Host "Mounting from -Mount pattern '$pattern': $dirPath ($rwStatus)" -ForegroundColor Cyan
697+
$VolumeMappings += "${dirPath}:${dirPath}${mountOption}"
698+
$MountPoints += $dirPath
699+
}
700+
}
701+
}
702+
else
703+
{
704+
Write-Host "Warning: Base path '$basePath' for pattern '$pattern' does not exist" -ForegroundColor Yellow
705+
}
706+
}
707+
else
708+
{
709+
# No glob - mount directly if it's a directory
710+
if (Test-Path $pattern -PathType Container)
711+
{
712+
$rwStatus = if ($isWritable) { "writable" } else { "readonly" }
713+
Write-Host "Mounting from -Mount: $pattern ($rwStatus)" -ForegroundColor Cyan
714+
$VolumeMappings += "${pattern}:${pattern}${mountOption}"
715+
$MountPoints += $pattern
716+
}
717+
else
718+
{
719+
Write-Host "Warning: Mount path '$pattern' does not exist or is not a directory" -ForegroundColor Yellow
720+
}
721+
}
722+
}
723+
}
724+
613725
# Execute auto-generated DockerMounts.g.ps1 script to add more directory mounts.
614726
$dockerMountsScript = Join-Path $EngPath 'DockerMounts.g.ps1'
615727
if (Test-Path $dockerMountsScript)
@@ -1094,7 +1206,7 @@ if (-not $BuildImage)
10941206
{
10951207
# Run standard build mode
10961208
# Delete now and not in the container because it's much faster and lock error messages are more relevant.
1097-
Write-Host "Building the product in the container." -ForegroundColor Green
1209+
Write-Host "Running the script in the container." -ForegroundColor Green
10981210

10991211
# Prepare Build.ps1 arguments
11001212
if ($StartVsmon)

0 commit comments

Comments
 (0)