rosotacom-dev 2.5.dev21__tar.gz → 2.5.dev24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (345) hide show
  1. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/pr-merge-gate.yml +111 -2
  2. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/PKG-INFO +28 -2
  3. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/README.md +26 -0
  4. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/ci.md +69 -6
  5. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/pyproject.toml +1 -1
  6. rosotacom_dev-2.5.dev24/requirements.txt +1 -0
  7. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/cli.py +133 -3
  8. rosotacom_dev-2.5.dev24/src/rosotacom/image_cache.py +169 -0
  9. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/ros2docker.json +11 -3
  10. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/external.ros2docker.json +2 -2
  11. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/external.ros2docker.json +2 -2
  12. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external.ros2docker.json +1 -1
  13. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external.ros2docker.json +1 -1
  14. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external.ros2docker.json +1 -1
  15. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external.ros2docker.json +1 -1
  16. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ros2docker.json.example +11 -3
  17. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/PKG-INFO +28 -2
  18. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/SOURCES.txt +2 -0
  19. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/requires.txt +1 -1
  20. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/scm_file_list.json +2 -0
  21. rosotacom_dev-2.5.dev24/src/rosotacom_dev.egg-info/scm_version.json +8 -0
  22. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_packaged_resources.py +9 -1
  23. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_security_maintenance_config.py +8 -2
  24. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_workflow_contracts.py +23 -0
  25. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/conftest.py +23 -9
  26. rosotacom_dev-2.5.dev24/tests/unit/test_image_cache.py +262 -0
  27. rosotacom_dev-2.5.dev21/requirements.txt +0 -1
  28. rosotacom_dev-2.5.dev21/src/rosotacom_dev.egg-info/scm_version.json +0 -8
  29. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.devcontainer/devcontainer.json.template +0 -0
  30. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.devcontainer/re_generate_devcontainer_json.py +0 -0
  31. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/CODEOWNERS +0 -0
  32. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/documentation-audit.md +0 -0
  33. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/implementation-cleanup.md +0 -0
  34. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/quality-workflow.md +0 -0
  35. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/redesign.md +0 -0
  36. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/release-process.md +0 -0
  37. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/repository-diagnosis.md +0 -0
  38. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/test-ci-audit.md +0 -0
  39. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/ISSUE_TEMPLATE/work-item.md +0 -0
  40. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  41. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/dependabot.yml +0 -0
  42. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/benchmark-calibrate.yml +0 -0
  43. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/benchmark-gate.yml +0 -0
  44. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/dev-release.yml +0 -0
  45. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/image-scan.yml +0 -0
  46. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/nightly-e2e.yml +0 -0
  47. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/pr-lightweight.yml +0 -0
  48. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.github/workflows/release.yml +0 -0
  49. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/.pre-commit-config.yaml +0 -0
  50. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/AGENTS.md +0 -0
  51. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/CONCURRENCY.md +0 -0
  52. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/CONTRIBUTING.md +0 -0
  53. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/DEVELOPMENT_PRINCIPLES.md +0 -0
  54. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/LICENSE.txt +0 -0
  55. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/MANIFEST.in +0 -0
  56. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/budgets.jsonl +0 -0
  57. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/README.md +0 -0
  58. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/capacity-size-tight-cyclone.json +0 -0
  59. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/probe-loss-gop-tight-cyclone.json +0 -0
  60. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/probe-loss-nominal-cyclone.json +0 -0
  61. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/probe-loss-tight-cyclone.json +0 -0
  62. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/probe-loss-tight-fastdds.json +0 -0
  63. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/calibration/probe-loss-tight-zenoh.json +0 -0
  64. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/codecov.yml +0 -0
  65. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/deployment-configuration.md +0 -0
  66. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/benchmark-ab.md +0 -0
  67. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/benchmark-bag-as-load.md +0 -0
  68. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/ffmpeg-keyframes.md +0 -0
  69. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/README.md +0 -0
  70. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/cyclone-spdp-discovery-bursts.md +0 -0
  71. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/delay-alone-no-loss.md +0 -0
  72. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/head-of-line-irregularity.md +0 -0
  73. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/jitter-loss-bandwidth-latency.md +0 -0
  74. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/lighter-message-loss.md +0 -0
  75. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/findings/zero-loss-boundaries-18kb-20hz.md +0 -0
  76. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/forensics-report.md +0 -0
  77. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/geo-link-map.md +0 -0
  78. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/github-repository-settings.md +0 -0
  79. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/link-trace.md +0 -0
  80. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/owner-runbook.md +0 -0
  81. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/performance-bands.md +0 -0
  82. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/playbooks/README.md +0 -0
  83. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/quality-model.md +0 -0
  84. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release-notes/TEMPLATE.md +0 -0
  85. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release-notes/v2.1.md +0 -0
  86. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release-notes/v2.2.md +0 -0
  87. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release-notes/v2.3.md +0 -0
  88. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release-notes/v2.4.md +0 -0
  89. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/release.md +0 -0
  90. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0001-expectation-driven-test-suite.md +0 -0
  91. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0002-expectation-concepts.md +0 -0
  92. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0003-metric-backbone.md +0 -0
  93. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0004-network-profiles.md +0 -0
  94. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0005-benchmark-genres-and-ci.md +0 -0
  95. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0006-dynamic-qos.md +0 -0
  96. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/0007-regression-gate.md +0 -0
  97. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/rfcs/README.md +0 -0
  98. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/testing.md +0 -0
  99. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/docs/work-items.md +0 -0
  100. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/install.sh +0 -0
  101. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/justfile +0 -0
  102. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/media/control_center.png +0 -0
  103. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/media/echo_test.png +0 -0
  104. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/media/forensics-report-example.png +0 -0
  105. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/media/host.png +0 -0
  106. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/media/vehicle.png +0 -0
  107. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/rosotacom.yaml +0 -0
  108. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/run_session_in_container.py +0 -0
  109. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/session-configuration.md +0 -0
  110. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/setup.cfg +0 -0
  111. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/__init__.py +0 -0
  112. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/__main__.py +0 -0
  113. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/anonymize.py +0 -0
  114. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/bag_ground_truth.py +0 -0
  115. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/benched_set.py +0 -0
  116. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/benchmark.py +0 -0
  117. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/benchmark_delta.py +0 -0
  118. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/bundle_check.py +0 -0
  119. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/cli_benchmark.py +0 -0
  120. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/deployment.py +0 -0
  121. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/ffmpeg_packet.py +0 -0
  122. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/forensics.py +0 -0
  123. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/geomap.py +0 -0
  124. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/network_profiles.py +0 -0
  125. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/network_shaper.py +0 -0
  126. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/plots.py +0 -0
  127. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/py.typed +0 -0
  128. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/benched-set.yaml +0 -0
  129. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/.gitignore +0 -0
  130. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/README.md +0 -0
  131. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/__init__.py +0 -0
  132. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/bags/8_drop_reference/metadata.yaml +0 -0
  133. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/deployment.example.yaml +0 -0
  134. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/profiles.yaml +0 -0
  135. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/rosotacom.yaml +0 -0
  136. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scenarios/2_native_chatter/scenario-definition.yaml +0 -0
  137. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_a.sh +0 -0
  138. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/1_heartbeat/run_machine_b.sh +0 -0
  139. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_communication.sh +0 -0
  140. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/run_external.py +0 -0
  141. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_a/wait_for_echo_topic.sh +0 -0
  142. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_communication.sh +0 -0
  143. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/2_native_chatter/machine_b/run_external.py +0 -0
  144. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external.ros2docker.json +0 -0
  145. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/external_session.yaml +0 -0
  146. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/foxglove.json +0 -0
  147. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_communication.sh +0 -0
  148. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_a/run_external.py +0 -0
  149. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  150. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/costmap_1second/metadata.yaml +0 -0
  151. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/external.ros2docker.json +0 -0
  152. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_communication.sh +0 -0
  153. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/3_comp_occ_grid/machine_b/run_external.py +0 -0
  154. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external.ros2docker.json +0 -0
  155. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/external_session.yaml +0 -0
  156. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/foxglove.json +0 -0
  157. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_communication.sh +0 -0
  158. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_a/run_external.py +0 -0
  159. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/costmap_1second_0.mcap +0 -0
  160. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/costmap_1second/metadata.yaml +0 -0
  161. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/external.ros2docker.json +0 -0
  162. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_communication.sh +0 -0
  163. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/4_comp_occ_grid_zen/machine_b/run_external.py +0 -0
  164. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/external_session.yaml +0 -0
  165. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_communication.sh +0 -0
  166. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_a/run_external.py +0 -0
  167. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/external_session.yaml +0 -0
  168. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_communication.sh +0 -0
  169. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/5_sized_payload/machine_b/run_external.py +0 -0
  170. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/external_session.yaml +0 -0
  171. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_communication.sh +0 -0
  172. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_a/run_external.py +0 -0
  173. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/external_session.yaml +0 -0
  174. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_communication.sh +0 -0
  175. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/scripts/6_sized_payload_zen/machine_b/run_external.py +0 -0
  176. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/10_restamp/session-definition.yaml +0 -0
  177. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/11_trickle/session-definition.yaml +0 -0
  178. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/12_content_integrity/session-definition.yaml +0 -0
  179. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/13_link_latency/session-definition.yaml +0 -0
  180. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/14_remote_assist_anonymized/session-definition.yaml +0 -0
  181. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/15_remote_assist_anonymized_costmap/session-definition.yaml +0 -0
  182. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/16_remote_assist_anonymized_camera/session-definition.yaml +0 -0
  183. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/17_synthetic_camera_quality/session-definition.yaml +0 -0
  184. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/1_heartbeat/session-definition.yaml +0 -0
  185. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/1_heartbeat_status/session-definition.yaml +0 -0
  186. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/2_native_chatter/session-definition.yaml +0 -0
  187. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/3_comp_occ_grid/session-definition.yaml +0 -0
  188. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/4_comp_occ_grid_zen/session-definition.yaml +0 -0
  189. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/5_sized_payload/session-definition.yaml +0 -0
  190. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/6_sized_payload_zen/session-definition.yaml +0 -0
  191. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/7_latched_static/session-definition.yaml +0 -0
  192. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/8_drop/session-definition.yaml +0 -0
  193. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/9_throttle/session-definition.yaml +0 -0
  194. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/bench_1_1_capacity/session-definition.yaml +0 -0
  195. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/bench_1_2_load_sweep/session-definition.yaml +0 -0
  196. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/bench_1_3_ramp/session-definition.yaml +0 -0
  197. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/bench_1_4_recovery/session-definition.yaml +0 -0
  198. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/examples/sessions/bench_1_5_camera/session-definition.yaml +0 -0
  199. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/.vscode/c_cpp_properties.json +0 -0
  200. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/.vscode/settings.json +0 -0
  201. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_easy_mode.xml.template +0 -0
  202. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_initial_peer_list.xml.template +0 -0
  203. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v2.xml.template +0 -0
  204. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v3.xml.template +0 -0
  205. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v4.xml.template +0 -0
  206. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/archive/fastdds_v5.xml.template +0 -0
  207. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/create_zenoh_json5.py +0 -0
  208. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/cyclonedds_minimal.xml.template +0 -0
  209. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/cyclonedds_tuned.xml.template +0 -0
  210. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/fastdds_unicast.xml.template +0 -0
  211. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/get_ota_xml.py +0 -0
  212. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/utils.py +0 -0
  213. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ota_configs/zenoh.json5.template +0 -0
  214. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/.gitkeep +0 -0
  215. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/CMakeLists.txt +0 -0
  216. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/COLCON_IGNORE +0 -0
  217. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/LICENSE.txt +0 -0
  218. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/package.xml +0 -0
  219. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/src/hz_monitor.cpp +0 -0
  220. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_compressor.cpp +0 -0
  221. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_cpp/src/universal_decompressor.cpp +0 -0
  222. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/CMakeLists.txt +0 -0
  223. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/LICENSE.txt +0 -0
  224. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/CompressedData.msg +0 -0
  225. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/EchoHeartbeat.msg +0 -0
  226. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/OtaStamped.msg +0 -0
  227. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/msg/SizedPayload.msg +0 -0
  228. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_msgs/package.xml +0 -0
  229. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/LICENSE.txt +0 -0
  230. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/__init__.py +0 -0
  231. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/base_bridge_relay.py +0 -0
  232. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_in.py +0 -0
  233. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/bridge_out.py +0 -0
  234. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/heartbeat_echo.py +0 -0
  235. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/hz_monitor.py +0 -0
  236. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_bandwidth_reducer.py +0 -0
  237. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/image_pixel_capper.py +0 -0
  238. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/lanelet_saver.py +0 -0
  239. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/latch_relay.py +0 -0
  240. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/costmap_remapper.py +0 -0
  241. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/marker_remapper.py +0 -0
  242. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/speed_test.py +0 -0
  243. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/tf_remapper.py +0 -0
  244. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/traffic_monitor.py +0 -0
  245. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/legacy/wifi_network_analysis_1.py +0 -0
  246. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_bytes.py +0 -0
  247. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/link_trace.py +0 -0
  248. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/local_global_frame_bridge.py +0 -0
  249. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/localization_map_split_reduce_saver.py +0 -0
  250. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/msg_transform.py +0 -0
  251. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pair_management.py +0 -0
  252. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/pub_sub_pair.py +0 -0
  253. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/qos.py +0 -0
  254. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_in.py +0 -0
  255. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/relay_out.py +0 -0
  256. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/restamp.py +0 -0
  257. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/sized_publisher.py +0 -0
  258. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency.py +0 -0
  259. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/stage_latency_core.py +0 -0
  260. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview.py +0 -0
  261. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/status_overview_core.py +0 -0
  262. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/tf_map_to_cart_saver.py +0 -0
  263. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_monitor.py +0 -0
  264. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/topic_resolution.py +0 -0
  265. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/trickle.py +0 -0
  266. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_compressor.py +0 -0
  267. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_decompressor.py +0 -0
  268. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_unwrapper.py +0 -0
  269. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/com_py/universal_ota_wrapper.py +0 -0
  270. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/package.xml +0 -0
  271. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/resource/com_py +0 -0
  272. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/setup.cfg +0 -0
  273. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/setup.py +0 -0
  274. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/test/test_copyright.py +0 -0
  275. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/test/test_flake8.py +0 -0
  276. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/ros2src/com_py/test/test_pep257.py +0 -0
  277. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/content/base/session_plugin_base.yaml +0 -0
  278. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/content/network/up_connectivity.py +0 -0
  279. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/content/network/wifi_network_analysis_2.py +0 -0
  280. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/anonymize_bag.py +0 -0
  281. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/catmux_log_setup.sh +0 -0
  282. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/create_session_yaml.py +0 -0
  283. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/generate_session_files.py +0 -0
  284. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/run_session.py +0 -0
  285. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/run_session_in_external_terminal.py +0 -0
  286. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/resources/ws/session/creation/strip_ansi.py +0 -0
  287. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/status_eval.py +0 -0
  288. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/stream_stats.py +0 -0
  289. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/trace_profiles.py +0 -0
  290. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/transit.py +0 -0
  291. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom/video_quality.py +0 -0
  292. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/dependency_links.txt +0 -0
  293. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/entry_points.txt +0 -0
  294. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/src/rosotacom_dev.egg-info/top_level.txt +0 -0
  295. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/stop_session_in_container.py +0 -0
  296. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/terminology.md +0 -0
  297. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_benched_set_registry.py +0 -0
  298. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_findings.py +0 -0
  299. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_markdown_links.py +0 -0
  300. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_ota_source_bundle.py +0 -0
  301. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_package_metadata.py +0 -0
  302. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_profile_covers_generated_commands.py +0 -0
  303. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_pytest_policy.py +0 -0
  304. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_readme_examples.py +0 -0
  305. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/contract/test_session_configuration_doc.py +0 -0
  306. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_anonymize_e2e.py +0 -0
  307. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_benchmark_ab.py +0 -0
  308. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_benchmark_capacity.py +0 -0
  309. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_benchmark_replay.py +0 -0
  310. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_parallel_smoke.py +0 -0
  311. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_smoke.py +0 -0
  312. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_timeline_stepping.py +0 -0
  313. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/e2e/test_video_quality_e2e.py +0 -0
  314. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/generate_rmw_matrix.py +0 -0
  315. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-local_zenoh-ros2dds-ota/session-definition.yaml +0 -0
  316. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota/session-definition.yaml +0 -0
  317. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/rmw_matrix/1_heartbeat_cyclone-ota-tuned/session-definition.yaml +0 -0
  318. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/rmw_matrix/1_heartbeat_fastdds/session-definition.yaml +0 -0
  319. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/sessions/rmw_matrix/1_heartbeat_zen-endpoints/session-definition.yaml +0 -0
  320. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_anonymize.py +0 -0
  321. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_bag_ground_truth.py +0 -0
  322. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_benched_set.py +0 -0
  323. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_benchmark.py +0 -0
  324. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_benchmark_delta.py +0 -0
  325. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_bundle_check.py +0 -0
  326. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_cli.py +0 -0
  327. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_cli_benchmark.py +0 -0
  328. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_concurrency.py +0 -0
  329. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_deployment.py +0 -0
  330. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_ffmpeg_packet.py +0 -0
  331. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_forensics.py +0 -0
  332. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_geomap.py +0 -0
  333. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_link_bytes.py +0 -0
  334. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_link_trace.py +0 -0
  335. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_network_profiles.py +0 -0
  336. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_network_shaper.py +0 -0
  337. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_ota_xml.py +0 -0
  338. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_plots.py +0 -0
  339. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_stage_latency.py +0 -0
  340. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_status_eval.py +0 -0
  341. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_status_overview.py +0 -0
  342. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_stream_stats.py +0 -0
  343. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_trace_profiles.py +0 -0
  344. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_transit.py +0 -0
  345. {rosotacom_dev-2.5.dev21 → rosotacom_dev-2.5.dev24}/tests/unit/test_video_quality.py +0 -0
