Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
schedule:
# Run the CI automatically twice per day to look for flakyness.
- cron: "0 */12 * * *"
workflow_dispatch:

defaults:
run:
Expand Down Expand Up @@ -115,6 +116,15 @@ jobs:
ros_distribution: noetic
ros_version: 1

# ROS-O (ROS One) - ROS 1 for Ubuntu 20.04+
- docker_image: ubuntu:jammy
ros_distribution: one
ros_version: 1

- docker_image: ubuntu:noble
ros_distribution: one
ros_version: 1

# Humble Hawksbill (May 2022 - May 2027)
- docker_image: ubuntu:jammy
ros_distribution: humble
Expand Down
1 change: 1 addition & 0 deletions __test__/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe("required-ros-distributions/noetic workflow tests", () => {
describe("validate distribution test", () => {
it("test valid", async () => {
await expect(utils.validateDistro(["noetic"])).toBe(true);
await expect(utils.validateDistro(["one"])).toBe(true);
await expect(utils.validateDistro(["humble"])).toBe(true);
await expect(utils.validateDistro(["iron"])).toBe(true);
await expect(utils.validateDistro(["jazzy"])).toBe(true);
Expand Down
100 changes: 93 additions & 7 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6682,6 +6682,7 @@ const pip3Packages = [
"colcon-recursive-crawl==0.2.1",
"colcon-ros==0.3.23",
"colcon-test-result==0.3.8",
"meson>=0.60.0,<0.64.0",
"coverage",
"cryptography",
"empy<4",
Expand Down Expand Up @@ -7144,7 +7145,13 @@ function configOs() {
yield utils.exec("sudo", ["bash", "-c", "echo 'Etc/UTC' > /etc/timezone"]);
yield utils.exec("sudo", ["apt-get", "update"]);
// Install tools required to configure the worker system.
yield apt.runAptGetInstall(["curl", "gnupg2", "locales", "lsb-release"]);
yield apt.runAptGetInstall([
"ca-certificates",
"curl",
"gnupg2",
"locales",
"lsb-release",
]);
// Select a locale supporting Unicode.
yield utils.exec("sudo", ["locale-gen", "en_US", "en_US.UTF-8"]);
core.exportVariable("LANG", "en_US.UTF-8");
Expand Down Expand Up @@ -7172,24 +7179,44 @@ function addAptRepoKey() {
yield utils.exec("sudo", ["apt-key", "add", keyFilePath]);
});
}
/**
* Add ROS-O (ROS One) APT repository key.
*
* Downloads and installs the GPG key for the ROS-O repository.
*/
function addRosOneAptRepoKey() {
return __awaiter(this, void 0, void 0, function* () {
// Ensure the keyrings directory exists and ca-certificates is up to date
yield utils.exec("sudo", ["mkdir", "-p", "/etc/apt/keyrings"]);
yield utils.exec("sudo", ["update-ca-certificates"]);
yield utils.exec("sudo", [
"bash",
"-c",
"curl -sSL https://ros.packages.techfak.net/gpg.key -o /etc/apt/keyrings/ros-one-keyring.gpg",
]);
});
}
// Ubuntu distribution for ROS 1
const ros1UbuntuVersion = "focal";
/**
* Add OSRF APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
* @param needsRos1 whether ROS 1 packages are needed
* @param needsRos2 whether ROS 2 packages are needed
*/
function addAptRepo(ubuntuCodename, use_ros2_testing) {
function addAptRepo(ubuntuCodename, use_ros2_testing, needsRos1, needsRos2) {
return __awaiter(this, void 0, void 0, function* () {
// There is now no Ubuntu version overlap between ROS 1 and ROS 2
if (ros1UbuntuVersion === ubuntuCodename) {
// Add ROS 1 repository if needed
if (needsRos1) {
yield utils.exec("sudo", [
"bash",
"-c",
`echo "deb http://packages.ros.org/ros/ubuntu ${ubuntuCodename} main" > /etc/apt/sources.list.d/ros-latest.list`,
]);
}
else {
// Add ROS 2 repository if needed
if (needsRos2) {
yield utils.exec("sudo", [
"bash",
"-c",
Expand All @@ -7199,6 +7226,24 @@ function addAptRepo(ubuntuCodename, use_ros2_testing) {
yield utils.exec("sudo", ["apt-get", "update"]);
});
}
/**
* Add ROS-O (ROS One) APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
* @param use_testing whether to use the testing repository
*/
function addRosOneAptRepo(ubuntuCodename, use_testing) {
return __awaiter(this, void 0, void 0, function* () {
const arch = yield utils.getArch();
const repo = use_testing ? `${ubuntuCodename}-testing` : ubuntuCodename;
yield utils.exec("sudo", [
"bash",
"-c",
`echo "deb [arch=${arch} signed-by=/etc/apt/keyrings/ros-one-keyring.gpg] https://ros.packages.techfak.net ${repo} main" > /etc/apt/sources.list.d/ros-one.list`,
]);
yield utils.exec("sudo", ["apt-get", "update"]);
});
}
/**
* Initialize rosdep.
*/
Expand All @@ -7216,6 +7261,20 @@ function rosdepInit() {
yield utils.exec("sudo", ["rosdep", "init"]);
});
}
/**
* Configure rosdep for ROS-O (ROS One).
*
* Adds custom rosdep source for ROS-O packages.
*/
function configureRosOneRosdep() {
return __awaiter(this, void 0, void 0, function* () {
yield utils.exec("sudo", [
"bash",
"-c",
'echo "yaml https://ros.packages.techfak.net/ros-one.yaml one" > /etc/ros/rosdep/sources.list.d/1-ros-one.list',
]);
});
}
/**
* Install ROS 1 or 2 (development packages and/or ROS binaries) on a Linux worker.
*/
Expand All @@ -7224,10 +7283,32 @@ function runLinux() {
// Get user input & validate
const use_ros2_testing = core.getInput("use-ros2-testing") === "true";
const installConnext = core.getInput("install-connext") === "true";
const requiredDistros = utils.getRequiredRosDistributions();
const needsRosOne = requiredDistros.includes("one");
// Determine which ROS versions are needed
// ROS 1 distributions: noetic (from packages.ros.org/ros)
// ROS 2 distributions: rolling, humble, jazzy, iron, kilted, etc. (from packages.ros.org/ros2)
// ROS-O "one": separate repository (ros.packages.techfak.net)
const ros1Distros = ["noetic"];
const needsRos1 = requiredDistros.some((distro) => ros1Distros.includes(distro));
const needsRos2 = requiredDistros.some((distro) => !ros1Distros.includes(distro) && distro !== "one");
yield configOs();
yield addAptRepoKey();
const ubuntuCodename = yield utils.determineDistribCodename();
yield addAptRepo(ubuntuCodename, use_ros2_testing);
// For backward compatibility when no ROS distributions are specified:
// - Focal (Ubuntu 20.04): add ROS 1 repository (for focal-specific dependencies)
// - Other versions: add ROS 2 repository (for jammy/noble-specific dependencies)
// For ROS-O (one): also add ROS 2 repository as it depends on ROS 2 packages
const addRos1Repo = needsRos1 || ubuntuCodename === ros1UbuntuVersion;
const addRos2Repo = needsRos2 ||
needsRosOne ||
(requiredDistros.length === 0 && ubuntuCodename !== ros1UbuntuVersion);
yield addAptRepo(ubuntuCodename, use_ros2_testing, addRos1Repo, addRos2Repo);
// Add ROS-O repository if needed
if (needsRosOne) {
yield addRosOneAptRepoKey();
yield addRosOneAptRepo(ubuntuCodename, use_ros2_testing);
}
if ("noble" !== ubuntuCodename) {
// Temporary fix to avoid error mount: /var/lib/grub/esp: special device (...) does not exist.
const arch = yield utils.getArch();
Expand All @@ -7244,7 +7325,11 @@ function runLinux() {
yield pip.installPython3Dependencies();
}
yield rosdepInit();
for (const rosDistro of utils.getRequiredRosDistributions()) {
// Configure rosdep for ROS-O if needed
if (needsRosOne) {
yield configureRosOneRosdep();
}
for (const rosDistro of requiredDistros) {
yield apt.runAptGetInstall([`ros-${rosDistro}-desktop`]);
}
});
Expand Down Expand Up @@ -7570,6 +7655,7 @@ function getRequiredRosDistributions() {
//list of valid linux distributions
const validDistro = [
"noetic",
"one",
"humble",
"iron",
"jazzy",
Expand Down
1 change: 1 addition & 0 deletions src/package_manager/pip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const pip3Packages: string[] = [
"colcon-recursive-crawl==0.2.1",
"colcon-ros==0.3.23",
"colcon-test-result==0.3.8",
"meson>=0.60.0,<0.64.0",
"coverage",
"cryptography",
"empy<4",
Expand Down
109 changes: 103 additions & 6 deletions src/setup-ros-ubuntu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ async function configOs(): Promise<void> {
await utils.exec("sudo", ["apt-get", "update"]);

// Install tools required to configure the worker system.
await apt.runAptGetInstall(["curl", "gnupg2", "locales", "lsb-release"]);
await apt.runAptGetInstall([
"ca-certificates",
"curl",
"gnupg2",
"locales",
"lsb-release",
]);

// Select a locale supporting Unicode.
await utils.exec("sudo", ["locale-gen", "en_US", "en_US.UTF-8"]);
Expand Down Expand Up @@ -112,26 +118,49 @@ async function addAptRepoKey(): Promise<void> {
await utils.exec("sudo", ["apt-key", "add", keyFilePath]);
}

/**
* Add ROS-O (ROS One) APT repository key.
*
* Downloads and installs the GPG key for the ROS-O repository.
*/
async function addRosOneAptRepoKey(): Promise<void> {
// Ensure the keyrings directory exists and ca-certificates is up to date
await utils.exec("sudo", ["mkdir", "-p", "/etc/apt/keyrings"]);
await utils.exec("sudo", ["update-ca-certificates"]);
await utils.exec("sudo", [
"bash",
"-c",
"curl -sSL https://ros.packages.techfak.net/gpg.key -o /etc/apt/keyrings/ros-one-keyring.gpg",
]);
}

// Ubuntu distribution for ROS 1
const ros1UbuntuVersion = "focal";

/**
* Add OSRF APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
* @param needsRos1 whether ROS 1 packages are needed
* @param needsRos2 whether ROS 2 packages are needed
*/
async function addAptRepo(
ubuntuCodename: string,
use_ros2_testing: boolean,
needsRos1: boolean,
needsRos2: boolean,
): Promise<void> {
// There is now no Ubuntu version overlap between ROS 1 and ROS 2
if (ros1UbuntuVersion === ubuntuCodename) {
// Add ROS 1 repository if needed
if (needsRos1) {
await utils.exec("sudo", [
"bash",
"-c",
`echo "deb http://packages.ros.org/ros/ubuntu ${ubuntuCodename} main" > /etc/apt/sources.list.d/ros-latest.list`,
]);
} else {
}

// Add ROS 2 repository if needed
if (needsRos2) {
await utils.exec("sudo", [
"bash",
"-c",
Expand All @@ -144,6 +173,26 @@ async function addAptRepo(
await utils.exec("sudo", ["apt-get", "update"]);
}

/**
* Add ROS-O (ROS One) APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
* @param use_testing whether to use the testing repository
*/
async function addRosOneAptRepo(
ubuntuCodename: string,
use_testing: boolean,
): Promise<void> {
const arch = await utils.getArch();
const repo = use_testing ? `${ubuntuCodename}-testing` : ubuntuCodename;
await utils.exec("sudo", [
"bash",
"-c",
`echo "deb [arch=${arch} signed-by=/etc/apt/keyrings/ros-one-keyring.gpg] https://ros.packages.techfak.net ${repo} main" > /etc/apt/sources.list.d/ros-one.list`,
]);
await utils.exec("sudo", ["apt-get", "update"]);
}

/**
* Initialize rosdep.
*/
Expand All @@ -160,6 +209,19 @@ async function rosdepInit(): Promise<void> {
await utils.exec("sudo", ["rosdep", "init"]);
}

/**
* Configure rosdep for ROS-O (ROS One).
*
* Adds custom rosdep source for ROS-O packages.
*/
async function configureRosOneRosdep(): Promise<void> {
await utils.exec("sudo", [
"bash",
"-c",
'echo "yaml https://ros.packages.techfak.net/ros-one.yaml one" > /etc/ros/rosdep/sources.list.d/1-ros-one.list',
]);
}

/**
* Install ROS 1 or 2 (development packages and/or ROS binaries) on a Linux worker.
*/
Expand All @@ -168,12 +230,42 @@ export async function runLinux(): Promise<void> {
const use_ros2_testing = core.getInput("use-ros2-testing") === "true";
const installConnext = core.getInput("install-connext") === "true";

const requiredDistros = utils.getRequiredRosDistributions();
const needsRosOne = requiredDistros.includes("one");

// Determine which ROS versions are needed
// ROS 1 distributions: noetic (from packages.ros.org/ros)
// ROS 2 distributions: rolling, humble, jazzy, iron, kilted, etc. (from packages.ros.org/ros2)
// ROS-O "one": separate repository (ros.packages.techfak.net)
const ros1Distros = ["noetic"];
const needsRos1 = requiredDistros.some((distro) =>
ros1Distros.includes(distro),
);
const needsRos2 = requiredDistros.some(
(distro) => !ros1Distros.includes(distro) && distro !== "one",
);

await configOs();

await addAptRepoKey();

const ubuntuCodename = await utils.determineDistribCodename();
await addAptRepo(ubuntuCodename, use_ros2_testing);
// For backward compatibility when no ROS distributions are specified:
// - Focal (Ubuntu 20.04): add ROS 1 repository (for focal-specific dependencies)
// - Other versions: add ROS 2 repository (for jammy/noble-specific dependencies)
// For ROS-O (one): also add ROS 2 repository as it depends on ROS 2 packages
const addRos1Repo = needsRos1 || ubuntuCodename === ros1UbuntuVersion;
const addRos2Repo =
needsRos2 ||
needsRosOne ||
(requiredDistros.length === 0 && ubuntuCodename !== ros1UbuntuVersion);
await addAptRepo(ubuntuCodename, use_ros2_testing, addRos1Repo, addRos2Repo);

// Add ROS-O repository if needed
if (needsRosOne) {
await addRosOneAptRepoKey();
await addRosOneAptRepo(ubuntuCodename, use_ros2_testing);
}

if ("noble" !== ubuntuCodename) {
// Temporary fix to avoid error mount: /var/lib/grub/esp: special device (...) does not exist.
Expand All @@ -195,7 +287,12 @@ export async function runLinux(): Promise<void> {

await rosdepInit();

for (const rosDistro of utils.getRequiredRosDistributions()) {
// Configure rosdep for ROS-O if needed
if (needsRosOne) {
await configureRosOneRosdep();
}

for (const rosDistro of requiredDistros) {
await apt.runAptGetInstall([`ros-${rosDistro}-desktop`]);
}
}
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export function getRequiredRosDistributions(): string[] {
//list of valid linux distributions
const validDistro: string[] = [
"noetic",
"one",
"humble",
"iron",
"jazzy",
Expand Down