diff --git a/src/rust/README.md b/src/rust/README.md index c19855aac..022f8abbe 100644 --- a/src/rust/README.md +++ b/src/rust/README.md @@ -31,7 +31,12 @@ Installs Rust, common Rust utilities, and their required dependencies ## OS Support -This Feature should work on recent versions of Debian/Ubuntu-based distributions with the `apt` package manager installed. +This Feature should work on recent versions of Debian/Ubuntu, RedHat Enterprise Linux, Fedora, Alma, RockyLinux +and Mariner distributions with the `apt`, `yum`, `dnf`, `microdnf` and `tdnf` package manager installed. + + +**Note:** Alpine is not supported because the rustup-init binary requires glibc to run, but Alpine Linux does not include `glibc` +by default. Instead, it uses musl libc, which is not binary-compatible with glibc. `bash` is required to execute the `install.sh` script. diff --git a/src/rust/devcontainer-feature.json b/src/rust/devcontainer-feature.json index ae67e0bdc..9c3399ab8 100644 --- a/src/rust/devcontainer-feature.json +++ b/src/rust/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "rust", - "version": "1.3.3", + "version": "1.4.0", "name": "Rust", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/rust", "description": "Installs Rust, common Rust utilities, and their required dependencies", diff --git a/src/rust/install.sh b/src/rust/install.sh index d89fbe492..7481a05f5 100755 --- a/src/rust/install.sh +++ b/src/rust/install.sh @@ -19,11 +19,77 @@ UPDATE_RUST="${UPDATE_RUST:-"false"}" set -e -# Clean up -if [ "$(ls -1 /var/lib/apt/lists/ | wc -l)" -gt -1 ]; then - rm -rf /var/lib/apt/lists/* +# Detect the Linux distribution and package manager +PKG_MANAGER="" + +# Bring in ID, ID_LIKE, VERSION_ID, VERSION_CODENAME +. /etc/os-release +# Get an adjusted ID independent of distro variants +if [ "${ID}" = "debian" ] || [ "${ID_LIKE}" = "debian" ]; then + ADJUSTED_ID="debian" +elif [ "${ID}" = "alpine" ]; then + ADJUSTED_ID="alpine" +elif [[ "${ID}" = "rhel" || "${ID}" = "fedora" || "${ID}" = "mariner" || "${ID_LIKE}" = *"rhel"* || "${ID_LIKE}" = *"fedora"* || "${ID_LIKE}" = *"mariner"* ]]; then + ADJUSTED_ID="rhel" + VERSION_CODENAME="${ID}${VERSION_ID}" +else + echo "Linux distro ${ID} not supported." + exit 1 +fi + +if [ "${ADJUSTED_ID}" = "rhel" ] && [ "${VERSION_CODENAME-}" = "centos7" ]; then + # As of 1 July 2024, mirrorlist.centos.org no longer exists. + # Update the repo files to reference vault.centos.org. + sed -i s/mirror.centos.org/vault.centos.org/g /etc/yum.repos.d/*.repo + sed -i s/^#.*baseurl=http/baseurl=http/g /etc/yum.repos.d/*.repo + sed -i s/^mirrorlist=http/#mirrorlist=http/g /etc/yum.repos.d/*.repo +fi + + +# Detect package manager +if command -v apt-get >/dev/null 2>&1; then + PKG_MANAGER="apt" +elif command -v dnf >/dev/null 2>&1; then + PKG_MANAGER="dnf" +elif command -v yum >/dev/null 2>&1; then + PKG_MANAGER="yum" +elif command -v microdnf >/dev/null 2>&1; then + PKG_MANAGER="microdnf" +elif command -v tdnf >/dev/null 2>&1; then + PKG_MANAGER="tdnf" +else + echo "No supported package manager found. Supported: apt, dnf, yum, microdnf, tdnf" + exit 1 fi +echo "Detected package manager: $PKG_MANAGER" + +# Clean up based on package manager +clean_package_cache() { + case "$PKG_MANAGER" in + apt) + if [ "$(ls -1 /var/lib/apt/lists/ 2>/dev/null | wc -l)" -gt 0 ]; then + rm -rf /var/lib/apt/lists/* + fi + ;; + dnf|yum|microdnf) + if command -v dnf >/dev/null 2>&1; then + dnf clean all + elif command -v yum >/dev/null 2>&1; then + yum clean all + elif command -v microdnf >/dev/null 2>&1; then + microdnf clean all + fi + ;; + tdnf) + tdnf clean all + ;; + esac +} + +# Initial cleanup +clean_package_cache + if [ "$(id -u)" -ne 0 ]; then echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.' exit 1 @@ -106,48 +172,174 @@ check_nightly_version_formatting() { updaterc() { if [ "${UPDATE_RC}" = "true" ]; then - echo "Updating /etc/bash.bashrc and /etc/zsh/zshrc..." - if [[ "$(cat /etc/bash.bashrc)" != *"$1"* ]]; then - echo -e "$1" >> /etc/bash.bashrc + echo "Updating shell configuration files..." + local bashrc_file="/etc/bash.bashrc" + + # Different distributions use different bashrc locations + if [ ! -f "$bashrc_file" ]; then + if [ -f "/etc/bashrc" ]; then + bashrc_file="/etc/bashrc" + elif [ -f "/etc/bash/bashrc" ]; then + bashrc_file="/etc/bash/bashrc" + fi + fi + + if [ -f "$bashrc_file" ] && [[ "$(cat "$bashrc_file")" != *"$1"* ]]; then + echo -e "$1" >> "$bashrc_file" fi + if [ -f "/etc/zsh/zshrc" ] && [[ "$(cat /etc/zsh/zshrc)" != *"$1"* ]]; then echo -e "$1" >> /etc/zsh/zshrc fi fi } -apt_get_update() -{ - if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then - echo "Running apt-get update..." - apt-get update -y - fi +# Package update functions +pkg_mgr_update() { + case "$PKG_MANAGER" in + apt) + if [ "$(find /var/lib/apt/lists/* 2>/dev/null | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi + ;; + dnf) + dnf check-update || true + ;; + yum) + yum check-update || true + ;; + microdnf) + # microdnf doesn't have check-update + true + ;; + tdnf) + tdnf makecache || true + ;; + esac +} + +# Check if package is installed +is_package_installed() { + local package=$1 + case "$PKG_MANAGER" in + apt) + dpkg -s "$package" >/dev/null 2>&1 + ;; + dnf|yum|microdnf|tdnf) + rpm -q "$package" >/dev/null 2>&1 + ;; + esac } -# Checks if packages are installed and installs them if not +# Unified package checking and installation function check_packages() { - if ! dpkg -s "$@" >/dev/null 2>&1; then - apt_get_update - apt-get -y install --no-install-recommends "$@" + local packages=("$@") + local missing_packages=() + + # Check if curl-minimal is installed and swap it with curl + if is_package_installed "curl-minimal"; then + echo "curl-minimal is installed. Swapping it with curl..." + case "$PKG_MANAGER" in + dnf|yum|microdnf) + ${PKG_MANAGER} swap curl-minimal curl -y + ;; + tdnf) + tdnf remove -y curl-minimal + tdnf install -y curl + ;; + *) + echo "Package manager does not support swapping curl-minimal with curl. Please handle this manually." + ;; + esac + fi + + # Map package names based on distribution + for i in "${!packages[@]}"; do + case "$PKG_MANAGER" in + dnf|yum|microdnf|tdnf) + case "${packages[$i]}" in + "libc6-dev") packages[$i]="glibc-devel" ;; + "python3-minimal") packages[$i]="python3" ;; + "libpython3.*") packages[$i]="python3-devel" ;; + "gnupg2") packages[$i]="gnupg" ;; + esac + ;; + esac + done + + # Check which packages are missing + for package in "${packages[@]}"; do + if [ -n "$package" ] && ! is_package_installed "$package"; then + missing_packages+=("$package") + fi + done + + # Install missing packages + if [ ${#missing_packages[@]} -gt 0 ]; then + pkg_mgr_update + case "$PKG_MANAGER" in + apt) + apt-get -y install --no-install-recommends "${missing_packages[@]}" + ;; + dnf) + dnf install -y "${missing_packages[@]}" + ;; + yum) + yum install -y "${missing_packages[@]}" + ;; + microdnf) + microdnf install -y "${missing_packages[@]}" + ;; + tdnf) + tdnf install -y "${missing_packages[@]}" + ;; + esac fi } export DEBIAN_FRONTEND=noninteractive # Install curl, lldb, python3-minimal,libpython and rust dependencies if missing -if ! dpkg -s curl ca-certificates gnupg2 lldb python3-minimal gcc libc6-dev > /dev/null 2>&1; then - apt_get_update - apt-get -y install --no-install-recommends curl ca-certificates gcc libc6-dev - apt-get -y install lldb python3-minimal libpython3.? +echo "Installing required dependencies..." +check_packages curl ca-certificates gcc libc6-dev gnupg2 git + +# Install optional dependencies (continue if they fail) +case "$PKG_MANAGER" in + apt) + check_packages lldb python3-minimal libpython3.? || true + ;; + dnf|yum|microdnf) + check_packages lldb python3 python3-devel || true + ;; + tdnf) + check_packages python3 python3-devel || true + # LLDB might not be available in Photon/Mariner + ;; +esac + +# Get architecture +if command -v dpkg >/dev/null 2>&1; then + architecture="$(dpkg --print-architecture)" +else + architecture="$(uname -m)" + # Convert common architectures to Debian equivalents + case ${architecture} in + x86_64) + architecture="amd64" + ;; + aarch64) + architecture="arm64" + ;; + esac fi -architecture="$(dpkg --print-architecture)" download_architecture="${architecture}" case ${download_architecture} in - amd64) + amd64|x86_64) download_architecture="x86_64" ;; - arm64) + arm64|aarch64) download_architecture="aarch64" ;; *) echo "(!) Architecture ${architecture} not supported." @@ -225,7 +417,6 @@ EOF chmod -R g+r+w "${RUSTUP_HOME}" "${CARGO_HOME}" # Clean up -rm -rf /var/lib/apt/lists/* +clean_package_cache echo "Done!" - diff --git a/test/rust/rust_with_almalinux_8.sh b/test/rust/rust_with_almalinux_8.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_almalinux_8.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_almalinux_9.sh b/test/rust/rust_with_almalinux_9.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_almalinux_9.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_centos.sh b/test/rust/rust_with_centos.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_centos.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_fedora.sh b/test/rust/rust_with_fedora.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_fedora.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_mariner.sh b/test/rust/rust_with_mariner.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_mariner.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_rockylinux_8.sh b/test/rust/rust_with_rockylinux_8.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_rockylinux_8.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/rust_with_rockylinux_9.sh b/test/rust/rust_with_rockylinux_9.sh new file mode 100644 index 000000000..0635916aa --- /dev/null +++ b/test/rust/rust_with_rockylinux_9.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Definition specific tests +check "cargo version" cargo --version +check "rustc version" rustc --version +check "correct rust version" rustup target list | grep aarch64-unknown-linux-gnu + + +# Report result +reportResults + diff --git a/test/rust/scenarios.json b/test/rust/scenarios.json index 83df710fb..003417958 100644 --- a/test/rust/scenarios.json +++ b/test/rust/scenarios.json @@ -15,5 +15,68 @@ "targets": "aarch64-unknown-linux-gnu" } } - } + }, + "rust_with_centos": { + "image": "centos:centos7", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_almalinux_8": { + "image": "almalinux:8", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_almalinux_9": { + "image": "almalinux:9", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_rockylinux_8": { + "image": "rockylinux:8", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_rockylinux_9": { + "image": "rockylinux:9", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_fedora": { + "image": "fedora", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + }, + "rust_with_mariner": { + "image": "mcr.microsoft.com/cbl-mariner/base/core:2.0", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu" + } + } + } } \ No newline at end of file