From b84edd4098231e460eaeb5c6ad9e68283e1dbe8d Mon Sep 17 00:00:00 2001 From: Venkumahanti Subhankar Date: Wed, 26 Aug 2026 10:12:43 +0000 Subject: [PATCH 1/3] fix(github-cli): install binary-only extensions correctly --- src/github-cli/scripts/install-extensions.sh | 16 ++++++++++++---- test/github-cli/install_extensions.sh | 1 + test/github-cli/scenarios.json | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/github-cli/scripts/install-extensions.sh b/src/github-cli/scripts/install-extensions.sh index f8a893534..0fe3b8581 100644 --- a/src/github-cli/scripts/install-extensions.sh +++ b/src/github-cli/scripts/install-extensions.sh @@ -26,10 +26,12 @@ install_extension() { mkdir -p "${extensions_root}" if [ ! -d "${extensions_root}/${repo_name}" ]; then - git \ - -c credential.helper= \ - -c credential.helper='!gh auth git-credential' \ - clone --depth 1 "https://github.com/${extension}.git" "${extensions_root}/${repo_name}" + if ! gh extension install "${extension}"; then + git \ + -c credential.helper= \ + -c credential.helper='!gh auth git-credential' \ + clone --depth 1 "https://github.com/${extension}.git" "${extensions_root}/${repo_name}" + fi fi } @@ -79,6 +81,12 @@ if [ "$#" -ge 2 ]; then url="${url#ssh://git@github.com/}" url="${url#git@github.com:}" echo "$url" + elif [ -f "$d/manifest.yml" ]; then + owner="$(sed -n 's/^owner: //p' "$d/manifest.yml")" + name="$(sed -n 's/^name: //p' "$d/manifest.yml")" + if [ -n "$owner" ] && [ -n "$name" ]; then + echo "$owner/$name" + fi fi done fi diff --git a/test/github-cli/install_extensions.sh b/test/github-cli/install_extensions.sh index 78cb126f9..a12f562b3 100644 --- a/test/github-cli/install_extensions.sh +++ b/test/github-cli/install_extensions.sh @@ -9,6 +9,7 @@ check "gh-version" gh --version check "gh-extension-installed" gh extension list | grep -q 'dlvhdr/gh-dash' check "gh-extension-installed-2" gh extension list | grep -q 'github/gh-copilot' +check "gh-aw-runs" gh aw version # Report result reportResults diff --git a/test/github-cli/scenarios.json b/test/github-cli/scenarios.json index eafee3c59..e8b842f96 100644 --- a/test/github-cli/scenarios.json +++ b/test/github-cli/scenarios.json @@ -13,7 +13,7 @@ "features": { "github-cli": { "version": "latest", - "extensions": "dlvhdr/gh-dash,github/gh-copilot" + "extensions": "dlvhdr/gh-dash,github/gh-copilot,github/gh-aw" } } } From 4bb8d2c99e64c2dd5a2e1ae23571116c63b80976 Mon Sep 17 00:00:00 2001 From: Venkumahanti Subhankar Date: Wed, 26 Aug 2026 12:04:21 +0000 Subject: [PATCH 2/3] fix: regex parsing --- src/github-cli/scripts/install-extensions.sh | 4 ++-- test/github-cli/install_extensions.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/github-cli/scripts/install-extensions.sh b/src/github-cli/scripts/install-extensions.sh index 0fe3b8581..05285aa7b 100644 --- a/src/github-cli/scripts/install-extensions.sh +++ b/src/github-cli/scripts/install-extensions.sh @@ -82,8 +82,8 @@ if [ "$#" -ge 2 ]; then url="${url#git@github.com:}" echo "$url" elif [ -f "$d/manifest.yml" ]; then - owner="$(sed -n 's/^owner: //p' "$d/manifest.yml")" - name="$(sed -n 's/^name: //p' "$d/manifest.yml")" + owner="$(sed -nE 's/^[[:space:]]*owner:[[:space:]]*"?([^"#]+)"?.*$/\1/p' "$d/manifest.yml" | sed -n '1p')" + name="$(sed -nE 's/^[[:space:]]*name:[[:space:]]*"?([^"#]+)"?.*$/\1/p' "$d/manifest.yml" | sed -n '1p')" if [ -n "$owner" ] && [ -n "$name" ]; then echo "$owner/$name" fi diff --git a/test/github-cli/install_extensions.sh b/test/github-cli/install_extensions.sh index a12f562b3..1538ff3f2 100644 --- a/test/github-cli/install_extensions.sh +++ b/test/github-cli/install_extensions.sh @@ -9,6 +9,7 @@ check "gh-version" gh --version check "gh-extension-installed" gh extension list | grep -q 'dlvhdr/gh-dash' check "gh-extension-installed-2" gh extension list | grep -q 'github/gh-copilot' +check "gh-extension-installed-3" gh extension list | grep -q 'github/gh-aw' check "gh-aw-runs" gh aw version # Report result From 356351d171935ce883d2b4bcf104f312b0baf415 Mon Sep 17 00:00:00 2001 From: Venkumahanti Subhankar Date: Wed, 2 Sep 2026 11:00:00 +0000 Subject: [PATCH 3/3] feat(rust): Add "none" value for Rust feature `components` --- src/rust/README.md | 2 +- src/rust/devcontainer-feature.json | 7 +++-- src/rust/install.sh | 32 ++++++++++++--------- test/rust/rust_with_none_components.sh | 40 ++++++++++++++++++++++++++ test/rust/scenarios.json | 10 +++++++ 5 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 test/rust/rust_with_none_components.sh diff --git a/src/rust/README.md b/src/rust/README.md index eca22932c..bf17736ce 100644 --- a/src/rust/README.md +++ b/src/rust/README.md @@ -18,7 +18,7 @@ Installs Rust, common Rust utilities, and their required dependencies | version | Select or enter a version of Rust to install. | string | latest | | profile | Select a rustup install profile. | string | minimal | | targets | Optional comma separated list of additional Rust targets to install. | string | - | -| components | Optional, comma separated list of Rust components to be installed | string | rust-analyzer,rust-src,rustfmt,clippy | +| components | Optional, comma separated list of Rust components to be installed. Set to 'none' to install no components beyond the selected profile. | string | rust-analyzer,rust-src,rustfmt,clippy | ## Customizations diff --git a/src/rust/devcontainer-feature.json b/src/rust/devcontainer-feature.json index d8d399cde..f94d0dc18 100644 --- a/src/rust/devcontainer-feature.json +++ b/src/rust/devcontainer-feature.json @@ -1,6 +1,6 @@ { "id": "rust", - "version": "1.5.1", + "version": "1.6.0", "name": "Rust", "documentationURL": "https://github.com/devcontainers/features/tree/main/src/rust", "description": "Installs Rust, common Rust utilities, and their required dependencies", @@ -61,12 +61,13 @@ "components": { "type": "string", "default": "rust-analyzer,rust-src,rustfmt,clippy", - "description": "Optional, comma separated list of Rust components to be installed", + "description": "Optional, comma separated list of Rust components to be installed. Set to 'none' to install no components beyond the selected profile.", "proposals": [ "rust-analyzer,rust-src,rustfmt,clippy", "rust-analyzer,rust-src", "rustfmt,clippy,rust-docs", - "llvm-tools-preview,rust-src,rustfmt" + "llvm-tools-preview,rust-src,rustfmt", + "none" ] } }, diff --git a/src/rust/install.sh b/src/rust/install.sh index 56bb35ea8..171d666c0 100755 --- a/src/rust/install.sh +++ b/src/rust/install.sh @@ -10,7 +10,9 @@ RUST_VERSION="${VERSION:-"latest"}" RUSTUP_PROFILE="${PROFILE:-"minimal"}" RUSTUP_TARGETS="${TARGETS:-""}" -IFS=',' read -ra components <<< "${COMPONENTS:-rust-analyzer,rust-src,rustfmt,clippy}" +# Set to "none" to install no components beyond the selected profile. +RUSTUP_COMPONENTS="${COMPONENTS:-rust-analyzer,rust-src,rustfmt,clippy}" +IFS=',' read -ra components <<< "${RUSTUP_COMPONENTS}" export CARGO_HOME="${CARGO_HOME:-"/usr/local/cargo"}" export RUSTUP_HOME="${RUSTUP_HOME:-"/usr/local/rustup"}" @@ -396,19 +398,23 @@ if [ "${UPDATE_RUST}" = "true" ]; then echo "Updating Rust..." rustup update 2>&1 fi -# Install Rust components -echo "Installing Rust components..." -for component in "${components[@]}"; do - # Trim leading and trailing whitespace - component="${component#"${component%%[![:space:]]*}"}" && component="${component%"${component##*[![:space:]]}"}" - if [ -n "${component}" ]; then - echo "Installing Rust component: ${component}" - if ! rustup component add "${component}" 2>&1; then - echo "Warning: Failed to install component '${component}'. It may not be available for this toolchain." >&2 - exit 1 +# Install Rust components (skip entirely when explicitly set to "none") +if [ "${RUSTUP_COMPONENTS}" = "none" ]; then + echo "Skipping Rust components installation as 'components' is set to 'none'." +else + echo "Installing Rust components..." + for component in "${components[@]}"; do + # Trim leading and trailing whitespace + component="${component#"${component%%[![:space:]]*}"}" && component="${component%"${component##*[![:space:]]}"}" + if [ -n "${component}" ]; then + echo "Installing Rust component: ${component}" + if ! rustup component add "${component}" 2>&1; then + echo "Warning: Failed to install component '${component}'. It may not be available for this toolchain." >&2 + exit 1 + fi fi - fi -done + done +fi if [ -n "${RUSTUP_TARGETS}" ]; then IFS=',' read -ra targets <<< "${RUSTUP_TARGETS}" diff --git a/test/rust/rust_with_none_components.sh b/test/rust/rust_with_none_components.sh new file mode 100644 index 000000000..3cd56442f --- /dev/null +++ b/test/rust/rust_with_none_components.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -e + +# Optional: Import test library +source dev-container-features-test-lib + +# Helper function to check component is installed +check_component_installed() { + local component=$1 + if rustup component list | grep -q "${component}.*installed"; then + return 0 # Component is installed (success) + else + return 1 # Component is not installed (failure) + fi +} + +# Helper function to check component is NOT installed +check_component_not_installed() { + local component=$1 + if rustup component list | grep -q "${component}.*installed"; then + return 1 # Component is installed (failure) + else + return 0 # Component is not installed (success) + fi +} + +# 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 + +# When components is set to "none", none of the default components should be installed +check "rust-analyzer not installed" check_component_not_installed "rust-analyzer" +check "rust-src not installed" check_component_not_installed "rust-src" +check "rustfmt not installed" check_component_not_installed "rustfmt" +check "clippy not installed" check_component_not_installed "clippy" + +# Report result +reportResults diff --git a/test/rust/scenarios.json b/test/rust/scenarios.json index 87c93bfa7..de6c08fb1 100644 --- a/test/rust/scenarios.json +++ b/test/rust/scenarios.json @@ -64,6 +64,16 @@ "components": "" } } + }, + "rust_with_none_components": { + "image": "ubuntu:noble", + "features": { + "rust": { + "version": "latest", + "targets": "aarch64-unknown-linux-gnu", + "components": "none" + } + } }, "rust_with_centos": { "image": "centos:centos7",