Skip to content

Instantly share code, notes, and snippets.

@wsxq2
Last active June 11, 2026 05:38
Show Gist options
  • Select an option

  • Save wsxq2/b014023d89670853769a9306f405b1d6 to your computer and use it in GitHub Desktop.

Select an option

Save wsxq2/b014023d89670853769a9306f405b1d6 to your computer and use it in GitHub Desktop.
a script that init ros2 humble on ubuntu 22.04 with docker. use `-h` to show usage
#!/usr/bin/env bash
set -euo pipefail
ROS_DISTRO="${ROS_DISTRO:-humble}"
UBUNTU_CODENAME="${UBUNTU_CODENAME:-jammy}"
INSTALL_DOCKER=false
DOCKER_PROXY="${DOCKER_PROXY:-}"
RUN_TESTS=false
usage() {
cat <<EOF
Usage: $0 [OPTIONS]
Install and configure ROS 2 Humble on Ubuntu Jammy.
Options:
--install-docker Also install Docker CE and enable services.
--docker-proxy URL Configure Docker daemon proxy, for example http://127.0.0.1:7890.
--run-tests Run quick smoke tests after installation.
-h, --help Show this help.
Environment:
ROS_DISTRO ROS distribution to install. Default: humble.
UBUNTU_CODENAME Ubuntu codename. Default: jammy.
DOCKER_PROXY Same as --docker-proxy.
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--install-docker)
INSTALL_DOCKER=true
shift
;;
--docker-proxy)
[[ $# -ge 2 ]] || { echo "Missing value for --docker-proxy" >&2; exit 2; }
DOCKER_PROXY="$2"
shift 2
;;
--run-tests)
RUN_TESTS=true
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 2
;;
esac
done
log() {
printf '\n[%s] %s\n' "$(date '+%H:%M:%S')" "$*"
}
require_ubuntu() {
if [[ ! -r /etc/os-release ]]; then
echo "Cannot read /etc/os-release." >&2
exit 1
fi
# shellcheck disable=SC1091
. /etc/os-release
if [[ "${ID:-}" != "ubuntu" ]]; then
echo "This script is intended for Ubuntu; detected ID=${ID:-unknown}." >&2
exit 1
fi
if [[ "${VERSION_CODENAME:-}" != "$UBUNTU_CODENAME" ]]; then
echo "This script targets Ubuntu ${UBUNTU_CODENAME}; detected ${VERSION_CODENAME:-unknown}." >&2
exit 1
fi
}
run_sudo() {
if [[ "${EUID}" -eq 0 ]]; then
"$@"
else
sudo "$@"
fi
}
write_root_file_if_changed() {
local path="$1"
local tmp
tmp="$(mktemp)"
cat > "$tmp"
if [[ -f "$path" ]] && cmp -s "$tmp" "$path"; then
rm -f "$tmp"
log "Unchanged: $path"
return
fi
run_sudo install -D -m 0644 "$tmp" "$path"
rm -f "$tmp"
log "Updated: $path"
}
backup_once() {
local path="$1"
local backup="${path}.bak"
if [[ -f "$path" && ! -e "$backup" ]]; then
run_sudo cp -a "$path" "$backup"
log "Backed up $path to $backup"
fi
}
install_packages() {
local missing=()
local pkg
for pkg in "$@"; do
if ! dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
missing+=("$pkg")
fi
done
if [[ "${#missing[@]}" -eq 0 ]]; then
log "Packages already installed: $*"
return
fi
log "Installing packages: ${missing[*]}"
run_sudo apt-get install -y "${missing[@]}"
}
apt_update() {
log "Updating apt package indexes"
run_sudo apt-get update
}
download_root_file() {
local url="$1"
local path="$2"
if command -v curl >/dev/null 2>&1; then
run_sudo curl -fsSL "$url" -o "$path"
elif command -v wget >/dev/null 2>&1; then
run_sudo wget -qO "$path" "$url"
else
echo "Need curl or wget to download $url before apt can be updated." >&2
exit 1
fi
}
ensure_docker_key_for_existing_source() {
local docker_source="/etc/apt/sources.list.d/docker.list"
if [[ ! -f "$docker_source" ]] || ! grep -q 'download.docker.com/linux/ubuntu' "$docker_source"; then
return
fi
log "Repairing existing Docker apt source before apt update"
run_sudo install -m 0755 -d /etc/apt/keyrings
if [[ ! -s /etc/apt/keyrings/docker.asc ]]; then
download_root_file https://download.docker.com/linux/ubuntu/gpg /etc/apt/keyrings/docker.asc
run_sudo chmod a+r /etc/apt/keyrings/docker.asc
fi
write_root_file_if_changed "$docker_source" <<EOF
deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME} stable
EOF
}
configure_locale() {
log "Configuring locale"
install_packages locales
if ! locale -a | grep -qi '^en_US\.utf8$'; then
run_sudo locale-gen en_US en_US.UTF-8
fi
if ! grep -q '^LANG=en_US.UTF-8$' /etc/default/locale 2>/dev/null; then
run_sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
fi
export LANG=en_US.UTF-8
}
configure_ubuntu_sources() {
log "Configuring Ubuntu apt mirror"
backup_once /etc/apt/sources.list
write_root_file_if_changed /etc/apt/sources.list <<EOF
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ ${UBUNTU_CODENAME} main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ ${UBUNTU_CODENAME}-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ ${UBUNTU_CODENAME}-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ ${UBUNTU_CODENAME}-security main restricted universe multiverse
EOF
}
configure_ros_sources() {
log "Configuring ROS 2 apt source"
install_packages curl gnupg2 software-properties-common
run_sudo add-apt-repository -y universe
if [[ ! -s /usr/share/keyrings/ros-archive-keyring.gpg ]]; then
download_root_file https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \
/usr/share/keyrings/ros-archive-keyring.gpg
fi
write_root_file_if_changed /etc/apt/sources.list.d/ros2.list <<EOF
deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] https://mirrors.tuna.tsinghua.edu.cn/ros2/ubuntu ${UBUNTU_CODENAME} main
EOF
}
configure_rosdep_sources() {
local rosdep_file="/etc/ros/rosdep/sources.list.d/20-default.list"
log "Configuring rosdep source list"
if [[ -s "$rosdep_file" ]]; then
log "Unchanged: $rosdep_file"
return
fi
run_sudo install -d -m 0755 /etc/ros/rosdep/sources.list.d
run_sudo curl -L \
https://mirrors.tuna.tsinghua.edu.cn/github-raw/ros/rosdistro/master/rosdep/sources.list.d/20-default.list \
-o "$rosdep_file"
}
install_ros() {
configure_ubuntu_sources
ensure_docker_key_for_existing_source
apt_update
configure_locale
configure_ros_sources
configure_rosdep_sources
apt_update
install_packages "ros-${ROS_DISTRO}-desktop" ros-dev-tools
}
remove_conflicting_docker_packages() {
local packages=(
docker.io
docker-doc
docker-compose
docker-compose-v2
podman-docker
containerd
runc
)
local installed=()
local pkg
for pkg in "${packages[@]}"; do
if dpkg-query -W -f='${Status}' "$pkg" 2>/dev/null | grep -q "install ok installed"; then
installed+=("$pkg")
fi
done
if [[ "${#installed[@]}" -gt 0 ]]; then
log "Removing conflicting Docker packages: ${installed[*]}"
run_sudo apt-get remove -y "${installed[@]}"
else
log "No conflicting Docker packages found"
fi
}
configure_docker_sources() {
log "Configuring Docker apt source"
install_packages ca-certificates curl
run_sudo install -m 0755 -d /etc/apt/keyrings
if [[ ! -s /etc/apt/keyrings/docker.asc ]]; then
download_root_file https://download.docker.com/linux/ubuntu/gpg /etc/apt/keyrings/docker.asc
run_sudo chmod a+r /etc/apt/keyrings/docker.asc
fi
write_root_file_if_changed /etc/apt/sources.list.d/docker.list <<EOF
deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME} stable
EOF
}
configure_docker_daemon() {
if [[ -z "$DOCKER_PROXY" ]]; then
log "Skipping Docker daemon proxy configuration"
return
fi
log "Configuring Docker daemon proxy"
write_root_file_if_changed /etc/docker/daemon.json <<EOF
{
"proxies": {
"http-proxy": "${DOCKER_PROXY}",
"https-proxy": "${DOCKER_PROXY}",
"no-proxy": "127.0.0.0/8,192.168.0.0/16"
}
}
EOF
}
install_docker() {
remove_conflicting_docker_packages
configure_docker_sources
apt_update
install_packages docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
configure_docker_daemon
if getent group docker >/dev/null; then
log "Group already exists: docker"
else
run_sudo groupadd docker
fi
if id -nG "$USER" | tr ' ' '\n' | grep -qx docker; then
log "User $USER is already in docker group"
else
run_sudo usermod -aG docker "$USER"
log "Added $USER to docker group; log out and back in for this to affect new shells"
fi
run_sudo systemctl enable docker.service
run_sudo systemctl enable containerd.service
run_sudo systemctl restart docker
}
run_smoke_tests() {
log "Running smoke tests"
# shellcheck disable=SC1091
source "/opt/ros/${ROS_DISTRO}/setup.bash"
ros2 --help >/dev/null
if "$INSTALL_DOCKER"; then
run_sudo docker run --rm hello-world
fi
}
main() {
require_ubuntu
install_ros
if "$INSTALL_DOCKER"; then
install_docker
else
log "Skipping Docker installation. Pass --install-docker to enable it."
fi
if "$RUN_TESTS"; then
run_smoke_tests
fi
log "Done"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment