diff --git a/.github/workflows/scenario-cloud.yml b/.github/workflows/scenario-cloud.yml index 1e81976..a407227 100644 --- a/.github/workflows/scenario-cloud.yml +++ b/.github/workflows/scenario-cloud.yml @@ -84,7 +84,7 @@ jobs: - name: Install xPack OpenOCD # The SDK flashes through OpenOCD's CMSIS-DAP TCP backend (cmsis_dap_tcp), which is newer - # than OpenOCD 0.12.0 (apt's 0.12.0 lacks it); see selftest-cloud.yml. + # than OpenOCD 0.12.0 (apt's 0.12.0 lacks it). run: | V=0.12.0-7 curl -fsSL "https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v$V/xpack-openocd-$V-linux-x64.tar.gz" \ diff --git a/.github/workflows/selftest-cloud.yml b/.github/workflows/selftest-cloud.yml deleted file mode 100644 index 94d74bf..0000000 --- a/.github/workflows/selftest-cloud.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Selftest (cloud HIL) - -# Runs the STM32 selftest on the physical device registered as "benchpod-v1.0.0", -# driven over embeddedci.com from pytest (no BenchPod on the runner). Auth is the -# job's GitHub OIDC token — no secret needed (the repo must be trusted in the -# EmbeddedCI web app under BenchPod → GitHub Actions, allowed to drive the device). -# -# Triggers only when the firmware (selftest.c) or this workflow changes. -on: - push: - paths: - - "selftest-stm32/selftest.c" - - "selftest-stm32/tests/**" - - ".github/workflows/selftest-cloud.yml" - workflow_dispatch: {} - -permissions: - id-token: write # REQUIRED: lets the job mint a GitHub OIDC token for embeddedci - contents: read - -jobs: - selftest: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - - # --- Build the firmware (arm-none-eabi + STM32CubeF4 HAL) --- - - name: Install ARM toolchain - uses: carlosperate/arm-none-eabi-gcc-action@v1 - with: - release: "13.2.Rel1" # official Arm GNU toolchain, pinned + cached across runs - - - name: Install xPack OpenOCD - # OpenOCD runs on the runner: the SDK drives the pod's on-board CMSIS-DAP probe through - # OpenOCD's cmsis-dap adapter with its TCP backend (cmsis_dap_tcp), bridged through the - # cloud tunnel to the device. That backend is newer than OpenOCD 0.12.0 — Ubuntu's apt - # openocd 0.12.0 lacks it (the SDK fails up front with a clear message). xPack OpenOCD - # ships a post-0.12.0 master snapshot that has it. - run: | - V=0.12.0-7 - curl -fsSL "https://github.com/xpack-dev-tools/openocd-xpack/releases/download/v$V/xpack-openocd-$V-linux-x64.tar.gz" \ - | sudo tar xz -C /opt - echo "/opt/xpack-openocd-$V/bin" >> "$GITHUB_PATH" - - - name: Fetch STM32CubeF4 HAL - # The HAL driver and the CMSIS device headers are git SUBMODULES of STM32CubeF4 (separate - # repos), so a plain clone leaves them empty. Sparse-checkout just Drivers/CMSIS (for the - # core CMSIS Include/), then init only the two submodules the Makefile actually needs — - # avoids pulling the huge Projects/ and the dozens of BSP/Middleware submodules. - run: | - git clone --depth 1 --filter=blob:none --sparse \ - https://github.com/STMicroelectronics/STM32CubeF4 selftest-stm32/STM32CubeF4 - cd selftest-stm32/STM32CubeF4 - git sparse-checkout set Drivers/CMSIS - git submodule update --init --depth 1 \ - Drivers/STM32F4xx_HAL_Driver Drivers/CMSIS/Device/ST/STM32F4xx - - - name: Build selftest.elf - run: make -C selftest-stm32 CUBE_F4="$GITHUB_WORKSPACE/selftest-stm32/STM32CubeF4" - - # --- Run it on benchpod-v1.0.0 over the cloud --- - - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Install the embeddedci SDK (cloud + pytest extras) - # Pinned to the frozen 2.x API. The [pytest] extra pulls in pytest (the SDK is a pytest - # *plugin*, pytest is not a runtime dep); [cloud] adds the embeddedci.com transport. - run: pip install "embeddedci[cloud,pytest]>=2.0,<3" - - - name: Self-test on benchpod-v1.0.0 (cloud) - # The test requests the `build_report` fixture, so this run is also recorded on - # embeddedci.com as a GitHub-sourced build: the firmware (elf/bin/hex) is uploaded and the - # pytest pass/fail is captured, reusing the same OIDC token (id-token: write above). The - # uploaded firmware can then be re-flashed from the BenchPod page in the web UI. - # --benchpod-build-target labels the build's platform for the flash modal. - # The LA bank voltage (3.3 V for the STM32) is set in selftest-stm32/tests/conftest.py. - run: | - pytest selftest-stm32/tests -v \ - --benchpod-connection=embeddedci:benchpod-v1.0.0 \ - --benchpod-firmware="$GITHUB_WORKSPACE/selftest-stm32/build/selftest.elf" \ - --benchpod-build-target=stm32f4 diff --git a/selftest-stm32/README.md b/selftest-stm32/README.md index 0329cf0..8a46881 100644 --- a/selftest-stm32/README.md +++ b/selftest-stm32/README.md @@ -1,34 +1,26 @@ -# STM32 selftest — cloud HIL via GitHub Actions +# STM32 selftest: a minimal hardware-in-the-loop test `selftest.c` is a tiny STM32F446 firmware that boots, prints a UART banner ending in `APP_OK`, and exposes a `ping`/`status` console. It's the "hello world" of an EmbeddedCI hardware-in-the-loop test driven entirely from CI. -## What the workflow does +## Run it over the cloud -[`.github/workflows/selftest-cloud.yml`](../.github/workflows/selftest-cloud.yml) runs on every -change to `selftest.c` (or the workflow itself): +The pod does not have to be on your network: the SDK reaches it **through embeddedci.com**, +flashes the firmware over the cloud, power-cycles the target and asserts the `APP_OK` UART +marker. Use the device name from the **BenchPod** page in the EmbeddedCI web app and an API key +(or a `benchpod login` session): -1. builds `selftest.c` → `build/selftest.elf` (arm-none-eabi + STM32CubeF4), -2. installs the `embeddedci` SDK and runs `pytest selftest-stm32/tests`, -3. the test connects to the device **through embeddedci.com** with the connection - `embeddedci:benchpod-v1.0.0`, flashes the firmware over the cloud, power-cycles the - target, and asserts the `APP_OK` UART marker. - -There is **no BenchPod on the GitHub runner** — the physical pod lives wherever it's plugged in -and connects out to embeddedci.com. Auth is the job's **GitHub OIDC token** (`permissions: -id-token: write`); no API key or secret is stored. - -## One-time setup +```bash +pip install "embeddedci[pytest]>=2.0,<3" +make CUBE_F4=/path/to/STM32CubeF4 +BENCHPOD_API_KEY=eci_... pytest selftest-stm32/tests -v \ + --benchpod-connection=embeddedci: \ + --benchpod-firmware=selftest-stm32/build/selftest.elf +``` -1. **Register the device** from the machine wired to the pod: - ```bash - benchpod register --connection - ``` - then rename it to `benchpod-v1.0.0` on the **BenchPod** page in the EmbeddedCI web app - (names are URL-safe and unique per org). -2. **Trust this repo**: BenchPod → **GitHub Actions** → add `OWNER/REPO` (click *Look up* to fill - the numeric ids) and allow it to drive **benchpod-v1.0.0** (Any device, or this specific one). +To run this from GitHub Actions instead, see [`scenario-sensors-stm32`](../scenario-sensors-stm32) +and its workflow: it authenticates with the job's GitHub OIDC token, so no secret is stored. ## Run it locally (against a pod on your LAN) diff --git a/selftest-stm32/tests/test_selftest.py b/selftest-stm32/tests/test_selftest.py index 4314fc4..919b83b 100644 --- a/selftest-stm32/tests/test_selftest.py +++ b/selftest-stm32/tests/test_selftest.py @@ -1,11 +1,8 @@ -"""Cloud hardware-in-the-loop self-test for the device registered as ``benchpod-v1.0.0``. +"""Hardware-in-the-loop self-test: flash ``build/selftest.elf`` and check that it boots. -Runs in GitHub Actions (see ``.github/workflows/selftest-cloud.yml``): the workflow builds -``selftest.c`` into ``build/selftest.elf``, then pytest drives the device **through -embeddedci.com** — there is no BenchPod on the runner. The connection -``embeddedci:benchpod-v1.0.0`` authenticates with the workflow's GitHub OIDC token, the -server bridges a tunnel to the physical BenchPod, and we flash + UART-verify the firmware -over the cloud exactly as we would locally. +Pass ``--benchpod-connection`` a LAN address or ``embeddedci:`` (through +embeddedci.com, authenticated with an API key or a ``benchpod login`` session); the test +flashes and UART-verifies the firmware the same way over either (see the README). Wiring (BenchPod logic-analyzer channels → DUT). The pod has no dedicated SWD/UART pins — it exposes 12 generic LA channels (``pins.pin_1`` .. ``pins.pin_12``) and any @@ -109,7 +106,7 @@ def dut(benchpod, wiring): @pytest.mark.hardware def test_selftest_boots_over_cloud(dut, wiring, firmware, build_report): - """Flash selftest.elf to benchpod-v1.0.0 over the cloud and assert it boots cleanly. + """Flash selftest.elf to the pod's DUT and assert it boots cleanly. The ``build_report`` fixture is the opt-in that records this run as a GitHub-sourced build on embeddedci.com (uploading the firmware and capturing pass/fail) — but only inside GitHub Actions