@@ -192,6 +192,96 @@ jobs:
192
192
  fi
193
193
  echo "All preflight checks passed"
194
194
 
195
+ image:
196
+ # Publish the images the e2e suite builds, once per set of build inputs,
197
+ # instead of rebuilding them in each of thirteen jobs. #236 measured that
198
+ # build at 154s per job: 57s pulling the multi-GB ROS base from Docker Hub,
199
+ # 40s exporting layers, 47s of apt and pip. A pull from GHCR — co-located
200
+ # with the runners, no Hub rate limit, one transfer instead of a transfer
201
+ # plus a build — replaces all of it, and takes twelve of the thirteen Hub
202
+ # pulls off the merge gate's critical path.
203
+ #
204
+ # The tag is a hash of everything that decides the image (base digest,
205
+ # package lists, Dockerfile, entrypoint, ros2docker version, build UID):
206
+ # `rosotacom image references` derives it, nothing here chooses it. A
207
+ # build-input change is therefore a different tag, so "the published image
208
+ # is stale" is not a state this can reach — which is the objection #226
209
+ # raised against caching the image inline.
210
+ #
211
+ # Runs beside preflight rather than after it: on a hit it inspects two
212
+ # manifests and stops, so it is never on the critical path.
213
+ name: image
214
+ runs-on: ubuntu-latest
215
+ timeout-minutes: 60
216
+ if: github.event_name != 'pull_request' || github.event.pull_request.draft == false
217
+ permissions:
218
+ contents: read
219
+ packages: write
220
+ outputs:
221
+ repository: ${{ steps.publish.outputs.repository }}
222
+
223
+ steps:
224
+ - name: Check out repository
225
+ uses: actions/checkout@v6
226
+
227
+ - name: Set up Python
228
+ uses: actions/setup-python@v6
229
+ with:
230
+ python-version: "3.12"
231
+ cache: pip
232
+
233
+ - name: Install just
234
+ run: |
235
+ sudo apt-get update
236
+ sudo apt-get install -y just
237
+
238
+ - name: Set up local environment
239
+ run: just setup
240
+
241
+ - name: Log in to GHCR
242
+ run: echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin
243
+
244
+ - name: Publish every image the e2e suite builds
245
+ id: publish
246
+ run: |
247
+ set -euo pipefail
248
+ repository="ghcr.io/$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')/rosotacom-e2e"
249
+ # The packaged example project, because that is the one the e2e tests
250
+ # copy. The repository's own rosotacom.yaml points at profiles and
251
+ # artifacts that live beside the checkout in the private harness, so it
252
+ # does not resolve here.
253
+ project="$(.venv/bin/rosotacom resources path examples)/rosotacom.yaml"
254
+ .venv/bin/rosotacom image references --project "$project" --repository "$repository" > references.txt
255
+ cat references.txt
256
+
257
+ while read -r reference; do
258
+ if docker manifest inspect "$reference" >/dev/null 2>&1; then
259
+ echo "present: $reference"
260
+ continue
261
+ fi
262
+ echo "missing, building: $reference"
263
+ .venv/bin/rosotacom image build --project "$project" --reference "$reference"
264
+ docker push "$reference" || echo "::warning::could not push $reference"
265
+ done < references.txt
266
+
267
+ # Report the repository only when every reference resolves. A
268
+ # read-only GITHUB_TOKEN (a fork PR, Dependabot) can log in and pull
269
+ # but not push, and the slices must then build as they always did
270
+ # rather than fail on a pull that was never going to work. Probed
271
+ # rather than predicted from the event, so it stays right when
272
+ # GitHub's token rules change.
273
+ available=true
274
+ while read -r reference; do
275
+ docker manifest inspect "$reference" >/dev/null 2>&1 || available=false
276
+ done < references.txt
277
+
278
+ if [[ "$available" == "true" ]]; then
279
+ echo "repository=$repository" >> "$GITHUB_OUTPUT"
280
+ else
281
+ echo "repository=" >> "$GITHUB_OUTPUT"
282
+ echo "::warning::published images unavailable; each e2e slice will build its own"
283
+ fi
284
+
195
285
  e2e:
196
286
  # One matrix job, not six near-identical copies. The copies were how
197
287
  # `e2e-media` ended up listed in ci-success's `needs` with no check on its
@@ -210,8 +300,11 @@ jobs:
210
300
  name: e2e-${{ matrix.slice }}
211
301
  runs-on: ubuntu-latest
212
302
  timeout-minutes: 90
213
- needs: preflight-success
214
- if: always() && !cancelled() && needs.preflight-success.result == 'success'
303
+ needs: [preflight-success, image]
304
+ if: always() && !cancelled() && needs.preflight-success.result == 'success' && needs.image.result == 'success'
305
+ permissions:
306
+ contents: read
307
+ packages: read
215
308
  strategy:
216
309
  fail-fast: false
217
310
  matrix:
@@ -240,7 +333,18 @@ jobs:
240
333
  - name: Show Docker environment
241
334
  run: docker info
242
335
 
336
+ - name: Log in to GHCR
337
+ if: needs.image.outputs.repository != ''
338
+ run: echo '${{ secrets.GITHUB_TOKEN }}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin
339
+
243
340
  - name: Run E2E slice
341
+ env:
342
+ # Empty when the `image` job could not publish, and then rosotacom
343
+ # builds exactly as before. Set, and each image build becomes a pull
344
+ # of the tag this tree's own build inputs hash to — rosotacom derives
345
+ # that tag itself and never accepts one, so it cannot be pointed at an
346
+ # image built from something else.
347
+ ROSOTACOM_IMAGE_CACHE: ${{ needs.image.outputs.repository }}
244
348
  run: just test-e2e-slice ${{ matrix.slice }}
245
349
 
246
350
  - name: Upload session artifacts
@@ -261,6 +365,7 @@ jobs:
261
365
  - merge-lightweight
262
366
  - package
263
367
  - preflight-success
368
+ - image
264
369
  - e2e
265
370
  if: always() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false)
266
371
 
@@ -291,6 +396,10 @@ jobs:
291
396
  echo "preflight-success failed or was skipped: ${{ needs.preflight-success.result }}"
292
397
  exit 1
293
398
  fi
399
+ if [[ "${{ needs.image.result }}" != "success" ]]; then
400
+ echo "image failed or was skipped: ${{ needs.image.result }}"
401
+ exit 1
402
+ fi
294
403
  if [[ "${{ needs.e2e.result }}" != "success" ]]; then
295
404
  echo "one or more e2e slices failed or were skipped: ${{ needs.e2e.result }}"
296
405
  exit 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rosotacom-dev
3
- Version: 2.5.dev21
3
+ Version: 2.5.dev24
4
4
  Summary: Host CLI for ROS OTA communication sessions.
5
5
  Author: develNor
6
6
  License-Expression: BSD-3-Clause
@@ -27,7 +27,7 @@ Requires-Dist: argcomplete<4,>=3.6
27
27
  Requires-Dist: mcap<2,>=1.4
