Merge pull request #9 from sirkirby/002-gh-jobs-homebrew #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 | |
| # 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 | |
| # 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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/TenSecondTom | |
| - name: Run smoke test (--version) | |
| run: | | |
| echo "🧪 Running smoke test: --version" | |
| ./artifacts/TenSecondTom --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/TenSecondTom | |
| - name: Run smoke test (--version) | |
| run: | | |
| echo "🧪 Running smoke test: --version" | |
| ./artifacts/TenSecondTom --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 | |
| } |