Skip to content

ci: add per-plugin platform filter and release.sh override hooks#74

Merged
pabloinigoblasco merged 1 commit into
mainfrom
feat/release-platform-filter-and-override
Apr 28, 2026
Merged

ci: add per-plugin platform filter and release.sh override hooks#74
pabloinigoblasco merged 1 commit into
mainfrom
feat/release-platform-filter-and-override

Conversation

@pabloinigoblasco
Copy link
Copy Markdown
Contributor

Summary

Two opt-in extension points to the generic Build & Release workflow. Fully backward compatible — every existing plugin keeps its current behaviour because the new fields are absent.

Why

Plugins are diverging:

  • Python-embedding plugins (e.g. toolbox_reactive_scripts_editor) cannot build on Windows ARM64 today (transitive mpdecimal rejection — see PR build: enable Windows ARM64 via local mpdecimal recipe override #73).
  • ROS-aware plugins (data_stream_ros2) need a Docker-based multi-distribution build that doesn't fit the default conan + cmake matrix.

We could special-case the workflow per plugin, but that doesn't scale. These hooks let each plugin opt into the deviation it needs without forking the release pipeline.

Hooks

1. supported_platforms in manifest.json

"supported_platforms": ["linux-x86_64", "linux-arm64", "macos-x86_64", "macos-arm64", "windows-x86_64"]

Matrix entries whose canonical platform is not listed are skipped from build, package, upload and release. The platform identifier is canonical <os>-<arch> using the same normalization release_tools.py already applies (PLATFORM_ARCH_MAP / PLATFORM_OS_MAP) — aarch64arm64, x64x86_64.

2. <plugin>/release.sh override

When a plugin ships an executable release.sh, the Build & Test step runs it instead of the default ./build.sh + ctest. The script owns build, tests, and producing artifacts under build/<plugin>/Release/ for the rest of the pipeline.

Cleanup

The bulk of the resolution logic moved into a new release_tools.py resolve-build-scope subcommand, replacing the inline Python and bash that previously lived in the workflow's Resolve build scope step. The step is now a thin wrapper:

- name: Resolve build scope
  id: build-scope
  shell: bash
  run: |
    python3 scripts/release_tools.py resolve-build-scope \
      --tag "${GITHUB_REF_NAME:-}" \
      --tag-type "${GITHUB_REF_TYPE:-}" \
      --os-label "${{ matrix.os_label }}" \
      --arch "${{ matrix.arch }}" \
      >> "${GITHUB_OUTPUT}"

The workflow file ends up 41 lines shorter than before.

Test plan

  • CI passes with no plugin tagged (workflow_dispatch / scheduled): default behaviour unchanged.
  • CI passes for an existing plugin tag (no supported_platforms, no release.sh): every matrix entry runs as before.
  • When a plugin declares supported_platforms, only listed platforms produce artifacts; submit-to-registry sees only those.
  • When a plugin ships executable release.sh, Build & Test invokes it; subsequent steps consume build/<plugin>/Release/ as today.

Follow-ups

Two opt-in extension points to the generic Build & Release workflow,
both fully backward compatible (every existing plugin keeps its current
behaviour because the new fields are absent).

1. supported_platforms (manifest.json)

   When a plugin's manifest declares an array like
     "supported_platforms": ["linux-x86_64", "linux-arm64", ...]
   matrix entries whose canonical platform is not listed are skipped
   from build, package, upload and release. Useful for plugins that do
   not (yet) support every host (e.g. Python-embedding plugins blocked
   on Windows ARM, or ROS-aware plugins that only ship for Linux).

   The platform identifier is canonical "<os>-<arch>" using the same
   normalization release_tools.py already applies elsewhere
   (PLATFORM_ARCH_MAP / PLATFORM_OS_MAP) — aarch64 -> arm64,
   x64 -> x86_64.

2. <plugin>/release.sh override

   When a plugin ships an executable release.sh, the Build & Test step
   runs it instead of the default ./build.sh + ctest flow. The script
   takes responsibility for build, tests, and producing the artifacts
   the rest of the pipeline expects under build/<plugin>/Release/.
   Used by plugins whose build pipeline diverges from the conan + cmake
   default (e.g. multi-distribution Docker builds).

The bulk of the resolution logic moves into a new release_tools.py
subcommand, resolve-build-scope, replacing the inline Python and bash
that previously lived in the workflow's "Resolve build scope" step. The
workflow step is now a thin wrapper that pipes the script's key=value
output into $GITHUB_OUTPUT.
@pabloinigoblasco pabloinigoblasco merged commit eb94928 into main Apr 28, 2026
3 checks passed
@pabloinigoblasco pabloinigoblasco deleted the feat/release-platform-filter-and-override branch April 28, 2026 10:39
pabloinigoblasco added a commit that referenced this pull request Apr 29, 2026
…anner

The host plugin scanner (scanPluginDsos in pj_plugins/src/plugin_catalog.cpp)
walks the extension directory recursively, dlopens every file with a DSO
suffix, and probes each for the four standard plugin family symbols
(PJ_get_data_source_vtable, PJ_get_message_parser_vtable,
PJ_get_toolbox_vtable, PJ_get_dialog_vtable). Until this commit, every
per-distro inner shipped under dist/<distro>/ exported two of those
symbols (DataSource and Dialog) — so the host registered four phantom
streamers (one per supported ROS distro) on top of the legitimate proxy
entry point, and clicking on a distro that did not match the host's ROS
install produced "undefined symbol" failures because the host loaded the
inner directly with no rclcpp linkage available.

Fix the symbol partition:

- distro/src/data_stream_ros2.cpp: drop PJ_DATA_SOURCE_PLUGIN /
  PJ_DIALOG_PLUGIN macros (those name the standard symbols the scanner
  looks for) and emit two private getters instead —
  PJ_ros2_inner_get_data_source_vtable and
  PJ_ros2_inner_get_dialog_vtable. The inner still exports
  pj_plugin_abi_version (file-scope, weak), so the scanner's ABI check
  passes; manifest-family probing then fails cleanly and the inner is
  reported as a diagnostic, never as a plugin.

- proxy/src/data_stream_ros2.cpp: add PJ_get_dialog_vtable next to the
  existing PJ_get_data_source_vtable, both backed by a single call_once
  that loads the inner once and caches both vtables. The inner is
  resolved via the new private symbol names. As a side-effect, fix the
  inner-path computation to match the actual on-disk layout
  (dist/<distro>/inner.so, not the older flat dist/inner.so).

- docker/run-local.sh: drop the .pjmanifest.json sidecar from the
  bundle. plotjuggler_core's discovery is now embedded-manifest only
  (PR #74 in core); the sidecar is inspection-only and adds no value
  to the marketplace ZIP.

- CMakeLists.txt: link the proxy against pj_dialog_protocol (header-only
  C ABI) so it can declare PJ_dialog_vtable_t.

Net behavior: the marketplace extension exposes exactly one streamer
("ROS 2 Topic Subscriber") whose runtime distro selection is performed
by the proxy. Hosts without ROS see a single broken plugin with a clear
error message instead of four duplicates with cryptic dlopen failures.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant