From d61a2c3437f5be583a387925cbd850d5878333e3 Mon Sep 17 00:00:00 2001 From: Stephan Rodemeier Date: Tue, 14 Apr 2026 11:11:45 +0200 Subject: [PATCH] Add releases via github actions This adds automatic builds via Github Actions. A new release will be created when a new commit is pushed to the "master" branch. The description for the release will be taken from the latest commit, in the case of a merged pull request that will be the merge commit. To add new boards for building, extend the arrays in the build.sh script. To start, I included all boards from `supported_boards.md`. --- .github/workflows/build.yml | 221 ++++++++++++++++++++++++++++++++++++ build.sh | 151 ++++++++++++++++++++++++ requirements.txt | 1 + 3 files changed, 373 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 build.sh create mode 100644 requirements.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ac6ef55 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,221 @@ +name: build firmware + +on: + push: + branches: [ "master" ] + +env: + zephyr_sdk: 1.0.1 + python_version: 3.12 + toolchains: arm-zephyr-eabi:xtensa-espressif_esp32s3_zephyr-elf:riscv64-zephyr-elf + +jobs: + setup-zephyr: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: '${{ env.python_version }}' + cache: 'pip' + - run: pip install -r requirements.txt + + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 + with: + app-path: zephcore + toolchains: ${{ env.toolchains }} + sdk-version: ${{ env.zephyr_sdk }} + + - name: Cache Espressif BLE controller blobs + id: cache-espressif-blobs + uses: actions/cache@v5 + with: + path: modules/hal/espressif + key: ${{ runner.os }}-espressif-blobs-${{ hashFiles('modules/hal/espressif/.git/HEAD') }} + + - name: Download Espressif BLE controller blobs + if: steps.cache-espressif-blobs.outputs.cache-hit != 'true' + run: west blobs fetch hal_espressif + + - uses: actions/cache@v5 + with: + path: modules + key: cache-zephyr-modules-${{ github.sha }} + + build-nrf: + needs: setup-zephyr + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: '${{ env.python_version }}' + cache: 'pip' + - run: pip install -r requirements.txt + + - uses: actions/cache/restore@v5 + with: + path: modules + key: cache-zephyr-modules-${{ github.sha }} + + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 + with: + app-path: zephcore + toolchains: ${{ env.toolchains }} + sdk-version: ${{ env.zephyr_sdk }} + + - name: Cache Espressif BLE controller blobs + id: cache-espressif-blobs + uses: actions/cache@v5 + with: + path: modules/hal/espressif + key: ${{ runner.os }}-espressif-blobs-${{ hashFiles('modules/hal/espressif/.git/HEAD') }} + + - name: Download Espressif BLE controller blobs + if: steps.cache-espressif-blobs.outputs.cache-hit != 'true' + run: west blobs fetch hal_espressif + + - name: build firmwares + run: bash build.sh nrf + + - uses: actions/cache@v5 + with: + path: firmware + key: cache-firmware-nrf-${{ github.sha }} + + build-esp32-repeaters: + needs: setup-zephyr + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: '${{ env.python_version }}' + cache: 'pip' + - run: pip install -r requirements.txt + + - uses: actions/cache/restore@v5 + with: + path: modules + key: cache-zephyr-modules-${{ github.sha }} + + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 + with: + app-path: zephcore + toolchains: ${{ env.toolchains }} + sdk-version: ${{ env.zephyr_sdk }} + + - name: Cache Espressif BLE controller blobs + id: cache-espressif-blobs + uses: actions/cache@v5 + with: + path: modules/hal/espressif + key: ${{ runner.os }}-espressif-blobs-${{ hashFiles('modules/hal/espressif/.git/HEAD') }} + + - name: Download Espressif BLE controller blobs + if: steps.cache-espressif-blobs.outputs.cache-hit != 'true' + run: west blobs fetch hal_espressif + + - name: build firmwares + run: bash build.sh esp32 repeaters + + - uses: actions/cache@v5 + with: + path: firmware + key: cache-firmware-esp32-repeaters-${{ github.sha }} + + build-esp32-companions: + needs: setup-zephyr + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - uses: actions/setup-python@v6 + with: + python-version: '${{ env.python_version }}' + cache: 'pip' + - run: pip install -r requirements.txt + + - uses: actions/cache/restore@v5 + with: + path: modules + key: cache-zephyr-modules-${{ github.sha }} + + - name: Setup Zephyr project + uses: zephyrproject-rtos/action-zephyr-setup@v1 + with: + app-path: zephcore + toolchains: ${{ env.toolchains }} + sdk-version: ${{ env.zephyr_sdk }} + + - name: Cache Espressif BLE controller blobs + id: cache-espressif-blobs + uses: actions/cache@v5 + with: + path: modules/hal/espressif + key: ${{ runner.os }}-espressif-blobs-${{ hashFiles('modules/hal/espressif/.git/HEAD') }} + + - name: Download Espressif BLE controller blobs + if: steps.cache-espressif-blobs.outputs.cache-hit != 'true' + run: west blobs fetch hal_espressif + + - name: build firmwares + run: bash build.sh esp32 companions + + - uses: actions/cache@v5 + with: + path: firmware + key: cache-firmware-esp32-companions-${{ github.sha }} + + create-release: + needs: [build-nrf, build-esp32-companions, build-esp32-repeaters] + runs-on: ubuntu-latest + + steps: + - name: generate release tag + id: tag + run: | + version=v$(date +"%Y%m%d.%H%M%S") + echo "release_tag=$version" >> $GITHUB_OUTPUT + + - uses: actions/cache/restore@v5 + with: + path: firmware + key: cache-firmware-nrf-${{ github.sha }} + + - uses: actions/cache/restore@v5 + with: + path: firmware + key: cache-firmware-esp32-companions-${{ github.sha }} + + - uses: actions/cache/restore@v5 + with: + path: firmware + key: cache-firmware-esp32-repeaters-${{ github.sha }} + + - name: upload firmware artifacts + uses: actions/upload-artifact@v7 + with: + name: firmware-${{ steps.tag.outputs.release_tag }} + path: firmware + + - name: create release + uses: softprops/action-gh-release@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ steps.tag.outputs.release_tag }} + name: Firmware ${{ steps.tag.outputs.release_tag }} + body: "" + draft: false + files: firmware/* \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..56eea04 --- /dev/null +++ b/build.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash + +set -euo pipefail +mkdir -p firmware + +COMMIT_HASH=$(git rev-parse --short HEAD) + +nRF_boards=( + rak4631 + rak3401_1watt + wio_tracker_l1 + t1000_e + thinknode_m1 + thinknode_m3 + thinknode_m6 + rak_wismesh_tag + ikoka_nano_30dbm + sensecap_solar +) + +ESP32_boards=( + xiao_esp32c3 + xiao_esp32c6/esp32c6/hpcore + xiao_esp32s3/esp32s3/procpu + lilygo_tlora_c6/esp32c6/hpcore + station_g2/esp32s3/procpu + heltec_wifi_lora32_v3/esp32s3/procpu + heltec_wifi_lora32_v4/esp32s3/procpu + heltec_wifi_lora32_v43/esp32s3/procpu +) + +if [[ $1 == "nrf" ]]; then + for board in "${nRF_boards[@]}"; do + board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g') + + # build nRF companions + echo "Now building $board companion" + if [[ $board == "wio_tracker_l1" ]]; then + west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/prod.conf" -DCONFIG_ZEPHCORE_EASTER_EGG_DOOM=y + else + west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/prod.conf" + fi + mv build/zephyr/zephyr.uf2 firmware/"$board"-companion-"$COMMIT_HASH".uf2 + mv build/zephyr/zephyr.zip firmware/"$board"-companion-"$COMMIT_HASH".zip + + # build nRF repeaters + echo "Now building $board repeater" + west build -b "$board" zephcore --pristine -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf" + mv build/zephyr/zephyr.uf2 firmware/"$board"-repeater-"$COMMIT_HASH".uf2 + mv build/zephyr/zephyr.zip firmware/"$board"-repeater-"$COMMIT_HASH".zip + done +fi + +if [[ $1 == "esp32" ]]; then + for board in "${ESP32_boards[@]}"; do + board_clean_for_path=$(echo "$board" | sed -e 's/\//-/g') + + if [[ $board =~ (esp32[^/]+) ]]; then + chip="${BASH_REMATCH[1]}" + else + echo "Unknown chip for: $board" + exit 1; + fi + + if [[ $2 == "companions" ]]; then + # build ESP32 companions + echo "Now building $board companion" + west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/prod.conf" + FLASH_SIZE=$( + python3 -c ' +import re +import sys + +path = sys.argv[1] if len(sys.argv) > 1 else "build/zephcore/zephyr/zephyr.dts" +dts = open(path, encoding="utf-8", errors="replace").read() + +def parse_cell(tok: str) -> int: + t = tok.strip() + return int(t, 16) if t.lower().startswith("0x") else int(t) + +m = re.search( + r"flash0:\s*flash@[^{]*\{[^}]*?reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+" + r"((?:0x)?[0-9a-fA-F]+)\s*>", + dts, + re.DOTALL, +) +if not m: + m = re.search( + r"compatible\s*=\s*\"soc-nv-flash\"\s*;\s*[\s\S]*?" + r"reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+((?:0x)?[0-9a-fA-F]+)\s*>", + dts, + ) +if not m: + raise SystemExit("flash reg not found in " + path) + +size = parse_cell(m.group(1)) +print(str(size // 1048576) + "MB") + ' "${ZEPHYR_DTS:-build/zephcore/zephyr/zephyr.dts}" + ) + python -m esptool --chip "$chip" merge-bin \ + --output firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH"-merged.bin \ + --flash-mode dio --flash-freq 40m --flash-size "$FLASH_SIZE" \ + 0x00000 build/mcuboot/zephyr/zephyr.bin \ + 0x20000 build/zephcore/zephyr/zephyr.signed.bin + mv build/zephcore/zephyr/zephyr.signed.bin firmware/"$board_clean_for_path"-companion-"$COMMIT_HASH".bin + fi + + if [[ $2 == "repeaters" ]]; then + # build ESP32 repeaters + echo "Now building $board repeater" + west build -b "$board" zephcore --pristine --sysbuild -- -DEXTRA_CONF_FILE="boards/common/repeater.conf;boards/common/prod.conf" + FLASH_SIZE=$( + python3 -c ' +import re +import sys + +path = sys.argv[1] if len(sys.argv) > 1 else "build/zephcore/zephyr/zephyr.dts" +dts = open(path, encoding="utf-8", errors="replace").read() + +def parse_cell(tok: str) -> int: + t = tok.strip() + return int(t, 16) if t.lower().startswith("0x") else int(t) + +m = re.search( + r"flash0:\s*flash@[^{]*\{[^}]*?reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+" + r"((?:0x)?[0-9a-fA-F]+)\s*>", + dts, + re.DOTALL, +) +if not m: + m = re.search( + r"compatible\s*=\s*\"soc-nv-flash\"\s*;\s*[\s\S]*?" + r"reg\s*=\s*<\s*(?:0x[0-9a-fA-F]+|[0-9]+)\s+((?:0x)?[0-9a-fA-F]+)\s*>", + dts, + ) +if not m: + raise SystemExit("flash reg not found in " + path) + +size = parse_cell(m.group(1)) +print(str(size // 1048576) + "MB") + ' "${ZEPHYR_DTS:-build/zephcore/zephyr/zephyr.dts}" + ) + python -m esptool --chip "$chip" merge-bin \ + --output firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH"-merged.bin \ + --flash-mode dio --flash-freq 40m --flash-size "$FLASH_SIZE" \ + 0x00000 build/mcuboot/zephyr/zephyr.bin \ + 0x20000 build/zephcore/zephyr/zephyr.signed.bin + mv build/zephcore/zephyr/zephyr.signed.bin firmware/"$board_clean_for_path"-repeater-"$COMMIT_HASH".bin + fi + done +fi \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..9f5050d --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +adafruit-nrfutil==0.5.3.post16 \ No newline at end of file