Skip to content

Commit b2337fa

Browse files
gfraiteurclaude
andcommitted
Add upstream-merge command with Claude Code integration
- Rename DownstreamMerge to UpstreamMerge (runs on downstream branch, merges from upstream) - Add ClaudeCodeHelper to invoke Claude Code for merge conflict resolution - Add SecretAttribute to mark sensitive environment variables for masking - Add DockerSpec.Dockerfile property for custom Dockerfile support - Add ToolInvocationOptions.StandardInput for piping to processes - Add ToolInvocationOptions.EchoOutputToConsole for real-time output - Add ENG_REPO_DIRECTORY environment variable for snapshot mode - Update Build.ps1 with -Snapshot flag to copy build output to temp - Parse Claude's JSON stream output and display human-readable progress - Support colored output based on message type (errors, success, etc.) - Strip ANSI escape codes and non-ASCII characters from output Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f3d3886 commit b2337fa

31 files changed

+3903
-2821
lines changed

Build.ps1

Lines changed: 174 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,155 +1,174 @@
1-
# *** DO NOT EDIT THIS FILE DIRECTLY ***
2-
# This file is auto-generated from src/PostSharp.Engineering.BuildTools/Resources/Build.ps1
3-
# Edit the source version, then run `./Build.ps1 generate-scripts` to regenerate this file.
4-
5-
[CmdletBinding(PositionalBinding = $false)]
6-
param(
7-
[switch]$Interactive, # Opens an interactive PowerShell session
8-
[switch]$StartVsmon, # Enable the remote debugger.
9-
[switch]$NoCache, # Bypass the build cache for `eng`, and force a rebuild.
10-
[Parameter(ValueFromRemainingArguments)]
11-
[string[]]$BuildArgs # Arguments passed to `Build.ps1` within the container.
12-
)
13-
14-
####
15-
# These settings are replaced by the generate-scripts command.
16-
$EngPath = 'eng'
17-
$ProductName = 'PostSharpEngineering'
18-
####
19-
20-
if ($StartVsmon)
21-
{
22-
$vsmonport = 4024
23-
Write-Host "Starting Visual Studio Remote Debugger, listening at port $vsmonport." -ForegroundColor Cyan
24-
$vsmonProcess = Start-Process -FilePath "C:\msvsmon\msvsmon.exe" `
25-
-ArgumentList "/noauth","/anyuser","/silent","/port:$vsmonport","/timeout:2147483647" `
26-
-NoNewWindow -PassThru
27-
}
28-
29-
# Change the prompt and window title in Docker.
30-
if ($env:RUNNING_IN_DOCKER)
31-
{
32-
function global:prompt
33-
{
34-
$host.UI.RawUI.WindowTitle = "[docker] " + (Get-Location).Path
35-
"[docker] $( Get-Location )> "
36-
}
37-
}
38-
39-
40-
if (-not $Interactive -or $BuildArgs)
41-
{
42-
# The generate-scripts command implies -NoCache
43-
if ($BuildArgs -contains 'generate-scripts')
44-
{
45-
$NoCache = $true
46-
}
47-
48-
# Change the working directory so we can use a global.json that is specific to eng.
49-
$previousLocation = Get-Location
50-
51-
Set-Location (Join-Path $PSScriptRoot $EngPath "src")
52-
53-
try
54-
{
55-
# Build caching: check if we need to rebuild
56-
$projectPath = Join-Path $PSScriptRoot $EngPath "src" "Build$ProductName.csproj"
57-
58-
# Find the output DLL by looking for the first DLL in bin/Debug/*/
59-
$binDebugPath = Join-Path $PSScriptRoot $EngPath "src" "bin" "Debug"
60-
$outputDll = $null
61-
if (Test-Path $binDebugPath)
62-
{
63-
$outputDll = Get-ChildItem -Path $binDebugPath -Filter "Build$ProductName.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
64-
}
65-
66-
$needsBuild = $false
67-
68-
if ($NoCache)
69-
{
70-
# Cache bypassed by -NoCache switch
71-
$needsBuild = $true
72-
}
73-
elseif (-not $outputDll -or -not (Test-Path $outputDll))
74-
{
75-
# DLL doesn't exist, need to build
76-
$needsBuild = $true
77-
}
78-
else
79-
{
80-
# Get the DLL's last write time
81-
$dllTime = (Get-Item $outputDll).LastWriteTime
82-
83-
# Check files in $EngPath/src (non-recursive)
84-
$engSrcFiles = Get-ChildItem -Path (Join-Path $PSScriptRoot $EngPath "src") -File -ErrorAction SilentlyContinue
85-
86-
# Check files in $EngPath (non-recursive)
87-
$engFiles = Get-ChildItem -Path (Join-Path $PSScriptRoot $EngPath) -File -ErrorAction SilentlyContinue
88-
89-
# Check files in $ScriptRoot (non-recursive)
90-
$rootFiles = Get-ChildItem -Path $PSScriptRoot -File -ErrorAction SilentlyContinue
91-
92-
# Combine all files and check if any are newer than the DLL
93-
$allFiles = @($engSrcFiles) + @($engFiles) + @($rootFiles)
94-
foreach ($file in $allFiles)
95-
{
96-
if ($file.LastWriteTime -gt $dllTime)
97-
{
98-
$needsBuild = $true
99-
break
100-
}
101-
}
102-
}
103-
104-
if ($needsBuild)
105-
{
106-
# Build is needed
107-
Write-Host "Building Build$ProductName..." -ForegroundColor Cyan
108-
& dotnet build $projectPath
109-
if ($LASTEXITCODE -ne 0)
110-
{
111-
throw "Build failed with exit code $LASTEXITCODE"
112-
}
113-
114-
# Re-find the output DLL after build
115-
if (Test-Path $binDebugPath)
116-
{
117-
$outputDll = Get-ChildItem -Path $binDebugPath -Filter "Build$ProductName.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
118-
}
119-
120-
# Update the DLL timestamp to mark the cache as valid
121-
if ($outputDll -and (Test-Path $outputDll))
122-
{
123-
(Get-Item $outputDll).LastWriteTime = Get-Date
124-
}
125-
else
126-
{
127-
throw "Build succeeded but output DLL '$outputDll' not found."
128-
}
129-
}
130-
131-
# Run the project using dotnet exec (faster than dotnet run)
132-
if (-not $outputDll -or -not (Test-Path $outputDll))
133-
{
134-
throw "Output DLL not found. Expected path: '$outputDll'."
135-
}
136-
137-
& dotnet exec $outputDll $BuildArgs
138-
139-
if ($StartVsmon)
140-
{
141-
Write-Host ""
142-
Write-Host "Killing vsmon.exe."
143-
$vsmonProcess.Kill()
144-
}
145-
}
146-
finally
147-
{
148-
Set-Location $previousLocation
149-
}
150-
}
151-
152-
if ($Interactive)
153-
{
154-
Write-Host "Entering interactive PowerShell." -ForegroundColor Green
155-
}
1+
# *** DO NOT EDIT THIS FILE DIRECTLY ***
2+
# This file is auto-generated from src/PostSharp.Engineering.BuildTools/Resources/Build.ps1
3+
# Edit the source version, then run `./Build.ps1 generate-scripts` to regenerate this file.
4+
5+
[CmdletBinding(PositionalBinding = $false)]
6+
param(
7+
[switch]$Interactive, # Opens an interactive PowerShell session
8+
[switch]$StartVsmon, # Enable the remote debugger.
9+
[switch]$NoCache, # Bypass the build cache for `eng`, and force a rebuild.
10+
[switch]$Snapshot, # Copy eng/src to temp directory to avoid file locking during Claude conflict resolution.
11+
[Parameter(ValueFromRemainingArguments)]
12+
[string[]]$BuildArgs # Arguments passed to `Build.ps1` within the container.
13+
)
14+
15+
####
16+
# These settings are replaced by the generate-scripts command.
17+
$EngPath = 'eng'
18+
$ProductName = 'PostSharpEngineering'
19+
####
20+
21+
if ($StartVsmon)
22+
{
23+
$vsmonport = 4024
24+
Write-Host "Starting Visual Studio Remote Debugger, listening at port $vsmonport." -ForegroundColor Cyan
25+
$vsmonProcess = Start-Process -FilePath "C:\msvsmon\msvsmon.exe" `
26+
-ArgumentList "/noauth","/anyuser","/silent","/port:$vsmonport","/timeout:2147483647" `
27+
-NoNewWindow -PassThru
28+
}
29+
30+
# Change the prompt and window title in Docker.
31+
if ($env:RUNNING_IN_DOCKER)
32+
{
33+
function global:prompt
34+
{
35+
$host.UI.RawUI.WindowTitle = "[docker] " + (Get-Location).Path
36+
"[docker] $( Get-Location )> "
37+
}
38+
}
39+
40+
41+
if (-not $Interactive -or $BuildArgs)
42+
{
43+
# The generate-scripts command implies -NoCache
44+
if ($BuildArgs -contains 'generate-scripts')
45+
{
46+
$NoCache = $true
47+
}
48+
49+
# Change the working directory so we can use a global.json that is specific to eng.
50+
$previousLocation = Get-Location
51+
52+
# Snapshot mode: copy eng/src to temp to avoid file locking during Claude conflict resolution
53+
$engSrcPath = Join-Path $PSScriptRoot $EngPath "src"
54+
$snapshotDir = $null
55+
if ($Snapshot)
56+
{
57+
$snapshotDir = Join-Path $env:TEMP "eng-snapshot-$([Guid]::NewGuid().ToString('N').Substring(0,8))"
58+
Write-Host "Creating snapshot of eng/src at $snapshotDir..." -ForegroundColor Cyan
59+
Copy-Item -Path $engSrcPath -Destination $snapshotDir -Recurse -Force
60+
$engSrcPath = $snapshotDir
61+
}
62+
63+
Set-Location $engSrcPath
64+
65+
try
66+
{
67+
# Build caching: check if we need to rebuild
68+
$projectPath = Join-Path $PSScriptRoot $EngPath "src" "Build$ProductName.csproj"
69+
70+
# Find the output DLL by looking for the first DLL in bin/Debug/*/
71+
$binDebugPath = Join-Path $PSScriptRoot $EngPath "src" "bin" "Debug"
72+
$outputDll = $null
73+
if (Test-Path $binDebugPath)
74+
{
75+
$outputDll = Get-ChildItem -Path $binDebugPath -Filter "Build$ProductName.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
76+
}
77+
78+
$needsBuild = $false
79+
80+
if ($NoCache)
81+
{
82+
# Cache bypassed by -NoCache switch
83+
$needsBuild = $true
84+
}
85+
elseif (-not $outputDll -or -not (Test-Path $outputDll))
86+
{
87+
# DLL doesn't exist, need to build
88+
$needsBuild = $true
89+
}
90+
else
91+
{
92+
# Get the DLL's last write time
93+
$dllTime = (Get-Item $outputDll).LastWriteTime
94+
95+
# Check files in $EngPath/src (non-recursive)
96+
$engSrcFiles = Get-ChildItem -Path (Join-Path $PSScriptRoot $EngPath "src") -File -ErrorAction SilentlyContinue
97+
98+
# Check files in $EngPath (non-recursive)
99+
$engFiles = Get-ChildItem -Path (Join-Path $PSScriptRoot $EngPath) -File -ErrorAction SilentlyContinue
100+
101+
# Check files in $ScriptRoot (non-recursive)
102+
$rootFiles = Get-ChildItem -Path $PSScriptRoot -File -ErrorAction SilentlyContinue
103+
104+
# Combine all files and check if any are newer than the DLL
105+
$allFiles = @($engSrcFiles) + @($engFiles) + @($rootFiles)
106+
foreach ($file in $allFiles)
107+
{
108+
if ($file.LastWriteTime -gt $dllTime)
109+
{
110+
$needsBuild = $true
111+
break
112+
}
113+
}
114+
}
115+
116+
if ($needsBuild)
117+
{
118+
# Build is needed
119+
Write-Host "Building Build$ProductName..." -ForegroundColor Cyan
120+
& dotnet build $projectPath
121+
if ($LASTEXITCODE -ne 0)
122+
{
123+
throw "Build failed with exit code $LASTEXITCODE"
124+
}
125+
126+
# Re-find the output DLL after build
127+
if (Test-Path $binDebugPath)
128+
{
129+
$outputDll = Get-ChildItem -Path $binDebugPath -Filter "Build$ProductName.dll" -Recurse -ErrorAction SilentlyContinue | Select-Object -First 1 | ForEach-Object { $_.FullName }
130+
}
131+
132+
# Update the DLL timestamp to mark the cache as valid
133+
if ($outputDll -and (Test-Path $outputDll))
134+
{
135+
(Get-Item $outputDll).LastWriteTime = Get-Date
136+
}
137+
else
138+
{
139+
throw "Build succeeded but output DLL '$outputDll' not found."
140+
}
141+
}
142+
143+
# Run the project using dotnet exec (faster than dotnet run)
144+
if (-not $outputDll -or -not (Test-Path $outputDll))
145+
{
146+
throw "Output DLL not found. Expected path: '$outputDll'."
147+
}
148+
149+
& dotnet exec $outputDll $BuildArgs
150+
151+
if ($StartVsmon)
152+
{
153+
Write-Host ""
154+
Write-Host "Killing vsmon.exe."
155+
$vsmonProcess.Kill()
156+
}
157+
}
158+
finally
159+
{
160+
Set-Location $previousLocation
161+
162+
# Cleanup snapshot directory if it was created
163+
if ($snapshotDir -and (Test-Path $snapshotDir))
164+
{
165+
Write-Host "Cleaning up snapshot directory..." -ForegroundColor Cyan
166+
Remove-Item -Path $snapshotDir -Recurse -Force -ErrorAction SilentlyContinue
167+
}
168+
}
169+
}
170+
171+
if ($Interactive)
172+
{
173+
Write-Host "Entering interactive PowerShell." -ForegroundColor Green
174+
}