28
28
  Requires-Dist: Pillow<13,>=10
29
29
  Requires-Dist: PyYAML>=6
30
- Requires-Dist: ros2docker<0.2,>=0.1.4
30
+ Requires-Dist: ros2docker<0.2,>=0.1.5.dev8
31
31
  Provides-Extra: dev
32
32
  Requires-Dist: build>=1.2; extra == "dev"
33
33
  Requires-Dist: check-wheel-contents>=0.6; extra == "dev"
@@ -244,6 +244,32 @@ example to bake `com_msgs` into its own container image. Ask for the resource by
244
244
  name rather than reconstructing a path: the names are part of the CLI contract,
245
245
  the layout behind them is not.
246
246
 
247
+ ### Reusing an image instead of rebuilding it
248
+
249
+ Somewhere that starts many rosotacom sessions from the same tree — CI above all
250
+ — can build each image once, publish it, and have every later run pull it:
251
+
252
+ ```bash
253
+ # What this project's images are called: <repository>:<hash of the build inputs>
254
+ rosotacom image references --repository ghcr.io/owner/rosotacom-e2e
255
+
256
+ # Build the one a reference names, then push it
257
+ rosotacom image build --reference ghcr.io/owner/rosotacom-e2e:<hash>
258
+ docker push ghcr.io/owner/rosotacom-e2e:<hash>
259
+
260
+ # Adopt published images anywhere rosotacom would otherwise build
261
+ export ROSOTACOM_IMAGE_CACHE=ghcr.io/owner/rosotacom-e2e
262
+ ```
263
+
264
+ The tag is a hash of everything that decides the image — base image and its
265
+ pinned digest, apt and pip packages, the Dockerfile and entrypoint ros2docker
266
+ stages, the build UID — so a build-input change is a different tag and a
267
+ published image can never be stale for the tree that adopts it. Nothing accepts
268
+ a reference: rosotacom derives the one its own inputs allow and pulls exactly
269
+ that, and fails rather than quietly rebuilding if it is not there. With
270
+ `ROSOTACOM_IMAGE_CACHE` unset, nothing changes. See
271
+ [docs/ci.md](docs/ci.md) for how the merge gate uses this.
272
+
247
273
  See the [example project README](src/rosotacom/resources/examples/README.md)
248
274
  for the copyable example layout, the
249
275
  [session configuration reference](session-configuration.md) for the session
@@ -199,6 +199,32 @@ example to bake `com_msgs` into its own container image. Ask for the resource by
199
199
  name rather than reconstructing a path: the names are part of the CLI contract,
200
200
  the layout behind them is not.
201
201
 
