From ea47a1808248d6b55364451b833293677a24d570 Mon Sep 17 00:00:00 2001 From: matlabbe Date: Mon, 21 Sep 2026 08:59:05 -0700 Subject: [PATCH] CI: use ubuntu arm runners instead of QEMU --- .github/workflows/android.yml | 78 ++++++++++ .github/workflows/docker-ros.yml | 227 +++++++++++++++++++++++++++++ .github/workflows/docker.yml | 243 ------------------------------- README.md | 3 +- docker/README.md | 33 +++++ 5 files changed, 340 insertions(+), 244 deletions(-) create mode 100644 .github/workflows/android.yml create mode 100644 .github/workflows/docker-ros.yml delete mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..e79a19ef --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,78 @@ +name: android + +# Android build environments (docker/noble/android/rtabmap_apiXX). +# amd64 only, so a single native runner and no manifest juggling. +# Note: these build FROM introlab3it/rtabmap:android-noble-deps, which is not +# produced by any workflow; it is still built and pushed by hand. + +on: + push: + branches: + - 'master' + pull_request: + branches: + - '**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + docker: + # A manual dispatch is honored only on master, the only ref we push from. + if: ${{ github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master' }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + docker_tag: [android23, android24, android26, android30] + include: + - docker_tag: android23 + docker_tags: | + introlab3it/rtabmap:android23 + introlab3it/rtabmap:tango + api_version: 23 + - docker_tag: android24 + docker_tags: | + introlab3it/rtabmap:android24 + api_version: 24 + - docker_tag: android26 + docker_tags: | + introlab3it/rtabmap:android26 + api_version: 26 + - docker_tag: android30 + docker_tags: | + introlab3it/rtabmap:android30 + api_version: 30 + + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to DockerHub + # Only needed when pushing; skipped on pull requests (secrets are + # unavailable for fork PRs and we don't push there anyway). + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + platforms: linux/amd64 + file: ./docker/noble/android/rtabmap_apiXX/Dockerfile + build-args: | + API_VERSION=${{ matrix.api_version }} + tags: ${{ matrix.docker_tags }} + cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_tag }} + cache-to: type=inline diff --git a/.github/workflows/docker-ros.yml b/.github/workflows/docker-ros.yml new file mode 100644 index 00000000..5c46a61f --- /dev/null +++ b/.github/workflows/docker-ros.yml @@ -0,0 +1,227 @@ +name: docker-ros + +# ROS images: focal/noetic (ROS1) and jammy/humble, noble/jazzy, noble-kilted, +# resolute (ROS2). Android images live in android.yml. +# +# Every arch is built natively: amd64 on an x86 runner, arm64 on a GitHub +# arm64 runner, so no QEMU emulation is involved. Because a single Docker Hub +# tag cannot hold two independently pushed architectures, each build pushes an +# arch-suffixed tag (e.g. :resolute-amd64 / :resolute-arm64) and a final job +# joins them into the real multi-arch tag (:resolute) with `imagetools create`. +# +# The runner image only hosts the build; it does not have to match the Ubuntu +# release inside the image, so ubuntu-26.04{,-arm} is used for all of them +# (ubuntu-22.04{,-arm} and ubuntu-24.04{,-arm} also exist, if ever needed). +# That is also why focal can sit in this matrix: a 20.04 userland runs fine on +# a newer host kernel, and ros:noetic-perception publishes a native arm64 image. + +on: + push: + branches: + - 'master' + pull_request: + branches: + - '**' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + docker_deps: + + # The ###-deps images used to be too flaky to build here at all (seg faults, + # arm64 build timeouts under QEMU -- see + # https://github.com/introlab/rtabmap/issues/1454) and had to be built by + # hand from a 20.04 box with an upgraded qemu-user-static. Building each + # arch natively removes that cause. + # Normally skipped on pull requests; built and pushed only from master + # (push or manual dispatch), since it pushes the :*-deps tags to Docker Hub. + # + # TEMPORARY (revert before merge): also run on pull requests, without + # pushing, purely to measure how long a cold native deps build takes on CI. + # The resulting images are discarded -- the docker job below still pulls the + # :*-deps manifests already on Docker Hub. + if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/master' }} + + runs-on: ${{ matrix.runner }} + + strategy: + fail-fast: false + matrix: + docker_dir: [focal, jammy, noble, noble-kilted, resolute] + arch: [amd64, arm64] + include: + - arch: amd64 + runner: ubuntu-26.04 + docker_platform: linux/amd64 + - arch: arm64 + runner: ubuntu-26.04-arm + docker_platform: linux/arm64 + + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to DockerHub + # TEMPORARY (revert before merge): condition added with the PR-timing run. + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + # TEMPORARY (revert before merge): was `push: true`. + push: ${{ github.event_name != 'pull_request' }} + platforms: ${{ matrix.docker_platform }} + file: ./docker/${{ matrix.docker_dir }}/deps/Dockerfile + tags: introlab3it/rtabmap:${{ matrix.docker_dir }}-deps-${{ matrix.arch }} + cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_dir }}-deps-${{ matrix.arch }} + cache-to: type=inline + + docker_deps_manifest: + needs: docker_deps + # Same gate as docker_deps, so both are skipped together on pull requests + # (github.ref is refs/pull//merge there): the per-arch -deps tags this + # joins are only ever pushed from master. + if: ${{ !cancelled() && !failure() && github.ref == 'refs/heads/master' }} + runs-on: ubuntu-26.04 + + strategy: + fail-fast: false + matrix: + docker_dir: [focal, jammy, noble, noble-kilted, resolute] + + steps: + - + name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Create multi-arch manifest + run: | + docker buildx imagetools create \ + -t introlab3it/rtabmap:${{ matrix.docker_dir }}-deps \ + introlab3it/rtabmap:${{ matrix.docker_dir }}-deps-amd64 \ + introlab3it/rtabmap:${{ matrix.docker_dir }}-deps-arm64 + + docker: + needs: docker_deps_manifest + # Run even when the deps jobs are skipped (they are, on pull requests): + # the runtime Dockerfiles then pull the :*-deps manifest already on Docker Hub. + # A manual dispatch is honored only on master, the only ref we push from. + # + # TEMPORARY (revert before merge): the `github.event_name == 'pull_request'` + # escape keeps these builds running even if the PR-only timing build of + # docker_deps fails, since its images are discarded anyway. On master a deps + # failure still blocks this job. + if: ${{ !cancelled() && (github.event_name == 'pull_request' || !failure()) && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master') }} + runs-on: ${{ matrix.runner }} + + strategy: + fail-fast: false + matrix: + docker_dir: [focal, jammy, noble, noble-kilted, resolute] + arch: [amd64, arm64] + include: + - arch: amd64 + runner: ubuntu-26.04 + docker_platform: linux/amd64 + - arch: arm64 + runner: ubuntu-26.04-arm + docker_platform: linux/arm64 + + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to DockerHub + # Only needed when pushing; skipped on pull requests (secrets are + # unavailable for fork PRs and we don't push there anyway). + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + platforms: ${{ matrix.docker_platform }} + file: ./docker/${{ matrix.docker_dir }}/Dockerfile + tags: introlab3it/rtabmap:${{ matrix.docker_dir }}-${{ matrix.arch }} + cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_dir }}-${{ matrix.arch }} + cache-to: type=inline + + docker_manifest: + needs: docker + # Nothing to join on pull requests, where the per-arch tags are never pushed. + if: ${{ !cancelled() && !failure() && github.event_name != 'pull_request' && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master') }} + runs-on: ubuntu-26.04 + + strategy: + fail-fast: false + matrix: + docker_dir: [focal, jammy, noble, noble-kilted, resolute] + include: + - docker_dir: focal + docker_tags: | + introlab3it/rtabmap:focal + introlab3it/rtabmap:20.04 + - docker_dir: jammy + docker_tags: | + introlab3it/rtabmap:jammy + introlab3it/rtabmap:22.04 + - docker_dir: noble + docker_tags: | + introlab3it/rtabmap:noble + introlab3it/rtabmap:24.04 + - docker_dir: noble-kilted + docker_tags: | + introlab3it/rtabmap:noble-kilted + # :latest tracks the newest ROS2 image (currently resolute, ROS2 lyrical + # on ubuntu 26.04) -- move it along with the next distro bump. + - docker_dir: resolute + docker_tags: | + introlab3it/rtabmap:resolute + introlab3it/rtabmap:26.04 + introlab3it/rtabmap:latest + + steps: + - + name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Create multi-arch manifest + env: + DOCKER_TAGS: ${{ matrix.docker_tags }} + run: | + tag_args=() + while read -r tag; do + if [ -n "$tag" ]; then + tag_args+=(-t "$tag") + fi + done <<< "$DOCKER_TAGS" + docker buildx imagetools create "${tag_args[@]}" \ + introlab3it/rtabmap:${{ matrix.docker_dir }}-amd64 \ + introlab3it/rtabmap:${{ matrix.docker_dir }}-arm64 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml deleted file mode 100644 index a37d1940..00000000 --- a/.github/workflows/docker.yml +++ /dev/null @@ -1,243 +0,0 @@ -name: docker - -on: - push: - branches: - - 'master' - pull_request: - branches: - - '**' - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - docker_deps: - - # Disabling ###-deps step from CI because it is too flaky (seg faults, arm64 build timeout...) - # Only way I was able to build all images is to do it from a ubuntu 20.04 computer with: - # $ sudo add-apt-repository ppa:canonical-server/server-backports - # $ sudo apt-get update - # $ sudo apt-get upgrade qemu-user-static - # $ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes -c yes - # More info: https://github.com/introlab/rtabmap/issues/1454 - # Skipped on pull requests; built and pushed only from master (push or - # manual dispatch), since it pushes the :*-deps tags to Docker Hub. - if: github.ref == 'refs/heads/master' - - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - docker_tag: [focal-deps, jammy-deps, noble-deps, noble-kilted-deps, resolute-deps] - include: - - docker_tag: focal-deps - docker_tags: | - introlab3it/rtabmap:focal-deps - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'focal/deps' - - docker_tag: jammy-deps - docker_tags: | - introlab3it/rtabmap:jammy-deps - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'jammy/deps' - - docker_tag: noble-deps - docker_tags: | - introlab3it/rtabmap:noble-deps - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'noble/deps' - - docker_tag: noble-kilted-deps - docker_tags: | - introlab3it/rtabmap:noble-kilted-deps - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'noble-kilted/deps' - - docker_tag: resolute-deps - docker_tags: | - introlab3it/rtabmap:resolute-deps - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'resolute/deps' - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: true - platforms: ${{ matrix.docker_platforms }} - file: ./docker/${{ matrix.docker_path }}/Dockerfile - tags: ${{ matrix.docker_tags }} - cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_tag }} - cache-to: type=inline - - docker: - needs: docker_deps - # Run even when docker_deps is skipped (it is, on pull requests). - # A manual dispatch is honored only on master, the only ref we push from. - if: ${{ !cancelled() && !failure() && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/master') }} - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - docker_tag: [bionic, focal, jammy, noble, noble-kilted, resolute, android23, android24, android26, android30] - include: - - docker_tag: bionic - docker_tags: | - introlab3it/rtabmap:bionic - introlab3it/rtabmap:18.04 - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'bionic' - - docker_tag: focal - docker_tags: | - introlab3it/rtabmap:focal - introlab3it/rtabmap:20.04 - introlab3it/rtabmap:latest - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'focal' - - docker_tag: jammy - docker_tags: | - introlab3it/rtabmap:jammy - introlab3it/rtabmap:22.04 - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'jammy' - - docker_tag: noble - docker_tags: | - introlab3it/rtabmap:noble - introlab3it/rtabmap:24.04 - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'noble' - - docker_tag: noble-kilted - docker_tags: | - introlab3it/rtabmap:noble-kilted - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'noble-kilted' - - docker_tag: resolute - docker_tags: | - introlab3it/rtabmap:resolute - introlab3it/rtabmap:26.04 - docker_args: | - NOT_USED=0 - docker_platforms: | - linux/amd64 - linux/arm64 - docker_path: 'resolute' - - docker_tag: android23 - docker_tags: | - introlab3it/rtabmap:android23 - introlab3it/rtabmap:tango - docker_args: | - API_VERSION=23 - docker_platforms: | - linux/amd64 - docker_path: 'noble/android/rtabmap_apiXX' - - docker_tag: android24 - docker_tags: | - introlab3it/rtabmap:android24 - docker_args: | - API_VERSION=24 - docker_platforms: | - linux/amd64 - docker_path: 'noble/android/rtabmap_apiXX' - - docker_tag: android26 - docker_tags: | - introlab3it/rtabmap:android26 - docker_args: | - API_VERSION=26 - docker_platforms: | - linux/amd64 - docker_path: 'noble/android/rtabmap_apiXX' - - docker_tag: android30 - docker_tags: | - introlab3it/rtabmap:android30 - docker_args: | - API_VERSION=30 - docker_platforms: | - linux/amd64 - docker_path: 'noble/android/rtabmap_apiXX' - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Login to DockerHub - # Only needed when pushing; skipped on pull requests (secrets are - # unavailable for fork PRs and we don't push there anyway). - if: github.event_name != 'pull_request' - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: ${{ github.event_name != 'pull_request' }} - platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || matrix.docker_platforms }} - file: ./docker/${{ matrix.docker_path }}/Dockerfile - build-args: | - ${{ matrix.docker_args }} - tags: ${{ matrix.docker_tags }} - cache-from: type=registry,ref=introlab3it/rtabmap:${{ matrix.docker_tag }} - cache-to: type=inline - diff --git a/README.md b/README.md index 75297533..a47597b8 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,8 @@ This project is supported by [IntRoLab - Intelligent / Interactive / Integrated CMake Windows Build Status
CMake MaCOS Build Status
CMake ROS Build Status
-
Docker Build Status + Docker ROS Build Status
+
Android Build Status diff --git a/docker/README.md b/docker/README.md index ccec6c94..50397525 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,3 +1,36 @@ ### Docker * Go to the [wiki](https://github.com/introlab/rtabmap/wiki/Installation#docker) for usage examples and how to build locally the images. + +#### Tags + +All images are published to [introlab3it/rtabmap](https://hub.docker.com/r/introlab3it/rtabmap) as +multi-arch manifests (`linux/amd64` and `linux/arm64`). + +| Tag | Alias | Base | ROS | +| --- | --- | --- | --- | +| `resolute` | `26.04`, `latest` | Ubuntu 26.04 | ROS2 Lyrical | +| `noble-kilted` | | Ubuntu 24.04 | ROS2 Kilted | +| `noble` | `24.04` | Ubuntu 24.04 | ROS2 Jazzy | +| `jammy` | `22.04` | Ubuntu 22.04 | ROS2 Humble | +| `focal` | `20.04` | Ubuntu 20.04 | ROS1 Noetic | + +Each image is built on top of a matching `-deps` image holding the third-party +dependencies, so that a source change only rebuilds the top layer. + +> [!IMPORTANT] +> **`latest` now points to the newest ROS2 image** (`resolute`), where it used to point +> to `focal` (ROS1 Noetic). Pulling `introlab3it/rtabmap` without a tag therefore gets you +> a different ROS version than before. Use `introlab3it/rtabmap:focal` to stay on ROS1, or +> pin an explicit tag in general rather than relying on `latest`. + +`bionic` / `18.04` (ROS1 Melodic) is no longer built; the last published image stays on +Docker Hub but will not be updated. Its Dockerfile is kept in [bionic/](bionic) for reference. + +The `-amd64` / `-arm64` tags are per-architecture build outputs that CI joins into +the manifests above (see [.github/workflows/docker-ros.yml](../.github/workflows/docker-ros.yml)); +use the plain tags instead. + +Android build environments (`android23`, `android24`, `android26`, `android30`, `tango`) are +`linux/amd64` only and are built by +[.github/workflows/android.yml](../.github/workflows/android.yml).