Skip to content

Feature/diff eef (#12) #5

Feature/diff eef (#12)

Feature/diff eef (#12) #5

name: Build Common Deb
# Runs in https://github.com/fiveages-sim/robot-descriptions-common.
# The repository root is the colcon workspace.
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: Release tag to upload the deb asset to
required: false
type: string
ros_distro:
description: ROS distro used in the install path
required: false
default: jazzy
type: string
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event.inputs.tag || github.ref_name }}
cancel-in-progress: true
env:
DEFAULT_ROS_DISTRO: jazzy
PACKAGE_NAME: ros-jazzy-robot-descriptions-common
DEB_FILE_PREFIX: ros-jazzy-robot-descriptions-common
INSTALL_PREFIX: /opt/ros/jazzy
jobs:
build_common_deb:
name: build common deb (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
ros_distro: jazzy
image: ros:jazzy-ros-base-noble
- arch: arm64
runner: ubuntu-24.04-arm
ros_distro: jazzy
image: ros:jazzy-ros-base-noble
container:
image: ${{ matrix.image }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release metadata
id: meta
shell: bash
env:
INPUT_TAG: ${{ github.event.inputs.tag }}
INPUT_ROS_DISTRO: ${{ github.event.inputs.ros_distro }}
MATRIX_ROS_DISTRO: ${{ matrix.ros_distro }}
DEB_ARCH: ${{ matrix.arch }}
run: |
set -eo pipefail
if [[ -n "${INPUT_TAG:-}" ]]; then
tag="$INPUT_TAG"
elif [[ "${GITHUB_REF_TYPE:-}" == "tag" ]]; then
tag="${GITHUB_REF_NAME}"
else
echo "A release tag is required for workflow_dispatch." >&2
exit 1
fi
if [[ "${tag}" != [vV]* ]]; then
tag="v${tag}"
fi
ros_distro="${INPUT_ROS_DISTRO:-$MATRIX_ROS_DISTRO}"
if [[ "${ros_distro}" != "${MATRIX_ROS_DISTRO}" ]]; then
echo "This containerized deb workflow currently supports only ROS 2 ${MATRIX_ROS_DISTRO}." >&2
echo "Requested: ${ros_distro}" >&2
exit 1
fi
version="${tag#v}"
version="${version#V}"
version="$(printf '%s' "$version" | sed 's/-/~/g')"
if [[ -z "$version" ]]; then
echo "Unable to derive a package version from tag: $tag" >&2
exit 1
fi
deb_file="${DEB_FILE_PREFIX}_${version}_${DEB_ARCH}.deb"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "ros_distro=$ros_distro" >> "$GITHUB_OUTPUT"
echo "deb_arch=$DEB_ARCH" >> "$GITHUB_OUTPUT"
echo "deb_file=$deb_file" >> "$GITHUB_OUTPUT"
- name: Validate workspace
shell: bash
run: |
set -eo pipefail
if [[ ! -f robot_common_launch/package.xml ]]; then
echo "robot_common_launch/package.xml not found at repo root." >&2
exit 1
fi
- name: Install build tools
shell: bash
run: |
set -eo pipefail
apt-get update
apt-get install -y \
python3-rosdep \
python3-colcon-core \
python3-colcon-common-extensions \
python3-vcstool \
python3-pip \
python3-catkin-pkg-modules \
sudo \
zstd \
fakeroot \
dpkg-dev \
rsync \
build-essential \
cmake \
ros-${{ steps.meta.outputs.ros_distro }}-ament-cmake \
ros-${{ steps.meta.outputs.ros_distro }}-ament-cmake-core \
ros-${{ steps.meta.outputs.ros_distro }}-ament-package
source "/opt/ros/${{ steps.meta.outputs.ros_distro }}/setup.bash"
python3 -c "import ament_package; print('ament_package ready:', ament_package.__file__)"
- name: Prepare rosdep user
shell: bash
run: |
set -eo pipefail
if ! id -u rosdep >/dev/null 2>&1; then
useradd --create-home --shell /bin/bash rosdep
fi
mkdir -p /home/rosdep/.ros/rosdep
chown -R rosdep:rosdep /home/rosdep/.ros
echo "rosdep ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/rosdep
chmod 0440 /etc/sudoers.d/rosdep
- name: Cache rosdep sources
id: cache-rosdep
uses: actions/cache@v4
with:
path: /home/rosdep/.ros/rosdep/sources.cache
key: rosdep-${{ runner.os }}-${{ matrix.arch }}-${{ steps.meta.outputs.ros_distro }}-${{ hashFiles('**/package.xml') }}
restore-keys: |
rosdep-${{ runner.os }}-${{ matrix.arch }}-${{ steps.meta.outputs.ros_distro }}-
rosdep-${{ runner.os }}-${{ matrix.arch }}-
- name: Update rosdep sources
shell: bash
run: |
set -eo pipefail
chown -R rosdep:rosdep /home/rosdep/.ros
if [[ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]]; then
PYTHONWARNINGS="ignore:pkg_resources is deprecated as an API:DeprecationWarning" rosdep init
fi
if [[ "${{ steps.cache-rosdep.outputs.cache-hit }}" == "true" ]]; then
echo "Using restored rosdep sources cache."
else
sudo -H -u rosdep env \
PYTHONWARNINGS="ignore:pkg_resources is deprecated as an API:DeprecationWarning" \
rosdep update
fi
- name: Install common ROS dependencies
shell: bash
env:
ROS_DISTRO: ${{ steps.meta.outputs.ros_distro }}
run: |
set -eo pipefail
source "/opt/ros/${ROS_DISTRO}/setup.bash"
ros_python_path="/opt/ros/${ROS_DISTRO}/lib/python$(python3 -c 'import sys; print(str(sys.version_info.major) + "." + str(sys.version_info.minor))')/site-packages"
sudo -H -u rosdep env \
GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" \
PYTHONPATH="${ros_python_path}:${PYTHONPATH:-}" \
PYTHONWARNINGS="ignore:pkg_resources is deprecated as an API:DeprecationWarning" \
ROS_DISTRO="${ROS_DISTRO}" \
bash -lc '
source "/opt/ros/${ROS_DISTRO}/setup.bash"
rosdep install \
--from-paths "${GITHUB_WORKSPACE}" \
--ignore-src \
--rosdistro "${ROS_DISTRO}" \
-r \
-y
'
- name: Build common install space
shell: bash
env:
ROS_DISTRO: ${{ steps.meta.outputs.ros_distro }}
run: |
set -eo pipefail
source "/opt/ros/${ROS_DISTRO}/setup.bash"
ros_python_path="/opt/ros/${ROS_DISTRO}/lib/python$(python3 -c 'import sys; print(str(sys.version_info.major) + "." + str(sys.version_info.minor))')/site-packages"
export PYTHONPATH="${ros_python_path}:${PYTHONPATH:-}"
build_root="${RUNNER_TEMP}/colcon-common"
package_root="${RUNNER_TEMP}/deb-build/package"
install_prefix="${INSTALL_PREFIX}"
install_root="${package_root}${install_prefix}"
rm -rf "$build_root" "$package_root"
mkdir -p "$build_root" "$package_root"
colcon --log-base "${build_root}/log" build \
--base-paths "${GITHUB_WORKSPACE}" \
--build-base "${build_root}/build" \
--install-base "${install_root}" \
--merge-install \
--cmake-args \
--no-warn-unused-cli \
-DBUILD_TESTING=OFF \
-DCMAKE_BUILD_TYPE=Release
python3 -c "import sys; exec('''import os\ninstall_root = sys.argv[1]\nruntime_prefix = sys.argv[2]\nfor dirpath, _, filenames in os.walk(install_root):\n for filename in filenames:\n path = os.path.join(dirpath, filename)\n try:\n with open(path, 'r', encoding='utf-8') as f:\n content = f.read()\n except Exception:\n continue\n if install_root not in content:\n continue\n with open(path, 'w', encoding='utf-8') as f:\n f.write(content.replace(install_root, runtime_prefix))''')" "$install_root" "$install_prefix"
rm -f \
"${install_root}/setup.bash" \
"${install_root}/setup.sh" \
"${install_root}/setup.zsh" \
"${install_root}/setup.ps1" \
"${install_root}/local_setup.bash" \
"${install_root}/local_setup.sh" \
"${install_root}/local_setup.zsh" \
"${install_root}/local_setup.ps1" \
"${install_root}/_local_setup_util.py" \
"${install_root}/_local_setup_util_sh.py" \
"${install_root}/_local_setup_util_ps1.py" \
"${install_root}/.colcon_install_layout" \
"${install_root}/COLCON_IGNORE"
if [[ ! -d "${install_root}/share/ament_index/resource_index/packages" ]]; then
echo "ament index was not generated in the install space." >&2
exit 1
fi
for required_pkg in robot_common_launch sensor_models component_models jodell_description; do
if [[ ! -d "${install_root}/share/${required_pkg}" ]]; then
echo "${required_pkg} was not installed into the package root." >&2
exit 1
fi
done
- name: Build deb package
id: build
shell: bash
env:
ROS_DISTRO: ${{ steps.meta.outputs.ros_distro }}
VERSION: ${{ steps.meta.outputs.version }}
DEB_FILE: ${{ steps.meta.outputs.deb_file }}
DEB_ARCH: ${{ steps.meta.outputs.deb_arch }}
run: |
set -eo pipefail
package_root="${RUNNER_TEMP}/deb-build/package"
install_prefix="${INSTALL_PREFIX}"
mkdir -p "$package_root/DEBIAN"
installed_size="$(du -sk "$package_root" | cut -f1)"
cat > "$package_root/DEBIAN/control" <<EOF
Package: ${PACKAGE_NAME}
Version: ${VERSION}
Section: misc
Priority: optional
Architecture: ${DEB_ARCH}
Depends: ros-${ROS_DISTRO}-ros-base, ros-${ROS_DISTRO}-ament-index-python, ros-${ROS_DISTRO}-controller-manager, ros-${ROS_DISTRO}-joint-state-publisher-gui, ros-${ROS_DISTRO}-launch, ros-${ROS_DISTRO}-launch-ros, ros-${ROS_DISTRO}-rclpy, ros-${ROS_DISTRO}-robot-state-publisher, ros-${ROS_DISTRO}-xacro
Provides: robot-descriptions-jazzy-common
Maintainer: ${GITHUB_REPOSITORY} maintainers <github-actions[bot]@users.noreply.github.com>
Installed-Size: ${installed_size}
Description: Shared robot_descriptions common ROS packages
Prebuilt packages from fiveages-sim/robot-descriptions-common installed into ${INSTALL_PREFIX} while excluding shared prefix setup files.
EOF
if [[ ! -d "${package_root}${install_prefix}/share/robot_common_launch" ]]; then
echo "robot_common_launch was not installed into the package root." >&2
exit 1
fi
dpkg-deb --root-owner-group --build "$package_root" "$DEB_FILE"
echo "deb_path=${GITHUB_WORKSPACE}/${DEB_FILE}" >> "$GITHUB_OUTPUT"
- name: Upload workflow artifact
uses: actions/upload-artifact@v4
with:
name: robot-descriptions-common-${{ steps.meta.outputs.version }}-${{ matrix.arch }}
path: ${{ steps.meta.outputs.deb_file }}
if-no-files-found: error
retention-days: 7
publish_common_deb:
name: publish common debs
runs-on: ubuntu-24.04
needs: build_common_deb
outputs:
tag: ${{ steps.meta.outputs.tag }}
ros_distro: ${{ steps.meta.outputs.ros_distro }}
version: ${{ steps.meta.outputs.version }}
steps:
- name: Resolve release metadata
id: meta
shell: bash
env:
INPUT_TAG: ${{ github.event.inputs.tag }}
INPUT_ROS_DISTRO: ${{ github.event.inputs.ros_distro }}
run: |
set -eo pipefail
if [[ -n "${INPUT_TAG:-}" ]]; then
tag="$INPUT_TAG"
elif [[ "${GITHUB_REF_TYPE:-}" == "tag" ]]; then
tag="${GITHUB_REF_NAME}"
else
echo "A release tag is required for workflow_dispatch." >&2
exit 1
fi
if [[ "${tag}" != [vV]* ]]; then
tag="v${tag}"
fi
ros_distro="${INPUT_ROS_DISTRO:-$DEFAULT_ROS_DISTRO}"
version="${tag#v}"
version="${version#V}"
version="$(printf '%s' "$version" | sed 's/-/~/g')"
echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "ros_distro=$ros_distro" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
- name: Download built deb artifacts
uses: actions/download-artifact@v4
with:
pattern: robot-descriptions-common-${{ steps.meta.outputs.version }}-*
merge-multiple: true
- name: Publish debs to GitHub Release
shell: bash
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.meta.outputs.tag }}
run: |
set -eo pipefail
ls -lh *.deb
if ! gh release view "$TAG" --repo "${GITHUB_REPOSITORY}" > /dev/null 2>&1; then
gh release create "$TAG" \
--repo "${GITHUB_REPOSITORY}" \
--title "$TAG" \
--notes "Automated release for Debian packages."
fi
gh release upload "$TAG" ./*.deb --repo "${GITHUB_REPOSITORY}" --clobber
verify_common_deb:
name: verify common deb (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
needs: publish_common_deb
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
image: ros:jazzy-ros-base-noble
- arch: arm64
runner: ubuntu-24.04-arm
image: ros:jazzy-ros-base-noble
container:
image: ${{ matrix.image }}
steps:
- name: Resolve verify metadata
id: meta
shell: bash
env:
TAG: ${{ needs.publish_common_deb.outputs.tag }}
VERSION: ${{ needs.publish_common_deb.outputs.version }}
ROS_DISTRO: ${{ needs.publish_common_deb.outputs.ros_distro }}
DEB_ARCH: ${{ matrix.arch }}
run: |
set -eo pipefail
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "ros_distro=${ROS_DISTRO}" >> "$GITHUB_OUTPUT"
echo "deb_file=${DEB_FILE_PREFIX}_${VERSION}_${DEB_ARCH}.deb" >> "$GITHUB_OUTPUT"
- name: Install verification tools
shell: bash
run: |
set -eo pipefail
apt-get update
apt-get install -y ca-certificates curl cmake build-essential
mkdir -p -m 755 /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg
chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list
apt-get update
apt-get install -y gh
- name: Download deb from release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
set -eo pipefail
deb_file="${{ steps.meta.outputs.deb_file }}"
tag="${{ steps.meta.outputs.tag }}"
for attempt in {1..30}; do
rm -f "${deb_file}"
if gh release download "$tag" \
--repo "${GITHUB_REPOSITORY}" \
--pattern "${deb_file}"; then
break
fi
echo "Asset ${deb_file} is not available yet (attempt ${attempt}/30); retrying in 20s..."
sleep 20
done
if [[ ! -f "${deb_file}" ]]; then
echo "Timed out waiting for ${deb_file} in release ${tag}." >&2
gh release view "$tag" \
--repo "${GITHUB_REPOSITORY}" \
--json assets \
--jq '.assets[].name' || true
exit 1
fi
ls -lh "${deb_file}"
- name: Install deb and verify package discovery
shell: bash
run: |
set -eo pipefail
deb_file="${{ steps.meta.outputs.deb_file }}"
ros_distro="${{ steps.meta.outputs.ros_distro }}"
dpkg -i "./${deb_file}" || apt-get -f install -y
set +u
source "/opt/ros/${ros_distro}/setup.bash"
set -u
export CMAKE_PREFIX_PATH="${INSTALL_PREFIX}:${CMAKE_PREFIX_PATH:-}"
export AMENT_PREFIX_PATH="${INSTALL_PREFIX}:${AMENT_PREFIX_PATH:-}"
python3 - <<'PY'
from ament_index_python.packages import get_package_share_directory
for package_name in [
"robot_common_launch",
"sensor_models",
"component_models",
"jodell_description",
]:
print(package_name, get_package_share_directory(package_name))
import robot_common_launch
print("robot_common_launch module:", robot_common_launch.__file__)
PY
rm -rf downstream_check
mkdir -p downstream_check
cat > downstream_check/CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.16)
project(robot_descriptions_common_downstream_check)
find_package(sensor_models REQUIRED)
find_package(component_models REQUIRED)
find_package(jodell_description REQUIRED)
EOF
cmake -Wno-dev -S downstream_check -B downstream_check/build