202
+ ### Reusing an image instead of rebuilding it
203
+
204
+ Somewhere that starts many rosotacom sessions from the same tree — CI above all
205
+ — can build each image once, publish it, and have every later run pull it:
206
+
207
+ ```bash
208
+ # What this project's images are called: <repository>:<hash of the build inputs>
209
+ rosotacom image references --repository ghcr.io/owner/rosotacom-e2e
210
+
211
+ # Build the one a reference names, then push it
212
+ rosotacom image build --reference ghcr.io/owner/rosotacom-e2e:<hash>
213
+ docker push ghcr.io/owner/rosotacom-e2e:<hash>
214
+
215
+ # Adopt published images anywhere rosotacom would otherwise build
216
+ export ROSOTACOM_IMAGE_CACHE=ghcr.io/owner/rosotacom-e2e
217
+ ```
218
+
219
+ The tag is a hash of everything that decides the image — base image and its
220
+ pinned digest, apt and pip packages, the Dockerfile and entrypoint ros2docker
221
+ stages, the build UID — so a build-input change is a different tag and a
222
+ published image can never be stale for the tree that adopts it. Nothing accepts
223
+ a reference: rosotacom derives the one its own inputs allow and pulls exactly
224
+ that, and fails rather than quietly rebuilding if it is not there. With
225
+ `ROSOTACOM_IMAGE_CACHE` unset, nothing changes. See
226
+ [docs/ci.md](docs/ci.md) for how the merge gate uses this.
227
+
202
228
  See the [example project README](src/rosotacom/resources/examples/README.md)
203
229
  for the copyable example layout, the
204
230
  [session configuration reference](session-configuration.md) for the session
@@ -83,6 +83,63 @@ config, catmux pane logs, Docker logs when available, and the smoke verification
83
83
  log under `session-instances/`; collect that directory as the first debugging
84
84
  artifact when an E2E job fails.
85
85
 
86
+ ### The e2e image is published once, not rebuilt per job
87
+
88
+ Every e2e job used to build the project image from scratch. #236 measured that
89
+ at 154s per job — 57s pulling the multi-GB ROS base from Docker Hub, 40s
90
+ exporting layers, 47s of apt and pip — which at thirteen slices is 33 minutes of
91
+ runner time and thirteen Hub pulls on the critical path of every merge gate.
92
+
93
+ The `image` job now publishes those images to
94
+ `ghcr.io/<owner>/rosotacom-e2e` and each slice pulls instead of building. GHCR
95
+ is co-located with the runners and has no Hub rate limit, so this is one
96
+ transfer where there used to be a transfer plus a build. Measured on run
97
+ 31678648935: **154s → 70s per job**, and the slowest slice's pytest run fell
98
+ from 8m19s to 6m50s.
99
+
100
+ The tag is the whole safety argument. It is a SHA-256 of everything that decides
101
+ what the image contains — the `docker build` command ros2docker renders from a
102
+ config (base image and its pinned digest, `APT_PACKAGES`, `PIP_PACKAGES`, the
103
+ build UID) and every file in the context it stages (Dockerfile, entrypoint, any
104
+ baked packages). Change any of them and the name changes, so "the published
105
+ image is stale" is not a state this can reach, which is the objection #226
106
+ raised against caching the image inline.
107
+
108
+ Nothing ever *accepts* a reference. `rosotacom image references` derives them;
109
+ the publisher builds and pushes only what is missing, and only under the name
110
+ its own inputs hash to (`rosotacom image build --reference`). A consumer is told
111
+ a repository, in `ROSOTACOM_IMAGE_CACHE`, and derives the tag itself:
112
+
113
+ ```bash
114
+ # What this project's build inputs are called
115
+ rosotacom image references --repository ghcr.io/owner/rosotacom-e2e
116
+
117
+ # Adopt them instead of building, anywhere rosotacom would build
118
+ ROSOTACOM_IMAGE_CACHE=ghcr.io/owner/rosotacom-e2e just test-e2e-slice heartbeat
119
+ ```
120
+
121
+ With the variable unset, nothing changes: rosotacom builds as it always did. Set
122
+ and the image absent, the job fails rather than falling back — a miss means the
123
+ publisher did not run or did not agree with this tree, and a silent rebuild
124
+ would hide both while costing exactly what this removes. The one place that
125
+ decision is made is the `image` job, which reports its repository only after
126
+ confirming every reference resolves; a read-only `GITHUB_TOKEN` (a fork PR,
127
+ Dependabot) therefore leaves the slices building as before.
128
+
129
+ Two properties are worth keeping in mind when changing this:
130
+
131
+ - **Publisher and consumer read one list.** `2_native_chatter` builds a second
132
+ image from a different package list, so `rosotacom image references` walks the
133
+ project config *and* every scenario application. Publishing only the project
134
+ image would leave that slice adopting something nobody published.
135
+ - **The release and nightly lanes still build from scratch**, as does the
136
+ nightly `image-scan`. That is deliberate: they are what would notice if a
137
+ published image and a fresh build ever stopped agreeing.
138
+
139
+ Content-addressed tags accumulate in the GHCR package — one per distinct set of
140
+ build inputs, so roughly one per change to the base digest or package lists.
141
+ Nothing prunes them yet.
142
+
86
143
  ### Balancing the e2e slices
87
144
 
88
145
  A slice is not its own pytest invocation. `just test-e2e-slice <name>` runs the
@@ -102,18 +159,24 @@ with a usage error, and a test owned twice fails
102
159
 
103
160
  The costs are warm pytest `call` durations — medians over seven merge-gate runs
104
161
  of 2026-08-11/12. On top of them each job pays a fixed