DockerBuild.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ param(
3030
####
3131
# These settings are replaced by the generate-scripts command.
3232
$EngPath = 'eng'
33-
$EnvironmentVariables = 'AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AZ_IDENTITY_USERNAME,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_DEVOPS_TOKEN,AZURE_DEVOPS_USER,AZURE_TENANT_ID,DOC_API_KEY,DOWNLOADS_API_KEY,ENG_USERNAME,GIT_USER_EMAIL,GIT_USER_NAME,GITHUB_AUTHOR_EMAIL,GITHUB_REVIEWER_TOKEN,GITHUB_TOKEN,IS_POSTSHARP_OWNED,IS_TEAMCITY_AGENT,MetalamaLicense,NUGET_ORG_API_KEY,PostSharpLicense,SIGNSERVER_SECRET,TEAMCITY_CLOUD_TOKEN,TYPESENSE_API_KEY,VS_MARKETPLACE_ACCESS_TOKEN,VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'
33+
$EnvironmentVariables = 'AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AZ_IDENTITY_USERNAME,AZURE_CLIENT_ID,AZURE_CLIENT_SECRET,AZURE_DEVOPS_TOKEN,AZURE_DEVOPS_USER,AZURE_TENANT_ID,CLAUDE_CODE_OAUTH_TOKEN,DOC_API_KEY,DOWNLOADS_API_KEY,ENG_USERNAME,GIT_USER_EMAIL,GIT_USER_NAME,GITHUB_AUTHOR_EMAIL,GITHUB_REVIEWER_TOKEN,GITHUB_TOKEN,IS_POSTSHARP_OWNED,IS_TEAMCITY_AGENT,MetalamaLicense,NUGET_ORG_API_KEY,PostSharpLicense,SIGNSERVER_SECRET,TEAMCITY_CLOUD_TOKEN,TYPESENSE_API_KEY,VS_MARKETPLACE_ACCESS_TOKEN,VSS_NUGET_EXTERNAL_FEED_ENDPOINTS'
3434
####
3535

3636
$ErrorActionPreference = "Stop"

src/PostSharp.Engineering.BuildTools/AppExtensions.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,14 @@ internal static void AddCommands( this CommandApp app, Product product )
101101
.WithData( data )
102102
.WithDescription( "Generates THIRD-PARTY-NOTICES.md" );
103103

104+
// Only add upstream-merge command if the product family has an upstream sibling
105+
if ( product.ProductFamily.UpstreamProductFamily != null )
106+
{
107+
root.AddCommand<UpstreamMergeCommand>( "upstream-merge" )
108+
.WithData( data )
109+
.WithDescription( "Merges code from the upstream development branch using Claude to resolve conflicts." );
110+
}
111+
104112
root.AddBranch(
105113
"dependencies",
106114
dependencies =>
@@ -260,10 +268,6 @@ internal static void AddCommands( this CommandApp app, Product product )
260268
.WithDescription( "Renames all files and directories recursively preserving GIT history." )
261269
.WithExample( @"""C:\src\Caravela.Compiler""", @"""Caravela""", @"""Metalama""" );
262270

263-
git.AddCommand<DownstreamMergeCommand>( "merge-downstream" )
264-
.WithData( data )
265-
.WithDescription( "Merges the code to the subsequent development branch." );
266-
267271
git.AddCommand<UpstreamCheckCommand>( "check-upstream" )
268272
.WithData( data )
269273
.WithDescription( "Checks the upstream product versions for unmerged changes." );

0 commit comments

Comments
 (0)