Skip to content

feat: auto-generate changelog in release notes + fix workflow depende… #6

feat: auto-generate changelog in release notes + fix workflow depende…

feat: auto-generate changelog in release notes + fix workflow depende… #6

# 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