Files
devenv/.github/workflows/build-images.yaml
T

239 lines
11 KiB
YAML

name: Build Cross-Compiled ROS 2 Images
permissions:
contents: read
packages: write
on:
workflow_dispatch:
inputs:
platform:
description: "Target platform"
required: true
type: choice
options: [rk3588]
default: rk3588
ros_distro:
description: "ROS 2 distribution"
required: true
type: choice
options: [rolling, jazzy, lyrical]
default: lyrical
push_images:
description: "Push built images to registry"
required: false
type: boolean
default: false
env:
REGISTRY: 192.168.1.60:3000
jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
platform: ${{ fromJSON(format('["{0}"]', inputs.platform)) }}
ros_distro: ${{ fromJSON(format('["{0}"]', inputs.ros_distro)) }}
fail-fast: false
steps:
- name: Set image namespace and tag
run: |
echo "IMAGE_NAMESPACE=${GITHUB_REPOSITORY,,}" >> $GITHUB_ENV
echo "IMAGE_TAG=$(date +%Y%m%d)" >> $GITHUB_ENV
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver: docker
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.PACKAGE_TOKEN }}
# ── Compute cache keys ────────────────────────────────────────────
# Each key = hash(own files) + upstream key, so any upstream change
# transitively invalidates all downstream stages.
- name: Compute cache keys
id: keys
run: |
CC_BASE_KEY=$(sha256sum cross_compile/cc_base.Dockerfile | cut -d' ' -f1)
CC_TOOLCHAIN_FILES=$(find cross_compile/${{ matrix.platform }} -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
CC_TOOLCHAIN_KEY=$(echo "$CC_TOOLCHAIN_FILES $CC_BASE_KEY" | sha256sum | cut -d' ' -f1)
VENDOR_FILES=$(find cross_compile/vendor_packages -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
VENDOR_KEY=$(echo "$VENDOR_FILES $CC_TOOLCHAIN_KEY" | sha256sum | cut -d' ' -f1)
ROS2_FILES=$(find cross_compile/ros2/${{ matrix.ros_distro }} cross_compile/ros2/ros_cpp -type f | sort | xargs sha256sum | sha256sum | cut -d' ' -f1)
ROS2_KEY=$(echo "$ROS2_FILES $VENDOR_KEY" | sha256sum | cut -d' ' -f1)
echo "cc_base=$CC_BASE_KEY" >> $GITHUB_OUTPUT
echo "cc_toolchain=$CC_TOOLCHAIN_KEY" >> $GITHUB_OUTPUT
echo "vendor=$VENDOR_KEY" >> $GITHUB_OUTPUT
echo "ros2=$ROS2_KEY" >> $GITHUB_OUTPUT
# ── Stage 1: cc-base ─────────────────────────────────────────────
- name: Check cc-base cache
id: cc-base
run: |
EXPECTED="${{ steps.keys.outputs.cc_base }}"
if docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:latest 2>/dev/null; then
docker tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:latest cc-base:latest
CACHED=$(docker inspect cc-base:latest --format '{{ index .Config.Labels "cache.key" }}' 2>/dev/null || echo "")
[ "$CACHED" = "$EXPECTED" ] && echo "hit=true" >> $GITHUB_OUTPUT || echo "hit=false" >> $GITHUB_OUTPUT
else
echo "hit=false" >> $GITHUB_OUTPUT
fi
- name: Build cc-base
if: steps.cc-base.outputs.hit != 'true'
run: |
docker compose -f cross_compile/compose.yaml build cc-base
TMPDIR=$(mktemp -d)
docker build \
--label "cache.key=${{ steps.keys.outputs.cc_base }}" \
-t cc-base:latest -f - "$TMPDIR" << 'EOF'
FROM cc-base:latest
EOF
rm -rf "$TMPDIR"
- name: Push cc-base
if: inputs.push_images
run: |
docker tag cc-base:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:${{ env.IMAGE_TAG }}
docker tag cc-base:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/cc-base:latest
# ── Stage 2: cc-toolchain ─────────────────────────────────────────
- name: Check cc-toolchain cache
id: cc-toolchain
run: |
EXPECTED="${{ steps.keys.outputs.cc_toolchain }}"
REMOTE="${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-toolchain:latest"
LOCAL="${{ matrix.platform }}/cc-toolchain:latest"
if docker pull "$REMOTE" 2>/dev/null; then
docker tag "$REMOTE" "$LOCAL"
CACHED=$(docker inspect "$LOCAL" --format '{{ index .Config.Labels "cache.key" }}' 2>/dev/null || echo "")
[ "$CACHED" = "$EXPECTED" ] && echo "hit=true" >> $GITHUB_OUTPUT || echo "hit=false" >> $GITHUB_OUTPUT
else
echo "hit=false" >> $GITHUB_OUTPUT
fi
- name: Build cc-toolchain (${{ matrix.platform }})
if: steps.cc-toolchain.outputs.hit != 'true'
run: |
PLATFORM=${{ matrix.platform }} docker compose -f cross_compile/compose.yaml build cc-toolchain
TMPDIR=$(mktemp -d)
docker build \
--label "cache.key=${{ steps.keys.outputs.cc_toolchain }}" \
-t ${{ matrix.platform }}/cc-toolchain:latest -f - "$TMPDIR" << 'EOF'
FROM ${{ matrix.platform }}/cc-toolchain:latest
EOF
rm -rf "$TMPDIR"
- name: Push cc-toolchain
if: inputs.push_images
run: |
docker tag ${{ matrix.platform }}/cc-toolchain:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-toolchain:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-toolchain:${{ env.IMAGE_TAG }}
docker tag ${{ matrix.platform }}/cc-toolchain:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-toolchain:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-toolchain:latest
# ── Stage 3: vendor-packages ──────────────────────────────────────
- name: Check vendor-packages cache
id: vendor
run: |
EXPECTED="${{ steps.keys.outputs.vendor }}"
REMOTE="${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-vendor-packages:latest"
LOCAL="${{ matrix.platform }}/cc-vendor-packages:latest"
if docker pull "$REMOTE" 2>/dev/null; then
docker tag "$REMOTE" "$LOCAL"
CACHED=$(docker inspect "$LOCAL" --format '{{ index .Config.Labels "cache.key" }}' 2>/dev/null || echo "")
[ "$CACHED" = "$EXPECTED" ] && echo "hit=true" >> $GITHUB_OUTPUT || echo "hit=false" >> $GITHUB_OUTPUT
else
echo "hit=false" >> $GITHUB_OUTPUT
fi
- name: Build vendor-packages (${{ matrix.platform }})
if: steps.vendor.outputs.hit != 'true'
run: |
PLATFORM=${{ matrix.platform }} docker compose -f cross_compile/compose.yaml build vendor-packages
TMPDIR=$(mktemp -d)
docker build \
--label "cache.key=${{ steps.keys.outputs.vendor }}" \
-t ${{ matrix.platform }}/cc-vendor-packages:latest -f - "$TMPDIR" << 'EOF'
FROM ${{ matrix.platform }}/cc-vendor-packages:latest
EOF
rm -rf "$TMPDIR"
- name: Push vendor-packages
if: inputs.push_images
run: |
docker tag ${{ matrix.platform }}/cc-vendor-packages:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-vendor-packages:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-vendor-packages:${{ env.IMAGE_TAG }}
docker tag ${{ matrix.platform }}/cc-vendor-packages:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-vendor-packages:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-vendor-packages:latest
# ── Stage 4: ros2 ─────────────────────────────────────────────────
- name: Check ros2 cache
id: ros2
run: |
EXPECTED="${{ steps.keys.outputs.ros2 }}"
REMOTE="${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest"
LOCAL="${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest"
if docker pull "$REMOTE" 2>/dev/null; then
docker tag "$REMOTE" "$LOCAL"
CACHED=$(docker inspect "$LOCAL" --format '{{ index .Config.Labels "cache.key" }}' 2>/dev/null || echo "")
[ "$CACHED" = "$EXPECTED" ] && echo "hit=true" >> $GITHUB_OUTPUT || echo "hit=false" >> $GITHUB_OUTPUT
else
echo "hit=false" >> $GITHUB_OUTPUT
fi
- name: Build ROS 2 (${{ matrix.ros_distro }})
if: steps.ros2.outputs.hit != 'true'
run: |
PLATFORM=${{ matrix.platform }} ROS_DISTRO=${{ matrix.ros_distro }} docker compose -f cross_compile/compose.yaml build ros2
TMPDIR=$(mktemp -d)
docker build \
--label "cache.key=${{ steps.keys.outputs.ros2 }}" \
-t ${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest -f - "$TMPDIR" << 'EOF'
FROM ${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest
EOF
rm -rf "$TMPDIR"
- name: Push ROS 2 image
if: inputs.push_images
run: |
docker tag ${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:${{ env.IMAGE_TAG }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:${{ env.IMAGE_TAG }}
docker tag ${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest \
${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.platform }}/cc-ros-${{ matrix.ros_distro }}:latest
# ── Summary ───────────────────────────────────────────────────────
- name: Image summary
if: always()
run: |
echo "### Built Images :rocket:" >> $GITHUB_STEP_SUMMARY
docker images \
--filter "reference=cc-*" \
--filter "reference=*/cc-*" \
--format "| \`{{.Repository}}:{{.Tag}}\` | {{.Size}} |" \
>> $GITHUB_STEP_SUMMARY