Skip to content

Commit

Permalink
Adding a GitHub action to install a Gazebo releases
Browse files Browse the repository at this point in the history
Initial work for supporting a GitHub action that can install Gazebo releases on  Ubuntu Linux. The code also includes a test on of the own action for the existing disitributions.

---------

Signed-off-by: Saurabh Kamat <[email protected]>
  • Loading branch information
sauk2 authored Jun 4, 2024
1 parent f119010 commit 2aa0b25
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 84 deletions.
57 changes: 49 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,59 @@
name: 'Test setup-gazebo'
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
- "releases/*"
schedule:
# Run the CI automatically twice per day to look for flakyness.
- cron: "0 */12 * * *"
jobs:
test_linux:
test_gazebo_install_ubuntu:
name: 'Check installation of Gazebo on Ubuntu'
runs-on: ubuntu-latest
container:
image: ${{ matrix.docker_image }}
strategy:
fail-fast: false
matrix:
docker_image:
- ubuntu:20.04
- ubuntu:22.04
gazebo_distribution:
- citadel
- fortress
- garden
- harmonic
include:
# Gazebo Citadel (Dec 2019 - Dec 2024)
- docker_image: ubuntu:focal
gazebo_distribution: citadel

# Gazebo Fortress (Sep 2021 - Sep 2026)
- docker_image: ubuntu:focal
gazebo_distribution: fortress

# Gazebo Garden (Sep 2022 - Nov 2024)
- docker_image: ubuntu:focal
gazebo_distribution: garden

# Gazebo Harmonic (Sep 2023 - Sep 2028)
- docker_image: ubuntu:jammy
gazebo_distribution: harmonic
steps:
- uses: actions/checkout@v2
- name: 'Linux setup'
uses: ./
- uses: actions/checkout@v4
- uses: actions/[email protected]
with:
node-version: "20.x"
- name: 'Check Gazebo installation on Ubuntu runner'
uses: ./
with:
required-gazebo-distributions: ${{ matrix.gazebo_distribution }}
- name: 'Test Gazebo installation'
run: |
if command -v ign > /dev/null; then
ign gazebo --versions
elif command -v gz > /dev/null; then
gz sim --versions
else
echo "Neither ign nor gz command found"
exit 1
fi
14 changes: 14 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
name: 'Setup Gazebo release'
description: |
Install a Gazebo release on a Linux system
required-gazebo-distributions:
description: |
List of Gazebo distributions to be installed.
Allowed Gazebo distributions
- citadel
- fortress
- garden
- harmonic
Multiple values can be passed using a whitespace delimited list
"fortress garden".
required: false
default: ''
runs:
using: node20
main: dist/index.js
82 changes: 79 additions & 3 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26189,7 +26189,6 @@ exports.runAptGetInstall = void 0;
const utils = __importStar(__nccwpck_require__(1314));
const aptCommandLine = [
"DEBIAN_FRONTEND=noninteractive",
"RTI_NC_LICENSE_ACCEPTED=yes",
"apt-get",
"install",
"--no-install-recommends",
Expand Down Expand Up @@ -26286,7 +26285,7 @@ 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(["wget", "curl", "gnupg2", "locales", "lsb-release", "ca-certificates"]);
// 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 All @@ -26301,12 +26300,51 @@ function configOs() {
yield apt.runAptGetInstall(["tzdata"]);
});
}
/**
* Add OSRF APT repository key.
*
* This is necessary even when building from source to install colcon, vcs, etc.
*/
function addAptRepoKey() {
return __awaiter(this, void 0, void 0, function* () {
yield utils.exec("sudo", [
"bash",
"-c",
`wget https://packages.osrfoundation.org/gazebo.gpg -O \
/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg`,
]);
});
}
/**
* Add OSRF APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
*/
function addAptRepo(ubuntuCodename) {
return __awaiter(this, void 0, void 0, function* () {
yield utils.exec("sudo", [
"bash",
"-c",
`echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] \
http://packages.osrfoundation.org/gazebo/ubuntu-stable ${ubuntuCodename} main" | \
sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null`,
]);
yield utils.exec("sudo", ["apt-get", "update"]);
});
}
/**
* Install Gazebo on a Linux worker.
*/
function runLinux() {
return __awaiter(this, void 0, void 0, function* () {
yield configOs();
yield addAptRepoKey();
// Add repo according to Ubuntu version
const ubuntuCodename = yield utils.determineDistribCodename();
yield addAptRepo(ubuntuCodename);
for (const gazeboDistro of utils.getRequiredGazeboDistributions()) {
yield apt.runAptGetInstall([`gz-${gazeboDistro}`]);
}
});
}
exports.runLinux = runLinux;
Expand Down Expand Up @@ -26411,7 +26449,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
});
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.determineDistribCodename = exports.exec = void 0;
exports.getRequiredGazeboDistributions = exports.validateDistro = exports.determineDistribCodename = exports.exec = void 0;
const actions_exec = __importStar(__nccwpck_require__(1514));
const core = __importStar(__nccwpck_require__(2186));
/**
Expand Down Expand Up @@ -26455,6 +26493,44 @@ function determineDistribCodename() {
});
}
exports.determineDistribCodename = determineDistribCodename;
// List of valid Gazebo distributions
const validDistro = ["citadel", "fortress", "garden", "harmonic"];
/**
* Validate all Gazebo input distribution names
*
* @param requiredGazeboDistributionsList
* @returns boolean Validity of Gazebo distribution
*/
function validateDistro(requiredGazeboDistributionsList) {
for (const gazeboDistro of requiredGazeboDistributionsList) {
if (validDistro.indexOf(gazeboDistro) <= -1) {
return false;
}
}
return true;
}
exports.validateDistro = validateDistro;
/**
* Gets the input of the Gazebo distributions to be installed and
* validates them
*
* @returns string[] List of validated Gazebo distributions
*/
function getRequiredGazeboDistributions() {
let requiredGazeboDistributionsList = [];
const requiredGazeboDistributions = core.getInput("required-gazebo-distributions");
if (requiredGazeboDistributions) {
requiredGazeboDistributionsList = requiredGazeboDistributions.split(RegExp("\\s"));
}
else {
throw new Error("Input cannot be empty.");
}
if (!validateDistro(requiredGazeboDistributionsList)) {
throw new Error("Input has invalid distribution names.");
}
return requiredGazeboDistributionsList;
}
exports.getRequiredGazeboDistributions = getRequiredGazeboDistributions;


/***/ }),
Expand Down
15 changes: 7 additions & 8 deletions src/package_manager/apt.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import * as utils from "../utils";

const aptCommandLine: string[] = [
"DEBIAN_FRONTEND=noninteractive",
"RTI_NC_LICENSE_ACCEPTED=yes",
"apt-get",
"install",
"--no-install-recommends",
"--quiet",
"--yes",
"DEBIAN_FRONTEND=noninteractive",
"apt-get",
"install",
"--no-install-recommends",
"--quiet",
"--yes",
];

/**
Expand All @@ -24,5 +23,5 @@ const aptCommandLine: string[] = [
* @returns Promise<number> exit code
*/
export async function runAptGetInstall(packages: string[]): Promise<number> {
return utils.exec("sudo", aptCommandLine.concat(packages));
return utils.exec("sudo", aptCommandLine.concat(packages));
}
112 changes: 73 additions & 39 deletions src/setup-gazebo-linux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,89 @@ import * as io from "@actions/io";
import * as apt from "./package_manager/apt";
import * as utils from "./utils";

import * as path from "path";
import fs from "fs";

