name: Build Game # Build pipeline for creating game executables across multiple platforms # # Features: # - Manual trigger with individual platform checkboxes # - Configurable version override (defaults to auto-generated) # - Configurable tool versions (Godot, Java, Android API, etc.) # - Flexible runner OS selection # - Tag-based automatic builds for releases # - Multi-platform builds (Windows, Linux, macOS, Android) # - Artifact storage for one week # - Comprehensive build configuration options on: # Manual trigger with platform selection workflow_dispatch: inputs: build_windows: description: 'Build for Windows' required: false default: true type: boolean build_linux: description: 'Build for Linux' required: false default: false type: boolean build_macos: description: 'Build for macOS' required: false default: false type: boolean build_android: description: 'Build for Android' required: false default: false type: boolean version: description: 'Version (leave empty for auto-generated)' required: false default: '' type: string build_type: description: 'Build type' required: true default: 'release' type: debug options: - release - debug godot_version: description: 'Godot version (leave empty for default)' required: false default: '' type: string runner_os: description: 'Runner OS (leave empty for default ubuntu-latest)' required: false default: '' type: string java_version: description: 'Java version (leave empty for default)' required: false default: '' type: string android_api_level: description: 'Android API level (leave empty for default)' required: false default: '' type: string # Automatic trigger on git tags (for releases) push: tags: - 'v*' # Version tags (v1.0.0, v2.1.0, etc.) - 'release-*' # Release tags env: # Core Configuration GODOT_VERSION: "4.4.1" PROJECT_NAME: "Skelly" BUILD_DIR: "builds" DEFAULT_VERSION: "1.0.0-dev" # GitHub Actions Versions ACTIONS_CHECKOUT_VERSION: "v4" ACTIONS_CACHE_VERSION: "v4" ACTIONS_UPLOAD_ARTIFACT_VERSION: "v3" ACTIONS_SETUP_JAVA_VERSION: "v4" # Third-party Actions Versions CHICKENSOFT_SETUP_GODOT_VERSION: "v1" ANDROID_ACTIONS_SETUP_ANDROID_VERSION: "v3" # Runner Configuration RUNNER_OS: "ubuntu-latest" # Java Configuration JAVA_DISTRIBUTION: "temurin" JAVA_VERSION: "17" # Android Configuration ANDROID_API_LEVEL: "33" ANDROID_BUILD_TOOLS_VERSION: "33.0.0" ANDROID_CMDLINE_TOOLS_VERSION: "latest" jobs: # Preparation job - determines build configuration prepare: name: Prepare Build runs-on: - "ubuntu-latest" outputs: platforms: ${{ steps.config.outputs.platforms }} build_type: ${{ steps.config.outputs.build_type }} version: ${{ steps.config.outputs.version }} artifact_name: ${{ steps.config.outputs.artifact_name }} steps: - name: Checkout repository uses: actions/checkout@${{ env.ACTIONS_CHECKOUT_VERSION }} with: fetch-depth: 0 - name: Configure build parameters id: config run: | # Override environment variables with user inputs if provided if [[ -n "${{ github.event.inputs.godot_version }}" ]]; then echo "GODOT_VERSION=${{ github.event.inputs.godot_version }}" >> $GITHUB_ENV echo "🔧 Using custom Godot version: ${{ github.event.inputs.godot_version }}" fi if [[ -n "${{ github.event.inputs.runner_os }}" ]]; then echo "RUNNER_OS=${{ github.event.inputs.runner_os }}" >> $GITHUB_ENV echo "🔧 Using custom runner OS: ${{ github.event.inputs.runner_os }}" fi if [[ -n "${{ github.event.inputs.java_version }}" ]]; then echo "JAVA_VERSION=${{ github.event.inputs.java_version }}" >> $GITHUB_ENV echo "🔧 Using custom Java version: ${{ github.event.inputs.java_version }}" fi if [[ -n "${{ github.event.inputs.android_api_level }}" ]]; then echo "ANDROID_API_LEVEL=${{ github.event.inputs.android_api_level }}" >> $GITHUB_ENV echo "ANDROID_BUILD_TOOLS_VERSION=${{ github.event.inputs.android_api_level }}.0.0" >> $GITHUB_ENV echo "🔧 Using custom Android API level: ${{ github.event.inputs.android_api_level }}" fi # Determine platforms to build if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then # Build platforms array from individual checkboxes platforms="" if [[ "${{ github.event.inputs.build_windows }}" == "true" ]]; then platforms="${platforms}windows," fi if [[ "${{ github.event.inputs.build_linux }}" == "true" ]]; then platforms="${platforms}linux," fi if [[ "${{ github.event.inputs.build_macos }}" == "true" ]]; then platforms="${platforms}macos," fi if [[ "${{ github.event.inputs.build_android }}" == "true" ]]; then platforms="${platforms}android," fi # Remove trailing comma platforms="${platforms%,}" build_type="${{ github.event.inputs.build_type }}" user_version="${{ github.event.inputs.version }}" else # Tag-triggered build - build all platforms platforms="windows,linux,macos,android" build_type="release" user_version="" fi # Determine version with improved logic if [[ -n "$user_version" ]]; then # User provided explicit version version="$user_version" echo "🏷️ Using user-specified version: $version" elif [[ "${{ github.ref_type }}" == "tag" ]]; then # Tag-triggered build - use tag name version="${{ github.ref_name }}" echo "🏷️ Using git tag version: $version" elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then # Manual dispatch without version - use default + git info commit_short=$(git rev-parse --short HEAD) branch_name="${{ github.ref_name }}" timestamp=$(date +%Y%m%d-%H%M) version="${{ env.DEFAULT_VERSION }}-${branch_name}-${commit_short}-${timestamp}" echo "🏷️ Using auto-generated version: $version" else # Fallback for other triggers commit_short=$(git rev-parse --short HEAD) branch_name="${{ github.ref_name }}" timestamp=$(date +%Y%m%d-%H%M) version="${{ env.DEFAULT_VERSION }}-${branch_name}-${commit_short}-${timestamp}" echo "🏷️ Using fallback version: $version" fi # Create artifact name artifact_name="${{ env.PROJECT_NAME }}-${version}-${build_type}" echo "platforms=${platforms}" >> $GITHUB_OUTPUT echo "build_type=${build_type}" >> $GITHUB_OUTPUT echo "version=${version}" >> $GITHUB_OUTPUT echo "artifact_name=${artifact_name}" >> $GITHUB_OUTPUT echo "🔧 Build Configuration:" echo " Platforms: ${platforms}" echo " Build Type: ${build_type}" echo " Version: ${version}" echo " Artifact: ${artifact_name}" echo "" echo "🔧 Tool Versions:" echo " Godot: ${GODOT_VERSION}" echo " Runner OS: ${RUNNER_OS}" echo " Java: ${JAVA_VERSION}" echo " Android API: ${ANDROID_API_LEVEL}" echo " Android Build Tools: ${ANDROID_BUILD_TOOLS_VERSION}" # Setup export templates (shared across all platform builds) setup-templates: name: Setup Export Templates runs-on: ${{ env.RUNNER_OS }} needs: prepare steps: - name: Cache export templates id: cache-templates uses: actions/cache@${{ env.ACTIONS_CACHE_VERSION }} with: path: ~/.local/share/godot/export_templates key: godot-templates-${{ env.GODOT_VERSION }} restore-keys: | godot-templates- - name: Setup Godot if: steps.cache-templates.outputs.cache-hit != 'true' uses: chickensoft-games/setup-godot@${{ env.CHICKENSOFT_SETUP_GODOT_VERSION }} with: version: ${{ env.GODOT_VERSION }} use-dotnet: false - name: Install export templates if: steps.cache-templates.outputs.cache-hit != 'true' run: | echo "📦 Installing Godot export templates..." mkdir -p ~/.local/share/godot/export_templates/${{ env.GODOT_VERSION }}.stable wget -q https://github.com/godotengine/godot/releases/download/${{ env.GODOT_VERSION }}-stable/Godot_v${{ env.GODOT_VERSION }}-stable_export_templates.tpz unzip -q Godot_v${{ env.GODOT_VERSION }}-stable_export_templates.tpz mv templates/* ~/.local/share/godot/export_templates/${{ env.GODOT_VERSION }}.stable/ echo "✅ Export templates installed successfully" ls -la ~/.local/share/godot/export_templates/${{ env.GODOT_VERSION }}.stable/ - name: Verify templates cache run: | echo "🔍 Verifying export templates are available:" ls -la ~/.local/share/godot/export_templates/${{ env.GODOT_VERSION }}.stable/ # Windows build job build-windows: name: Build Windows runs-on: ${{ env.RUNNER_OS }} needs: [prepare, setup-templates] if: contains(needs.prepare.outputs.platforms, 'windows') steps: - name: Checkout repository uses: actions/checkout@${{ env.ACTIONS_CHECKOUT_VERSION }} - name: Setup Godot uses: chickensoft-games/setup-godot@${{ env.CHICKENSOFT_SETUP_GODOT_VERSION }} with: version: ${{ env.GODOT_VERSION }} use-dotnet: false - name: Restore export templates cache uses: actions/cache@${{ env.ACTIONS_CACHE_VERSION }} with: path: ~/.local/share/godot/export_templates key: godot-templates-${{ env.GODOT_VERSION }} restore-keys: | godot-templates- - name: Create build directory run: mkdir -p ${{ env.BUILD_DIR }} - name: Import project assets run: | echo "📦 Importing project assets..." godot --headless --verbose --editor --quit || true sleep 2 - name: Build Windows executable run: | echo "🏗️ Building Windows executable..." godot --headless --verbose --export-${{ needs.prepare.outputs.build_type }} "Windows Desktop" \ ${{ env.BUILD_DIR }}/skelly-windows-${{ needs.prepare.outputs.version }}.exe # Verify build output if [[ -f "${{ env.BUILD_DIR }}/skelly-windows-${{ needs.prepare.outputs.version }}.exe" ]]; then echo "✅ Windows build successful" ls -la ${{ env.BUILD_DIR }}/ else echo "❌ Windows build failed" exit 1 fi - name: Upload Windows build uses: actions/upload-artifact@${{ env.ACTIONS_UPLOAD_ARTIFACT_VERSION }} with: name: ${{ needs.prepare.outputs.artifact_name }}-windows path: ${{ env.BUILD_DIR }}/skelly-windows-${{ needs.prepare.outputs.version }}.exe retention-days: 7 compression-level: 0 # Linux build job build-linux: name: Build Linux runs-on: ${{ env.RUNNER_OS }} needs: [prepare, setup-templates] if: contains(needs.prepare.outputs.platforms, 'linux') steps: - name: Checkout repository uses: actions/checkout@${{ env.ACTIONS_CHECKOUT_VERSION }} - name: Setup Godot uses: chickensoft-games/setup-godot@${{ env.CHICKENSOFT_SETUP_GODOT_VERSION }} with: version: ${{ env.GODOT_VERSION }} use-dotnet: false - name: Restore export templates cache uses: actions/cache@${{ env.ACTIONS_CACHE_VERSION }} with: path: ~/.local/share/godot/export_templates key: godot-templates-${{ env.GODOT_VERSION }} restore-keys: | godot-templates- - name: Create build directory run: mkdir -p ${{ env.BUILD_DIR }} - name: Import project assets run: | echo "📦 Importing project assets..." godot --headless --verbose --editor --quit || true sleep 2 - name: Build Linux executable run: | echo "🏗️ Building Linux executable..." godot --headless --verbose --export-${{ needs.prepare.outputs.build_type }} "Linux" \ ${{ env.BUILD_DIR }}/skelly-linux-${{ needs.prepare.outputs.version }}.x86_64 # Make executable chmod +x ${{ env.BUILD_DIR }}/skelly-linux-${{ needs.prepare.outputs.version }}.x86_64 # Verify build output if [[ -f "${{ env.BUILD_DIR }}/skelly-linux-${{ needs.prepare.outputs.version }}.x86_64" ]]; then echo "✅ Linux build successful" ls -la ${{ env.BUILD_DIR }}/ else echo "❌ Linux build failed" exit 1 fi - name: Upload Linux build uses: actions/upload-artifact@${{ env.ACTIONS_UPLOAD_ARTIFACT_VERSION }} with: name: ${{ needs.prepare.outputs.artifact_name }}-linux path: ${{ env.BUILD_DIR }}/skelly-linux-${{ needs.prepare.outputs.version }}.x86_64 retention-days: 7 compression-level: 0 # macOS build job build-macos: name: Build macOS runs-on: ${{ env.RUNNER_OS }} needs: [prepare, setup-templates] if: contains(needs.prepare.outputs.platforms, 'macos') steps: - name: Checkout repository uses: actions/checkout@${{ env.ACTIONS_CHECKOUT_VERSION }} - name: Setup Godot uses: chickensoft-games/setup-godot@${{ env.CHICKENSOFT_SETUP_GODOT_VERSION }} with: version: ${{ env.GODOT_VERSION }} use-dotnet: false - name: Restore export templates cache uses: actions/cache@${{ env.ACTIONS_CACHE_VERSION }} with: path: ~/.local/share/godot/export_templates key: godot-templates-${{ env.GODOT_VERSION }} restore-keys: | godot-templates- - name: Create build directory run: mkdir -p ${{ env.BUILD_DIR }} - name: Import project assets run: | echo "📦 Importing project assets..." godot --headless --verbose --editor --quit || true sleep 2 - name: Build macOS application run: | echo "🏗️ Building macOS application..." godot --headless --verbose --export-${{ needs.prepare.outputs.build_type }} "macOS" \ ${{ env.BUILD_DIR }}/skelly-macos-${{ needs.prepare.outputs.version }}.zip # Verify build output if [[ -f "${{ env.BUILD_DIR }}/skelly-macos-${{ needs.prepare.outputs.version }}.zip" ]]; then echo "✅ macOS build successful" ls -la ${{ env.BUILD_DIR }}/ else echo "❌ macOS build failed" exit 1 fi - name: Upload macOS build uses: actions/upload-artifact@${{ env.ACTIONS_UPLOAD_ARTIFACT_VERSION }} with: name: ${{ needs.prepare.outputs.artifact_name }}-macos path: ${{ env.BUILD_DIR }}/skelly-macos-${{ needs.prepare.outputs.version }}.zip retention-days: 7 compression-level: 0 # Android build job build-android: name: Build Android runs-on: ${{ env.RUNNER_OS }} needs: [prepare, setup-templates] if: contains(needs.prepare.outputs.platforms, 'android') steps: - name: Checkout repository uses: actions/checkout@${{ env.ACTIONS_CHECKOUT_VERSION }} - name: Setup Java uses: actions/setup-java@${{ env.ACTIONS_SETUP_JAVA_VERSION }} with: distribution: ${{ env.JAVA_DISTRIBUTION }} java-version: ${{ env.JAVA_VERSION }} - name: Setup Android SDK uses: android-actions/setup-android@${{ env.ANDROID_ACTIONS_SETUP_ANDROID_VERSION }} with: api-level: ${{ env.ANDROID_API_LEVEL }} build-tools: ${{ env.ANDROID_BUILD_TOOLS_VERSION }} - name: Install Android Build Tools run: | echo "🔧 Installing Android Build Tools..." # Set Android environment variables export ANDROID_HOME=${ANDROID_SDK_ROOT} echo "ANDROID_HOME=${ANDROID_SDK_ROOT}" >> $GITHUB_ENV echo "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" >> $GITHUB_ENV # Install build-tools using sdkmanager yes | ${ANDROID_SDK_ROOT}/cmdline-tools/${{ env.ANDROID_CMDLINE_TOOLS_VERSION }}/bin/sdkmanager --licenses || true ${ANDROID_SDK_ROOT}/cmdline-tools/${{ env.ANDROID_CMDLINE_TOOLS_VERSION }}/bin/sdkmanager "build-tools;${{ env.ANDROID_BUILD_TOOLS_VERSION }}" ${ANDROID_SDK_ROOT}/cmdline-tools/${{ env.ANDROID_CMDLINE_TOOLS_VERSION }}/bin/sdkmanager "platforms;android-${{ env.ANDROID_API_LEVEL }}" - name: Verify Android SDK Configuration run: | echo "📱 Verifying Android SDK setup..." echo "📱 Using API Level: ${{ env.ANDROID_API_LEVEL }}" echo "📱 Using Build Tools: ${{ env.ANDROID_BUILD_TOOLS_VERSION }}" # Verify SDK installation echo "📱 Android SDK Location: ${ANDROID_SDK_ROOT}" ls -la ${ANDROID_SDK_ROOT}/ echo "📱 Build Tools:" ls -la ${ANDROID_SDK_ROOT}/build-tools/ echo "📱 Platforms:" ls -la ${ANDROID_SDK_ROOT}/platforms/ || echo "No platforms directory" # Verify apksigner exists if [ -f "${ANDROID_SDK_ROOT}/build-tools/${{ env.ANDROID_BUILD_TOOLS_VERSION }}/apksigner" ]; then echo "✅ apksigner found at ${ANDROID_SDK_ROOT}/build-tools/${{ env.ANDROID_BUILD_TOOLS_VERSION }}/apksigner" else echo "❌ apksigner not found!" exit 1 fi - name: Setup Godot uses: chickensoft-games/setup-godot@${{ env.CHICKENSOFT_SETUP_GODOT_VERSION }} with: version: ${{ env.GODOT_VERSION }} use-dotnet: false - name: Configure Godot for Android run: | echo "🎮 Configuring Godot for Android builds..." # Create Godot config directory mkdir -p ~/.config/godot # Configure Android SDK path in Godot settings cat > ~/.config/godot/editor_settings-4.4.tres << EOF [gd_resource type="EditorSettings" format=3] [resource] export/android/android_sdk_path = "${ANDROID_SDK_ROOT}" export/android/debug_keystore = "" export/android/debug_keystore_user = "androiddebugkey" export/android/debug_keystore_pass = "android" export/android/force_system_user = false export/android/timestamping_authority_url = "" export/android/shutdown_adb_on_exit = true EOF echo "✅ Godot Android configuration complete" - name: Restore export templates cache uses: actions/cache@${{ env.ACTIONS_CACHE_VERSION }} with: path: ~/.local/share/godot/export_templates key: godot-templates-${{ env.GODOT_VERSION }} restore-keys: | godot-templates- - name: Create build directory run: mkdir -p ${{ env.BUILD_DIR }} - name: Import project assets run: | echo "📦 Importing project assets..." godot --headless --verbose --editor --quit || true sleep 2 - name: Build Android APK run: | echo "🏗️ Building Android APK..." # Verify Android environment echo "📱 Android SDK: ${ANDROID_SDK_ROOT}" echo "📱 API Level: ${{ env.ANDROID_API_LEVEL }}" echo "📱 Build Tools Version: ${{ env.ANDROID_BUILD_TOOLS_VERSION }}" echo "📱 Available Build Tools: $(ls ${ANDROID_SDK_ROOT}/build-tools/)" godot --headless --verbose --export-${{ needs.prepare.outputs.build_type }} "Android" \ ${{ env.BUILD_DIR }}/skelly-android-${{ needs.prepare.outputs.version }}.apk # Verify build output if [[ -f "${{ env.BUILD_DIR }}/skelly-android-${{ needs.prepare.outputs.version }}.apk" ]]; then echo "✅ Android build successful" ls -la ${{ env.BUILD_DIR }}/ # Show APK info echo "📱 APK Information:" file ${{ env.BUILD_DIR }}/skelly-android-${{ needs.prepare.outputs.version }}.apk else echo "❌ Android build failed" exit 1 fi - name: Upload Android build uses: actions/upload-artifact@${{ env.ACTIONS_UPLOAD_ARTIFACT_VERSION }} with: name: ${{ needs.prepare.outputs.artifact_name }}-android path: ${{ env.BUILD_DIR }}/skelly-android-${{ needs.prepare.outputs.version }}.apk retention-days: 7 compression-level: 0 # Summary job - creates release summary summary: name: Build Summary runs-on: ${{ env.RUNNER_OS }} needs: [prepare, setup-templates, build-windows, build-linux, build-macos, build-android] if: always() steps: - name: Generate build summary run: | echo "🎮 Build Summary for ${{ needs.prepare.outputs.artifact_name }}" echo "==================================" echo "" echo "📋 Configuration:" echo " Version: ${{ needs.prepare.outputs.version }}" echo " Build Type: ${{ needs.prepare.outputs.build_type }}" echo " Platforms: ${{ needs.prepare.outputs.platforms }}" echo "" echo "📊 Build Results:" platforms="${{ needs.prepare.outputs.platforms }}" if [[ "$platforms" == *"windows"* ]]; then windows_status="${{ needs.build-windows.result }}" echo " 🪟 Windows: $windows_status" fi if [[ "$platforms" == *"linux"* ]]; then linux_status="${{ needs.build-linux.result }}" echo " 🐧 Linux: $linux_status" fi if [[ "$platforms" == *"macos"* ]]; then macos_status="${{ needs.build-macos.result }}" echo " 🍎 macOS: $macos_status" fi if [[ "$platforms" == *"android"* ]]; then android_status="${{ needs.build-android.result }}" echo " 🤖 Android: $android_status" fi echo "" echo "📦 Artifacts are available for 7 days" echo "🔗 Download from: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - name: Check overall build status run: | # Check if any required builds failed platforms="${{ needs.prepare.outputs.platforms }}" failed_builds=() if [[ "$platforms" == *"windows"* ]] && [[ "${{ needs.build-windows.result }}" != "success" ]]; then failed_builds+=("Windows") fi if [[ "$platforms" == *"linux"* ]] && [[ "${{ needs.build-linux.result }}" != "success" ]]; then failed_builds+=("Linux") fi if [[ "$platforms" == *"macos"* ]] && [[ "${{ needs.build-macos.result }}" != "success" ]]; then failed_builds+=("macOS") fi if [[ "$platforms" == *"android"* ]] && [[ "${{ needs.build-android.result }}" != "success" ]]; then failed_builds+=("Android") fi if [[ ${#failed_builds[@]} -gt 0 ]]; then echo "❌ Build failed for: ${failed_builds[*]}" exit 1 else echo "✅ All builds completed successfully!" fi