name: Build Samples on: workflow_dispatch: push: branches: [ main, develop ] pull_request: branches: [ main, develop ] schedule: - cron: "0 3 * * 1" # Weekly (Mon) build to keep artifacts fresh permissions: contents: read env: DOTNET_NOLOGO: true CONFIGURATION: Release jobs: discover: runs-on: macos-latest outputs: projects: ${{ steps.collect.outputs.projects }} skipped: ${{ steps.collect.outputs.skipped }} steps: - name: Checkout uses: actions/checkout@v4 - name: Collect & filter Desktop platform project files id: collect shell: bash run: | set -e # Ensure jq is available (macOS image usually has it, fallback to brew) if ! command -v jq >/dev/null 2>&1; then echo "jq not found, installing via Homebrew"; brew update >/dev/null 2>&1 || true brew install jq >/dev/null 2>&1 fi # Find all Desktop platform csproj files: **/Platforms/Desktop/*.csproj (portable without mapfile) desktop_projects_tmp=$(mktemp) find . -type f -path '*/Platforms/Desktop/*.csproj' | sort > "$desktop_projects_tmp" if [ ! -s "$desktop_projects_tmp" ]; then echo "No Desktop platform projects found." echo "projects=[]" >> "$GITHUB_OUTPUT" echo "skipped=[]" >> "$GITHUB_OUTPUT" exit 0 fi echo "Testing restore for discovered Desktop projects:" success_tmp=$(mktemp) skipped_tmp=$(mktemp) while IFS= read -r proj; do echo "Attempting restore: $proj" if dotnet restore "$proj" >/dev/null 2>&1; then echo " OK $proj" echo "$proj" >> "$success_tmp" else echo " SKIP (restore failed) $proj" echo "$proj" >> "$skipped_tmp" fi done < "$desktop_projects_tmp" if [ ! -s "$success_tmp" ]; then echo "No projects restored successfully."; echo "projects=[]" >> "$GITHUB_OUTPUT" else json=$(jq -R . < "$success_tmp" | jq -s -c .) echo "projects=$json" >> "$GITHUB_OUTPUT" fi if [ -s "$skipped_tmp" ]; then skipped_json=$(jq -R . < "$skipped_tmp" | jq -s -c .) echo "skipped=$skipped_json" >> "$GITHUB_OUTPUT" else echo "skipped=[]" >> "$GITHUB_OUTPUT" fi echo "Restore filtering complete."; echo "Successful projects:"; cat "$success_tmp" || true echo "Skipped projects:"; cat "$skipped_tmp" || true rm -f "$desktop_projects_tmp" "$success_tmp" "$skipped_tmp" build-desktop-bundles: needs: discover if: ${{ needs.discover.outputs.projects != '[]' && needs.discover.outputs.projects != '' }} runs-on: macos-latest env: MGFXC_WINE_PATH: /Users/runner/.winemonogame DYLD_LIBRARY_PATH: /usr/lib:/usr/local/lib:/opt/homebrew/lib:/usr/local/bin strategy: fail-fast: false matrix: project: ${{ fromJson(needs.discover.outputs.projects) }} steps: - name: Checkout uses: actions/checkout@v4 - name: Setup .NET 8 uses: actions/setup-dotnet@v4 with: dotnet-version: | 8.0.x - name: Setup for MonoGame uses: infinitespace-studios/monogame-actions/install-wine@v1.0 - name: Install MonoPack run: dotnet tool install --global MonoPack - name: Build and Package Desktop Bundle id: package continue-on-error: true shell: bash run: | set -e PROJ="${{ matrix.project }}" NAME=$(basename "$PROJ" .csproj) PROJ_DIR=$(dirname "$PROJ") ARTIFACTS_DIR="./artifacts/$NAME" echo "Project path: $PROJ" echo "Project name: $NAME" echo "Project directory: $PROJ_DIR" echo "Output directory: $ARTIFACTS_DIR" # Check for macOS assets INFO_PLIST="$PROJ_DIR/Info.plist" ICNS_FILE="$PROJ_DIR/${NAME}.icns" if [ ! -f "$INFO_PLIST" ]; then echo "Error: Info.plist not found at $INFO_PLIST" echo "Please run ./generate-macos-assets.sh to generate macOS assets" exit 1 fi echo "Packaging $NAME for all desktop platforms" if [ -f "$ICNS_FILE" ]; then echo "Using icon file: $ICNS_FILE" monopack -p "$PROJ" \ -o "$ARTIFACTS_DIR" \ -rids win-x64,linux-x64,osx-x64,osx-arm64 \ -i "$INFO_PLIST" \ -c "$ICNS_FILE" \ -v \ --macos-universal else echo "No .icns file found, packaging without custom icon" monopack -p "$PROJ" \ -o "$ARTIFACTS_DIR" \ -rids win-x64,linux-x64,osx-x64,osx-arm64 \ -i "$INFO_PLIST" \ -v \ --macos-universal fi echo "Generated artifacts:" find "$ARTIFACTS_DIR" -type f -name "*.zip" || echo "No zip files found" ls -la "$ARTIFACTS_DIR" || echo "Artifacts directory not found" echo "name=$NAME" >> "$GITHUB_OUTPUT" - name: Record Build Result if: always() shell: bash run: | PROJ="${{ matrix.project }}" NAME=$(basename "$PROJ" .csproj) RESULT_DIR="./build-results" mkdir -p "$RESULT_DIR" if [ "${{ steps.package.outcome }}" = "success" ]; then jq -n --arg project "$NAME" --arg status "success" --arg error "" \ '{project: $project, status: $status, error: $error}' > "$RESULT_DIR/$NAME.json" else # Capture the error - this is a simplified version, actual error would be in logs jq -n --arg project "$NAME" --arg status "failure" --arg error "Build or packaging failed - see job logs for details" \ '{project: $project, status: $status, error: $error}' > "$RESULT_DIR/$NAME.json" fi - name: Upload Build Result if: always() uses: actions/upload-artifact@v4 with: name: build-result-${{ steps.package.outputs.name }} path: build-results/*.json retention-days: 1 - name: Upload Windows Bundle if: steps.package.outcome == 'success' uses: actions/upload-artifact@v4 with: name: ${{ steps.package.outputs.name }}-Windows path: artifacts/${{ steps.package.outputs.name }}/*-win-x64.zip if-no-files-found: error retention-days: 14 - name: Upload Linux Bundle if: steps.package.outcome == 'success' uses: actions/upload-artifact@v4 with: name: ${{ steps.package.outputs.name }}-Linux path: artifacts/${{ steps.package.outputs.name }}/*-linux-x64.tar.gz if-no-files-found: error retention-days: 14 - name: Upload macOS App Bundle if: steps.package.outcome == 'success' uses: actions/upload-artifact@v4 with: name: ${{ steps.package.outputs.name }}-macOS-Universal path: artifacts/${{ steps.package.outputs.name }}/*-universal.tar.gz if-no-files-found: error retention-days: 14 summary: needs: [discover, build-desktop-bundles] if: always() runs-on: macos-latest steps: - name: Download Build Results if: needs.discover.outputs.projects != '[]' && needs.discover.outputs.projects != '' uses: actions/download-artifact@v4 with: pattern: build-result-* path: build-results merge-multiple: true - name: Build Summary shell: bash run: | echo "# Desktop Sample Build Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY # Process build results if they exist if [ -d "build-results" ] && [ "$(ls -A build-results 2>/dev/null)" ]; then echo "## Build Results" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Project | Status | Error |" >> $GITHUB_STEP_SUMMARY echo "|---------|--------|-------|" >> $GITHUB_STEP_SUMMARY success_count=0 failure_count=0 for result_file in build-results/*.json; do if [ -f "$result_file" ]; then project=$(jq -r '.project' "$result_file") status=$(jq -r '.status' "$result_file") error=$(jq -r '.error' "$result_file") if [ "$status" = "success" ]; then echo "| $project | ✅ Success | - |" >> $GITHUB_STEP_SUMMARY success_count=$((success_count + 1)) else echo "| $project | ❌ Failed | $error |" >> $GITHUB_STEP_SUMMARY failure_count=$((failure_count + 1)) fi fi done echo "" >> $GITHUB_STEP_SUMMARY echo "**Summary:** $success_count succeeded, $failure_count failed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY elif [ "${{ needs.discover.outputs.projects }}" = "[]" ] || [ -z "${{ needs.discover.outputs.projects }}" ]; then echo "No Desktop platform projects were built (no successful restores)." >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY fi # Show skipped projects if [ -n "${{ needs.discover.outputs.skipped }}" ] && [ "${{ needs.discover.outputs.skipped }}" != "[]" ]; then echo "## Skipped Projects (Restore Failed)" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "${{ needs.discover.outputs.skipped }}" | jq -r '.[]' | while read -r proj; do name=$(basename "$proj" .csproj) echo "- ⏭️ $name" >> $GITHUB_STEP_SUMMARY done echo "" >> $GITHUB_STEP_SUMMARY fi echo "---" >> $GITHUB_STEP_SUMMARY echo "Cross-platform bundles (Windows, Linux, macOS) are available as separate artifacts for successful builds." >> $GITHUB_STEP_SUMMARY