1. Тема дня

Строим путь прошивки от коммита до безопасного релиза:

text
исходный код
→ воспроизводимая сборка в Docker
→ проверки и unit-тесты
→ контроль размера Flash/RAM
→ release bundle
→ подпись firmware
→ HIL-тестирование
→ staged rollout
→ OTA с rollback

Главная мысль: прошивка считается готовой не потому, что она один раз собралась на компьютере разработчика, а потому, что её можно повторно собрать в чистом окружении, проверить автоматически, идентифицировать и безопасно развернуть.

2. Зачем это нужно

Без CI трудно доказать:

text
- каким ESP-IDF и компилятором собрана прошивка;
- совпадает ли firmware.bin с Git-коммитом;
- не вырос ли образ за пределы OTA-раздела;
- не изменился ли sdkconfig случайно;
- прошла ли миграция NVS;
- работает ли rollback;
- не сломались ли UART parser, watchdog или phase detector.

3. Теория

CI, CD и HIL

CI:

text
compile
static checks
unit tests
size check
config check
artifact generation

CD формирует релизный комплект: firmware binary, manifest, SHA-256, signature, ELF/MAP, partition table, release notes. HIL проверяет реальную плату: flash, boot, UART console, GPIO, I2C, CAN/RS-485, watchdog reset, OTA rollback, power interruption.

Воспроизводимая сборка

Фиксируй:

text
Git commit
ESP-IDF version
compiler/toolchain version
sdkconfig
component versions
partition table
build flags

Не используй latest для production. Используй точный Docker image/tag и lock-файлы.

Что хранить в репозитории

text
project/
├── CMakeLists.txt
├── sdkconfig.defaults
├── partitions_ota.csv
├── dependencies.lock
├── main/
├── components/
├── tests/
├── ci/
└── .github/workflows/

Не хранить: build, private signing keys, device certificates, production passwords.

Release bundle

Храни для каждого релиза:

text
app.bin
bootloader.bin
partition-table.bin
flasher_args.json
project.elf
project.map
sdkconfig
size.json
manifest.json
SHA256SUMS
release-notes.md

ELF нужен для backtrace, core dump и расследования старых production crash.

Подпись отдельно от сборки

PR-job не должен получать signing key. Схема:

text
untrusted build job:
  source → unsigned firmware → tests
protected release job:
  download verified artifact
  check Git SHA
  sign firmware
  verify signature
  generate release bundle

HIL на Raspberry Pi

Raspberry Pi Zero 2W может быть HIL-оркестратором:

text
USB → ESP32 UART/JTAG
GPIO → входы через развязку
USB relay/MOSFET → питание DUT
CAN adapter
RS-485 adapter
Python test runner

4. Типичные ошибки

  • Использовать latest.
  • Собирать release на ноутбуке.
  • Хранить только .bin без .elf и .map.
  • Подписывать firmware в PR-job.
  • Не контролировать размер OTA-slot.
  • HIL проверяет только ping.
  • HIL-runner доступен недоверенному коду.
  • Сразу обновлять весь парк.

5. Практическое задание

Добавь firmware-ci.yml:

c
name: Firmware CI
on:
  pull_request:
  push:
    branches: [ main ]
    tags: [ "v*" ]
permissions:
  contents: read
env:
  IDF_IMAGE: espressif/idf:v6.0.2
  IDF_TARGET: esp32
  PROJECT_NAME: control
jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          submodules: recursive
      - name: Build in ESP-IDF Docker
        run: |
          docker run --rm \
            -v "$PWD:/project" \
            -w /project \
            -u "$(id -u):$(id -g)" \
            -e HOME=/tmp \
            -e IDF_TARGET="${IDF_TARGET}" \
            "${IDF_IMAGE}" \
            bash -lc 'idf.py set-target "$IDF_TARGET" && idf.py build && idf.py size --format json2 > build/size.json'

Добавь размерный budget: firmware не больше 85% OTA-slot.

6. Что попробовать дальше

  • Добавить manifest generation.
  • Добавить SHA256SUMS.
  • Сохранять ELF/MAP/size.json.
  • Сделать HIL smoke-test через pyserial.
  • Разделить unsigned build и protected signing job.

Задание

Pull request меняет firmware build script. Опишите границу доверия, запрещающую его job доступ к signing key и управление HIL runner.

Критерии самопроверки: Отдельно указать credentials, artifact verification и runner access. Успех unit tests не делает произвольный PR code доверенным.

Показать ответ автора

Выполнить недоверенную сборку и тесты без signing credentials и прямого HIL control. Защищённый release job проверяет artifact и Git SHA перед подписью; к аппаратному runner допускаются только проверенные доверенные входы.

Задание

Определите доказательства для расследования crash старого развёрнутого релиза и подтверждения установленного binary.

Критерии самопроверки: Использовать исторический matching ELF, а не новую сборку; включить воспроизводимые входы и identity установленного artifact.

Показать ответ автора

Сохранить точные firmware binary, manifest, SHA256SUMS, ELF/MAP, configuration и release identity, связанные с Git commit/toolchain. Сопоставить release identity устройства с bundle и использовать соответствующий ELF для анализа crash.