2020 [string ]$Dockerfile , # Path to custom Dockerfile (defaults to Dockerfile or Dockerfile.claude based on -Claude).
2121 [switch ]$NoInit , # Do not generate or call Init.g.ps1 (skips git config, safe.directory, etc).
2222 [string ]$Isolation = ' process' , # Docker isolation mode (process or hyperv). Memory/CPU limits only apply to hyperv.
23- [string ]$Memory , # Docker memory limit (e.g., "8g"). Only used with hyperv isolation .
23+ [string ]$Memory = ' 16g ' , # Minimum required memory for the container. On build agents, actual memory is calculated from BuildAgentMemorySize .
2424 [int ]$Cpus = [Environment ]::ProcessorCount, # Docker CPU limit. Only used with hyperv isolation.
2525 [string []]$Mount , # Additional directories to mount from host (readonly by default, append :w for writable). Supports * and ** glob patterns.
2626 [string []]$Env , # Additional environment variables to pass from host to container.
@@ -45,11 +45,7 @@ $EnvironmentVariables = '<ENVIRONMENT_VARIABLES>'
4545$ErrorActionPreference = " Stop"
4646$dockerContextDirectory = " $EngPath /docker-context"
4747
48- # Detect platform (use built-in variables if available, fallback for older PowerShell)
49- if ($null -eq $IsWindows )
50- {
51- $IsWindows = [System.Environment ]::OSVersion.Platform -eq [System.PlatformID ]::Win32NT
52- }
48+ # $IsWindows is a built-in automatic variable in PowerShell 6+.
5349$IsUnix = -not $IsWindows # Covers both Linux and macOS
5450
5551# Docker isolation is Windows-only
@@ -562,6 +558,10 @@ if ($Claude -and -not $NoMcp)
562558if ($Clean )
563559{
564560 Write-Host " Cleaning up." - ForegroundColor Green
561+ if (Test-Path " artifacts" )
562+ {
563+ Remove-Item artifacts - Force - Recurse - ErrorAction SilentlyContinue
564+ }
565565 Get-ChildItem " bin" - Recurse | Remove-Item - Force - Recurse - ErrorAction SilentlyContinue
566566 Get-ChildItem " obj" - Recurse | Remove-Item - Force - Recurse - ErrorAction SilentlyContinue
567567}
@@ -591,20 +591,20 @@ if (-not $KeepEnv)
591591 }
592592
593593 # Add git identity to environment
594+ $env: GIT_USER_EMAIL = git config -- global user.email
595+ $env: GIT_USER_NAME = git config -- global user.name
596+
594597 if ($env: IS_TEAMCITY_AGENT )
595598 {
596- # On TeamCity agents, check if the environment variables are set.
597- if (-not $env: GIT_USER_EMAIL -or -not $ env: GIT_USER_NAME )
599+ # On TeamCity agents, use defaults if not set.
600+ if (-not $env: GIT_USER_EMAIL )
598601 {
599- Write-Error " On TeamCity agents, the GIT_USER_EMAIL and GIT_USER_NAME environment variables must be set."
600- exit 1
602+ $env: GIT_USER_EMAIL = ' teamcity@postsharp.net'
603+ }
604+ if (-not $env: GIT_USER_NAME )
605+ {
606+ $env: GIT_USER_NAME = ' teamcity'
601607 }
602- }
603- else
604- {
605- # On developer machines, use the current git user.
606- $env: GIT_USER_EMAIL = git config -- global user.email
607- $env: GIT_USER_NAME = git config -- global user.name
608608 }
609609
610610 New-EnvJson - EnvironmentVariableList $EnvironmentVariables
@@ -1195,6 +1195,72 @@ if (-not $existingContainerId)
11951195 }
11961196}
11971197
1198+ # Memory validation and calculation
1199+ $hostReservedMemoryGB = 8
1200+
1201+ # Parse Memory to get numeric value in GB
1202+ function ConvertTo-MemoryGB
1203+ {
1204+ param ([string ]$MemoryString )
1205+
1206+ if ($MemoryString -match ' ^(\d+(?:\.\d+)?)\s*[gG][bB]?$' )
1207+ {
1208+ return [double ]$Matches [1 ]
1209+ }
1210+ elseif ($MemoryString -match ' ^(\d+(?:\.\d+)?)\s*[mM][bB]?$' )
1211+ {
1212+ return [double ]$Matches [1 ] / 1024
1213+ }
1214+ elseif ($MemoryString -match ' ^(\d+)$' )
1215+ {
1216+ # Assume bytes
1217+ return [double ]$Matches [1 ] / 1024 / 1024 / 1024
1218+ }
1219+ else
1220+ {
1221+ Write-Error " Invalid memory format: $MemoryString . Use formats like '12g', '12GB', '12288m', or '12288MB'."
1222+ exit 1
1223+ }
1224+ }
1225+
1226+ # Determine actual Docker memory limit
1227+ if ($env: BuildAgentMemorySize )
1228+ {
1229+ # On build agents, use BuildAgentMemorySize to determine actual container memory
1230+ $availableMemoryGB = ConvertTo-MemoryGB - MemoryString $env: BuildAgentMemorySize
1231+ Write-Host " Available memory: $availableMemoryGB GB" - ForegroundColor Cyan
1232+
1233+ $calculatedMemoryGB = [math ]::Floor($availableMemoryGB - $hostReservedMemoryGB )
1234+ Write-Host " Memory available for container: $calculatedMemoryGB GB (after reserving ${hostReservedMemoryGB} GB for host)" - ForegroundColor Cyan
1235+
1236+ if ($env: BuildAgentMaxMemory ) {
1237+ $dockerMemoryLimitGB = [math ]::Min($env: BuildAgentMaxMemory , $calculatedMemoryGB )
1238+ Write-Host " Max memory limit set to $env: BuildAgentMaxMemory GB from BuildAgentMaxMemory environment variable." - ForegroundColor Cyan
1239+ } else {
1240+ $dockerMemoryLimitGB = $calculatedMemoryGB
1241+ }
1242+ $dockerMemoryLimit = " ${dockerMemoryLimitGB} g"
1243+ Write-Host " Container memory set to $dockerMemoryLimit " - ForegroundColor Cyan
1244+ }
1245+ else
1246+ {
1247+ # On developer machines, use Memory parameter as actual limit
1248+ $dockerMemoryLimit = $Memory
1249+ Write-Host " Container memory set to $dockerMemoryLimit (from Memory parameter)" - ForegroundColor Cyan
1250+ }
1251+
1252+ Write-Host " Number of requested CPUs is set to $Cpus " - ForegroundColor Cyan
1253+
1254+ if ($env: BuildAgentMaxCpus )
1255+ {
1256+ $dockerCpus = [math ]::Min($env: BuildAgentMaxCpus , $Cpus )
1257+ Write-Host " BuildAgentMaxCpus is set to $env: BuildAgentMaxCpus , actual number of CPUs will be $dockerCpus " - ForegroundColor Cyan
1258+ }
1259+ else
1260+ {
1261+ $dockerCpus = $Cpus
1262+ }
1263+
11981264# GHCR authentication and pull logic
11991265$builtNewImage = $false
12001266$dockerConfigArg = @ ()
@@ -1298,8 +1364,8 @@ RUN if [ -n "`$MOUNTPOINTS" ]; then \
12981364
12991365 # Build docker build command with optional --memory (not supported in process isolation)
13001366 $dockerBuildCmd = @ (' build' , ' -t' , $ImageTag )
1301- if ($Memory -and $ Isolation -ne ' process' ) {
1302- $dockerBuildCmd += " --memory=$Memory "
1367+ if ($Isolation -ne ' process' ) {
1368+ $dockerBuildCmd += " --memory=$dockerMemoryLimit "
13031369 }
13041370 $dockerBuildCmd += @ (' --build-arg' , " MOUNTPOINTS=$mountPointsAsString " , ' -f' , ' -' , $dockerContextDirectory )
13051371
@@ -1344,7 +1410,8 @@ if (-not $BuildImage)
13441410{
13451411 # Common setup for both Claude and normal build modes
13461412 $pwshPath = if ($IsUnix ) { ' /usr/bin/pwsh' } else { ' C:\Program Files\PowerShell\7\pwsh.exe' }
1347- $initCall = if (-not $NoInit ) { " & c:\docker-context\Init.g.ps1; " } else { " " }
1413+ $initScriptPath = if ($IsUnix ) { ' /docker-context/Init.g.ps1' } else { ' c:\docker-context\Init.g.ps1' }
1414+ $initCall = if (-not $NoInit ) { " & '$initScriptPath '; " } else { " " }
13481415
13491416 # Convert volume mappings to docker args format (interleave "-v" flags)
13501417 $volumeArgs = @ ()
@@ -1455,6 +1522,29 @@ if (-not $BuildImage)
14551522 $BuildArgs = @ (" -StartVsmon" ) + $BuildArgs
14561523 }
14571524
1525+ # Pass through PowerShell verbosity preferences
1526+ $verbosityCommands = @ ()
1527+ if ($VerbosePreference -ne ' SilentlyContinue' )
1528+ {
1529+ $verbosityCommands += " `$ VerbosePreference = '$VerbosePreference '"
1530+ $BuildArgs = @ (" -Verbose" ) + $BuildArgs
1531+ }
1532+ if ($DebugPreference -ne ' SilentlyContinue' )
1533+ {
1534+ $verbosityCommands += " `$ DebugPreference = '$DebugPreference '"
1535+ $BuildArgs = @ (" -Debug" ) + $BuildArgs
1536+ }
1537+ if ($InformationPreference -ne ' SilentlyContinue' )
1538+ {
1539+ $verbosityCommands += " `$ InformationPreference = '$InformationPreference '"
1540+ }
1541+ if ($WarningPreference -ne ' Continue' )
1542+ {
1543+ $verbosityCommands += " `$ WarningPreference = '$WarningPreference '"
1544+ }
1545+
1546+ $verbositySetup = if ($verbosityCommands.Count -gt 0 ) { ($verbosityCommands -join ' ; ' ) + ' ; ' } else { ' ' }
1547+
14581548 if ($Interactive )
14591549 {
14601550 $pwshArgs = " -NoExit"
@@ -1482,7 +1572,7 @@ if (-not $BuildImage)
14821572 $scriptFullPath = Join-Path $ContainerSourceDir $Script
14831573 }
14841574 $scriptInvocation = " & '$scriptFullPath '"
1485- $inlineScript = " ${substCommandsInline}${initCall} cd '$SourceDirName '; $scriptInvocation $buildArgsString ; $pwshExitCommand "
1575+ $inlineScript = " ${substCommandsInline}${initCall} cd '$SourceDirName '; $verbositySetup$ scriptInvocation $buildArgsString ; $pwshExitCommand "
14861576
14871577 # No environment args for normal build
14881578 $envArgs = @ ()
@@ -1507,10 +1597,8 @@ if (-not $BuildImage)
15071597
15081598 # Only add --memory and --cpus when NOT using process isolation
15091599 if ($Isolation -ne ' process' ) {
1510- if ($Memory ) {
1511- $dockerCmd += " --memory=$Memory "
1512- }
1513- $dockerCmd += " --cpus=$Cpus "
1600+ $dockerCmd += " --memory=$dockerMemoryLimit "
1601+ $dockerCmd += " --cpus=$dockerCpus "
15141602 }
15151603
15161604 if ($isolationArg ) { $dockerCmd += $isolationArg }
0 commit comments