feat(ci): multi-chip support with auto-detection for WiFiProvisioning #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
| # GitHub Actions workflow for compiling Arduino firmware | ||
| # 用于编译 Arduino 固件的 GitHub Actions 工作流 | ||
| # | ||
| # This workflow automatically compiles ONLY the firmware that was modified. | ||
| # 此工作流自动只编译被修改的固件。 | ||
| # | ||
| # Smart build detection: | ||
| # 智能构建检测: | ||
| # - If library source (src/) changes: rebuild ALL firmware | ||
| # 如果库源码(src/)更改:重新构建所有固件 | ||
| # - If specific example changes: rebuild ONLY that firmware | ||
| # 如果特定示例更改:只重新构建该固件 | ||
| # - Manual trigger: can choose to build all or specific firmware | ||
| # 手动触发:可以选择构建所有或特定固件 | ||
| name: Build Firmware | ||
| on: | ||
| push: | ||
| branches: [main, master] | ||
| paths: | ||
| - 'arduino/**' | ||
| - '.github/workflows/build-firmware.yml' | ||
| pull_request: | ||
| paths: | ||
| - 'arduino/**' | ||
| workflow_dispatch: # Allow manual trigger | 允许手动触发 | ||
| inputs: | ||
| build_all: | ||
| description: 'Build all firmware (ignore change detection)' | ||
| required: false | ||
| default: 'false' | ||
| type: boolean | ||
| firmware: | ||
| description: 'Specific firmware to build (leave empty for auto-detect)' | ||
| required: false | ||
| default: '' | ||
| type: string | ||
| env: | ||
| ARDUINO_CLI_VERSION: "0.35.3" | ||
| ESP32_CORE_VERSION: "3.3.4" # Lock ESP32 Core version for build consistency | 锁定 ESP32 Core 版本以确保构建一致性 | ||
| # Library versions (locked for build stability) | 库版本(锁定以确保构建稳定性) | ||
| ARDUINOJSON_VERSION: "7.4.2" | ||
| WEBSOCKETS_VERSION: "2.7.1" | ||
| NEOPIXEL_VERSION: "1.15.2" | ||
| jobs: | ||
| # Detect which firmware needs to be built | 检测需要构建的固件 | ||
| detect-changes: | ||
| runs-on: ubuntu-latest | ||
| outputs: | ||
| build_iot_button: ${{ steps.check.outputs.build_iot_button }} | ||
| build_wifi_provisioning: ${{ steps.check.outputs.build_wifi_provisioning }} | ||
| library_changed: ${{ steps.check.outputs.library_changed }} | ||
| any_firmware_changed: ${{ steps.check.outputs.any_firmware_changed }} | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 2 # Need previous commit to detect changes | 需要上一个提交来检测更改 | ||
| - name: Detect changed files | ||
| id: check | ||
| run: | | ||
| echo "Detecting changed files..." | ||
| # For manual trigger with build_all, build everything | ||
| # 手动触发且 build_all 为 true 时,构建所有 | ||
| if [ "${{ github.event.inputs.build_all }}" == "true" ]; then | ||
| echo "Manual trigger: building all firmware" | ||
| echo "build_iot_button=true" >> $GITHUB_OUTPUT | ||
| echo "build_wifi_provisioning=true" >> $GITHUB_OUTPUT | ||
| echo "library_changed=true" >> $GITHUB_OUTPUT | ||
| echo "any_firmware_changed=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
| # For manual trigger with specific firmware | ||
| # 手动触发指定特定固件 | ||
| if [ -n "${{ github.event.inputs.firmware }}" ]; then | ||
| echo "Manual trigger: building specific firmware: ${{ github.event.inputs.firmware }}" | ||
| if [ "${{ github.event.inputs.firmware }}" == "IoTButtonV2_DeepSleep" ]; then | ||
| echo "build_iot_button=true" >> $GITHUB_OUTPUT | ||
| echo "build_wifi_provisioning=false" >> $GITHUB_OUTPUT | ||
| elif [ "${{ github.event.inputs.firmware }}" == "WiFiProvisioning" ]; then | ||
| echo "build_iot_button=false" >> $GITHUB_OUTPUT | ||
| echo "build_wifi_provisioning=true" >> $GITHUB_OUTPUT | ||
| fi | ||
| echo "library_changed=false" >> $GITHUB_OUTPUT | ||
| echo "any_firmware_changed=true" >> $GITHUB_OUTPUT | ||
| exit 0 | ||
| fi | ||
| # Get changed files | 获取更改的文件 | ||
| if [ "${{ github.event_name }}" == "pull_request" ]; then | ||
| CHANGED_FILES=$(git diff --name-only ${{ github.event.pull_request.base.sha }} ${{ github.sha }}) | ||
| else | ||
| CHANGED_FILES=$(git diff --name-only HEAD~1 HEAD 2>/dev/null || git diff --name-only HEAD) | ||
| fi | ||
| echo "Changed files:" | ||
| echo "$CHANGED_FILES" | ||
| echo "" | ||
| # Initialize flags | 初始化标志 | ||
| BUILD_IOT_BUTTON=false | ||
| BUILD_WIFI_PROVISIONING=false | ||
| LIBRARY_CHANGED=false | ||
| # Check if library source changed | 检查库源码是否更改 | ||
| if echo "$CHANGED_FILES" | grep -q "arduino/SeeedHADiscovery/src/"; then | ||
| echo "Library source changed - will rebuild ALL firmware" | ||
| LIBRARY_CHANGED=true | ||
| BUILD_IOT_BUTTON=true | ||
| BUILD_WIFI_PROVISIONING=true | ||
| fi | ||
| # Check if workflow file changed | 检查工作流文件是否更改 | ||
| if echo "$CHANGED_FILES" | grep -q ".github/workflows/build-firmware.yml"; then | ||
| echo "Workflow file changed - will rebuild ALL firmware" | ||
| BUILD_IOT_BUTTON=true | ||
| BUILD_WIFI_PROVISIONING=true | ||
| fi | ||
| # Check specific firmware changes | 检查特定固件更改 | ||
| if echo "$CHANGED_FILES" | grep -q "arduino/SeeedHADiscovery/examples/IoTButtonV2_DeepSleep/"; then | ||
| echo "IoTButtonV2_DeepSleep changed" | ||
| BUILD_IOT_BUTTON=true | ||
| fi | ||
| if echo "$CHANGED_FILES" | grep -q "arduino/SeeedHADiscovery/examples/WiFiProvisioning/"; then | ||
| echo "WiFiProvisioning changed" | ||
| BUILD_WIFI_PROVISIONING=true | ||
| fi | ||
| # Check if any firmware needs building | 检查是否有固件需要构建 | ||
| ANY_CHANGED=false | ||
| if [ "$BUILD_IOT_BUTTON" == "true" ] || [ "$BUILD_WIFI_PROVISIONING" == "true" ]; then | ||
| ANY_CHANGED=true | ||
| fi | ||
| # Output results | 输出结果 | ||
| echo "" | ||
| echo "Build decisions:" | ||
| echo " - IoTButtonV2_DeepSleep: $BUILD_IOT_BUTTON" | ||
| echo " - WiFiProvisioning: $BUILD_WIFI_PROVISIONING" | ||
| echo " - Library changed: $LIBRARY_CHANGED" | ||
| echo " - Any firmware to build: $ANY_CHANGED" | ||
| echo "build_iot_button=$BUILD_IOT_BUTTON" >> $GITHUB_OUTPUT | ||
| echo "build_wifi_provisioning=$BUILD_WIFI_PROVISIONING" >> $GITHUB_OUTPUT | ||
| echo "library_changed=$LIBRARY_CHANGED" >> $GITHUB_OUTPUT | ||
| echo "any_firmware_changed=$ANY_CHANGED" >> $GITHUB_OUTPUT | ||
| # Build IoTButtonV2_DeepSleep firmware | 构建 IoTButtonV2_DeepSleep 固件 | ||
| build-iot-button: | ||
| needs: detect-changes | ||
| if: needs.detect-changes.outputs.build_iot_button == 'true' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Arduino CLI | ||
| run: | | ||
| curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh | ||
| arduino-cli version | ||
| - name: Install platform and libraries | ||
| run: | | ||
| arduino-cli config init | ||
| arduino-cli config add board_manager.additional_urls "https://espressif.github.io/arduino-esp32/package_esp32_index.json" | ||
| arduino-cli core update-index | ||
| arduino-cli core install esp32:esp32@${{ env.ESP32_CORE_VERSION }} | ||
| # Install libraries with locked versions | 安装锁定版本的库 | ||
| arduino-cli lib install "ArduinoJson@${{ env.ARDUINOJSON_VERSION }}" | ||
| arduino-cli lib install "WebSockets@${{ env.WEBSOCKETS_VERSION }}" | ||
| arduino-cli lib install "Adafruit NeoPixel@${{ env.NEOPIXEL_VERSION }}" | ||
| echo "Installed library versions:" | ||
| arduino-cli lib list | ||
| mkdir -p ~/Arduino/libraries | ||
| cp -r arduino/SeeedHADiscovery ~/Arduino/libraries/ | ||
| - name: Compile IoTButtonV2_DeepSleep | ||
| run: | | ||
| mkdir -p build/IoTButtonV2_DeepSleep | ||
| arduino-cli compile \ | ||
| --fqbn "esp32:esp32:esp32c6:CDCOnBoot=cdc,PartitionScheme=huge_app,CPUFreq=80,FlashMode=qio,FlashSize=4M,UploadSpeed=921600" \ | ||
| --output-dir "build/IoTButtonV2_DeepSleep" \ | ||
| --export-binaries \ | ||
| "arduino/SeeedHADiscovery/examples/IoTButtonV2_DeepSleep/IoTButtonV2_DeepSleep.ino" | ||
| echo "Compiled files:" | ||
| ls -la build/IoTButtonV2_DeepSleep/ | ||
| - name: Prepare firmware files | ||
| run: | | ||
| cd build/IoTButtonV2_DeepSleep | ||
| for f in *.bin; do | ||
| if [[ "$f" == *".ino.bin" ]]; then | ||
| mv "$f" "firmware.bin" | ||
| elif [[ "$f" == *".bootloader.bin" ]]; then | ||
| mv "$f" "bootloader.bin" | ||
| elif [[ "$f" == *".partitions.bin" ]]; then | ||
| mv "$f" "partitions.bin" | ||
| fi | ||
| done | ||
| # Copy boot_app0.bin | ||
| ESP32_CORE_PATH=$(find ~/.arduino15/packages/esp32/hardware/esp32 -maxdepth 1 -type d -name "${{ env.ESP32_CORE_VERSION }}" | head -1) | ||
| if [ -n "$ESP32_CORE_PATH" ] && [ -f "$ESP32_CORE_PATH/tools/partitions/boot_app0.bin" ]; then | ||
| cp "$ESP32_CORE_PATH/tools/partitions/boot_app0.bin" ./boot_app0.bin | ||
| else | ||
| BOOT_APP0=$(find ~/.arduino15/packages/esp32 -name "boot_app0.bin" -type f | head -1) | ||
| cp "$BOOT_APP0" ./boot_app0.bin | ||
| fi | ||
| # Create manifest.json | ||
| cat > manifest.json << 'EOF' | ||
| { | ||
| "name": "IoTButtonV2_DeepSleep", | ||
| "version": "1.0.0", | ||
| "home_assistant_domain": "seeed_ha_discovery", | ||
| "funding_url": "https://www.seeedstudio.com/", | ||
| "new_install_prompt_erase": true, | ||
| "new_install_improv_wait_time": 0, | ||
| "builds": [ | ||
| { | ||
| "chipFamily": "ESP32-C6", | ||
| "parts": [ | ||
| { "path": "bootloader.bin", "offset": 0 }, | ||
| { "path": "partitions.bin", "offset": 32768 }, | ||
| { "path": "boot_app0.bin", "offset": 57344 }, | ||
| { "path": "firmware.bin", "offset": 65536 } | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
| EOF | ||
| echo "Final files:" | ||
| ls -la | ||
| - name: Upload firmware artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: firmware-IoTButtonV2_DeepSleep | ||
| path: build/IoTButtonV2_DeepSleep/ | ||
| retention-days: 30 | ||
| # Build WiFiProvisioning firmware for multiple ESP32 chips | 为多种 ESP32 芯片构建 WiFiProvisioning 固件 | ||
| # This is a generic example that works on all ESP32 variants | 这是一个适用于所有 ESP32 系列的通用示例 | ||
| build-wifi-provisioning: | ||
| needs: detect-changes | ||
| if: needs.detect-changes.outputs.build_wifi_provisioning == 'true' | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| include: | ||
| # ESP32-C3 (XIAO ESP32C3) | ESP32-C3(XIAO ESP32C3) | ||
| - chip: "C3" | ||
| fqbn: "esp32:esp32:esp32c3:CDCOnBoot=cdc,PartitionScheme=huge_app,CPUFreq=80,FlashMode=qio,FlashSize=4M,UploadSpeed=921600" | ||
| chip_family: "ESP32-C3" | ||
| bootloader_offset: 0 | ||
| # ESP32-C6 (XIAO ESP32C6) | ESP32-C6(XIAO ESP32C6) | ||
| - chip: "C6" | ||
| fqbn: "esp32:esp32:esp32c6:CDCOnBoot=cdc,PartitionScheme=huge_app,CPUFreq=80,FlashMode=qio,FlashSize=4M,UploadSpeed=921600" | ||
| chip_family: "ESP32-C6" | ||
| bootloader_offset: 0 | ||
| # ESP32-S3 (XIAO ESP32S3) | ESP32-S3(XIAO ESP32S3) | ||
| - chip: "S3" | ||
| fqbn: "esp32:esp32:esp32s3:CDCOnBoot=cdc,PartitionScheme=huge_app,USBMode=hwcdc,FlashMode=qio,FlashSize=8M,UploadSpeed=921600,PSRAM=opi" | ||
| chip_family: "ESP32-S3" | ||
| bootloader_offset: 0 | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| - name: Setup Arduino CLI | ||
| run: | | ||
| curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=/usr/local/bin sh | ||
| arduino-cli version | ||
| - name: Install platform and libraries | ||
| run: | | ||
| arduino-cli config init | ||
| arduino-cli config add board_manager.additional_urls "https://espressif.github.io/arduino-esp32/package_esp32_index.json" | ||
| arduino-cli core update-index | ||
| arduino-cli core install esp32:esp32@${{ env.ESP32_CORE_VERSION }} | ||
| # Install libraries with locked versions | 安装锁定版本的库 | ||
| arduino-cli lib install "ArduinoJson@${{ env.ARDUINOJSON_VERSION }}" | ||
| arduino-cli lib install "WebSockets@${{ env.WEBSOCKETS_VERSION }}" | ||
| arduino-cli lib install "Adafruit NeoPixel@${{ env.NEOPIXEL_VERSION }}" | ||
| echo "Installed library versions:" | ||
| arduino-cli lib list | ||
| mkdir -p ~/Arduino/libraries | ||
| cp -r arduino/SeeedHADiscovery ~/Arduino/libraries/ | ||
| - name: Compile WiFiProvisioning for ${{ matrix.chip }} | ||
| run: | | ||
| FIRMWARE_NAME="WiFiProvisioning-${{ matrix.chip }}" | ||
| mkdir -p build/${FIRMWARE_NAME} | ||
| echo "Compiling for ${{ matrix.chip_family }}..." | ||
| arduino-cli compile \ | ||
| --fqbn "${{ matrix.fqbn }}" \ | ||
| --output-dir "build/${FIRMWARE_NAME}" \ | ||
| --export-binaries \ | ||
| "arduino/SeeedHADiscovery/examples/WiFiProvisioning/WiFiProvisioning.ino" | ||
| echo "Compiled files:" | ||
| ls -la build/${FIRMWARE_NAME}/ | ||
| - name: Prepare firmware files for ${{ matrix.chip }} | ||
| run: | | ||
| # Create chip-specific subfolder | 创建芯片特定的子文件夹 | ||
| # Structure: WiFiProvisioning/C3/, WiFiProvisioning/C6/, WiFiProvisioning/S3/ | ||
| # ESP Web Tools will auto-detect chip and use correct subfolder | ||
| # ESP Web Tools 会自动检测芯片并使用正确的子文件夹 | ||
| CHIP_DIR="build/WiFiProvisioning/${{ matrix.chip }}" | ||
| mkdir -p ${CHIP_DIR} | ||
| # Move and rename binaries to chip subfolder | 移动并重命名二进制文件到芯片子文件夹 | ||
| cd build/WiFiProvisioning-${{ matrix.chip }} | ||
| for f in *.bin; do | ||
| if [[ "$f" == *".ino.bin" ]]; then | ||
| cp "$f" "../WiFiProvisioning/${{ matrix.chip }}/firmware.bin" | ||
| elif [[ "$f" == *".bootloader.bin" ]]; then | ||
| cp "$f" "../WiFiProvisioning/${{ matrix.chip }}/bootloader.bin" | ||
| elif [[ "$f" == *".partitions.bin" ]]; then | ||
| cp "$f" "../WiFiProvisioning/${{ matrix.chip }}/partitions.bin" | ||
| fi | ||
| done | ||
| # Copy boot_app0.bin to chip subfolder | ||
| ESP32_CORE_PATH=$(find ~/.arduino15/packages/esp32/hardware/esp32 -maxdepth 1 -type d -name "${{ env.ESP32_CORE_VERSION }}" | head -1) | ||
| if [ -n "$ESP32_CORE_PATH" ] && [ -f "$ESP32_CORE_PATH/tools/partitions/boot_app0.bin" ]; then | ||
| cp "$ESP32_CORE_PATH/tools/partitions/boot_app0.bin" "../WiFiProvisioning/${{ matrix.chip }}/boot_app0.bin" | ||
| else | ||
| BOOT_APP0=$(find ~/.arduino15/packages/esp32 -name "boot_app0.bin" -type f | head -1) | ||
| cp "$BOOT_APP0" "../WiFiProvisioning/${{ matrix.chip }}/boot_app0.bin" | ||
| fi | ||
| echo "Files for ${{ matrix.chip_family }}:" | ||
| ls -la ../WiFiProvisioning/${{ matrix.chip }}/ | ||
| - name: Upload firmware artifact for ${{ matrix.chip }} | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: firmware-WiFiProvisioning-${{ matrix.chip }} | ||
| path: build/WiFiProvisioning/${{ matrix.chip }}/ | ||
| retention-days: 30 | ||
| # Combine all built firmware | 合并所有构建的固件 | ||
| combine: | ||
| needs: [detect-changes, build-iot-button, build-wifi-provisioning] | ||
| if: always() && needs.detect-changes.outputs.any_firmware_changed == 'true' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Download all firmware artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| path: all-firmware/ | ||
| pattern: firmware-* | ||
| continue-on-error: true # Some artifacts may not exist | 某些 artifact 可能不存在 | ||
| - name: Check downloaded artifacts | ||
| run: | | ||
| echo "Downloaded artifacts:" | ||
| ls -la all-firmware/ 2>/dev/null || echo "No artifacts directory" | ||
| find all-firmware -type f 2>/dev/null || echo "No files found" | ||
| - name: Reorganize firmware files | ||
| run: | | ||
| mkdir -p combined-firmware | ||
| if [ -d "all-firmware" ]; then | ||
| # Handle IoTButtonV2_DeepSleep (single chip) | 处理 IoTButtonV2_DeepSleep(单芯片) | ||
| if [ -d "all-firmware/firmware-IoTButtonV2_DeepSleep" ]; then | ||
| echo "Adding IoTButtonV2_DeepSleep..." | ||
| cp -r "all-firmware/firmware-IoTButtonV2_DeepSleep" "combined-firmware/IoTButtonV2_DeepSleep" | ||
| fi | ||
| # Handle WiFiProvisioning (multi-chip) | 处理 WiFiProvisioning(多芯片) | ||
| # Merge all chip variants into one folder with subfolders | ||
| # 将所有芯片变体合并到一个带有子文件夹的目录中 | ||
| mkdir -p combined-firmware/WiFiProvisioning | ||
| for chip in C3 C6 S3; do | ||
| if [ -d "all-firmware/firmware-WiFiProvisioning-${chip}" ]; then | ||
| echo "Adding WiFiProvisioning/${chip}..." | ||
| mkdir -p "combined-firmware/WiFiProvisioning/${chip}" | ||
| cp -r "all-firmware/firmware-WiFiProvisioning-${chip}/"* "combined-firmware/WiFiProvisioning/${chip}/" | ||
| fi | ||
| done | ||
| # Create multi-build manifest.json for WiFiProvisioning | ||
| # 为 WiFiProvisioning 创建多芯片 manifest.json | ||
| # ESP Web Tools will auto-detect connected chip and use correct build | ||
| # ESP Web Tools 会自动检测连接的芯片并使用正确的构建 | ||
| if [ -d "combined-firmware/WiFiProvisioning" ] && [ "$(ls -A combined-firmware/WiFiProvisioning 2>/dev/null)" ]; then | ||
| echo "Creating multi-build manifest.json for WiFiProvisioning..." | ||
| # Start manifest | 开始 manifest | ||
| cat > combined-firmware/WiFiProvisioning/manifest.json << 'MANIFEST_START' | ||
| { | ||
| "name": "WiFiProvisioning", | ||
| "version": "1.0.0", | ||
| "home_assistant_domain": "seeed_ha_discovery", | ||
| "funding_url": "https://www.seeedstudio.com/", | ||
| "new_install_prompt_erase": true, | ||
| "new_install_improv_wait_time": 0, | ||
| "builds": [ | ||
| MANIFEST_START | ||
| # Add builds for each available chip | 为每个可用芯片添加构建 | ||
| FIRST_BUILD=true | ||
| if [ -d "combined-firmware/WiFiProvisioning/C3" ]; then | ||
| if [ "$FIRST_BUILD" = false ]; then echo "," >> combined-firmware/WiFiProvisioning/manifest.json; fi | ||
| FIRST_BUILD=false | ||
| cat >> combined-firmware/WiFiProvisioning/manifest.json << 'BUILD_C3' | ||
| { | ||
| "chipFamily": "ESP32-C3", | ||
| "parts": [ | ||
| { "path": "C3/bootloader.bin", "offset": 0 }, | ||
| { "path": "C3/partitions.bin", "offset": 32768 }, | ||
| { "path": "C3/boot_app0.bin", "offset": 57344 }, | ||
| { "path": "C3/firmware.bin", "offset": 65536 } | ||
| ] | ||
| } | ||
| BUILD_C3 | ||
| fi | ||
| if [ -d "combined-firmware/WiFiProvisioning/C6" ]; then | ||
| if [ "$FIRST_BUILD" = false ]; then echo "," >> combined-firmware/WiFiProvisioning/manifest.json; fi | ||
| FIRST_BUILD=false | ||
| cat >> combined-firmware/WiFiProvisioning/manifest.json << 'BUILD_C6' | ||
| { | ||
| "chipFamily": "ESP32-C6", | ||
| "parts": [ | ||
| { "path": "C6/bootloader.bin", "offset": 0 }, | ||
| { "path": "C6/partitions.bin", "offset": 32768 }, | ||
| { "path": "C6/boot_app0.bin", "offset": 57344 }, | ||
| { "path": "C6/firmware.bin", "offset": 65536 } | ||
| ] | ||
| } | ||
| BUILD_C6 | ||
| fi | ||
| if [ -d "combined-firmware/WiFiProvisioning/S3" ]; then | ||
| if [ "$FIRST_BUILD" = false ]; then echo "," >> combined-firmware/WiFiProvisioning/manifest.json; fi | ||
| FIRST_BUILD=false | ||
| cat >> combined-firmware/WiFiProvisioning/manifest.json << 'BUILD_S3' | ||
| { | ||
| "chipFamily": "ESP32-S3", | ||
| "parts": [ | ||
| { "path": "S3/bootloader.bin", "offset": 0 }, | ||
| { "path": "S3/partitions.bin", "offset": 32768 }, | ||
| { "path": "S3/boot_app0.bin", "offset": 57344 }, | ||
| { "path": "S3/firmware.bin", "offset": 65536 } | ||
| ] | ||
| } | ||
| BUILD_S3 | ||
| fi | ||
| # Close manifest | 关闭 manifest | ||
| cat >> combined-firmware/WiFiProvisioning/manifest.json << 'MANIFEST_END' | ||
| ] | ||
| } | ||
| MANIFEST_END | ||
| echo "Generated manifest.json:" | ||
| cat combined-firmware/WiFiProvisioning/manifest.json | ||
| fi | ||
| fi | ||
| echo "" | ||
| echo "==========================" | ||
| echo "Combined firmware structure:" | ||
| echo "==========================" | ||
| find combined-firmware -type f 2>/dev/null || echo "No firmware files" | ||
| - name: Upload combined firmware | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: all-firmware | ||
| path: combined-firmware/ | ||
| retention-days: 90 | ||
| if: hashFiles('combined-firmware/**') != '' | ||
| # Create release | 创建发布版本 | ||
| release: | ||
| needs: [detect-changes, combine] | ||
| if: always() && needs.detect-changes.outputs.any_firmware_changed == 'true' && github.ref == 'refs/heads/main' && github.event_name != 'pull_request' | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: write | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
| with: | ||
| fetch-depth: 0 # Full history for changelog | 完整历史用于更新日志 | ||
| - name: Download combined firmware | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| name: all-firmware | ||
| path: firmware/ | ||
| continue-on-error: true | ||
| - name: Check firmware exists | ||
| id: check_firmware | ||
| run: | | ||
| if [ -d "firmware" ] && [ "$(ls -A firmware 2>/dev/null)" ]; then | ||
| echo "has_firmware=true" >> $GITHUB_OUTPUT | ||
| echo "Firmware directory contents:" | ||
| find firmware -type f | ||
| else | ||
| echo "has_firmware=false" >> $GITHUB_OUTPUT | ||
| echo "No firmware to release" | ||
| fi | ||
| - name: Get version and changelog | ||
| id: version | ||
| if: steps.check_firmware.outputs.has_firmware == 'true' | ||
| run: | | ||
| VERSION=$(date +'%Y.%m.%d')-$(git rev-parse --short HEAD) | ||
| echo "version=$VERSION" >> $GITHUB_OUTPUT | ||
| # Get the last release tag | ||
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | ||
| echo "Generating changelog..." | ||
| if [ -n "$LAST_TAG" ]; then | ||
| echo "Changes since $LAST_TAG:" | ||
| CHANGELOG=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges | head -30) | ||
| else | ||
| echo "No previous tag found, using last 20 commits" | ||
| CHANGELOG=$(git log --pretty=format:"- %s (%h)" --no-merges -20) | ||
| fi | ||
| echo "$CHANGELOG" > changelog.txt | ||
| cat changelog.txt | ||
| - name: Package firmware | ||
| if: steps.check_firmware.outputs.has_firmware == 'true' | ||
| run: | | ||
| cd firmware | ||
| for dir in */; do | ||
| firmware_name="${dir%/}" | ||
| echo "Packaging $firmware_name..." | ||
| zip -r "${firmware_name}.zip" "$dir" | ||
| done | ||
| cd .. | ||
| zip -r "all-firmware.zip" firmware/ | ||
| mv all-firmware.zip firmware/ | ||
| echo "Created packages:" | ||
| ls -la firmware/*.zip | ||
| - name: Generate release body | ||
| if: steps.check_firmware.outputs.has_firmware == 'true' | ||
| run: | | ||
| CHANGELOG=$(cat changelog.txt) | ||
| # Generate firmware table | 生成固件表格 | ||
| # Categorize firmware by type | 按类型分类固件 | ||
| echo "| Firmware | Chip | Description |" > firmware_table.md | ||
| echo "|----------|------|-------------|" >> firmware_table.md | ||
| for dir in firmware/*/; do | ||
| firmware_name=$(basename "$dir") | ||
| if [ "$firmware_name" != "*.zip" ]; then | ||
| # Determine chip and description based on firmware name | ||
| # 根据固件名称确定芯片和描述 | ||
| case "$firmware_name" in | ||
| "IoTButtonV2_DeepSleep") | ||
| echo "| $firmware_name | ESP32-C6 | IoT Button with deep sleep (XIAO ESP32C6 only) |" >> firmware_table.md | ||
| ;; | ||
| "WiFiProvisioning-C3") | ||
| echo "| $firmware_name | ESP32-C3 | WiFi provisioning demo (XIAO ESP32C3) |" >> firmware_table.md | ||
| ;; | ||
| "WiFiProvisioning-C6") | ||
| echo "| $firmware_name | ESP32-C6 | WiFi provisioning demo (XIAO ESP32C6) |" >> firmware_table.md | ||
| ;; | ||
| "WiFiProvisioning-S3") | ||
| echo "| $firmware_name | ESP32-S3 | WiFi provisioning demo (XIAO ESP32S3) |" >> firmware_table.md | ||
| ;; | ||
| *) | ||
| echo "| $firmware_name | - | - |" >> firmware_table.md | ||
| ;; | ||
| esac | ||
| fi | ||
| done | ||
| FIRMWARE_TABLE=$(cat firmware_table.md) | ||
| cat > release_body.md << ENDOFHEADER | ||
| ## 🚀 Firmware Release | ||
| Pre-compiled firmware for Seeed HA Discovery devices. | ||
| 为 Seeed HA Discovery 设备预编译的固件。 | ||
| ### 📥 How to Flash | 如何烧录 | ||
| **Web Flasher (Recommended | 推荐):** | ||
| Visit [Web Flasher](https://limengdu.github.io/Seeed-Homeassistant-Discovery/flasher/) to flash directly from browser. | ||
| 访问 [网页烧录器](https://limengdu.github.io/Seeed-Homeassistant-Discovery/flasher/) 直接从浏览器烧录。 | ||
| ### 📦 Firmware in this release | 本次发布的固件 | ||
| $FIRMWARE_TABLE | ||
| > **Note:** WiFiProvisioning is available for multiple ESP32 chips. Choose the variant matching your hardware. | ||
| > **注意:** WiFiProvisioning 提供多种 ESP32 芯片版本,请选择与您硬件匹配的版本。 | ||
| ### 📝 Changelog | 更新日志 | ||
| $CHANGELOG | ||
| --- | ||
| **Build Info | 构建信息:** | ||
| - ESP32 Core: ${{ env.ESP32_CORE_VERSION }} | ||
| - ArduinoJson: ${{ env.ARDUINOJSON_VERSION }} | ||
| - WebSockets: ${{ env.WEBSOCKETS_VERSION }} | ||
| - Adafruit NeoPixel: ${{ env.NEOPIXEL_VERSION }} | ||
| - Build Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | ||
| - Commit: ${{ github.sha }} | ||
| ENDOFHEADER | ||
| cat release_body.md | ||
| - name: Create Release | ||
| if: steps.check_firmware.outputs.has_firmware == 'true' | ||
| uses: softprops/action-gh-release@v1 | ||
| with: | ||
| tag_name: firmware-${{ steps.version.outputs.version }} | ||
| name: Firmware Release ${{ steps.version.outputs.version }} | ||
| body_path: release_body.md | ||
| files: | | ||
| firmware/*.zip | ||