Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ on:

jobs:
call-docker-build:
uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.9.9
uses: ycpss91255-docker/template/.github/workflows/build-worker.yaml@v0.9.11
with:
image_name: ros1_bridge
build_runtime: true

call-release:
needs: call-docker-build
if: startsWith(github.ref, 'refs/tags/')
uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.9.9
uses: ycpss91255-docker/template/.github/workflows/release-worker.yaml@v0.9.11
with:
archive_name_prefix: ros1_bridge
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,13 @@ COPY --chmod=0755 script/entrypoint.sh /entrypoint.sh
COPY --chmod=0755 script/ros_entrypoint.sh /ros_entrypoint.sh
COPY --chmod=0644 "${BRIDGE_FILE}" /bridge.yaml

ENTRYPOINT ["/entrypoint.sh"]
ENTRYPOINT ["/ros_entrypoint.sh"]
CMD ["bash"]

############################## runtime ##############################
FROM devel AS runtime

ENTRYPOINT ["/entrypoint.sh"]
CMD ["ros2", "run", "ros1_bridge", "parameter_bridge"]

############################## test (ephemeral) ##############################
Expand All @@ -87,6 +88,7 @@ COPY --from=lint-tools /usr/local/bin/hadolint /usr/local/bin/hadolint
COPY .hadolint.yaml /lint/.hadolint.yaml
COPY Dockerfile /lint/Dockerfile
COPY template/script/docker/build.sh template/script/docker/run.sh template/script/docker/exec.sh template/script/docker/stop.sh /lint/
COPY template/script/docker/_lib.sh template/script/docker/i18n.sh /lint/
COPY script/*.sh /lint/
RUN shellcheck -S warning /lint/*.sh
RUN cd /lint && hadolint Dockerfile
Expand Down
5 changes: 3 additions & 2 deletions doc/changelog/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]

### Changed
- Upgrade `template/` subtree to [v0.9.11](https://github.com/ycpss91255-docker/template/releases/tag/v0.9.11). Brings arm64-native CI (multi-arch matrix on `ubuntu-24.04-arm`), Jetson auto-detect for `[deploy] runtime` / `[build] network` (no more manual `network = host` workaround on Jetson), `_sanitize_lang` i18n, `_lib.sh` / `i18n.sh` dedupe, and `upgrade.sh` destructive fast-forward guards.
- Rebuild `devel` stage from `ros:foxy-ros-base-focal` (multi-arch) plus the ROS 1 snapshot apt repo instead of the amd64-only `osrf/ros:foxy-ros1-bridge`. Enables Jetson (arm64) support.
- `ENV ROS1_DISTRO=noetic` / `ENV ROS2_DISTRO=foxy` now baked into the image so downstream scripts can reference the distro names without hardcoding.
- Test stage lint target uses `COPY script/*.sh /lint/` (glob) to pick up new scripts automatically.
- Bridge YAML examples now document the full `parameter_bridge` schema: `bridge.yaml` ships `services_1_to_2` / `services_2_to_1` entries and an inline QoS block on `/scan`; `config/scan_bridge.yaml` and `config/release_bridge.yaml` set sensor-data QoS (BEST_EFFORT for image streams, RELIABLE for `camera_info`). READMEs in all four languages note the topic (ROS 2) vs service (ROS 1) `type` format asymmetry.
- **Split `devel` and `runtime` into separate stages (USER-VISIBLE BEHAVIOR CHANGE).** `devel` CMD is now `bash` — `./run.sh` drops into an interactive shell instead of auto-launching `parameter_bridge`. The new `runtime` stage (`FROM devel`) keeps `CMD ["ros2", "run", "ros1_bridge", "parameter_bridge"]` for production-style auto-bridge deployments. CI builds both (`build_runtime: true` in `main.yaml`). Note: `./run.sh runtime` does not yet work because the auto-generated `compose.yaml` does not emit a `runtime` service (tracked upstream in template); invoke runtime via direct `docker build --target runtime && docker run` until template provides this.
- **Split `devel` and `runtime` into separate stages (USER-VISIBLE BEHAVIOR CHANGE).** `devel` CMD is now `bash` — `./run.sh` drops into an interactive shell instead of auto-launching `parameter_bridge`. `devel` ENTRYPOINT is `/ros_entrypoint.sh` (sources ROS1+ROS2 env only, no `rosparam load`) so the shell is usable immediately. The new `runtime` stage (`FROM devel`) keeps `CMD ["ros2", "run", "ros1_bridge", "parameter_bridge"]` and switches ENTRYPOINT back to `/entrypoint.sh` (which does `rosparam load /bridge.yaml` before launch) for production-style auto-bridge deployments. CI builds both (`build_runtime: true` in `main.yaml`). Note: `./run.sh runtime` does not yet work because the auto-generated `compose.yaml` does not emit a `runtime` service (tracked upstream in template); invoke runtime via direct `docker build --target runtime && docker run` until template provides this.

### Added
- `script/ros_entrypoint.sh` — osrf-compatible entrypoint that only sources both ROS distros (no `rosparam load`), available at `/ros_entrypoint.sh` in the image. The existing `/entrypoint.sh` remains the default `ENTRYPOINT`.
- `script/ros_entrypoint.sh` — osrf-compatible entrypoint that only sources both ROS distros (no `rosparam load`), available at `/ros_entrypoint.sh` in the image. `devel` stage uses this as its `ENTRYPOINT`; `runtime` stage keeps `/entrypoint.sh` (with `rosparam load`).
- Smoke tests: `ROS1_DISTRO`/`ROS2_DISTRO` env vars, `/ros_entrypoint.sh` existence + ability to source both ROS envs + expose `ros2`.

### Removed
Expand Down
133 changes: 123 additions & 10 deletions template/.github/workflows/build-worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,83 @@ on:
type: boolean
default: true
description: "Whether to build runtime stage"
platforms:
required: false
type: string
default: "linux/amd64"
description: |
Comma-separated target platforms. Each runs as a parallel job on
its own native runner (linux/amd64 → ubuntu-latest, linux/arm64
→ ubuntu-24.04-arm), so arm64 builds avoid QEMU emulation
(which would take 30-60 min per run on an amd64 host) and stay
in the 5-15 min range.

Each matrix shard runs the full pipeline (test-tools build,
test stage smoke tests, devel stage, runtime stage) natively
for its platform.

Default "linux/amd64" preserves single-platform behavior —
existing downstream repos see no CI change until they opt in
by adding `platforms: linux/amd64,linux/arm64` in their
main.yaml call.

Supported values: linux/amd64, linux/arm64. Other values are
rejected by the compute-matrix step.

jobs:
docker-build:
# ────────────────────────────────────────────────────────────────
# Parse the comma-separated `platforms` input into a matrix
# definition. Each supported platform gets the right runner label
# (native arm64 runners are available for public repos under the
# ycpss91255-docker org) and a canonical HARDWARE build-arg value.
# ────────────────────────────────────────────────────────────────
compute-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.matrix }}
steps:
- id: set
env:
PLATFORMS: ${{ inputs.platforms }}
run: |
items=""
IFS=',' read -ra plats <<< "${PLATFORMS}"
for p in "${plats[@]}"; do
p="$(echo "${p}" | tr -d '[:space:]')"
case "${p}" in
linux/amd64)
items+='{"platform":"linux/amd64","runner":"ubuntu-latest","hardware":"x86_64"},'
;;
linux/arm64)
items+='{"platform":"linux/arm64","runner":"ubuntu-24.04-arm","hardware":"aarch64"},'
;;
"")
continue
;;
*)
echo "::error::Unsupported platform '${p}'. Supported: linux/amd64, linux/arm64"
exit 1
;;
esac
done
if [ -z "${items}" ]; then
echo "::error::No valid platforms found in '${PLATFORMS}'"
exit 1
fi
json="{\"include\":[${items%,}]}"
echo "matrix=${json}" >> "${GITHUB_OUTPUT}"

# ────────────────────────────────────────────────────────────────
# Per-platform build. Each shard runs on its native runner so
# arm64 doesn't pay QEMU emulation cost. Full test + devel +
# runtime pipeline runs per platform.
# ────────────────────────────────────────────────────────────────
build:
needs: compute-matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.compute-matrix.outputs.matrix) }}
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -43,9 +116,17 @@ jobs:
fi

- name: Set up Docker Buildx
# docker-container driver is required even on single-platform
# builds now: the downstream Dockerfile's `test` stage
# references `test-tools:local` via `COPY --from=`, and with
# docker-container the tag built in the previous buildx step
# stays in the same builder's internal store, which subsequent
# build-push-action steps can resolve. The legacy `docker`
# driver stores images in the host daemon, which isn't
# accessible from a matrix job's buildx container.
uses: docker/setup-buildx-action@v3
with:
driver: docker
driver: docker-container

- name: Generate .env
run: |
Expand All @@ -54,7 +135,7 @@ jobs:
USER_GROUP=ci
USER_UID=1000
USER_GID=1000
HARDWARE=x86_64
HARDWARE=${{ matrix.hardware }}
DOCKER_HUB_USER=ci
GPU_ENABLED=false
IMAGE_NAME=${{ inputs.image_name }}
Expand All @@ -63,38 +144,47 @@ jobs:
mkdir -p /tmp/workspace

- name: Build test-tools image
run: |
if [ -f template/dockerfile/Dockerfile.test-tools ]; then
docker build -t test-tools:local \
-f template/dockerfile/Dockerfile.test-tools .
fi
# Must use buildx (not plain `docker build`) so `test-tools:local`
# lands in buildx's internal image store — subsequent build
# steps on the same builder resolve `COPY --from=test-tools:local`
# from there. docker-container's builder container doesn't see
# host daemon images.
uses: docker/build-push-action@v6
with:
context: .
file: template/dockerfile/Dockerfile.test-tools
tags: test-tools:local
platforms: ${{ matrix.platform }}
push: false

- name: Build test stage (includes smoke tests)
uses: docker/build-push-action@v6
with:
context: .
target: test
platforms: ${{ matrix.platform }}
push: false
build-args: |
USER=ci
GROUP=ci
UID=1000
GID=1000
HARDWARE=x86_64
HARDWARE=${{ matrix.hardware }}
${{ inputs.build_args }}

- name: Build devel stage
uses: docker/build-push-action@v6
with:
context: .
target: devel
platforms: ${{ matrix.platform }}
push: false
build-args: |
USER=ci
GROUP=ci
UID=1000
GID=1000
HARDWARE=x86_64
HARDWARE=${{ matrix.hardware }}
${{ inputs.build_args }}

- name: Build runtime stage
Expand All @@ -103,10 +193,33 @@ jobs:
with:
context: .
target: runtime
platforms: ${{ matrix.platform }}
push: false
build-args: |
USER=ci
GROUP=ci
UID=1000
GID=1000
${{ inputs.build_args }}

# ────────────────────────────────────────────────────────────────
# Stable-name aggregator so downstream branch-protection rules
# keep working. Keeping this job's name as `docker-build`
# preserves the status-check context that public-ycpss91255-docker
# container repos currently require on their `main` branch
# (`call-docker-build / docker-build`). Matrix shards publish as
# `call-docker-build / build (linux/amd64)` etc. — those are the
# actual build runs; this one just gates on them.
# ────────────────────────────────────────────────────────────────
docker-build:
needs: build
if: always()
runs-on: ubuntu-latest
steps:
- name: Aggregate matrix result
run: |
result="${{ needs.build.result }}"
echo "Matrix build result: ${result}"
if [ "${result}" != "success" ]; then
exit 1
fi
2 changes: 1 addition & 1 deletion template/.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.9.9
v0.9.11
91 changes: 91 additions & 0 deletions template/doc/changelog/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,97 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v0.9.11] - 2026-04-24

### Fixed
- **`_lib.sh` fallback `_detect_lang` returned `"zh"` for `zh_TW` (issue
#103)** — a copy-paste typo in the fallback used when `i18n.sh` was
absent (the Dockerfile `/lint` stage). Fixed to `"zh-TW"`. The
follow-up `#104` dedupe below then REMOVED the fallback entirely; the
only remaining `_detect_lang` is in `i18n.sh`.

### Changed
- **`[build] network` now defaults to `auto` (issue #102)**. On Jetson
(detected via `/etc/nv_tegra_release`) setup.sh resolves `auto` to
`host`, so first-time `./build.sh` succeeds without the DNS failures
that Jetson's broken bridge NAT used to cause. Desktop hosts stay on
Docker's default bridge. Explicit `host` / `bridge` / `none` /
`default` still pass through unchanged; new `off` value for explicit
opt-out. New `_resolve_build_network` helper mirrors
`_resolve_runtime`'s Jetson-aware pattern.
- **`_detect_lang` deduplicated: single canonical definition in
`i18n.sh` (issue #104)**. Previously `build.sh` / `run.sh` /
`exec.sh` / `stop.sh` / `_lib.sh` each shipped an inline fallback
`_detect_lang` for when `i18n.sh` wasn't reachable (Dockerfile
`/lint` stage). That invited drift — see #103 where `_lib.sh`'s
copy had silently returned `zh` instead of `zh-TW` for months.
`Dockerfile.example`'s test stage now COPYs `_lib.sh` + `i18n.sh` +
`_tui_conf.sh` alongside `*.sh`; scripts look up `_lib.sh` in the
template layout OR as a sibling, with a clear error when neither
exists. Downstream repos using a custom Dockerfile (not based on
`Dockerfile.example`) need to mirror this COPY in their test stage.
- **`_sanitize_lang` warning now localises to the system `$LANG`**. v0.9.7
Agent A scoped this helper out of i18n; a user with `LANG=zh_TW.UTF-8`
who typed `--lang xxx` still saw an English WARNING. Now we re-detect
from the system env (can't trust `_LANG` — it holds the invalid input
the user just passed) and print the warning in zh-TW / zh-CN / ja
where applicable, falling back to English for other locales.

### Added
- **Coverage audit follow-up (+9 unit tests)**. Kcov run flagged four
small untested branches in `_lib.sh` and `_tui_conf.sh`; filling them
raised non-TUI coverage from 94.4% → 95.7%. New tests:
- `_lib_msg count` / `caps` translation keys exercised in all four
languages (previously only Files / Identity / etc. were asserted).
- `_mount_container_path` helper — four cases (plain /
with-mode / env-var-interpolated / no-colon fallback). The symmetric
`_mount_host_path` was already covered; the container-side parser
had zero unit tests.
- `_upsert_conf_value` "section not found" branch — appends a fresh
`[section]` header + key when called against a conf that doesn't
yet have that section.
- `_upsert_conf_value` "section present, key absent at EOF" branch —
appends the key to the last section when target key isn't there.
- `_write_setup_conf` final-section override flush — an override key
whose target is the LAST section in the template gets emitted
via the EOF-flush path (previously only the mid-file append branch
was asserted).
- `_write_setup_conf` removed_keys + flush interplay — ensures a key
listed in `removed_keys` does NOT reappear via the EOF flush.

TUI interactive flows (`_edit_section_*`) in `setup_tui.sh` remain
at ~17% — they require a dialog/whiptail stub framework to drive,
cost doesn't justify coverage-for-its-own-sake. `setup_tui.sh`
validators / I/O helpers are covered at unit level via `tui_spec`.

## [v0.9.10] - 2026-04-24

### Added
- **Multi-arch support in `build-worker.yaml`** — new `platforms` input
(default `"linux/amd64"`, accepts `"linux/amd64,linux/arm64"`). Each
requested platform runs as a parallel matrix shard on its own native
runner (amd64 → `ubuntu-latest`, arm64 → `ubuntu-24.04-arm`), so arm64
builds avoid QEMU emulation and stay in the 5-15 min range instead of
30-60 min. Full pipeline (test-tools → test stage smoke → devel →
runtime) runs natively per platform. Covers Jetson (Nano / Xavier /
Orin, all aarch64) and modern Raspberry Pi (4 / 5 on 64-bit OS) and
standard x86 hosts. 32-bit ARM (armv7/v6) intentionally unsupported —
no native runner exists and QEMU emulation would balloon CI time;
modern Pi defaults to 64-bit OS.

### Changed
- **`build-worker.yaml` now uses the `docker-container` buildx driver**
(was `docker`). Required for multi-arch builds. Side effect:
`test-tools:local` is built via `docker/build-push-action@v6` (not
plain `docker build`) so the tag lands in buildx's internal image
store, visible to the subsequent test-stage build's
`COPY --from=test-tools:local` on the same builder.
- **Matrix job names**: per-platform shards are called
`call-docker-build / build (linux/amd64)` etc. A stable-name
aggregator job `call-docker-build / docker-build` gates on all
shards — downstream `main` branch protection rules that require
`call-docker-build / docker-build` keep working without changes.

## [v0.9.9] - 2026-04-24

### Added
Expand Down
Loading
Loading