UPDATE: gitignore #35
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
| name: CI/CD Pipeline | |
| on: | |
| push: | |
| branches: | |
| - main | |
| tags: | |
| - '1*' # Matches tags starting with 1 (100, 110, 112, etc.) | |
| - '2*' # Matches tags starting with 2 (200, 210, 211, etc.) | |
| - '3*' # Matches tags starting with 3 (300, 310, 312, etc.) | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| env: | |
| BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory | |
| PLATFORMIO_CACHE_DIR: ~/.platformio/.cache | |
| jobs: | |
| # Stage 1: Branch Development Pipeline | |
| branch-pipeline: | |
| name: Branch Pipeline (Compile + SonarQube) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref != 'refs/heads/main') | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.9" | |
| - name: Cache PlatformIO | |
| uses: actions/cache@v3 | |
| with: | |
| path: | | |
| ~/.cache/pip | |
| ${{ env.PLATFORMIO_CACHE_DIR }} | |
| key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pio- | |
| - name: Install dependencies | |
| run: | | |
| pip install --upgrade platformio | |
| sudo apt-get install build-essential bc jq | |
| - name: Compile project (release) | |
| run: make release | |
| - name: Compile project (minimal) | |
| run: make minimal | |
| - name: Check flash size limit | |
| run: | | |
| # Parse the ELF file that was already built to extract flash usage | |
| ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" | |
| if [ ! -f "$ELF_FILE" ]; then | |
| echo "❌ ERROR: ELF file not found at $ELF_FILE" | |
| exit 1 | |
| fi | |
| # Extract flash usage from ELF file using size command | |
| FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') | |
| if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then | |
| echo "❌ ERROR: Could not determine flash usage from ELF file" | |
| exit 1 | |
| fi | |
| echo "Flash used: $FLASH_USED bytes" | |
| # Check if flash usage exceeds 1,800,000 bytes (90% of 1.875MB app partition) | |
| if [ "$FLASH_USED" -gt 1800000 ]; then | |
| echo "❌ ERROR: Flash usage ($FLASH_USED bytes) exceeds limit of 1,800,000 bytes" | |
| echo "Flash usage: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| exit 1 | |
| else | |
| echo "✅ Flash usage OK: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| fi | |
| - name: Store map files as artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-artifacts-branch-${{ github.sha }} | |
| path: | | |
| .pio/build/ttgo-lora32-v1/firmware.map | |
| .pio/build/minimal/firmware.map | |
| retention-days: 30 | |
| if: always() | |
| - name: Generate Compilation DB | |
| run: pio run -t compiledb | |
| - name: SonarQube Scan | |
| uses: SonarSource/sonarqube-scan-action@v5 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| with: | |
| args: > | |
| --define sonar.cfamily.compile-commands="compile_commands.json" | |
| # Stage 2: Main Branch Pipeline | |
| main-pipeline: | |
| name: Main Pipeline (Compile + SonarQube) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/main' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.9" | |
| - name: Cache PlatformIO | |
| uses: actions/cache@v3 | |
| with: | |
| path: | | |
| ~/.cache/pip | |
| ${{ env.PLATFORMIO_CACHE_DIR }} | |
| key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pio- | |
| - name: Install dependencies | |
| run: | | |
| pip install --upgrade platformio | |
| sudo apt-get install build-essential bc jq | |
| - name: Compile project (release) | |
| run: make release | |
| - name: Compile project (minimal) | |
| run: make minimal | |
| - name: Check flash size limit | |
| run: | | |
| # Parse the ELF file that was already built to extract flash usage | |
| ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" | |
| if [ ! -f "$ELF_FILE" ]; then | |
| echo "❌ ERROR: ELF file not found at $ELF_FILE" | |
| exit 1 | |
| fi | |
| # Extract flash usage from ELF file using size command | |
| FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') | |
| if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then | |
| echo "❌ ERROR: Could not determine flash usage from ELF file" | |
| exit 1 | |
| fi | |
| echo "Flash used: $FLASH_USED bytes" | |
| # Check if flash usage exceeds 1,800,000 bytes (90% of 1.875MB app partition) | |
| if [ "$FLASH_USED" -gt 1800000 ]; then | |
| echo "❌ ERROR: Flash usage ($FLASH_USED bytes) exceeds limit of 1,800,000 bytes" | |
| echo "Flash usage: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| exit 1 | |
| else | |
| echo "✅ Flash usage OK: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| fi | |
| - name: Store map files as artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-artifacts-main-${{ github.sha }} | |
| path: | | |
| .pio/build/ttgo-lora32-v1/firmware.map | |
| .pio/build/minimal/firmware.map | |
| retention-days: 30 | |
| if: always() | |
| - name: Generate Compilation DB | |
| run: pio run -t compiledb | |
| - name: SonarQube Scan | |
| uses: SonarSource/sonarqube-scan-action@v5 | |
| env: | |
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} | |
| with: | |
| args: > | |
| --define sonar.cfamily.compile-commands="compile_commands.json" | |
| # Stage 3: Release Pipeline | |
| release-pipeline: | |
| name: Release Pipeline (Tag Validation + Build + Artifacts) | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/') | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Validate tag format | |
| id: validate-tag | |
| run: | | |
| # Extract tag name without 'refs/tags/' prefix | |
| TAG_NAME="${GITHUB_REF#refs/tags/}" | |
| echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT | |
| # Check if tag starts with 'v' (invalid) | |
| if [[ "$TAG_NAME" == v* ]]; then | |
| echo "❌ Error: Tag '$TAG_NAME' starts with 'v'. Tags must be 3-digit integers like '100', '110', '112', etc." | |
| echo "Valid examples: 100 (v1.0.0), 110 (v1.1.0), 112 (v1.1.2)" | |
| echo "Invalid examples: v100, 1.0, 1.1, 1000, 99" | |
| exit 1 | |
| fi | |
| # Check if tag matches 3-digit integer pattern | |
| if [[ "$TAG_NAME" =~ ^[0-9]{3}$ ]]; then | |
| echo "✅ Tag '$TAG_NAME' is a valid 3-digit integer" | |
| echo "firmware_version=$TAG_NAME" >> $GITHUB_OUTPUT | |
| else | |
| echo "❌ Error: Tag '$TAG_NAME' is not a valid 3-digit integer" | |
| echo "Valid format: XXX where X are numbers (e.g., 100, 110, 112)" | |
| echo "Invalid examples: 1.0, 1.1, 1000, 99, v100" | |
| exit 1 | |
| fi | |
| - name: Update platformio.ini with tag version | |
| run: | | |
| # Replace the hardcoded FIRMWARE_VERSION in platformio.ini | |
| sed -i "s/-D FIRMWARE_VERSION=[0-9]*/-D FIRMWARE_VERSION=${{ steps.validate-tag.outputs.firmware_version }}/" platformio.ini | |
| echo "Updated platformio.ini with FIRMWARE_VERSION=${{ steps.validate-tag.outputs.firmware_version }}" | |
| - name: Setup Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: "3.9" | |
| - name: Cache PlatformIO | |
| uses: actions/cache@v3 | |
| with: | |
| path: | | |
| ~/.cache/pip | |
| ${{ env.PLATFORMIO_CACHE_DIR }} | |
| key: ${{ runner.os }}-pio-${{ hashFiles('platformio.ini') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pio- | |
| - name: Install dependencies | |
| run: | | |
| pip install --upgrade platformio | |
| sudo apt-get install build-essential bc jq | |
| - name: Compile project (release) | |
| run: make release | |
| - name: Compile project (minimal) | |
| run: make minimal | |
| - name: Check flash size limit | |
| run: | | |
| # Parse the ELF file that was already built to extract flash usage | |
| ELF_FILE=".pio/build/ttgo-lora32-v1/firmware.elf" | |
| if [ ! -f "$ELF_FILE" ]; then | |
| echo "❌ ERROR: ELF file not found at $ELF_FILE" | |
| exit 1 | |
| fi | |
| # Extract flash usage from ELF file using size command | |
| FLASH_USED=$(size "$ELF_FILE" | tail -n 1 | awk '{print $1 + $2}') | |
| if [ -z "$FLASH_USED" ] || [ "$FLASH_USED" -eq 0 ]; then | |
| echo "❌ ERROR: Could not determine flash usage from ELF file" | |
| exit 1 | |
| fi | |
| echo "Flash used: $FLASH_USED bytes" | |
| # Check if flash usage exceeds 1,800,000 bytes (90% of 1.875MB app partition) | |
| if [ "$FLASH_USED" -gt 1800000 ]; then | |
| echo "❌ ERROR: Flash usage ($FLASH_USED bytes) exceeds limit of 1,800,000 bytes" | |
| echo "Flash usage: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| exit 1 | |
| else | |
| echo "✅ Flash usage OK: $FLASH_USED / 1,800,000 bytes ($(echo "scale=2; $FLASH_USED * 100 / 1800000" | bc)%)" | |
| fi | |
| - name: Create release artifacts | |
| run: | | |
| mkdir -p artifacts | |
| cp .pio/build/ttgo-lora32-v1/firmware.bin artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.bin | |
| cp .pio/build/ttgo-lora32-v1/firmware.map artifacts/firmware-${{ steps.validate-tag.outputs.tag_name }}.map | |
| cp .pio/build/ttgo-lora32-v1/partitions.bin artifacts/partitions-${{ steps.validate-tag.outputs.tag_name }}.bin | |
| # Create a manifest file with build info | |
| cat > artifacts/build-info-${{ steps.validate-tag.outputs.tag_name }}.json << EOF | |
| { | |
| "version": "${{ steps.validate-tag.outputs.tag_name }}", | |
| "build_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | |
| "commit": "${{ github.sha }}", | |
| "branch": "${{ github.ref_name }}", | |
| "files": { | |
| "firmware": "firmware-${{ steps.validate-tag.outputs.tag_name }}.bin", | |
| "map": "firmware-${{ steps.validate-tag.outputs.tag_name }}.map", | |
| "partitions": "partitions-${{ steps.validate-tag.outputs.tag_name }}.bin" | |
| } | |
| } | |
| EOF | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: firmware-${{ steps.validate-tag.outputs.tag_name }} | |
| path: artifacts/ | |
| retention-days: 30 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ steps.validate-tag.outputs.tag_name }} | |
| name: Release ${{ steps.validate-tag.outputs.tag_name }} | |
| body: | | |
| ## Firmware Release ${{ steps.validate-tag.outputs.tag_name }} | |
| ### Build Information | |
| - **Version**: ${{ steps.validate-tag.outputs.tag_name }} | |
| - **Build Date**: $(date -u +%Y-%m-%dT%H:%M:%SZ) | |
| - **Commit**: ${{ github.sha }} | |
| ### Artifacts | |
| - `firmware-${{ steps.validate-tag.outputs.tag_name }}.bin` - Binary firmware file for flashing | |
| - `firmware-${{ steps.validate-tag.outputs.tag_name }}.map` - Memory map file (for debugging memory usage) | |
| - `partitions-${{ steps.validate-tag.outputs.tag_name }}.bin` - Partition table | |
| - `build-info-${{ steps.validate-tag.outputs.tag_name }}.json` - Build metadata | |
| ### Installation | |
| Use the firmware binary file with your preferred ESP32 flashing tool. | |
| files: artifacts/* | |
| generate_release_notes: false | |
| draft: false | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |