diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 85cd6f639..1ae5acbaf 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -127,6 +127,13 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Free Disk Space + uses: jlumbroso/free-disk-space@main + with: + tool-cache: false + docker-images: false + swap-storage: false + - name: "Install latest devcontainer CLI" run: npm install -g @devcontainers/cli diff --git a/archive/test/homebrew-package/test_git_based_version.sh b/archive/test/homebrew-package/test_git_based_version.sh deleted file mode 100755 index 81e5b36a5..000000000 --- a/archive/test/homebrew-package/test_git_based_version.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -e - -source dev-container-features-test-lib - -check "type curl" type curl - -check "curl version is 7.80.0" curl --version | grep "7.80.0" - -reportResults diff --git a/archive/src/homebrew-package/README.md b/src/homebrew-package/README.md similarity index 100% rename from archive/src/homebrew-package/README.md rename to src/homebrew-package/README.md diff --git a/src/homebrew-package/devcontainer-feature.json b/src/homebrew-package/devcontainer-feature.json new file mode 100644 index 000000000..76d719f87 --- /dev/null +++ b/src/homebrew-package/devcontainer-feature.json @@ -0,0 +1,38 @@ +{ + "name": "Homebrew Package", + "id": "homebrew-package", + "version": "1.0.7", + "description": "Installs a Homebrew package.", + "documentationURL": "http://github.com/devcontainers-extra/features/tree/main/src/homebrew-package", + "installsAfter": [ + "ghcr.io/meaningful-ooo/devcontainer-features/homebrew:2" + ], + "options": { + "package": { + "type": "string", + "proposals": [ + "typescript", + "vtop", + "fkill-cli" + ], + "default": "", + "description": "Select the Homebrew package to install." + }, + "version": { + "type": "string", + "proposals": [ + "latest" + ], + "default": "latest", + "description": "Select the version of the Homebrew package to install." + }, + "installation_flags": { + "type": "string", + "proposals": [ + "--ignore-dependencies" + ], + "default": "", + "description": "Additional installation flags. These would be used as extra arguments to the brew command (`brew install @`)" + } + } +} \ No newline at end of file diff --git a/src/homebrew-package/install.sh b/src/homebrew-package/install.sh new file mode 100755 index 000000000..da0df37d7 --- /dev/null +++ b/src/homebrew-package/install.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +set -ex + +source ./library_scripts.sh + +PACKAGE=${PACKAGE:-""} +VERSION=${VERSION:-"latest"} +INSTALLATION_FLAGS=${INSTALLATION_FLAGS:-""} + +if [ -z "$PACKAGE" ]; then + echo -e "'package' variable is empty, skipping" + exit 0 +fi + +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 +fi + +check_packages() { + if ! dpkg -s "$@" >/dev/null 2>&1; then + if [ "$(find /var/lib/apt/lists/* | wc -l)" = "0" ]; then + echo "Running apt-get update..." + apt-get update -y + fi + apt-get -y install --no-install-recommends "$@" + fi +} + +ensure_curl() { + if ! type curl >/dev/null 2>&1; then + apt-get update -y && apt-get -y install --no-install-recommends curl ca-certificates + fi +} + +install_via_homebrew() { + package=$1 + version=$2 + installation_flags=$3 + + # install Homebrew if does not exists + if ! type brew >/dev/null 2>&1; then + echo "Installing Homebrew..." + + # nanolayer is a cli utility which keeps container layers as small as possible + # source code: https://github.com/devcontainers-extra/nanolayer + # `ensure_nanolayer` is a bash function that will find any existing nanolayer installations, + # and if missing - will download a temporary copy that automatically get deleted at the end + # of the script + ensure_nanolayer nanolayer_location "v0.4.29" + + $nanolayer_location \ + install \ + devcontainer-feature \ + "ghcr.io/meaningful-ooo/devcontainer-features/homebrew:2.0.4" \ + --option shallow_clone='true' --option update="true" + source /etc/profile.d/nanolayer-homebrew.sh + fi + + if [ "$version" = "latest" ]; then + package_full="$package" + else + package_full="${package}@${version}" + fi + # Solves CVE-2022-24767 mitigation in Git >2.35.2 + # For more information: https://github.blog/2022-04-12-git-security-vulnerability-announced/ + git config --system --add safe.directory "$(brew --prefix)/Homebrew/Library/Taps/homebrew/homebrew-core" + + su - "$_REMOTE_USER" </dev/null 2>&1; then + downloader=curl + elif type wget >/dev/null 2>&1; then + downloader=wget + else + downloader="" + fi + + # in case none of them is installed, install wget temporarly + if [ -z $downloader ] ; then + if [ -x "/usr/bin/apt-get" ] ; then + _apt_get_install $tempdir + elif [ -x "/sbin/apk" ] ; then + _apk_install $tempdir + else + echo "distro not supported" + exit 1 + fi + downloader="wget" + downloader_installed="true" + fi + + if [ $downloader = "wget" ] ; then + wget -q $url -O $output_location + else + curl -sfL $url -o $output_location + fi + + # NOTE: the cleanup procedure was not implemented using `trap X RETURN` only because + # alpine lack bash, and RETURN is not a valid signal under sh shell + if ! [ -z $downloader_installed ] ; then + if [ -x "/usr/bin/apt-get" ] ; then + _apt_get_cleanup $tempdir + elif [ -x "/sbin/apk" ] ; then + _apk_cleanup $tempdir + else + echo "distro not supported" + exit 1 + fi + fi + +} + + +ensure_nanolayer() { + # Ensure existance of the nanolayer cli program + local variable_name=$1 + + local required_version=$2 + # normalize version + if ! [[ $required_version == v* ]]; then + required_version=v$required_version + fi + + local nanolayer_location="" + + # If possible - try to use an already installed nanolayer + if [[ -z "${NANOLAYER_FORCE_CLI_INSTALLATION}" ]]; then + if [[ -z "${NANOLAYER_CLI_LOCATION}" ]]; then + if type nanolayer >/dev/null 2>&1; then + echo "Found a pre-existing nanolayer in PATH" + nanolayer_location=nanolayer + fi + elif [ -f "${NANOLAYER_CLI_LOCATION}" ] && [ -x "${NANOLAYER_CLI_LOCATION}" ] ; then + nanolayer_location=${NANOLAYER_CLI_LOCATION} + echo "Found a pre-existing nanolayer which were given in env variable: $nanolayer_location" + fi + + # make sure its of the required version + if ! [[ -z "${nanolayer_location}" ]]; then + local current_version + current_version=$($nanolayer_location --version) + if ! [[ $current_version == v* ]]; then + current_version=v$current_version + fi + + if ! [ $current_version == $required_version ]; then + echo "skipping usage of pre-existing nanolayer. (required version $required_version does not match existing version $current_version)" + nanolayer_location="" + fi + fi + + fi + + # If not previuse installation found, download it temporarly and delete at the end of the script + if [[ -z "${nanolayer_location}" ]]; then + + if [ "$(uname -sm)" == "Linux x86_64" ] || [ "$(uname -sm)" == "Linux aarch64" ]; then + tmp_dir=$(mktemp -d -t nanolayer-XXXXXXXXXX) + + clean_up () { + ARG=$? + rm -rf $tmp_dir + exit $ARG + } + trap clean_up EXIT + + + if [ -x "/sbin/apk" ] ; then + clib_type=musl + else + clib_type=gnu + fi + + tar_filename=nanolayer-"$(uname -m)"-unknown-linux-$clib_type.tgz + + # clean download will minimize leftover in case a downloaderlike wget or curl need to be installed + clean_download https://github.com/devcontainers-extra/nanolayer/releases/download/$required_version/$tar_filename $tmp_dir/$tar_filename + + tar xfzv $tmp_dir/$tar_filename -C "$tmp_dir" + chmod a+x $tmp_dir/nanolayer + nanolayer_location=$tmp_dir/nanolayer + + + else + echo "No binaries compiled for non-x86-linux architectures yet: $(uname -m)" + exit 1 + fi + fi + + # Expose outside the resolved location + declare -g ${variable_name}=$nanolayer_location + +} + + diff --git a/archive/test/homebrew-package/scenarios.json b/test/homebrew-package/scenarios.json similarity index 77% rename from archive/test/homebrew-package/scenarios.json rename to test/homebrew-package/scenarios.json index 6622c9e09..1996706ba 100644 --- a/archive/test/homebrew-package/scenarios.json +++ b/test/homebrew-package/scenarios.json @@ -21,20 +21,11 @@ "image": "mcr.microsoft.com/devcontainers/base:debian", "features": { "homebrew-package": { - "version": "14", + "version": "22", "package": "node" } } }, - "test_git_based_version": { - "image": "mcr.microsoft.com/devcontainers/base:debian", - "features": { - "homebrew-package": { - "version": "7.80.0", - "package": "curl" - } - } - }, "test_universal": { "image": "mcr.microsoft.com/devcontainers/universal:2", "features": { diff --git a/archive/test/homebrew-package/test_file_limit.sh b/test/homebrew-package/test_file_limit.sh similarity index 100% rename from archive/test/homebrew-package/test_file_limit.sh rename to test/homebrew-package/test_file_limit.sh diff --git a/archive/test/homebrew-package/test_latest.sh b/test/homebrew-package/test_latest.sh similarity index 100% rename from archive/test/homebrew-package/test_latest.sh rename to test/homebrew-package/test_latest.sh diff --git a/archive/test/homebrew-package/test_specific_version.sh b/test/homebrew-package/test_specific_version.sh similarity index 61% rename from archive/test/homebrew-package/test_specific_version.sh rename to test/homebrew-package/test_specific_version.sh index 3343c01e7..88ae12191 100755 --- a/archive/test/homebrew-package/test_specific_version.sh +++ b/test/homebrew-package/test_specific_version.sh @@ -6,6 +6,6 @@ source dev-container-features-test-lib check "type node" type node -check "node version is 14" node --version | grep "v14.*" +check "node version is 22" sh -c "node --version | grep 'v22.*'" reportResults diff --git a/archive/test/homebrew-package/test_universal.sh b/test/homebrew-package/test_universal.sh similarity index 100% rename from archive/test/homebrew-package/test_universal.sh rename to test/homebrew-package/test_universal.sh