diff --git a/.github/workflows/foxmesh-ci.yml b/.github/workflows/foxmesh-ci.yml new file mode 100644 index 0000000..61deb04 --- /dev/null +++ b/.github/workflows/foxmesh-ci.yml @@ -0,0 +1,247 @@ +name: Automated CI Build of FoxMesh + +on: + push: + branches: + - main + workflow_dispatch: + inputs: + target_branch: + description: 'Which branch do you want to build and test?' + required: true + default: 'main' + +jobs: + build-and-test: + runs-on: ubuntu-latest + # Ensure this workflow only runs inside this reopsitory + if: github.repository == 'tkuschel/FoxMesh' + + permissions: + contents: write # We want also create official Releases + + steps: + # 1. Checkout the target branch + - name: Checkout Target branch + uses: actions/checkout@7 + with: + ref: ${{ github.event.inputs.target_branch }} || github.ref }} + + # 2. Set up Python environment + - name: Set up Python 3.x + uses: actions/setup-python ^3.13 + with: + python-version: '3.x' + + # 3. Cache global PlatformIO directories, uses a dynamic key based on platformio.ini + - name: Cache PlatformIO + uses: actions/cache@v6 + with: + path: | + ~/.cache/pio + ~/.platformio + .pio + key: ${{ runner.os }}-pio-${{ hashFiles('**/platformio.ini') }} + restore-keys: | + ${{ runner.os }}-pio- + + - name: Install ESP32 platform + run: pio pkg install --silent --global --platform espressif32 + + - name: Compile the Project + run: pio run --silent + + # 11. Dynamically create the smart flashing scripts next to the T-Beam binary + # with optional bootloader detection and group check + # the table of flash addresses usually found at /partitions-4MB-safeboot.csv + - name: Create Desktop Flashing Scripts + run: | + # ---------------------------------------------------------------------- + # 0. Flash Address Configuration ← edit here to change the layout + # ---------------------------------------------------------------------- + FLASH_BOOTLOADER_ADDR="0x1000" + FLASH_PARTITIONS_ADDR="0x8000" + FLASH_OTADATA_ADDR="0xE000" + FLASH_SAFEBOOT_ADDR="0x10000" + FLASH_FIRMWARE_ADDR="0xC0000" + + # ---------------------------------------------------------------------- + # 1. Universal Linux Shell Script + # ---------------------------------------------------------------------- + cat << 'EOF' > flash_esp32_board.sh + #!/usr/bin/env bash + GREEN='\033[0;32m' + RED='\033[0;31m' + YELLOW='\033[1;33m' + NC='\033[0m' + + echo -e "${YELLOW}=== MeshCom Universal ESP32 Flashing Script ===${NC}" + + if ! command -v esptool &> /dev/null; then + echo -e "${RED}Error: esptool is not installed.${NC}" + echo "Please install it via your package manager or pip: 'pip install esptool'" + echo "Linux Arch: 'sudo pacman -Sy && sudo pacman -S extra-testing/esptool'" + exit 1 + fi + + FIRMWARE = "firmware.bin" + + + # Firmware File + if [ ! -f "firmware.bin" ]; then + echo -e "${RED}Error: No matching firmware binary found in this folder!${NC}" + exit 1 + fi + + echo -e "${GREEN}Selected Firmware:${NC} $FIRMWARE ($BOARD_NAME)" + PORT=$(ls /dev/ttyUSB* /dev/ttyACM* 2>/dev/null | head -n 1) + if [ -z "$PORT" ]; then + echo -e "${RED}Error: No connected ESP32 Board (ttyUSB/ttyACM) found!${NC}" + exit 1 + fi + echo -e "${GREEN}Detected Board on port:${NC} $PORT" + + # Dynamic Permission Check: Determine required group for the specific TTY device + REQUIRED_GROUP=$(stat -c '%G' "$PORT") + if [ "$REQUIRED_GROUP" != "root" ]; then + if ! groups "$USER" | grep -q "\b${REQUIRED_GROUP}\b"; then + echo -e "${RED}Error: You do not have permissions to access $PORT.${NC}" + echo -e "The device belongs to the group: ${YELLOW}$REQUIRED_GROUP${NC}" + echo -e "Please add your user to this group by running:" + echo -e " ${GREEN}sudo usermod -aG $REQUIRED_GROUP \$USER${NC}" + echo -e "After running the command, please log out and log back in for the changes to take effect." + exit 1 + fi + fi + + # Build flash arguments from the address table (only include files that exist) + FLASH_ARGS="" + if [ -f "bootloader.bin" ]; then + FLASH_ARGS="$FLASH_ARGS __BOOTLOADER_ADDR__ bootloader.bin" + fi + if [ -f "partitions.bin" ]; then + FLASH_ARGS="$FLASH_ARGS __PARTITIONS_ADDR__ partitions.bin" + fi + if [ -f "otadata.bin" ]; then + FLASH_ARGS="$FLASH_ARGS __OTADATA_ADDR__ otadata.bin" + fi + if [ -f "safeboot.bin" ]; then + FLASH_ARGS="$FLASH_ARGS __SAFEBOOT_ADDR__ safeboot.bin" + fi + FLASH_ARGS="$FLASH_ARGS __FIRMWARE_ADDR__ $FIRMWARE" + + echo -e "${GREEN}Flashing:${NC}$FLASH_ARGS" + esptool --chip esp32 --port "$PORT" --baud 921600 write-flash -z $FLASH_ARGS + + if [ $? -eq 0 ]; then + echo -e "${GREEN}=== Success! $BOARD_NAME successfully updated ===${NC}" + else + echo -e "${RED}=== Flash failed! ===${NC}" + exit 1 + fi + EOF + + # Substitute address placeholders with configured hex values + sed -i \ + -e "s|__BOOTLOADER_ADDR__|${FLASH_BOOTLOADER_ADDR}|g" \ + -e "s|__PARTITIONS_ADDR__|${FLASH_PARTITIONS_ADDR}|g" \ + -e "s|__OTADATA_ADDR__|${FLASH_OTADATA_ADDR}|g" \ + -e "s|__SAFEBOOT_ADDR__|${FLASH_SAFEBOOT_ADDR}|g" \ + -e "s|__FIRMWARE_ADDR__|${FLASH_FIRMWARE_ADDR}|g" \ + flash_esp32_board.sh + chmod +x flash_esp32_board.sh + + # ---------------------------------------------------- + # 2. Universal Windows Batch Script + # ---------------------------------------------------- + cat << 'EOF' > flash_esp32_board.bat + @echo off + setlocal enabledelayedexpansion + echo =================================================== + echo === MeshCom Universal ESP32 Windows Flashing ====== + echo =================================================== + + set "FIRMWARE=firmware.bin" + set "BOARD_NAME=" + + if exist "firmeware.bin" ( + set "BOARD_NAME=ESP32" + ) + + if "%FIRMWARE%"="" ( + echo [ERROR] No matching firmware binary found in this folder! + pause + exit /b 1 + ) + + echo [INFO] Selected Firmware: %FIRMWARE% (%BOARD_NAME%) + + set "COMPORT=" + for /f "tokens=2 delims=()" %%A in ('wmic path Win32_PnPEntity get Name ^| findstr /i "COM" ^| findstr /i "Silicon CP210 CH340 CH9102 USB-to-UART"') do set "COMPORT=%%A" + if "%COMPORT%"=="" ( + for /f "tokens=2 delims=()" %%A in ('wmic path Win32_PnPEntity get Name ^| findstr /i "(COM"') do set "COMPORT=%%A" + ) + if "%COMPORT%"=="" ( + echo [ERROR] No ESP32 Board or USB-UART bridge detected! + pause + exit /b 1 + ) + echo [INFO] Detected Board on port: %COMPORT% + + set "FLASH_ARGS=" + if exist "bootloader.bin" ( + set "FLASH_ARGS=!FLASH_ARGS! __BOOTLOADER_ADDR__ bootloader.bin" + ) + if exist "partitions.bin" ( + set "FLASH_ARGS=!FLASH_ARGS! __PARTITIONS_ADDR__ partitions.bin" + ) + if exist "otadata.bin" ( + set "FLASH_ARGS=!FLASH_ARGS! __OTADATA_ADDR__ otadata.bin" + ) + if exist "safeboot.bin" ( + set "FLASH_ARGS=!FLASH_ARGS! __SAFEBOOT_ADDR__ safeboot.bin" + ) + set "FLASH_ARGS=!FLASH_ARGS! __FIRMWARE_ADDR__ %FIRMWARE%" + + echo [INFO] Flashing: !FLASH_ARGS! + esptool --chip esp32 --port %COMPORT% --baud 921600 write-flash -z !FLASH_ARGS! + + if %ERRORLEVEL% EQU 0 ( + echo === SUCCESS! %BOARD_NAME% successfully updated === + ) else ( + echo [ERROR] Flash failed! + ) + pause + EOF + + # Substitute address placeholders with configured hex values + sed -i \ + -e "s|__BOOTLOADER_ADDR__|${FLASH_BOOTLOADER_ADDR}|g" \ + -e "s|__PARTITIONS_ADDR__|${FLASH_PARTITIONS_ADDR}|g" \ + -e "s|__OTADATA_ADDR__|${FLASH_OTADATA_ADDR}|g" \ + -e "s|__SAFEBOOT_ADDR__|${FLASH_SAFEBOOT_ADDR}|g" \ + -e "s|__FIRMWARE_ADDR__|${FLASH_FIRMWARE_ADDR}|g" \ + flash_esp32_board.bat + + # ---------------------------------------------------- + # 3. Distribute Scripts into target build directories + # ---------------------------------------------------- + # mkdir -p .pio/build/ttgo_tbeam + # mkdir -p .pio/build/ttgo-lora32-v21 + + # cp flash_esp32_board.sh .pio/build/ttgo_tbeam/ + # cp flash_esp32_board.bat .pio/build/ttgo_tbeam/ + + # cp flash_esp32_board.sh .pio/build/ttgo-lora32-v21/ + # cp flash_esp32_board.bat .pio/build/ttgo-lora32-v21/ + + # 12. Upload all compiled firmware artifacts with zip compression + - name: Upload Compiled Firmware Artifacts + uses: actions/upload-artifact@v7 + with: + name: firmware-build-${{ github.event.inputs.target_branch || github.ref_name }} + path: | + .pio/build/**/*.bin + flash_esp32_board.* + archive: true + retention-days: 7 # Artifacts will be automatically deleted after 7 days