Skip to content

Commit 7475105

Browse files
committed
ci(packaging): add SITL container image workflow
Build and publish multi-arch Docker images (amd64 + arm64) on git tags. Pipeline: build Noble .debs, build Docker images from them, create multi-arch manifests on Docker Hub and GHCR.
1 parent cc803f5 commit 7475105

File tree

4 files changed

+225
-24
lines changed

4 files changed

+225
-24
lines changed

.github/workflows/build_deb_package.yml

Lines changed: 191 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build .deb Packages
1+
name: SITL Packages and Containers
22

33
on:
44
push:
@@ -11,22 +11,65 @@ on:
1111
- 'boards/px4/sitl/sih.px4board'
1212
- '.github/workflows/build_deb_package.yml'
1313
- '.github/actions/build-deb/**'
14-
workflow_dispatch: {}
14+
workflow_dispatch:
15+
inputs:
16+
deploy_containers:
17+
description: 'Push container images to registry'
18+
required: false
19+
type: boolean
20+
default: false
1521

1622
concurrency:
1723
group: ${{ github.workflow }}-${{ github.ref }}
1824
cancel-in-progress: true
1925

2026
permissions:
2127
contents: read
28+
packages: write
2229

2330
env:
2431
RUNS_IN_DOCKER: true
2532

2633
jobs:
34+
35+
# ---------------------------------------------------------------------------
36+
# Setup: extract version and determine whether to push containers
37+
# ---------------------------------------------------------------------------
38+
setup:
39+
name: Setup
40+
runs-on: [runs-on,"runner=1cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
41+
outputs:
42+
px4_version: ${{ steps.px4_version.outputs.px4_version }}
43+
should_push: ${{ steps.push_check.outputs.should_push }}
44+
steps:
45+
- uses: runs-on/action@v2
46+
- uses: actions/checkout@v4
47+
with:
48+
fetch-tags: true
49+
submodules: false
50+
fetch-depth: 0
51+
52+
- name: Set PX4 version
53+
id: px4_version
54+
run: echo "px4_version=$(git describe --tags --match 'v[0-9]*')" >> $GITHUB_OUTPUT
55+
56+
- name: Check if we should push containers
57+
id: push_check
58+
run: |
59+
if [[ "${{ startsWith(github.ref, 'refs/tags/') }}" == "true" ]] || \
60+
[[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.deploy_containers }}" == "true" ]]; then
61+
echo "should_push=true" >> $GITHUB_OUTPUT
62+
else
63+
echo "should_push=false" >> $GITHUB_OUTPUT
64+
fi
65+
66+
# ---------------------------------------------------------------------------
67+
# Build .deb packages (all distros, arches, targets)
68+
# ---------------------------------------------------------------------------
2769
build-deb:
2870
name: "Build .deb (${{ matrix.target }}/${{ matrix.codename }}/${{ matrix.arch }})"
29-
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",spot=false]
71+
needs: setup
72+
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
3073
container:
3174
image: ${{ matrix.container }}
3275
volumes:
@@ -35,28 +78,36 @@ jobs:
3578
fail-fast: false
3679
matrix:
3780
include:
38-
# Default (Gazebo) builds
81+
# Gazebo builds
3982
- { codename: noble, arch: amd64, runner: x64, container: "ubuntu:24.04", target: default, setup_flags: "" }
4083
- { codename: noble, arch: arm64, runner: arm64, container: "ubuntu:24.04", target: default, setup_flags: "" }
4184
- { codename: jammy, arch: amd64, runner: x64, container: "ubuntu:22.04", target: default, setup_flags: "" }
4285
- { codename: jammy, arch: arm64, runner: arm64, container: "ubuntu:22.04", target: default, setup_flags: "" }
43-
# SIH (no Gazebo) builds
86+
# SIH builds
4487
- { codename: noble, arch: amd64, runner: x64, container: "ubuntu:24.04", target: sih, setup_flags: "--no-sim-tools" }
4588
- { codename: noble, arch: arm64, runner: arm64, container: "ubuntu:24.04", target: sih, setup_flags: "--no-sim-tools" }
4689
- { codename: jammy, arch: amd64, runner: x64, container: "ubuntu:22.04", target: sih, setup_flags: "--no-sim-tools" }
4790
- { codename: jammy, arch: arm64, runner: arm64, container: "ubuntu:22.04", target: sih, setup_flags: "--no-sim-tools" }
48-
4991
steps:
92+
- uses: runs-on/action@v2
93+
5094
- name: Fix git in container
5195
run: |
52-
apt update && apt install git -y
96+
apt-get update && apt-get install -y git
5397
git config --global --add safe.directory $(realpath .)
5498
5599
- uses: actions/checkout@v4
56100
with:
57101
fetch-depth: 0
58102
fetch-tags: true
59103

104+
- name: Cache apt packages
105+
uses: actions/cache@v4
106+
with:
107+
path: /var/cache/apt/archives
108+
key: apt-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}-${{ hashFiles('Tools/setup/ubuntu.sh') }}
109+
restore-keys: apt-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}-
110+
60111
- name: Install dependencies
61112
run: ./Tools/setup/ubuntu.sh --no-nuttx ${{ matrix.setup_flags }}
62113

