Skip to content

CI integration

Run the same labwired test command on your laptop and in GitHub Actions or GitLab. Pin a CLI release so firmware changes are judged by a fixed simulator version.

Default pin used in examples: v0.22.2.


Local first

curl -fsSL https://labwired.com/install.sh | LABWIRED_VERSION=v0.22.2 sh

labwired test \
  --script tests/firmware-test.yaml \
  --output-dir out/labwired \
  --junit out/labwired/junit.xml

Exit 0 = pass. Non-zero = fail. Typical artifacts: result.json, uart.log, JUnit XML.

Product page: labwired.com/ci.


GitHub Actions

Use the public action from w1ne/labwired-core. Pin both the action commit and the CLI version.

name: Firmware simulation

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build firmware
        run: # your normal firmware build; produce an ELF or flash image

      - id: labwired
        name: Run LabWired
        uses: w1ne/labwired-core/.github/actions/labwired-test@75a3d9e906bab90fc0281d1dd786fe479a910d48
        with:
          script: tests/firmware-test.yaml
          version: v0.22.2
          output-dir: out/labwired
          args: --no-uart-stdout

      - name: Link the automatic LabWired artifact
        if: always()
        run: echo "${{ steps.labwired.outputs.artifact-url }}" >> "$GITHUB_STEP_SUMMARY"

The public action reference is an immutable action-source pin to 75a3d9e906bab90fc0281d1dd786fe479a910d48. Inputs: script (required), version (default v0.22.2), output-dir, and args. The action downloads that CLI release, writes JUnit to output-dir/junit.xml, appends summary.md to the job summary, and always uploads the output directory (including on failure).

Outputs: status, summary-md, report-html, artifact-url, exit-code (via the labwired step id).


Container runner

The release image uses labwired as the entrypoint. Pass test after the image name — do not repeat labwired in the container command:

docker run --rm \
  --user "$(id -u):$(id -g)" \
  --volume "$PWD:/workspace" \
  --workdir /workspace \
  ghcr.io/w1ne/labwired:v0.22.2 \
  test --script tests/firmware-test.yaml \
       --output-dir out/labwired \
       --no-uart-stdout

When you bind-mount a workspace, pass the caller UID/GID so generated artifacts stay writable on the host.


GitLab CI

Clear the image entrypoint so GitLab can start its job shell. See integration-templates/gitlab-ci.yml:

test:firmware:
  image:
    name: ghcr.io/w1ne/labwired:v0.22.2
    entrypoint: [""]
  script:
    - labwired test --script tests/firmware-test.yaml --output-dir out/labwired --no-uart-stdout

Artifacts

Use --output-dir everywhere. A run writes result.json, uart.log, and JUnit under that directory. The GitHub action always uploads the directory; other CI systems should retain it on failure.


Advanced: build from source

cargo build --release -p labwired-cli
./target/release/labwired test \
  --script tests/firmware-test.yaml \
  --output-dir out/labwired

More templates: integration templates.


What to assert

Use the test script schema: UART substrings, register values, stop reasons, step limits. Prefer checks that match product behavior — not only “boot completed.”


Next

Run firmware (CLI) Local install and run / test
Test script schema YAML fields
Fidelity What pass means
Troubleshooting Max steps, empty UART, …