feat: auto-generate changelog in release notes + fix workflow depende… #6
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 all firmware examples for different boards | |
| # and creates releases with the compiled binaries for web flashing. | |
| # 此工作流自动为不同开发板编译所有固件示例,并创建包含编译好的二进制文件的发布版本,用于网页烧录。 | |
| 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: | |
| release: | |
| description: 'Create a release with firmware binaries' | |
| required: false | |
| default: 'false' | |
| type: boolean | |
| env: | |
| ARDUINO_CLI_VERSION: "0.35.3" | |
| ESP32_CORE_VERSION: "3.3.4" # Lock ESP32 Core version for build consistency | 锁定 ESP32 Core 版本以确保构建一致性 | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # ESP32-C6 based products | 基于 ESP32-C6 的产品 | |
| - firmware: "IoTButtonV2_DeepSleep" | |
| sketch: "arduino/SeeedHADiscovery/examples/IoTButtonV2_DeepSleep/IoTButtonV2_DeepSleep.ino" | |
| board: "esp32:esp32:esp32c6" | |
| board_options: "CDCOnBoot=cdc,PartitionScheme=huge_app,CPUFreq=80,FlashMode=qio,FlashSize=4M,UploadSpeed=921600" | |
| platform: "esp32:esp32" | |
| platform_url: "https://espressif.github.io/arduino-esp32/package_esp32_index.json" | |
| # ESP32-S3 based products (example) | 基于 ESP32-S3 的产品(示例) | |
| - firmware: "WiFiProvisioning" | |
| sketch: "arduino/SeeedHADiscovery/examples/WiFiProvisioning/WiFiProvisioning.ino" | |
| board: "esp32:esp32:esp32c6" | |
| board_options: "CDCOnBoot=cdc,PartitionScheme=huge_app,CPUFreq=80,FlashMode=qio,FlashSize=4M,UploadSpeed=921600" | |
| platform: "esp32:esp32" | |
| platform_url: "https://espressif.github.io/arduino-esp32/package_esp32_index.json" | |
| # Add more firmware configurations here | 在此添加更多固件配置 | |
| # - firmware: "CameraStream" | |
| # sketch: "arduino/SeeedHADiscovery/examples/CameraStream/CameraStream.ino" | |
| # board: "esp32:esp32:esp32s3" | |
| # board_options: "..." | |
| # platform: "esp32:esp32" | |
| # platform_url: "https://espressif.github.io/arduino-esp32/package_esp32_index.json" | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Arduino CLI | |
| run: | | |
| # Download and install Arduino CLI manually (more reliable) | |
| # 手动下载安装 Arduino CLI(更可靠) | |
| 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: | | |
| # Add board manager URL | 添加开发板管理器 URL | |
| arduino-cli config init | |
| arduino-cli config add board_manager.additional_urls "${{ matrix.platform_url }}" | |
| # Update index and install platform with locked version | 更新索引并安装锁定版本的平台 | |
| arduino-cli core update-index | |
| arduino-cli core install ${{ matrix.platform }}@${{ env.ESP32_CORE_VERSION }} | |
| echo "Installed ESP32 Core version: ${{ env.ESP32_CORE_VERSION }}" | |
| # Install required libraries | 安装所需库 | |
| arduino-cli lib install "ArduinoJson" | |
| arduino-cli lib install "WebSockets" | |
| arduino-cli lib install "Adafruit NeoPixel" | |
| # Install SeeedHADiscovery library from local | 从本地安装 SeeedHADiscovery 库 | |
| mkdir -p ~/Arduino/libraries | |
| cp -r arduino/SeeedHADiscovery ~/Arduino/libraries/ | |
| - name: Compile firmware | |
| run: | | |
| mkdir -p build/${{ matrix.firmware }} | |
| # Compile the sketch | 编译固件 | |
| arduino-cli compile \ | |
| --fqbn "${{ matrix.board }}:${{ matrix.board_options }}" \ | |
| --output-dir "build/${{ matrix.firmware }}" \ | |
| --export-binaries \ | |
| "${{ matrix.sketch }}" | |
| # List compiled files | 列出编译的文件 | |
| echo "Compiled files:" | |
| ls -la build/${{ matrix.firmware }}/ | |
| - name: Prepare firmware files for ESP Web Tools | |
| run: | | |
| cd build/${{ matrix.firmware }} | |
| # Rename files for clarity | 重命名文件以便识别 | |
| # ESP32 typically outputs: sketch.ino.bin, sketch.ino.bootloader.bin, sketch.ino.partitions.bin | |
| 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 from ESP32 Core (required for OTA boot selection) | |
| # 从 ESP32 Core 复制 boot_app0.bin(OTA 启动选择所需) | |
| # This file is at offset 0xe000 and is needed for proper boot sequence | |
| # 此文件在偏移地址 0xe000,正确启动序列所需 | |
| 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 | |
| echo "✓ Copied boot_app0.bin from ESP32 Core" | |
| else | |
| echo "⚠ Warning: boot_app0.bin not found, trying alternative path..." | |
| # Alternative search | 备选搜索路径 | |
| BOOT_APP0=$(find ~/.arduino15/packages/esp32 -name "boot_app0.bin" -type f | head -1) | |
| if [ -n "$BOOT_APP0" ]; then | |
| cp "$BOOT_APP0" ./boot_app0.bin | |
| echo "✓ Copied boot_app0.bin from: $BOOT_APP0" | |
| else | |
| echo "✗ Error: boot_app0.bin not found!" | |
| exit 1 | |
| fi | |
| fi | |
| # List final files | 列出最终文件 | |
| echo "Final firmware files:" | |
| ls -la | |
| - name: Create manifest.json for ESP Web Tools | |
| run: | | |
| cd build/${{ matrix.firmware }} | |
| # Determine chip family and offsets based on board | 根据开发板确定芯片系列和偏移地址 | |
| # ESP32-C3/C6/S3/H2 use 0x0 for bootloader | ESP32-C3/C6/S3/H2 的 bootloader 偏移为 0x0 | |
| # Original ESP32/ESP32-S2 use 0x1000 | 原版 ESP32/ESP32-S2 的偏移为 0x1000 | |
| CHIP_FAMILY="ESP32-C6" | |
| BOOTLOADER_OFFSET=0 | |
| PARTITIONS_OFFSET=32768 # 0x8000 | |
| BOOT_APP0_OFFSET=57344 # 0xe000 - OTA boot selection | OTA 启动选择 | |
| FIRMWARE_OFFSET=65536 # 0x10000 | |
| if [[ "${{ matrix.board }}" == *"esp32s3"* ]] || [[ "${{ matrix.board }}" == *"XIAO_ESP32S3"* ]]; then | |
| CHIP_FAMILY="ESP32-S3" | |
| BOOTLOADER_OFFSET=0 | |
| elif [[ "${{ matrix.board }}" == *"esp32s2"* ]]; then | |
| CHIP_FAMILY="ESP32-S2" | |
| BOOTLOADER_OFFSET=4096 # 0x1000 | |
| elif [[ "${{ matrix.board }}" == *"esp32c5"* ]] || [[ "${{ matrix.board }}" == *"XIAO_ESP32C5"* ]]; then | |
| # ESP32-C5 uses 0x2000 for bootloader | ESP32-C5 的 bootloader 偏移为 0x2000 | |
| CHIP_FAMILY="ESP32-C5" | |
| BOOTLOADER_OFFSET=8192 # 0x2000 | |
| elif [[ "${{ matrix.board }}" == *"esp32c3"* ]]; then | |
| CHIP_FAMILY="ESP32-C3" | |
| BOOTLOADER_OFFSET=0 | |
| elif [[ "${{ matrix.board }}" == *"esp32c6"* ]]; then | |
| CHIP_FAMILY="ESP32-C6" | |
| BOOTLOADER_OFFSET=0 | |
| elif [[ "${{ matrix.board }}" == *"esp32h2"* ]]; then | |
| CHIP_FAMILY="ESP32-H2" | |
| BOOTLOADER_OFFSET=0 | |
| elif [[ "${{ matrix.board }}" == *"esp32"* ]]; then | |
| CHIP_FAMILY="ESP32" | |
| BOOTLOADER_OFFSET=4096 # 0x1000 | |
| fi | |
| echo "Chip Family: ${CHIP_FAMILY}" | |
| echo "Bootloader Offset: ${BOOTLOADER_OFFSET}" | |
| echo "Boot App0 Offset: ${BOOT_APP0_OFFSET}" | |
| # Create manifest.json with all 4 binary files | 创建包含所有 4 个二进制文件的 manifest.json | |
| # Flash layout: bootloader -> partitions -> boot_app0 -> firmware | |
| # Flash 布局:bootloader -> partitions -> boot_app0 -> firmware | |
| cat > manifest.json << EOF | |
| { | |
| "name": "${{ matrix.firmware }}", | |
| "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": "${CHIP_FAMILY}", | |
| "parts": [ | |
| { "path": "bootloader.bin", "offset": ${BOOTLOADER_OFFSET} }, | |
| { "path": "partitions.bin", "offset": ${PARTITIONS_OFFSET} }, | |
| { "path": "boot_app0.bin", "offset": ${BOOT_APP0_OFFSET} }, | |
| { "path": "firmware.bin", "offset": ${FIRMWARE_OFFSET} } | |
| ] | |
| } | |
| ] | |
| } | |
| EOF | |
| echo "Created manifest.json:" | |
| cat manifest.json | |
| - name: Upload firmware artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: firmware-${{ matrix.firmware }} | |
| path: build/${{ matrix.firmware }}/ | |
| retention-days: 30 | |
| # Combine all firmware into one artifact | 将所有固件合并到一个 artifact | |
| combine: | |
| needs: build | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download all firmware artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: all-firmware/ | |
| pattern: firmware-* | |
| - name: Reorganize firmware files | |
| run: | | |
| mkdir -p combined-firmware | |
| for dir in all-firmware/firmware-*; do | |
| firmware_name=$(basename "$dir" | sed 's/firmware-//') | |
| cp -r "$dir" "combined-firmware/$firmware_name" | |
| done | |
| echo "Combined firmware structure:" | |
| find combined-firmware -type f | |
| - name: Upload combined firmware | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: all-firmware | |
| path: combined-firmware/ | |
| retention-days: 90 | |
| # Create release (when manually triggered with release=true) | 创建发布版本(手动触发且 release=true 时) | |
| release: | |
| needs: combine | |
| if: github.event.inputs.release == 'true' || github.ref == 'refs/heads/main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Download combined firmware | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: all-firmware | |
| path: firmware/ | |
| - name: Create firmware index | |
| run: | | |
| cd firmware | |
| # Create index.json listing all available firmware | 创建列出所有可用固件的 index.json | |
| echo '[' > index.json | |
| first=true | |
| for dir in */; do | |
| firmware_name="${dir%/}" | |
| if [ "$first" = true ]; then | |
| first=false | |
| else | |
| echo ',' >> index.json | |
| fi | |
| echo " {\"name\": \"$firmware_name\", \"manifest\": \"$firmware_name/manifest.json\"}" >> index.json | |
| done | |
| echo ']' >> index.json | |
| echo "Created index.json:" | |
| cat index.json | |
| - name: Get version and changelog | |
| id: version | |
| run: | | |
| VERSION=$(date +'%Y.%m.%d')-$(git rev-parse --short HEAD) | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| # Get the last release tag (if exists) | 获取上一个 release 标签(如果存在) | |
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| # Generate changelog | 生成更新日志 | |
| 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 | |
| # Save changelog to file (to handle multiline in GitHub Actions) | |
| # 保存更新日志到文件(处理 GitHub Actions 中的多行) | |
| echo "$CHANGELOG" > changelog.txt | |
| echo "Changelog generated:" | |
| cat changelog.txt | |
| - name: Package firmware for release | |
| run: | | |
| # Create zip files for each firmware to avoid filename conflicts | |
| # 为每个固件创建 zip 文件以避免文件名冲突 | |
| cd firmware | |
| for dir in */; do | |
| firmware_name="${dir%/}" | |
| echo "Packaging $firmware_name..." | |
| zip -r "${firmware_name}.zip" "$dir" | |
| done | |
| # Also create a combined zip with all firmware | 同时创建包含所有固件的合并 zip | |
| cd .. | |
| zip -r "all-firmware.zip" firmware/ | |
| mv all-firmware.zip firmware/ | |
| echo "Created packages:" | |
| ls -la firmware/*.zip | |
| - name: Generate release body | |
| id: release_body | |
| run: | | |
| # Read changelog | 读取更新日志 | |
| CHANGELOG=$(cat changelog.txt) | |
| # Create release body | 创建发布说明 | |
| 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/) 直接从浏览器烧录。 | |
| ### 📦 Included Firmware | 包含的固件 | |
| | Firmware | Chip | Description | | |
| |----------|------|-------------| | |
| | IoTButtonV2_DeepSleep | ESP32-C6 | Low-power IoT button with deep sleep | | |
| | WiFiProvisioning | ESP32-C6 | WiFi provisioning demo | | |
| ### 📁 Downloads | 下载 | |
| - `all-firmware.zip` - All firmware in one package | 所有固件合集 | |
| - Individual firmware zip files available below | 单独固件 zip 文件如下 | |
| ### 📝 Changelog | 更新日志 | |
| ENDOFHEADER | |
| # Append changelog | 添加更新日志 | |
| echo "$CHANGELOG" >> release_body.md | |
| # Add build info | 添加构建信息 | |
| cat >> release_body.md << ENDOFFOOTER | |
| --- | |
| **Build Info | 构建信息:** | |
| - ESP32 Core Version: ${{ env.ESP32_CORE_VERSION }} | |
| - Build Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC') | |
| - Commit: ${{ github.sha }} | |
| ENDOFFOOTER | |
| echo "Release body generated:" | |
| cat release_body.md | |
| - name: Create Release | |
| 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 | |