@@ -66,3 +117,136 @@ jobs:
66117
target: ${{ matrix.target }}
67118
artifact-name: px4-sitl-debs-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}
68119
ccache-key-prefix: deb-ccache-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}
120+
121+
# ---------------------------------------------------------------------------
122+
# Build Docker images from Noble .debs
123+
# ---------------------------------------------------------------------------
124+
build-docker:
125+
name: "Build Image (${{ matrix.image }}/${{ matrix.arch }})"
126+
needs: [setup, build-deb]
127+
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
128+
strategy:
129+
fail-fast: false
130+
matrix:
131+
include:
132+
- { image: sih, target: sih, arch: amd64, runner: x64, platform: "linux/amd64", dockerfile: Dockerfile.sih }
133+
- { image: sih, target: sih, arch: arm64, runner: arm64, platform: "linux/arm64", dockerfile: Dockerfile.sih }
134+
- { image: gazebo, target: default, arch: amd64, runner: x64, platform: "linux/amd64", dockerfile: Dockerfile.gazebo }
135+
- { image: gazebo, target: default, arch: arm64, runner: arm64, platform: "linux/arm64", dockerfile: Dockerfile.gazebo }
136+
steps:
137+
- uses: runs-on/action@v2
138+
- uses: actions/checkout@v4
139+
with:
140+
submodules: false
141+
fetch-depth: 1
142+
143+
- name: Download Noble .deb artifact
144+
uses: actions/download-artifact@v4
145+
with:
146+
name: px4-sitl-debs-${{ matrix.target }}-noble-${{ matrix.arch }}
147+
path: docker-context
148+
149+
- name: Prepare build context
150+
run: |
151+
cp Tools/packaging/px4-entrypoint.sh docker-context/
152+
ls -lh docker-context/
153+
154+
- name: Login to Docker Hub
155+
uses: docker/login-action@v3
156+
if: needs.setup.outputs.should_push == 'true'
157+
with:
158+
username: ${{ secrets.DOCKERHUB_USERNAME }}
159+
password: ${{ secrets.DOCKERHUB_TOKEN }}
160+
161+
- name: Login to GitHub Container Registry
162+
uses: docker/login-action@v3
163+
if: needs.setup.outputs.should_push == 'true'
164+
with:
165+
registry: ghcr.io
166+
username: ${{ github.actor }}
167+
password: ${{ secrets.GITHUB_TOKEN }}
168+
169+
- name: Set up Docker Buildx
170+
uses: docker/setup-buildx-action@v3
171+
with:
172+
driver: docker-container
173+
platforms: ${{ matrix.platform }}
174+
175+
- name: Build and push container image
176+
uses: docker/build-push-action@v6
177+
with:
178+
context: docker-context
179+
file: Tools/packaging/${{ matrix.dockerfile }}
180+
tags: |
181+
px4io/px4-sitl-${{ matrix.image }}:${{ needs.setup.outputs.px4_version }}-${{ matrix.arch }}
182+
ghcr.io/px4/px4-sitl-${{ matrix.image }}:${{ needs.setup.outputs.px4_version }}-${{ matrix.arch }}
183+
platforms: ${{ matrix.platform }}
184+
load: false
185+
push: ${{ needs.setup.outputs.should_push == 'true' }}
186+
provenance: false
187+
cache-from: type=gha,scope=sitl-${{ matrix.image }}-${{ matrix.arch }}
188+
cache-to: type=gha,mode=max,scope=sitl-${{ matrix.image }}-${{ matrix.arch }}
189+
190+
# ---------------------------------------------------------------------------
191+
# Deploy: create multi-arch manifests and push to registries
192+
# ---------------------------------------------------------------------------
193+
deploy:
194+
name: "Deploy (${{ matrix.image }})"
195+
needs: [setup, build-docker]
196+
if: needs.setup.outputs.should_push == 'true'
197+
runs-on: [runs-on,"runner=1cpu-linux-x64","image=ubuntu24-full-x64","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
198+
strategy:
199+
matrix:
200+
image: [sih, gazebo]
201+
steps:
202+
- uses: runs-on/action@v2
203+
204+
- name: Login to Docker Hub
205+
uses: docker/login-action@v3
206+
with:
207+
username: ${{ secrets.DOCKERHUB_USERNAME }}
208+
password: ${{ secrets.DOCKERHUB_TOKEN }}
209+
210+
- name: Login to GitHub Container Registry
211+
uses: docker/login-action@v3
212+
with:
213+
registry: ghcr.io
214+
username: ${{ github.actor }}
215+
password: ${{ secrets.GITHUB_TOKEN }}
216+
217+
- name: Verify per-arch images exist
218+
run: |
219+
for registry in px4io ghcr.io/px4; do
220+
for arch in amd64 arm64; do
221+
docker manifest inspect ${registry}/px4-sitl-${{ matrix.image }}:${{ needs.setup.outputs.px4_version }}-${arch} \
222+
|| echo "Warning: ${registry}/px4-sitl-${{ matrix.image }}:${{ needs.setup.outputs.px4_version }}-${arch} not found"
223+
done
224+
done
225+
226+
- name: Create and push multi-arch manifest (Docker Hub)
227+
run: |
228+
VERSION="${{ needs.setup.outputs.px4_version }}"
229+
IMAGE="px4io/px4-sitl-${{ matrix.image }}"
230+
231+
docker manifest create ${IMAGE}:${VERSION} \
232+
--amend ${IMAGE}:${VERSION}-arm64 \
233+
--amend ${IMAGE}:${VERSION}-amd64
234+
235+
docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-arm64 --arch arm64
236+
docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-amd64 --arch amd64
237+
238+
docker manifest push ${IMAGE}:${VERSION}
239+
240+
- name: Create and push multi-arch manifest (GHCR)
241+
run: |
242+
VERSION="${{ needs.setup.outputs.px4_version }}"
243+
IMAGE="ghcr.io/px4/px4-sitl-${{ matrix.image }}"
244+
245+
docker manifest create ${IMAGE}:${VERSION} \
246+
--amend ${IMAGE}:${VERSION}-arm64 \
247+
--amend ${IMAGE}:${VERSION}-amd64
248+
249+
docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-arm64 --arch arm64
250+
docker manifest annotate ${IMAGE}:${VERSION} ${IMAGE}:${VERSION}-amd64 --arch amd64
251+
252+
docker manifest push ${IMAGE}:${VERSION}

Tools/packaging/Dockerfile.gazebo

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# syntax=docker/dockerfile:1
12
# PX4 SITL Gazebo Harmonic runtime image
23
# Runs PX4 SITL with Gazebo Harmonic. Supports X11 forwarding for GUI.
34
#
@@ -28,9 +29,11 @@ LABEL description="PX4 SITL with Gazebo Harmonic simulation"
2829
ENV DEBIAN_FRONTEND=noninteractive
2930
ENV RUNS_IN_DOCKER=true
3031

31-
# Install Gazebo Harmonic and simulation dependencies (no NuttX toolchain)
32-
COPY --from=extract /staging /staging
33-
RUN apt-get update \
32+
# Install Gazebo Harmonic with buildkit cache mounts for apt
33+
# The --mount=type=cache persists /var/cache/apt and /var/lib/apt across builds
34+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
35+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
36+
apt-get update \
3437
&& apt-get install -y --no-install-recommends \
3538
bc \
3639
ca-certificates \
@@ -43,16 +46,15 @@ RUN apt-get update \
4346
> /etc/apt/sources.list.d/gazebo-stable.list \
4447
&& apt-get update \
4548
&& apt-get install -y --no-install-recommends \
46-
gz-harmonic \
47-
&& rm -rf /var/lib/apt/lists/*
49+
gz-harmonic
4850

4951
# Install PX4 files from .deb
50-
RUN cp -a /staging/opt/px4-gazebo /opt/px4-gazebo && rm -rf /staging
52+
COPY --from=extract /staging/opt/px4-gazebo /opt/px4-gazebo
5153
RUN ln -sf /opt/px4-gazebo/bin/px4-gazebo /usr/bin/px4-gazebo
5254

5355
# Create the DART physics engine symlink (avoids needing the -dev package)
54-
RUN GZ_PHYSICS_DIR=$(echo /usr/lib/*/gz-physics-7/engine-plugins) \
55-
&& if [ -d "$GZ_PHYSICS_DIR" ]; then \
56+
RUN GZ_PHYSICS_DIR=$(find /usr/lib -maxdepth 3 -type d -name "engine-plugins" -path "*/gz-physics-7/*" 2>/dev/null | head -1) \
57+
&& if [ -n "$GZ_PHYSICS_DIR" ] && [ -d "$GZ_PHYSICS_DIR" ]; then \
5658
VERSIONED=$(ls "$GZ_PHYSICS_DIR"/libgz-physics*-dartsim-plugin.so.* 2>/dev/null | head -1) \
5759
&& [ -n "$VERSIONED" ] \
5860
&& ln -sf "$(basename "$VERSIONED")" "$GZ_PHYSICS_DIR/libgz-physics-dartsim-plugin.so"; \
@@ -71,7 +73,12 @@ ENV HOME=/root
7173
# MAVLink, MAVSDK, DDS
7274
EXPOSE 14550/udp 14540/udp 8888/udp
7375

76+
# Platform-adaptive entrypoint: detects Docker Desktop (macOS/Windows) via
77+
# host.docker.internal and configures MAVLink + DDS to target the host.
78+
COPY px4-entrypoint.sh /opt/px4-gazebo/bin/px4-entrypoint.sh
79+
RUN chmod +x /opt/px4-gazebo/bin/px4-entrypoint.sh
80+
7481
WORKDIR /root
7582

76-
ENTRYPOINT ["/opt/px4-gazebo/bin/px4-gazebo"]
83+
ENTRYPOINT ["/opt/px4-gazebo/bin/px4-entrypoint.sh"]
7784
CMD []

Tools/packaging/Dockerfile.sih

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# syntax=docker/dockerfile:1
12
# PX4 SITL SIH runtime image
23
# Minimal container that runs PX4 with the SIH physics engine (no Gazebo).
34
#
@@ -13,18 +14,20 @@
1314

1415
FROM ubuntu:24.04 AS build
1516
COPY px4_*.deb /tmp/
16-
RUN apt-get update \
17+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
18+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
19+
apt-get update \
1720
&& apt-get install -y --no-install-recommends binutils \
1821
&& dpkg -x /tmp/px4_*.deb /staging \
19-
&& strip /staging/opt/px4/bin/px4 \
20-
&& rm -rf /var/lib/apt/lists/*
22+
&& strip /staging/opt/px4/bin/px4
2123

2224
FROM ubuntu:24.04
2325
LABEL maintainer="PX4 Development Team"
2426
LABEL description="PX4 SITL with SIH physics (no simulator dependencies)"
2527

26-
RUN apt-get update && apt-get install -y --no-install-recommends bc \
27-
&& rm -rf /var/lib/apt/lists/* /usr/share/doc /usr/share/man /usr/share/info
28+
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
29+
--mount=type=cache,target=/var/lib/apt,sharing=locked \
30+
apt-get update && apt-get install -y --no-install-recommends bc
2831

2932
COPY --from=build /staging/opt/px4 /opt/px4
3033
RUN ln -sf /opt/px4/bin/px4 /usr/bin/px4

Tools/packaging/px4-entrypoint.sh

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,23 @@
1010

1111
set -e
1212

13+
# Detect install prefix (SIH uses /opt/px4, Gazebo uses /opt/px4-gazebo)
14+
if [ -d /opt/px4-gazebo ]; then
15+
PX4_PREFIX=/opt/px4-gazebo
16+
else
17+
PX4_PREFIX=/opt/px4
18+
fi
19+
1320
if getent hosts host.docker.internal >/dev/null 2>&1; then
1421
DOCKER_HOST_IP=$(getent hosts host.docker.internal | awk '{print $1}')
1522

1623
# MAVLink: replace default target (127.0.0.1) with the Docker host IP
1724
sed -i "s/mavlink start -x -u/mavlink start -x -t $DOCKER_HOST_IP -u/g" \
18-
/opt/px4/etc/init.d-posix/px4-rc.mavlink
25+
"$PX4_PREFIX/etc/init.d-posix/px4-rc.mavlink"
1926

2027
# DDS: point uXRCE-DDS client at the host
2128
sed -i "s|uxrce_dds_client start -t udp|uxrce_dds_client start -t udp -h $DOCKER_HOST_IP|" \
22-
/opt/px4/etc/init.d-posix/rcS
29+
"$PX4_PREFIX/etc/init.d-posix/rcS"
2330
fi
2431

25-
exec /opt/px4/bin/px4 "$@"
32+
exec "$PX4_PREFIX/bin/px4" "$@"

0 commit comments

Comments
 (0)