/**
* Configure basic OS stuff.
*/
async function configOs(): Promise<void> {
// When this action runs in a Docker image, sudo may be missing.
// This installs sudo to avoid having to handle both cases (action runs as
// root, action does not run as root) everywhere in the action.
try {
await io.which("sudo", true);
} catch (err) {
await utils.exec("apt-get", ["update"]);
await utils.exec("apt-get", [
"install",
"--no-install-recommends",
"--quiet",
"--yes",
"sudo",
]);
}

await utils.exec("sudo", ["bash", "-c", "echo 'Etc/UTC' > /etc/timezone"]);
await utils.exec("sudo", ["apt-get", "update"]);

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

// Select a locale supporting Unicode.
await utils.exec("sudo", ["locale-gen", "en_US", "en_US.UTF-8"]);
core.exportVariable("LANG", "en_US.UTF-8");

// Enforce UTC time for consistency.
await utils.exec("sudo", ["bash", "-c", "echo 'Etc/UTC' > /etc/timezone"]);
await utils.exec("sudo", [
"ln",
"-sf",
"/usr/share/zoneinfo/Etc/UTC",
"/etc/localtime",
]);
await apt.runAptGetInstall(["tzdata"]);
// When this action runs in a Docker image, sudo may be missing.
// This installs sudo to avoid having to handle both cases (action runs as
// root, action does not run as root) everywhere in the action.
try {
await io.which("sudo", true);
} catch (err) {
await utils.exec("apt-get", ["update"]);
await utils.exec("apt-get", [
"install",
"--no-install-recommends",
"--quiet",
"--yes",
"sudo",
]);
}

await utils.exec("sudo", ["bash", "-c", "echo 'Etc/UTC' > /etc/timezone"]);
await utils.exec("sudo", ["apt-get", "update"]);

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

// Select a locale supporting Unicode.
await utils.exec("sudo", ["locale-gen", "en_US", "en_US.UTF-8"]);
core.exportVariable("LANG", "en_US.UTF-8");

// Enforce UTC time for consistency.
await utils.exec("sudo", ["bash", "-c", "echo 'Etc/UTC' > /etc/timezone"]);
await utils.exec("sudo", [
"ln",
"-sf",
"/usr/share/zoneinfo/Etc/UTC",
"/etc/localtime",
]);
await apt.runAptGetInstall(["tzdata"]);
}

/**
* Add OSRF APT repository key.
*
* This is necessary even when building from source to install colcon, vcs, etc.
*/
async function addAptRepoKey(): Promise<void> {
await utils.exec("sudo", [
"bash",
"-c",
`wget https://packages.osrfoundation.org/gazebo.gpg -O \
/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg`,
]);
}

/**
* Add OSRF APT repository.
*
* @param ubuntuCodename the Ubuntu version codename
*/
async function addAptRepo(ubuntuCodename: string): Promise<void> {
await utils.exec("sudo", [
"bash",
"-c",
`echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] \
http://packages.osrfoundation.org/gazebo/ubuntu-stable ${ubuntuCodename} main" | \
sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null`,
]);
await utils.exec("sudo", ["apt-get", "update"]);
}

/**
* Install Gazebo on a Linux worker.
*/
export async function runLinux(): Promise<void> {
await configOs();
await addAptRepoKey();

await configOs();
// Add repo according to Ubuntu version
const ubuntuCodename = await utils.determineDistribCodename();
await addAptRepo(ubuntuCodename);

for (const gazeboDistro of utils.getRequiredGazeboDistributions()) {
await apt.runAptGetInstall([`gz-${gazeboDistro}`]);
}
}
8 changes: 4 additions & 4 deletions src/setup-gazebo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ async function run() {
}
catch (error) {
let errorMessage = "Unknown error";
if (error instanceof Error) {
errorMessage = error.message;
}
core.setFailed(errorMessage);
if (error instanceof Error) {
errorMessage = error.message;
}
core.setFailed(errorMessage);
}
}

Expand Down
Loading

0 comments on commit 2aa0b25

Please sign in to comment.