name: Build and Test Arduino Firmware on: push: branches: [ main, develop ] paths: - 'code/RocketLauncher/**' - '.github/workflows/**' tags: - 'v*' pull_request: branches: [ main ] paths: - 'code/RocketLauncher/**' release: types: [ published, created ] workflow_dispatch: inputs: version: description: 'Semantic version (e.g., 1.0.0)' required: true type: string env: PLATFORMIO_VERSION: 6.1.7 jobs: version: name: Determine Version runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} is-release: ${{ steps.version.outputs.is-release }} tag: ${{ steps.version.outputs.tag }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Determine version id: version run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then echo "version=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT echo "is-release=true" >> $GITHUB_OUTPUT echo "tag=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then TAG_VERSION=${GITHUB_REF#refs/tags/} echo "version=${TAG_VERSION#v}" >> $GITHUB_OUTPUT echo "is-release=true" >> $GITHUB_OUTPUT echo "tag=${TAG_VERSION}" >> $GITHUB_OUTPUT else # Get latest tag and increment patch version LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0") LATEST_VERSION=${LATEST_TAG#v} IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION" NEW_PATCH=$((PATCH + 1)) NEW_VERSION="$MAJOR.$MINOR.$NEW_PATCH" echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT echo "is-release=false" >> $GITHUB_OUTPUT echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT fi echo "Version: ${{ steps.version.outputs.version }}" echo "Is Release: ${{ steps.version.outputs.is-release }}" echo "Tag: ${{ steps.version.outputs.tag }}" cmake-build: name: CMake Build and Test runs-on: ubuntu-latest needs: version steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup CMake uses: jwlawson/actions-setup-cmake@v1.13 with: cmake-version: '4.1.0' - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install platformio==${{ env.PLATFORMIO_VERSION }} - name: Cache PlatformIO packages uses: actions/cache@v3 with: path: | ~/.platformio ~/.cache/pip key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }} restore-keys: | ${{ runner.os }}-pio- - name: Configure CMake working-directory: ./code/RocketLauncher run: | cmake --preset default - name: Build CMake Project working-directory: ./code/RocketLauncher run: | cmake --build . --preset default - name: Run CMake Tests working-directory: ./code/RocketLauncher run: | ctest --preset default --output-on-failure - name: Upload CMake test results uses: actions/upload-artifact@v4 if: always() with: name: cmake-test-results path: ./code/RocketLauncher/build/ retention-days: 30 test: name: Run PlatformIO Unit Tests runs-on: ubuntu-latest needs: [version, cmake-build] steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install platformio==${{ env.PLATFORMIO_VERSION }} - name: Cache PlatformIO packages uses: actions/cache@v3 with: path: | ~/.platformio ~/.cache/pip key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }} restore-keys: | ${{ runner.os }}-pio- - name: Run PlatformIO Unit Tests working-directory: ./code/RocketLauncher run: | pio test -e native - name: Upload PlatformIO test results uses: actions/upload-artifact@v4 if: always() with: name: platformio-test-results path: ./code/RocketLauncher/.pio/test/native/ retention-days: 30 build-simulation: name: Build Simulation Firmware runs-on: ubuntu-latest needs: [version, cmake-build, test] steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install platformio==${{ env.PLATFORMIO_VERSION }} - name: Cache PlatformIO packages uses: actions/cache@v3 with: path: | ~/.platformio ~/.cache/pip key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }} restore-keys: | ${{ runner.os }}-pio- - name: Build Simulation Firmware working-directory: ./code/RocketLauncher run: | pio run -e simulide - name: Create output directory run: mkdir -p ./code/RocketLauncher/out/simulide - name: Copy firmware files working-directory: ./code/RocketLauncher run: | cp .pio/build/simulide/firmware.hex out/simulide/ cp .pio/build/simulide/firmware.elf out/simulide/ - name: Upload simulation firmware uses: actions/upload-artifact@v4 with: name: simulation-firmware path: | ./code/RocketLauncher/out/simulide/firmware.hex ./code/RocketLauncher/out/simulide/firmware.elf retention-days: 90 build-hardware: name: Build Hardware Firmware runs-on: ubuntu-latest needs: [version, cmake-build, test] steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v4 with: python-version: '3.11' - name: Install PlatformIO run: | python -m pip install --upgrade pip pip install platformio==${{ env.PLATFORMIO_VERSION }} - name: Cache PlatformIO packages uses: actions/cache@v3 with: path: | ~/.platformio ~/.cache/pip key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }} restore-keys: | ${{ runner.os }}-pio- - name: Build Hardware Firmware working-directory: ./code/RocketLauncher run: | pio run -e uno_hw - name: Upload hardware firmware uses: actions/upload-artifact@v4 with: name: hardware-firmware path: | ./code/RocketLauncher/.pio/build/uno_hw/firmware.hex ./code/RocketLauncher/.pio/build/uno_hw/firmware.elf retention-days: 90 create-tag: name: Create Git Tag runs-on: ubuntu-latest needs: [version, build-simulation, build-hardware] if: ${{ needs.version.outputs.is-release == 'true' }} outputs: tag: ${{ needs.version.outputs.tag }} upload_url: ${{ steps.release_info.outputs.upload_url }} release_id: ${{ steps.release_info.outputs.release_id }} html_url: ${{ steps.release_info.outputs.html_url }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Configure Git run: | git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - name: Check if tag exists id: check-tag run: | if git tag -l | grep -q "^${{ needs.version.outputs.tag }}$"; then echo "exists=true" >> $GITHUB_OUTPUT echo "Tag ${{ needs.version.outputs.tag }} already exists, skipping creation" else echo "exists=false" >> $GITHUB_OUTPUT fi - name: Create and push tag if: steps.check-tag.outputs.exists != 'true' run: | git tag ${{ needs.version.outputs.tag }} git push origin ${{ needs.version.outputs.tag }} - name: Tag already exists if: steps.check-tag.outputs.exists == 'true' run: | echo "Tag ${{ needs.version.outputs.tag }} already exists, continuing with release process" - name: Check if GitHub release exists id: check_release run: | # Check if a GitHub release already exists for this tag RELEASE_INFO=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/${{ github.repository }}/releases/tags/${{ needs.version.outputs.tag }}" || echo "") if [[ "$RELEASE_INFO" != "" && "$(echo "$RELEASE_INFO" | jq -r '.id // empty')" != "" ]]; then echo "exists=true" >> $GITHUB_OUTPUT echo "id=$(echo "$RELEASE_INFO" | jq -r '.id')" >> $GITHUB_OUTPUT echo "upload_url=$(echo "$RELEASE_INFO" | jq -r '.upload_url')" >> $GITHUB_OUTPUT echo "html_url=$(echo "$RELEASE_INFO" | jq -r '.html_url')" >> $GITHUB_OUTPUT echo "GitHub release for tag ${{ needs.version.outputs.tag }} already exists, will skip creation" else echo "exists=false" >> $GITHUB_OUTPUT echo "id=" >> $GITHUB_OUTPUT echo "upload_url=" >> $GITHUB_OUTPUT echo "html_url=" >> $GITHUB_OUTPUT fi - name: Create GitHub Release id: create_release if: steps.check_release.outputs.exists != 'true' uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ needs.version.outputs.tag }} release_name: "Luke's Rocket Launcher v${{ needs.version.outputs.version }}" body: | ## 🚀 Luke's Rocket Launcher v${{ needs.version.outputs.version }} ### What's New - Automated build and testing with CMake - Pre-compiled firmware for Arduino and SimulIDE - Comprehensive unit test coverage - Modern CMake build system for IDE integration ### Downloads - **Simulation Firmware**: For SimulIDE testing - **Hardware Firmware**: For Arduino upload ### Installation 1. Download the appropriate firmware file 2. For Arduino: Use Arduino IDE or PlatformIO to upload 3. For SimulIDE: Load the wiring file and firmware will auto-load ### Build Information - **Version**: ${{ needs.version.outputs.version }} - **Build Date**: ${{ github.event.head_commit.timestamp }} - **Commit**: ${{ github.sha }} - **Build System**: CMake + PlatformIO draft: false prerelease: false - name: Get Release Info id: release_info run: | if [[ "${{ steps.check_release.outputs.exists }}" == "true" ]]; then # Use existing release info from check_release step echo "release_id=${{ steps.check_release.outputs.id }}" >> $GITHUB_OUTPUT echo "upload_url=${{ steps.check_release.outputs.upload_url }}" >> $GITHUB_OUTPUT echo "html_url=${{ steps.check_release.outputs.html_url }}" >> $GITHUB_OUTPUT echo "Using existing GitHub release for tag ${{ needs.version.outputs.tag }}" else # Use newly created release info from create_release step echo "release_id=${{ steps.create_release.outputs.id }}" >> $GITHUB_OUTPUT echo "upload_url=${{ steps.create_release.outputs.upload_url }}" >> $GITHUB_OUTPUT echo "html_url=${{ steps.create_release.outputs.html_url }}" >> $GITHUB_OUTPUT echo "Created new GitHub release for tag ${{ needs.version.outputs.tag }}" fi create-release: name: Create Release Assets runs-on: ubuntu-latest needs: [version, build-simulation, build-hardware, create-tag] if: ${{ needs.version.outputs.is-release == 'true' }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download all artifacts uses: actions/download-artifact@v4 with: path: ./artifacts - name: Create release package run: | mkdir -p release/firmware/hardware mkdir -p release/firmware/simulide mkdir -p release/3dprints mkdir -p release/simulide # Copy firmware files to proper directories cp -r artifacts/hardware-firmware/* release/firmware/hardware/ cp -r artifacts/simulation-firmware/* release/firmware/simulide/ # Copy 3D print files (if they exist) if [ -d "prints" ]; then cp -r prints/* release/3dprints/ 2>/dev/null || true fi # Copy SimulIDE wiring files if [ -d "wiring" ]; then cp -r wiring/*.sim1 release/simulide/ 2>/dev/null || true fi # Create a comprehensive README for the release cat > release/README.md << 'EOF' # Luke's Rocket Launcher v${{ needs.create-tag.outputs.tag }} ## 📁 Package Structure ``` ├── firmware/ │ ├── hardware/ # Arduino UNO firmware files │ └── simulide/ # SimulIDE simulation firmware ├── 3dprints/ # 3D printable parts (SLA files) ├── simulide/ # SimulIDE wiring files (.sim1) └── README.md # This file ``` ## 🚀 Firmware Files ### Hardware (Arduino UNO) - `firmware/hardware/firmware.hex` - Hex file for Arduino upload - `firmware/hardware/firmware.elf` - ELF file for debugging ### Simulation (SimulIDE) - `firmware/simulide/firmware.hex` - Hex file for SimulIDE simulation - `firmware/simulide/firmware.elf` - ELF file for debugging ## 🖨️ 3D Prints - `3dprints/` - Contains all 3D printable parts - Files are optimized for SLA printing - Includes waterproof case components ## 🔌 SimulIDE Files - `simulide/*.sim1` - Circuit simulation files - Load these in SimulIDE for testing - Firmware auto-loads from hex files ## 📋 Installation ### For Hardware 1. Download the package 2. Use Arduino IDE or PlatformIO to upload `firmware/hardware/firmware.hex` 3. Or use `./scripts/build.sh upload` from the project directory ### For Simulation 1. Download the package 2. Open SimulIDE 3. Load `simulide/rocker_launcher_controls.sim1` 4. The firmware will auto-load from `firmware/simulide/firmware.hex` ## 🏗️ Build Information - **Version**: ${{ needs.create-tag.outputs.tag }} - **Built on**: ${{ github.event.head_commit.timestamp }} - **Commit**: ${{ github.sha }} - **Platform**: ${{ runner.os }} - **Build System**: CMake + PlatformIO ## 📚 Documentation - [Project README](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher#readme) - [Wiring Guide](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/tree/main/wiring) - [3D Prints](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/tree/main/prints) EOF - name: Create release zip run: | cd release zip -r ../lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.zip . cd .. - name: Upload release assets uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.create-tag.outputs.upload_url }} asset_path: ./lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.zip asset_name: lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.zip asset_content_type: application/zip build-status: name: Build Status Report runs-on: ubuntu-latest needs: [cmake-build, test, build-simulation, build-hardware] if: always() steps: - name: Checkout code uses: actions/checkout@v4 - name: Generate build report run: | echo "## 🚀 Build Status Report" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Build completed at:** $(date)" >> $GITHUB_STEP_SUMMARY echo "**Commit:** ${{ github.sha }}" >> $GITHUB_STEP_SUMMARY echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY if [ "${{ needs.cmake-build.result }}" == "success" ]; then echo "✅ **CMake Build:** SUCCESS" >> $GITHUB_STEP_SUMMARY else echo "❌ **CMake Build:** FAILED" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.test.result }}" == "success" ]; then echo "✅ **PlatformIO Tests:** PASSED" >> $GITHUB_STEP_SUMMARY else echo "❌ **PlatformIO Tests:** FAILED" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.build-simulation.result }}" == "success" ]; then echo "✅ **Simulation Build:** SUCCESS" >> $GITHUB_STEP_SUMMARY else echo "❌ **Simulation Build:** FAILED" >> $GITHUB_STEP_SUMMARY fi if [ "${{ needs.build-hardware.result }}" == "success" ]; then echo "✅ **Hardware Build:** SUCCESS" >> $GITHUB_STEP_SUMMARY else echo "❌ **Hardware Build:** FAILED" >> $GITHUB_STEP_SUMMARY fi echo "" >> $GITHUB_STEP_SUMMARY echo "**Build System:** CMake + PlatformIO" >> $GITHUB_STEP_SUMMARY echo "**Firmware files are available as artifacts above.**" >> $GITHUB_STEP_SUMMARY publish-package: name: Publish to GitHub Packages runs-on: ubuntu-latest needs: [version, build-simulation, build-hardware, create-tag] if: ${{ needs.version.outputs.is-release == 'true' }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Download artifacts uses: actions/download-artifact@v4 with: path: ./artifacts - name: Create package manifest run: | mkdir -p package/firmware/hardware mkdir -p package/firmware/simulide mkdir -p package/3dprints mkdir -p package/simulide # Copy firmware files to proper directories cp -r artifacts/hardware-firmware/* package/firmware/hardware/ cp -r artifacts/simulation-firmware/* package/firmware/simulide/ # Copy 3D print files (if they exist) if [ -d "prints" ]; then cp -r prints/* package/3dprints/ 2>/dev/null || true fi # Copy SimulIDE wiring files if [ -d "wiring" ]; then cp -r wiring/*.sim1 package/simulide/ 2>/dev/null || true fi # Create package.json for npm-style packaging cat > package/package.json << EOF { "name": "lukes-rocket-launcher-firmware", "version": "${{ needs.version.outputs.version }}", "description": "Arduino firmware for Luke's Rocket Launch Controller", "main": "firmware/hardware/firmware.hex", "files": [ "firmware/**/*.hex", "firmware/**/*.elf", "3dprints/**/*", "simulide/**/*.sim1", "README.md" ], "repository": { "type": "git", "url": "https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher.git" }, "keywords": [ "arduino", "rocket", "launcher", "firmware", "simulide", "3d-printing", "cmake", "platformio" ], "author": "KofTwentyTwo", "license": "MIT", "bugs": { "url": "https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/issues" }, "homepage": "https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher#readme" } EOF # Create comprehensive README cat > package/README.md << EOF # Luke's Rocket Launcher v${{ needs.version.outputs.version }} ## 📁 Package Structure \`\`\` ├── firmware/ │ ├── hardware/ # Arduino UNO firmware files │ └── simulide/ # SimulIDE simulation firmware ├── 3dprints/ # 3D printable parts (SLA files) ├── simulide/ # SimulIDE wiring files (.sim1) └── README.md # This file \`\`\` ## 🚀 Firmware Files ### Hardware (Arduino UNO) - \`firmware/hardware/firmware.hex\` - Hex file for Arduino upload - \`firmware/hardware/firmware.elf\` - ELF file for debugging ### Simulation (SimulIDE) - \`firmware/simulide/firmware.hex\` - Hex file for SimulIDE simulation - \`firmware/simulide/firmware.elf\` - ELF file for debugging ## 🖨️ 3D Prints - \`3dprints/\` - Contains all 3D printable parts - Files are optimized for SLA printing - Includes waterproof case components ## 🔌 SimulIDE Files - \`simulide/*.sim1\` - Circuit simulation files - Load these in SimulIDE for testing - Firmware auto-loads from hex files ## 📋 Installation ### For Hardware 1. Download the package 2. Use Arduino IDE or PlatformIO to upload \`firmware/hardware/firmware.hex\` 3. Or use \`./scripts/build.sh upload\` from the project directory ### For Simulation 1. Download the package 2. Open SimulIDE 3. Load \`simulide/rocker_launcher_controls.sim1\` 4. The firmware will auto-load from \`firmware/simulide/firmware.hex\` ## 🏗️ Build Information - **Version**: ${{ needs.version.outputs.version }} - **Built on**: ${{ github.event.head_commit.timestamp }} - **Commit**: ${{ github.sha }} - **Platform**: ${{ runner.os }} - **Build System**: CMake + PlatformIO ## 📚 Documentation - [Project README](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher#readme) - [Wiring Guide](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/tree/main/wiring) - [3D Prints](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/tree/main/prints) ## Changelog See [GitHub Releases](https://github.com/KofTwentyTwo/Lukes-Rocket-Launcher/releases) for detailed changelog. EOF - name: Create package archive run: | cd package # Create TAR archive for release upload tar -czf ../lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.tar.gz . # Create ZIP archive (available for GitHub Packages, not uploaded to release) zip -r ../lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.zip . cd .. - name: Publish to GitHub Packages uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ needs.create-tag.outputs.upload_url }} asset_path: ./lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.tar.gz asset_name: lukes-rocket-launcher-firmware-v${{ needs.version.outputs.version }}.tar.gz asset_content_type: application/gzip # Note: ZIP file is created but not uploaded to avoid duplicate release assets # TAR file is uploaded to the release, ZIP is available for GitHub Packages if needed