From eb6abd7b3f1223153f6033b7a4d230704c5e6508 Mon Sep 17 00:00:00 2001 From: Giovanni Remigi Date: Thu, 1 Oct 2026 15:18:06 +0200 Subject: [PATCH 1/3] Mount the repo into the editor, not the objectives' folder The editor mounted src/plugins/stepit_objectives/objectives itself. A bind mount follows the folder, not its path, and git deletes and recreates a folder when it switches to a branch that lacks it, as gh pr merge did to main before src/plugins existed: the editor then showed the deleted folder, empty, until its container was recreated. It now mounts the repo at ~/rig, as the commander does, and opens the objectives through BEHAVIORS_DIR. Co-Authored-By: Claude Opus 5.5 --- docker/docker-compose.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 635dbfe..334aee2 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -112,10 +112,15 @@ services: init: true depends_on: - stepit-commander + # Edit the rig's objectives: the commander reads them again before each + # goal, so what the editor saves is what the Run button executes. The whole + # repo is mounted, not the folder alone: git deletes and recreates a folder + # when a branch it switches to lacks it, and a mount of the folder would + # then show the deleted one, empty, until the container is recreated. volumes: - # Edit the rig's objectives: the commander reads them again before each - # goal, so what the editor saves is what the Run button executes. - - ../src/plugins/stepit_objectives/objectives:/home/developer/behaviors + - ..:/home/developer/rig + environment: + BEHAVIORS_DIR: /home/developer/rig/src/plugins/stepit_objectives/objectives command: serve.sh # The camera driver, with its test page and the pictures on From 40c53a992ea4782a4a0e065a2f8c10b5359100df Mon Sep 17 00:00:00 2001 From: Giovanni Remigi Date: Thu, 1 Oct 2026 15:18:06 +0200 Subject: [PATCH 2/3] Validate the objectives in CI, with the StepIt Editor's validator A new job of ci.yml, objectives, builds the editor's native validator against the ROS package of BehaviorTree.CPP, the library and the version the commander loads trees with, and runs validate on src/plugins/stepit_objectives/objectives: the editor's own checks, then the library loading every file with the node models of the rig's behaviors. A broken objective fails the pull request instead of being refused by the commander on the robot. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 4 +++- README.md | 21 +++++++++++++++--- 3 files changed, 69 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 487a0c3..9c01a9a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,3 +51,51 @@ jobs: run: bin/${{ matrix.workspace }}/build.sh - name: Test run: bin/${{ matrix.workspace }}/test.sh + + # The rig's objectives, checked by the StepIt Editor's validator: the + # editor's own checks, then BehaviorTree.CPP loading every file, with the + # node models of the rig's behaviors, the library and the version the + # commander loads them with. A broken objective, e.g. an unknown node or + # port, or a missing subtree, fails here instead of on the robot. + objectives: + name: objectives + runs-on: ubuntu-24.04 + container: ros:jazzy-ros-base + defaults: + run: + shell: bash + steps: + # nodejs runs the JavaScript actions below under act, which does not + # provide one in a container as GitHub does; setup-node installs the + # Node.js the editor uses. + - name: Install git, BehaviorTree.CPP and Node.js + run: apt-get update -qq && apt-get install -qq -y git ros-jazzy-behaviortree-cpp nodejs + - uses: actions/checkout@v4 + # Only the editor's submodule, over HTTPS, as for the commander above. + - name: Check out the editor + run: | + git config --global --add safe.directory '*' + git config --global url.https://github.com/.insteadOf git@github.com: + git submodule update --init --depth 1 modules/stepit-editor + - uses: pnpm/action-setup@v4 + with: + package_json_file: modules/stepit-editor/package.json + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: modules/stepit-editor/pnpm-lock.yaml + # Steps bypass the image's entrypoint, which would source ROS: the + # validator needs it to build, and to find the library when it runs. + - name: Build the validator + working-directory: modules/stepit-editor + run: | + source /opt/ros/jazzy/setup.bash + pnpm install --frozen-lockfile + cmake -S validator -B build/validator -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=/opt/ros/jazzy + cmake --build build/validator --parallel + - name: Validate the objectives + working-directory: modules/stepit-editor + run: | + source /opt/ros/jazzy/setup.bash + pnpm run validate "$GITHUB_WORKSPACE/src/plugins/stepit_objectives/objectives" diff --git a/CLAUDE.md b/CLAUDE.md index 22bb1e6..802bddd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -128,7 +128,9 @@ they reach the real robot: run them with `ROS_DOMAIN_ID=77`. `.github/workflows`: `ci.yml` builds and tests each workspace with the `bin/` scripts in `ros:jazzy-ros-base` (the `plugins` job builds `modules/stepit-commander` first, checked out over HTTPS: `.gitmodules` lists SSH URLs); `ci-format.yml` runs pre-commit without the ament hooks; -`ci-ros-lint.yml` runs those, per package. A new package must be added to the package list +`ci-ros-lint.yml` runs those, per package. The `objectives` job of `ci.yml` builds the editor's native +validator (submodule `stepit-editor`) against the ROS package of BehaviorTree.CPP and runs +`validate` on `src/plugins/stepit_objectives/objectives`: a broken objective fails CI. A new package must be added to the package list of `ci-ros-lint.yml` and to the paths of the ament hooks in `.pre-commit-config.yaml`. ## Conventions diff --git a/README.md b/README.md index 0433dfd..7792838 100644 --- a/README.md +++ b/README.md @@ -480,7 +480,8 @@ only overrides: [`src/plugins`](src/plugins) holds the rig's behaviors and objectives, built on top of the commander's workspace; - the folder the editor opens: the rig's `src/plugins/stepit_objectives/objectives`, - instead of the editor's examples; + instead of the editor's examples, through `BEHAVIORS_DIR`, with this repo + mounted at `~/rig`; - a service of the rig's own, `stepit-macro`, on an image defined in [`docker/Dockerfile`](docker/Dockerfile): it mounts this repo at `~/ws`, and `/dev/input` from the host for the gamepad, and runs @@ -508,14 +509,28 @@ modules: | Workflow | What it checks | |---|---| -| [`ci.yml`](.github/workflows/ci.yml) | Builds and tests both workspaces, each in a job of its own, with the scripts of [`bin`](bin), in a `ros:jazzy-ros-base` container. The job of `src/plugins` first builds the commander's workspace from the `stepit-commander` submodule. | +| [`ci.yml`](.github/workflows/ci.yml) | Builds and tests both workspaces, each in a job of its own, with the scripts of [`bin`](bin), in a `ros:jazzy-ros-base` container. The job of `src/plugins` first builds the commander's workspace from the `stepit-commander` submodule. A third job, `objectives`, validates the objectives with the StepIt Editor's validator, see below. | | [`ci-format.yml`](.github/workflows/ci-format.yml) | The pre-commit hooks that need no ROS: clang-format, black, codespell, and the checks of whitespace and files. | | [`ci-ros-lint.yml`](.github/workflows/ci-ros-lint.yml) | The ament linters of every package: copyright, lint_cmake and cpplint. | The build cannot use `industrial_ci`, as the modules do: it builds one workspace from the sources of the repo, and `src/plugins` is built on top of -the commander's. CI checks out only the `stepit-commander` submodule, over +the commander's. CI checks out only the `stepit-commander` and `stepit-editor` submodules, over HTTPS; the other modules have CI of their own. +The `objectives` job runs the editor's validator, `validate`, on +[`src/plugins/stepit_objectives/objectives`](src/plugins/stepit_objectives/objectives): +the editor's own checks, then BehaviorTree.CPP loading every file with the node +models of the rig's behaviors, built from the ROS package, the library and the +version the commander loads them with. A broken objective, e.g. an unknown node +or port, or a subtree that does not exist, fails the pull request instead of +being refused by the commander on the robot. Run the same check by hand in the +editor's container, where it validates that folder by default: + +``` +./docker/dock.sh shell stepit-editor +validate.sh +``` + The workflows run locally with [Nektos `act`](https://github.com/nektos/act), e.g. `act -W .github/workflows/ci.yml`. From 1e3a2dbfb6b43a74f29244693f3e534716a0e02f Mon Sep 17 00:00:00 2001 From: Giovanni Remigi Date: Thu, 1 Oct 2026 15:18:48 +0200 Subject: [PATCH 3/3] Update stepit-editor Its native validator builds against the ROS package of BehaviorTree.CPP, which the objectives job needs, and is tested in its CI. Co-Authored-By: Claude Opus 5.5 --- modules/stepit-editor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/stepit-editor b/modules/stepit-editor index 83a2a03..c4c6d7e 160000 --- a/modules/stepit-editor +++ b/modules/stepit-editor @@ -1 +1 @@ -Subproject commit 83a2a03cdc9e7d027b588c22aafab7b671d9721d +Subproject commit c4c6d7e68dbb5340dda07fd169e16faf7fbe11b6