Skip to content

Build

Build #12

Workflow file for this run

# Build Workflow
#
# This workflow builds cross-platform executables when code is merged to main branch.
# It runs tests to verify main branch integrity, then builds self-contained executables
# for macOS (x64, ARM64) and Windows (x64), and performs smoke tests on each.
#
# Triggers: Push to 'main' branch, Manual dispatch
# Performance Target: ≤15 minutes total
# Artifacts: Self-contained executables with metadata and checksums
name: Build
on:
push:
branches: [main]
workflow_dispatch: # Allow manual triggering for debugging
inputs:
skip_tests:
description: 'Skip test job (for faster workflow testing)'
required: false
type: boolean
default: false
# Cancel in-progress builds for the same commit to save resources
concurrency:
group: build-${{ github.sha }}
cancel-in-progress: true
# Environment variables available to all jobs
env:
DOTNET_VERSION: '9.0.x'
MAX_ARTIFACT_SIZE_MB: 50
jobs:
# Job 1: Test
# Re-run all tests to verify main branch integrity
# Performance Target: ≤5 minutes
test:
name: Test on Main
runs-on: ubuntu-latest
if: ${{ github.event.inputs.skip_tests != 'true' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore dependencies
run: dotnet restore
- name: Build solution
run: |
dotnet build \
--configuration Release \
--no-restore \
--warnaserror
- name: Run all tests
run: |
dotnet test \
--configuration Release \
--no-restore \
--verbosity normal
# Fail workflow if any tests fail on main branch
# Job 2: Build macOS x64
# Build self-contained executable for macOS x64
# Performance Target: ≤5 minutes
build-macos-x64:
name: Build macOS x64
runs-on: macos-latest
needs: test
if: ${{ always() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Publish macOS x64 executable
run: |
dotnet publish src/TenSecondTom.csproj \
--configuration Release \
--runtime osx-x64 \
--self-contained true \
--output ./publish/osx-x64 \
-p:PublishSingleFile=true
- name: Rename executable to 'tom'
run: mv ./publish/osx-x64/TenSecondTom ./publish/osx-x64/tom
- name: Verify artifact size
run: |
FILE_PATH="./publish/osx-x64/tom"
FILE_SIZE=$(stat -f%z "$FILE_PATH")
FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024))
MAX_SIZE_MB=${{ env.MAX_ARTIFACT_SIZE_MB }}
echo "📦 Executable size: ${FILE_SIZE_MB}MB"
echo "📏 Maximum allowed: ${MAX_SIZE_MB}MB"
if [ $FILE_SIZE_MB -gt $MAX_SIZE_MB ]; then
echo "❌ Error: Executable size (${FILE_SIZE_MB}MB) exceeds ${MAX_SIZE_MB}MB limit"
echo "Consider enabling more aggressive trimming or reducing dependencies"
exit 1
fi
echo "✅ Size check passed"
- name: Calculate checksum
id: checksum
run: |
CHECKSUM=$(shasum -a 256 ./publish/osx-x64/tom | cut -d ' ' -f 1)
echo "sha256=$CHECKSUM" >> $GITHUB_OUTPUT
echo "📝 SHA256: $CHECKSUM"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ten-second-tom-osx-x64
path: |
./publish/osx-x64/tom
./publish/osx-x64/appsettings*.json
./publish/osx-x64/*.dylib
retention-days: 90
# Job 3: Build macOS ARM64
# Build self-contained executable for macOS ARM64 (Apple Silicon)
# Performance Target: ≤5 minutes
build-macos-arm64:
name: Build macOS ARM64
runs-on: macos-latest
needs: test
if: ${{ always() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Publish macOS ARM64 executable
run: |
dotnet publish src/TenSecondTom.csproj \
--configuration Release \
--runtime osx-arm64 \
--self-contained true \
--output ./publish/osx-arm64 \
-p:PublishSingleFile=true
- name: Rename executable to 'tom'
run: mv ./publish/osx-arm64/TenSecondTom ./publish/osx-arm64/tom
- name: Verify artifact size
run: |
FILE_PATH="./publish/osx-arm64/tom"
FILE_SIZE=$(stat -f%z "$FILE_PATH")
FILE_SIZE_MB=$((FILE_SIZE / 1024 / 1024))
MAX_SIZE_MB=${{ env.MAX_ARTIFACT_SIZE_MB }}
echo "📦 Executable size: ${FILE_SIZE_MB}MB"
echo "📏 Maximum allowed: ${MAX_SIZE_MB}MB"
if [ $FILE_SIZE_MB -gt $MAX_SIZE_MB ]; then
echo "❌ Error: Executable size (${FILE_SIZE_MB}MB) exceeds ${MAX_SIZE_MB}MB limit"
echo "Consider enabling more aggressive trimming or reducing dependencies"
exit 1
fi
echo "✅ Size check passed"
- name: Calculate checksum
id: checksum
run: |
CHECKSUM=$(shasum -a 256 ./publish/osx-arm64/tom | cut -d ' ' -f 1)
echo "sha256=$CHECKSUM" >> $GITHUB_OUTPUT
echo "📝 SHA256: $CHECKSUM"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ten-second-tom-osx-arm64
path: |
./publish/osx-arm64/tom
./publish/osx-arm64/appsettings*.json
./publish/osx-arm64/*.dylib
retention-days: 90
# Job 4: Build Windows x64
# Build self-contained executable for Windows x64
# Performance Target: ≤5 minutes
build-windows-x64:
name: Build Windows x64
runs-on: windows-latest
needs: test
if: ${{ always() && (needs.test.result == 'success' || needs.test.result == 'skipped') }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Restore dependencies
run: dotnet restore
- name: Publish Windows x64 executable
run: |
dotnet publish src/TenSecondTom.csproj `
--configuration Release `
--runtime win-x64 `
--self-contained true `
--output ./publish/win-x64 `
-p:PublishSingleFile=true
- name: Rename executable to 'tom.exe'
run: Move-Item ./publish/win-x64/TenSecondTom.exe ./publish/win-x64/tom.exe
- name: Verify artifact size
shell: pwsh
run: |
$filePath = "./publish/win-x64/tom.exe"
$fileSize = (Get-Item $filePath).Length
$fileSizeMB = [math]::Round($fileSize / 1MB)
$maxSizeMB = $env:MAX_ARTIFACT_SIZE_MB
Write-Host "📦 Executable size: ${fileSizeMB}MB"
Write-Host "📏 Maximum allowed: ${maxSizeMB}MB"
if ($fileSizeMB -gt $maxSizeMB) {
Write-Host "❌ Error: Executable size (${fileSizeMB}MB) exceeds ${maxSizeMB}MB limit"
Write-Host "Consider enabling more aggressive trimming or reducing dependencies"
exit 1
}
Write-Host "✅ Size check passed"
- name: Calculate checksum
shell: pwsh
id: checksum
run: |
$hash = (Get-FileHash -Path ./publish/win-x64/tom.exe -Algorithm SHA256).Hash
echo "sha256=$hash" >> $env:GITHUB_OUTPUT
Write-Host "📝 SHA256: $hash"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ten-second-tom-win-x64
path: |
./publish/win-x64/tom.exe
./publish/win-x64/appsettings*.json
./publish/win-x64/*.dll
retention-days: 90
# Job 5: Verify macOS x64
# Smoke test macOS x64 executable
# Performance Target: <1 minute
verify-macos-x64:
name: Verify macOS x64
runs-on: macos-latest
needs: build-macos-x64
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ten-second-tom-osx-x64
path: ./artifacts
- name: Set executable permissions
run: chmod +x ./artifacts/tom
- name: Run smoke test (--version)
run: |
echo "🧪 Running smoke test: --version"
./artifacts/tom --version
if [ $? -eq 0 ]; then
echo "✅ Smoke test passed: executable runs successfully"
else
echo "❌ Smoke test failed: executable did not run"
exit 1
fi
# Job 6: Verify macOS ARM64
# Smoke test macOS ARM64 executable
# Performance Target: <1 minute
verify-macos-arm64:
name: Verify macOS ARM64
runs-on: macos-latest
needs: build-macos-arm64
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ten-second-tom-osx-arm64
path: ./artifacts
- name: Set executable permissions
run: chmod +x ./artifacts/tom
- name: Run smoke test (--version)
run: |
echo "🧪 Running smoke test: --version"
./artifacts/tom --version
if [ $? -eq 0 ]; then
echo "✅ Smoke test passed: executable runs successfully"
else
echo "❌ Smoke test failed: executable did not run"
exit 1
fi
# Job 7: Verify Windows x64
# Smoke test Windows x64 executable
# Performance Target: <1 minute
verify-windows-x64:
name: Verify Windows x64
runs-on: windows-latest
needs: build-windows-x64
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: ten-second-tom-win-x64
path: ./artifacts
- name: Run smoke test (--version)
shell: pwsh
run: |
Write-Host "🧪 Running smoke test: --version"
$output = & ./artifacts/tom.exe --version
if ($LASTEXITCODE -eq 0) {
Write-Host "✅ Smoke test passed: executable runs successfully"
Write-Host "Version output: $output"
} else {
Write-Host "❌ Smoke test failed: executable did not run"
exit 1
}