105
- `RUNNER_SETUP_SECONDS + IMAGE_BUILD_SECONDS` (3m29s: 55s of runner setup, then the
106
- project image built inside whichever test runs first). Because that constant is
107
- per job and not per test, balancing warm costs balances wall clock.
162
+ `RUNNER_SETUP_SECONDS + IMAGE_BUILD_SECONDS` (2m05s: 55s of runner setup, then
163
+ the project image obtained inside whichever test runs first). Because that
164
+ constant is per job and not per test, balancing warm costs balances wall clock.
165
+
166
+ `IMAGE_BUILD_SECONDS` models the merge gate, where that image is pulled rather
167
+ than built (see above); the release and nightly lanes still build and pay the
168
+ older 154s. One slice does not currently reproduce its recorded split: `media`
169
+ spends about 24s more than its two costs predict, and it is the critical path,
170
+ so it is the first thing to remeasure.
108
171
 
109
172
  ### How many slices
110
173
 
111
174
  `floor_seconds()` is `fixed + slowest single test` — the fastest any partition
112
175
  of this suite could be, because one job has to run that test and pays the fixed
113
- cost like every other. It is currently **7m56s**, of which 2m34s is the image
114
- build (#236 is about removing it).
176
+ cost like every other. It is currently **6m32s**, of which 1m10s is the image
177
+ (7m56s before #236 published it instead of rebuilding it per job).
115
178
 
116
- Thirteen slices sit at 8m38s, 1.09x the floor. That is the point of choosing N
179
+ Thirteen slices sit at 7m14s, 1.11x the floor. That is the point of choosing N
117
180
  from the numbers rather than from taste: six balanced slices were 13m37s,
118
181
  twelve reach the floor, and past twelve every extra job is pure cost. The
119
182
  contract test asserts distance to the floor rather than the spread between
@@ -19,7 +19,7 @@ dependencies = [
19
19
  "mcap>=1.4,<2",
20
20
  "Pillow>=10,<13",
21
21
  "PyYAML>=6",
22
- "ros2docker>=0.1.4,<0.2",
22
+ "ros2docker>=0.1.5.dev8,<0.2",
23
23
  ]
24
24
  classifiers = [
25
25
  "Development Status :: 3 - Alpha",
@@ -0,0 +1 @@
1
+ ros2docker==0.1.5.dev8
@@ -55,6 +55,15 @@ from .deployment import (
55
55
  parse_assignments,
56
56
  resolve_peer_bindings,
57
57
  )
58
+ from .image_cache import (
59
+ IMAGE_CACHE_ENV,
60
+ ImageCacheError,
61
+ adopt_cached_image,
62
+ cache_repository,
63
+ cached_reference,
64
+ image_fingerprint,
65
+ reference_tag,
66
+ )
58
67
 
59
68
  anonymize_lib = importlib.import_module("rosotacom.anonymize")
60
69
 
@@ -597,6 +606,53 @@ def _scoped_image_name_from_base(base: str, suffix: str) -> str:
597
606
  return f"{base}-{suffix}"
598
607
 
599
608
 
609
+ def _build_image(config_file: Path, override: Mapping[str, object]) -> None:
610
+ """Build the image these inputs describe, or adopt the published one.
611
+
612
+ With ``ROSOTACOM_IMAGE_CACHE`` unset this is exactly ``ros2docker build``.
613
+ With it set, the build is replaced by a pull of ``<repository>:<hash of
614
+ these build inputs>`` — see `rosotacom.image_cache` for why that name is
615
+ the whole safety argument.
616
+
617
+ The fingerprint deliberately ignores ``override``: it carries the container
618
+ name, the scoped image name, and sometimes isolated-network run args, none
619
+ of which reach ``docker build``. Feeding them in would give the same image
620
+ a different name per session.
621
+ """
622
+ repository = cache_repository()
623
+ if repository is None:
624
+ ros2docker_build(config_file=config_file, override=override)
625
+ return
626
+
627
+ image_name = str(override["image_name"])
628
+ reference = cached_reference(repository, image_fingerprint(config_file))
629
+ print(f"Adopting published image {reference} as {image_name} (build skipped)")
630
+ adopt_cached_image(reference, image_name)
631
+
632
+
633
+ def _project_build_inputs(runtime: RuntimeConfig) -> list[tuple[str, Path]]:
634
+ """Every ``(label, ros2docker config)`` this project can build an image from.
635
+
636
+ The project image is one of several: each scenario application names its own
637
+ config, and `2_native_chatter` builds a second image from a different
638
+ package list. Publishing only the project image would leave the adopt path
639
+ strict about an image nobody published, so the publisher and the consumer
640
+ read the same list.
641
+ """
642
+ inputs: list[tuple[str, Path]] = [("project", runtime.ros2docker_config)]
643
+ seen = {runtime.ros2docker_config.resolve()}
644
+ for scenario_name in _scenario_names(runtime):
645
+ definition = _load_scenario_definition(_resolve_scenario(scenario_name, runtime))
646
+ for identity, applications in sorted(definition.applications.items()):
647
+ for application in applications:
648
+ config = application.ros2docker_config.resolve()
649
+ if config in seen:
650
+ continue
651
+ seen.add(config)
652
+ inputs.append((f"{scenario_name}/{identity}/{application.name}", config))
653
+ return inputs
654
+
655
+
600
656
  def _instance_name_token(instance_id: str) -> str:
601
657
  # Underscore-free so the instance component of a container name can be split
602
658
  # off unambiguously (the surrounding name parts are underscore-separated).
@@ -4911,7 +4967,7 @@ def start_session(args: argparse.Namespace) -> str:
4911
4967
 
4912
4968
  common_override = {"container_name": container_name, "image_name": image_name}
4913
4969
  if mode == "detached":
4914
- ros2docker_build(config_file=runtime.ros2docker_config, override=common_override)
4970
+ _build_image(runtime.ros2docker_config, common_override)
4915
4971
  ros2docker_run(
4916
4972
  config_file=runtime.ros2docker_config,
4917
4973
  override={**common_override, "run_type": "up", **run_override},
@@ -4926,7 +4982,7 @@ def start_session(args: argparse.Namespace) -> str:
4926
4982
  )
4927
4983
  _write_docker_log(container_name, instance, identity)
4928
4984
  else:
4929
- ros2docker_build(config_file=runtime.ros2docker_config, override=common_override)
4985
+ _build_image(runtime.ros2docker_config, common_override)
4930
4986
  ros2docker_run(
4931
4987
  config_file=runtime.ros2docker_config,
4932
4988
  override={
@@ -5258,7 +5314,7 @@ def run_scenario_application(args: argparse.Namespace) -> int:
5258
5314
  network_name,
5259
5315
  getattr(args, "network_ip", None),
5260
5316
  )
5261
- ros2docker_build(config_file=application.ros2docker_config, override=override)
5317
+ _build_image(application.ros2docker_config, override)
5262
5318
  ros2docker_run(config_file=application.ros2docker_config, override=override)
5263
5319
  return 0
5264
5320
 
@@ -5332,6 +5388,55 @@ def examples_create_command(args: argparse.Namespace) -> int:
5332
5388
  return 0
5333
5389
 
5334
5390
 
5391
+ def image_references_command(args: argparse.Namespace) -> int:
5392
+ """Print the content-addressed reference of every image this project builds.
5393
+
5394
+ The publisher's whole job is this list: build and push what is missing. It
5395
+ never chooses a tag, so it cannot publish an image under a name that
5396
+ describes different inputs.
5397
+ """
5398
+ _require_ros2docker()
5399
+ runtime = _load_runtime_config(args)
5400
+ repository = args.repository or cache_repository()
5401
+ if not repository:
5402
+ raise RuntimeError(f"Pass --repository or set {IMAGE_CACHE_ENV} to the repository that holds published images.")
5403
+
5404
+ printed: set[str] = set()
5405
+ for _label, config in _project_build_inputs(runtime):
5406
+ reference = cached_reference(repository, image_fingerprint(config))
5407
+ # Two scenario applications with the same build inputs are one image.
5408
+ if reference in printed:
5409
+ continue
5410
+ printed.add(reference)
5411
+ print(reference)
5412
+ return 0
5413
+
5414
+
5415
+ def image_build_command(args: argparse.Namespace) -> int:
5416
+ """Build the image whose build inputs hash to the tag in ``--reference``.
5417
+
5418
+ Taking a reference rather than a config keeps the publisher honest: the only
5419
+ thing it can push is an image whose inputs actually hash to the name it is
5420
+ pushing under.
5421
+ """
5422
+ _require_ros2docker()
5423
+ runtime = _load_runtime_config(args)
5424
+ reference = args.reference
5425
+ wanted = reference_tag(reference)
5426
+
5427
+ for label, config in _project_build_inputs(runtime):
5428
+ if image_fingerprint(config) != wanted:
5429
+ continue
5430
+ print(f"Building {label} ({config}) as {reference}")
5431
+ ros2docker_build(config_file=config, override={"image_name": reference})
5432
+ return 0
5433
+
5434
+ raise ImageCacheError(
5435
+ f"No image this project builds has the fingerprint {wanted}. Take the reference from "
5436
+ f"`rosotacom image references`; it changes whenever the build inputs do."
5437
+ )
5438
+
5439
+
5335
5440
  def resources_path_command(args: argparse.Namespace) -> int:
5336
5441
  """Print the host path of a packaged resource, for external tooling."""
5337
5442
 
@@ -8648,6 +8753,7 @@ TOP_LEVEL_COMMANDS = {
8648
8753
  "list-sessions",
8649
8754
  "scenario",
8650
8755
  "examples",
8756
+ "image",
8651
8757
  "resources",
8652
8758
  "config",
8653
8759
  "bundle",
@@ -9376,6 +9482,30 @@ def main(argv: list[str] | None = None) -> int:
9376
9482
  examples_create_parser.add_argument("--force", action="store_true", help="Replace the target if it exists.")
9377
9483
  examples_create_parser.set_defaults(func=examples_create_command)
9378
9484
 
9485
+ image_parser = subparsers.add_parser("image", help="Publish and consume the project's container images.")
9486
+ image_subparsers = image_parser.add_subparsers(dest="image_action", required=True)
9487
+
9488
+ image_references_parser = image_subparsers.add_parser(
9489
+ "references", help="Print the content-addressed reference of every image this project builds."
9490
+ )
9491
+ _add_common_config_args(image_references_parser)
9492
+ image_references_parser.add_argument(
9493
+ "--repository",
9494
+ help=f"Repository holding published images, e.g. ghcr.io/owner/name. Defaults to {IMAGE_CACHE_ENV}.",
9495
+ )
9496
+ image_references_parser.set_defaults(func=image_references_command)
9497
+
9498
+ image_build_parser = image_subparsers.add_parser(
9499
+ "build", help="Build the image whose build inputs hash to the tag in --reference."
9500
+ )
9501
+ _add_common_config_args(image_build_parser)
9502
+ image_build_parser.add_argument(
9503
+ "--reference",
9504
+ required=True,
9505
+ help="Full reference from `rosotacom image references`. Its tag decides which image is built.",
9506
+ )
9507
+ image_build_parser.set_defaults(func=image_build_command)
9508
+
9379
9509
  resources_parser = subparsers.add_parser("resources", help="Locate resources packaged with rosotacom.")
9380
9510
  resources_subparsers = resources_parser.add_subparsers(dest="resources_action", required=True)
9381
9511
  resources_path_parser = resources_subparsers.add_parser("path", help="Print the host path of a packaged resource.")