Skip to content

Commit c83f5be

Browse files
Merge pull request #62 from supervoidcoder/copilot/ci-upgrade-release-and-build
ci: matrix builds with native ARM64 runners and PR test comments
2 parents 511ca95 + cbbed06 commit c83f5be

2 files changed

Lines changed: 234 additions & 20 deletions

File tree

.github/workflows/build.yml

Lines changed: 213 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,230 @@ name: Build Check
22
on: [pull_request, push]
33
permissions:
44
contents: read
5+
pull-requests: write # Required to post comments on PRs
56
jobs:
6-
build-windows:
7-
runs-on: windows-latest
7+
# Matrix job that compiles and tests all architectures
8+
build-and-test:
9+
strategy:
10+
fail-fast: false # Continue testing other architectures even if one fails
11+
matrix:
12+
include:
13+
# x64 architecture - native runner
14+
- arch: x64
15+
runner: windows-latest
16+
vcvars_arch: x64
17+
# x86 architecture - runs on x64 via WoW64
18+
- arch: x86
19+
runner: windows-latest
20+
vcvars_arch: x64_x86
21+
# ARM64 architecture - native ARM runner for testing
22+
- arch: arm64
23+
runner: windows-11-arm
24+
vcvars_arch: arm64
25+
26+
runs-on: ${{ matrix.runner }}
27+
828
steps:
929
- uses: actions/checkout@v4
1030

11-
- name: Setup MSVC
31+
- name: Setup MSVC for ${{ matrix.arch }}
1232
uses: ilammy/msvc-dev-cmd@v1
33+
with:
34+
arch: ${{ matrix.vcvars_arch }}
1335

14-
- name: Compile
36+
- name: Compile for ${{ matrix.arch }}
1537
shell: pwsh
1638
run: |
17-
cl /O2 /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:win-witr.exe
18-
# Add the current directory (where win-witr.exe was compiled) to PATH
19-
$env:PATH = "$PWD;$env:PATH"
20-
21-
# Verify the exe is accessible
22-
Write-Host "Checking win-witr.exe availability..."
23-
win-witr --version
24-
- name: Run Tests
39+
# Compile the executable with architecture-specific name
40+
cl /O2 /std:c++20 /EHsc main.cpp /DUNICODE /D_UNICODE /Fe:win-witr-${{ matrix.arch }}.exe
41+
42+
# Create a copy with the standard name for tests
43+
Copy-Item -Path "win-witr-${{ matrix.arch }}.exe" -Destination "win-witr.exe"
44+
45+
# Add the current directory to PATH
46+
$env:PATH = "$PWD;$env:PATH"
47+
48+
# Verify the exe is accessible
49+
Write-Host "Checking win-witr-${{ matrix.arch }}.exe availability..."
50+
.\win-witr-${{ matrix.arch }}.exe --version
51+
52+
- name: Run Tests for ${{ matrix.arch }}
53+
id: run_tests
2554
shell: pwsh
2655
run: |
56+
# Initialize test counters
57+
$totalTests = 0
58+
$passedTests = 0
59+
$failedTests = 0
60+
$testResults = @()
61+
62+
# Add the current directory to PATH
63+
$env:PATH = "$PWD;$env:PATH"
64+
2765
# Run all test .bat files
2866
Get-ChildItem -Path tests -Recurse -Filter *.bat | ForEach-Object {
29-
Write-Host "Running test: $($_.FullName)"
30-
& $_.FullName
31-
if ($LASTEXITCODE -ne 0) {
32-
Write-Error "Test failed: $($_.Name)"
33-
exit 1
67+
$totalTests++
68+
$testName = $_.Name
69+
Write-Host "Running test: $testName"
70+
71+
# Initialize output variable outside try block
72+
$output = $null
73+
74+
try {
75+
# Run the test and capture output and exit code
76+
$output = & $_.FullName 2>&1
77+
$exitCode = $LASTEXITCODE
78+
79+
if ($exitCode -eq 0) {
80+
$passedTests++
81+
# Include output in the result
82+
$outputText = if ($output) { "`n Output: $($output -join "`n ")" } else { "" }
83+
$testResults += "✅ $testName - PASSED$outputText"
84+
Write-Host "✅ Test passed: $testName"
85+
} else {
86+
$failedTests++
87+
# Include output and exit code in the result
88+
$outputText = if ($output) { "`n Output: $($output -join "`n ")" } else { "" }
89+
$testResults += "❌ $testName - FAILED (Exit code: $exitCode)$outputText"
90+
Write-Host "❌ Test failed: $testName (Exit code: $exitCode)"
91+
}
92+
} catch {
93+
$failedTests++
94+
# Include exception and any captured output
95+
$outputText = if ($output) { "`n Output: $($output -join "`n ")" } else { "" }
96+
$testResults += "❌ $testName - FAILED (Exception: $_)$outputText"
97+
Write-Host "❌ Test failed with exception: $testName"
3498
}
3599
}
36-
100+
101+
# Output test summary
102+
Write-Host "`n=== Test Summary for ${{ matrix.arch }} ==="
103+
Write-Host "Total: $totalTests"
104+
Write-Host "Passed: $passedTests"
105+
Write-Host "Failed: $failedTests"
106+
107+
# Store results for PR comment
108+
$results = @{
109+
total = $totalTests
110+
passed = $passedTests
111+
failed = $failedTests
112+
details = $testResults -join "`n"
113+
}
114+
115+
# Export results to GitHub output using multiline format
116+
"total=$totalTests" >> $env:GITHUB_OUTPUT
117+
"passed=$passedTests" >> $env:GITHUB_OUTPUT
118+
"failed=$failedTests" >> $env:GITHUB_OUTPUT
119+
$delimiter = "EOF_DETAILS_$(Get-Date -Format 'yyyyMMddHHmmss')"
120+
"details<<$delimiter" >> $env:GITHUB_OUTPUT
121+
$testResults -join "`n" >> $env:GITHUB_OUTPUT
122+
"$delimiter" >> $env:GITHUB_OUTPUT
123+
124+
# Exit with error if any tests failed
125+
if ($failedTests -gt 0) {
126+
Write-Error "Some tests failed for ${{ matrix.arch }}"
127+
exit 1
128+
}
129+
130+
# Upload test results as artifacts for the comment job
131+
- name: Save test results
132+
if: always()
133+
shell: pwsh
134+
run: |
135+
# Determine job status based on previous steps
136+
$jobStatus = if ("${{ steps.run_tests.outcome }}" -eq "success") { "success" } else { "failure" }
137+
138+
$results = @{
139+
arch = "${{ matrix.arch }}"
140+
status = $jobStatus
141+
total = "${{ steps.run_tests.outputs.total }}"
142+
passed = "${{ steps.run_tests.outputs.passed }}"
143+
failed = "${{ steps.run_tests.outputs.failed }}"
144+
details = "${{ steps.run_tests.outputs.details }}"
145+
}
146+
147+
$results | ConvertTo-Json | Out-File -FilePath "test-results-${{ matrix.arch }}.json"
148+
149+
- name: Upload test results artifact
150+
if: always()
151+
uses: actions/upload-artifact@v4
152+
with:
153+
name: test-results-${{ matrix.arch }}
154+
path: test-results-${{ matrix.arch }}.json
155+
156+
# Job to post test results as a PR comment
157+
post-test-results:
158+
needs: build-and-test
159+
if: always() && github.event_name == 'pull_request'
160+
runs-on: ubuntu-latest
161+
permissions:
162+
pull-requests: write
163+
164+
steps:
165+
- name: Download all test results
166+
uses: actions/download-artifact@v4
167+
with:
168+
pattern: test-results-*
169+
merge-multiple: true
170+
171+
- name: Generate and post comment
172+
uses: actions/github-script@v7
173+
with:
174+
script: |
175+
const fs = require('fs');
176+
177+
// Read all test result files
178+
const files = fs.readdirSync('.').filter(f => f.startsWith('test-results-') && f.endsWith('.json'));
179+
180+
let commentBody = '## 🧪 Test Results\n\n';
181+
let allPassed = true;
182+
183+
// Process each architecture
184+
for (const file of files) {
185+
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
186+
const arch = data.arch;
187+
const status = data.status || 'unknown';
188+
const total = data.total || 0;
189+
const passed = data.passed || 0;
190+
const failed = data.failed || 0;
191+
192+
// Mark as failed if either tests failed OR job status is not success
193+
if (failed > 0 || status !== 'success') {
194+
allPassed = false;
195+
}
196+
197+
// Determine emoji based on both test results and job status
198+
const emoji = (failed === 0 && status === 'success') ? '✅' : '❌';
199+
commentBody += `### ${emoji} ${arch.toUpperCase()}\n`;
200+
201+
// Show appropriate message based on job status
202+
if (status !== 'success') {
203+
commentBody += `**Job failed** - Tests may not have run due to compile or setup failure\n\n`;
204+
} else {
205+
commentBody += `**${passed}/${total} tests passed**\n\n`;
206+
}
207+
208+
if (data.details && data.details.trim()) {
209+
commentBody += '<details>\n';
210+
commentBody += '<summary>Test Details</summary>\n\n';
211+
commentBody += '```\n';
212+
commentBody += data.details;
213+
commentBody += '\n```\n';
214+
commentBody += '</details>\n\n';
215+
}
216+
}
217+
218+
if (allPassed) {
219+
commentBody += '\n✨ All tests passed across all architectures!\n';
220+
} else {
221+
commentBody += '\n⚠️ Some tests failed. Please review the details above.\n';
222+
}
223+
224+
// Post comment to PR
225+
await github.rest.issues.createComment({
226+
owner: context.repo.owner,
227+
repo: context.repo.repo,
228+
issue_number: context.issue.number,
229+
body: commentBody
230+
});
231+

.github/workflows/release.yml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ permissions:
1313
jobs:
1414
prepare:
1515
name: Prepare release (check files, version, notes)
16+
# Only run if PR was actually merged
17+
if: github.event.pull_request.merged == true
1618
runs-on: ubuntu-latest
1719
outputs:
1820
should_release: ${{ steps.check_files.outputs.should_release }}
@@ -155,6 +157,14 @@ jobs:
155157
Write-Output "DEBUG: Base SHA: $baseSha"
156158
Write-Output "DEBUG: Head SHA: $headSha"
157159
160+
# Get the merge commit message using the actual merge commit SHA
161+
# The merge_commit_sha is the SHA of the commit created when the PR is merged
162+
$mergeCommitSha = "${{ github.event.pull_request.merge_commit_sha }}"
163+
Write-Output "DEBUG: Merge commit SHA: $mergeCommitSha"
164+
165+
$mergeCommitBody = git log -1 --pretty=format:"%b" $mergeCommitSha
166+
Write-Output "DEBUG: Merge commit body: $mergeCommitBody"
167+
158168
# Get all commit messages in the PR
159169
$commits = @(git log --pretty=format:"%h|%s|%b" "$baseSha..$headSha")
160170
Write-Output "DEBUG: Found $($commits.Count) commits in PR"
@@ -214,8 +224,17 @@ jobs:
214224
}
215225
}
216226
217-
# Build release notes
218-
$releaseNotes = "## Changes`n`n"
227+
# Build release notes with merge commit extended description at the top
228+
$releaseNotes = ""
229+
230+
# Add merge commit extended description if it exists
231+
if (-not [string]::IsNullOrWhiteSpace($mergeCommitBody)) {
232+
$releaseNotes += "## Release Notes`n`n"
233+
$releaseNotes += "$mergeCommitBody`n`n"
234+
$releaseNotes += "---`n`n"
235+
}
236+
237+
$releaseNotes += "## Changes`n`n"
219238
220239
if ($features.Count -gt 0) {
221240
$releaseNotes += "### ✨ Features`n"

0 commit comments

Comments
 (0)