From bf357fa48131743376bafd3a9c8e186eadf8dcfd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:37:18 +0000 Subject: [PATCH 1/6] Initial plan From 8c41a7462b6d04a278717d9b6e45997cdefecd17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:42:16 +0000 Subject: [PATCH 2/6] Add common-setup.sh helper script with user selection logic and comprehensive tests Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/_common/common-setup.sh | 78 ++++++++++++ test/_common/test-common-setup.sh | 203 ++++++++++++++++++++++++++++++ 2 files changed, 281 insertions(+) create mode 100644 src/_common/common-setup.sh create mode 100755 test/_common/test-common-setup.sh diff --git a/src/_common/common-setup.sh b/src/_common/common-setup.sh new file mode 100644 index 000000000..550fd9bed --- /dev/null +++ b/src/_common/common-setup.sh @@ -0,0 +1,78 @@ +#!/bin/bash +#------------------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See https://github.com/devcontainers/features/blob/main/LICENSE for license information. +#------------------------------------------------------------------------------------------------------------------------- +# +# Helper script for common feature setup tasks, including user selection logic. +# Maintainer: The Dev Container spec maintainers + +# Determine the appropriate non-root user +# Usage: determine_user_from_input USERNAME [FALLBACK_USER] +# +# This function resolves the USERNAME variable based on the input value: +# - If USERNAME is "auto" or "automatic", it will detect an existing non-root user +# - If USERNAME is "none" or doesn't exist, it will fall back to root +# - Otherwise, it validates the specified USERNAME exists +# +# Arguments: +# USERNAME - The username input (typically from feature configuration) +# FALLBACK_USER - Optional fallback user when no user is found in automatic mode (defaults to "root") +# +# Returns: +# The resolved username is printed to stdout +# +# Examples: +# USERNAME=$(determine_user_from_input "automatic") +# USERNAME=$(determine_user_from_input "vscode") +# USERNAME=$(determine_user_from_input "auto" "vscode") +# +determine_user_from_input() { + local input_username="${1:-automatic}" + local fallback_user="${2:-root}" + local resolved_username="" + + if [ "${input_username}" = "auto" ] || [ "${input_username}" = "automatic" ]; then + # Automatic mode: try to detect an existing non-root user + + # First, check if _REMOTE_USER is set and is not root + if [ -n "${_REMOTE_USER:-}" ] && [ "${_REMOTE_USER}" != "root" ]; then + resolved_username="${_REMOTE_USER}" + else + # Try to find a non-root user from a list of common usernames + # The list includes: devcontainer, vscode, node, codespace, and the user with UID 1000 + local possible_users=("devcontainer" "vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '')") + + for current_user in "${possible_users[@]}"; do + # Skip empty entries + if [ -z "${current_user}" ]; then + continue + fi + + # Check if user exists + if id -u "${current_user}" > /dev/null 2>&1; then + resolved_username="${current_user}" + break + fi + done + + # If no user found, use the fallback + if [ -z "${resolved_username}" ]; then + resolved_username="${fallback_user}" + fi + fi + elif [ "${input_username}" = "none" ]; then + # Explicit "none" means use root + resolved_username="root" + else + # Specific username provided - validate it exists + if id -u "${input_username}" > /dev/null 2>&1; then + resolved_username="${input_username}" + else + # User doesn't exist, fall back to root + resolved_username="root" + fi + fi + + echo "${resolved_username}" +} diff --git a/test/_common/test-common-setup.sh b/test/_common/test-common-setup.sh new file mode 100755 index 000000000..e2a5a6dcb --- /dev/null +++ b/test/_common/test-common-setup.sh @@ -0,0 +1,203 @@ +#!/bin/bash +#------------------------------------------------------------------------------------------------------------------------- +# Tests for common-setup.sh helper functions +# These tests validate the determine_user_from_input function +#------------------------------------------------------------------------------------------------------------------------- + +set -e + +# Source the helper script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../../src/_common/common-setup.sh" + +# Test counters +PASSED=0 +FAILED=0 +TOTAL=0 + +# Helper function to run a test +run_test() { + local test_name="$1" + local expected="$2" + local actual="$3" + + TOTAL=$((TOTAL + 1)) + + if [ "${expected}" = "${actual}" ]; then + echo "✓ PASS: ${test_name}" + PASSED=$((PASSED + 1)) + else + echo "✗ FAIL: ${test_name}" + echo " Expected: '${expected}'" + echo " Actual: '${actual}'" + FAILED=$((FAILED + 1)) + fi +} + +# Test 1: Automatic mode finds existing user or fallback +test_automatic_no_users() { + local result=$(determine_user_from_input "automatic") + # Should find either a known user or fallback to root + # On this system, there may be a UID 1000 user (like packer) + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-root}" + run_test "Automatic mode with no matching common users finds UID 1000 or root" "${expected}" "${result}" +} + +# Test 2: Automatic mode with fallback user +test_automatic_with_fallback() { + local result=$(determine_user_from_input "automatic" "vscode") + # Should find a user or use the fallback - check if UID 1000 exists + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-vscode}" + run_test "Automatic mode with custom fallback finds UID 1000 or uses fallback" "${expected}" "${result}" +} + +# Test 3: Explicit "none" should return root +test_none_returns_root() { + local result=$(determine_user_from_input "none") + run_test "Explicit 'none' returns root" "root" "${result}" +} + +# Test 4: Explicit "none" ignores fallback +test_none_ignores_fallback() { + local result=$(determine_user_from_input "none" "vscode") + run_test "Explicit 'none' ignores fallback" "root" "${result}" +} + +# Test 5: Existing user (root) should return root +test_existing_user_root() { + local result=$(determine_user_from_input "root") + run_test "Existing user 'root' returns root" "root" "${result}" +} + +# Test 6: Non-existing user should return root +test_nonexisting_user() { + local result=$(determine_user_from_input "nonexistentuser12345") + run_test "Non-existing user returns root" "root" "${result}" +} + +# Test 7: Auto mode (synonym for automatic) +test_auto_synonym() { + local result=$(determine_user_from_input "auto" "customfallback") + # Should behave same as automatic - find UID 1000 or use fallback + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-customfallback}" + run_test "Auto mode with fallback finds UID 1000 or uses fallback" "${expected}" "${result}" +} + +# Test 8: _REMOTE_USER environment variable (when set and not root) +test_remote_user_set() { + # Test with an existing user (root is always available) + # We'll use a user that exists on the system + local existing_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo 'root') + + export _REMOTE_USER="${existing_user}" + local result=$(determine_user_from_input "automatic") + unset _REMOTE_USER + + run_test "_REMOTE_USER set to non-root user" "${existing_user}" "${result}" +} + +# Test 9: _REMOTE_USER set to root should use fallback logic +test_remote_user_root() { + export _REMOTE_USER="root" + local result=$(determine_user_from_input "automatic" "mydefault") + unset _REMOTE_USER + + # Should use fallback logic - find UID 1000 or use fallback + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-mydefault}" + run_test "_REMOTE_USER set to root uses fallback logic" "${expected}" "${result}" +} + +# Test 10: Finding vscode user if it exists +test_find_vscode_user() { + # Check if vscode user exists + if id -u vscode > /dev/null 2>&1; then + local result=$(determine_user_from_input "automatic") + # Should find vscode (it's second in priority after devcontainer) + run_test "Finds vscode user in automatic mode" "vscode" "${result}" + else + # Skip this test if vscode user doesn't exist + run_test "Finds vscode user in automatic mode (SKIPPED - user doesn't exist)" "SKIP" "SKIP" + fi +} + +# Test 11: Finding devcontainer user (highest priority) +test_find_devcontainer_user() { + # Check if devcontainer user exists + if id -u devcontainer > /dev/null 2>&1; then + local result=$(determine_user_from_input "automatic") + # Should find devcontainer (highest priority) + run_test "Finds devcontainer user (highest priority)" "devcontainer" "${result}" + else + # Skip this test if devcontainer user doesn't exist + run_test "Finds devcontainer user (highest priority) (SKIPPED - user doesn't exist)" "SKIP" "SKIP" + fi +} + +# Test 12: Finding user with UID 1000 +test_find_uid_1000() { + # Check if there's a user with UID 1000 + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + + if [ -n "${uid_1000_user}" ] && \ + ! id -u devcontainer > /dev/null 2>&1 && \ + ! id -u vscode > /dev/null 2>&1 && \ + ! id -u node > /dev/null 2>&1 && \ + ! id -u codespace > /dev/null 2>&1; then + # Only test if UID 1000 exists and no higher priority users exist + local result=$(determine_user_from_input "automatic") + run_test "Finds user with UID 1000" "${uid_1000_user}" "${result}" + else + # Skip this test if conditions aren't met + run_test "Finds user with UID 1000 (SKIPPED - conditions not met)" "SKIP" "SKIP" + fi +} + +# Test 13: Empty input defaults to "automatic" +test_empty_input() { + local result=$(determine_user_from_input "" "mydefault") + # Should behave as automatic mode - find UID 1000 or use fallback + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-mydefault}" + run_test "Empty input treated as automatic and finds UID 1000 or uses fallback" "${expected}" "${result}" +} + +# Run all tests +echo "Running tests for common-setup.sh..." +echo "======================================" +echo "" + +test_automatic_no_users +test_automatic_with_fallback +test_none_returns_root +test_none_ignores_fallback +test_existing_user_root +test_nonexisting_user +test_auto_synonym +test_remote_user_set +test_remote_user_root +test_find_vscode_user +test_find_devcontainer_user +test_find_uid_1000 +test_empty_input + +# Print summary +echo "" +echo "======================================" +echo "Test Summary:" +echo " Total: ${TOTAL}" +echo " Passed: ${PASSED}" +echo " Failed: ${FAILED}" +echo "======================================" + +# Exit with appropriate code +if [ ${FAILED} -eq 0 ]; then + echo "All tests passed! ✓" + exit 0 +else + echo "Some tests failed! ✗" + exit 1 +fi From 8c1c8dcfacc90cdef58fe77f3de0c20b0597e15e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:47:16 +0000 Subject: [PATCH 3/6] Update all feature scripts to use common-setup.sh helper for user selection Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/anaconda/install.sh | 20 +++++------------- src/common-utils/main.sh | 28 ++++++++----------------- src/conda/install.sh | 20 +++++------------- src/desktop-lite/install.sh | 20 +++++------------- src/docker-in-docker/install.sh | 20 +++++------------- src/docker-outside-of-docker/install.sh | 20 +++++------------- src/go/install.sh | 20 +++++------------- src/hugo/install.sh | 20 +++++------------- src/java/install.sh | 20 +++++------------- src/kubectl-helm-minikube/install.sh | 20 +++++------------- src/node/install.sh | 20 +++++------------- src/oryx/install.sh | 20 +++++------------- src/php/install.sh | 24 ++++++--------------- src/python/install.sh | 20 +++++------------- src/ruby/install.sh | 20 +++++------------- src/rust/install.sh | 20 +++++------------- src/sshd/install.sh | 20 +++++------------- 17 files changed, 90 insertions(+), 262 deletions(-) diff --git a/src/anaconda/install.sh b/src/anaconda/install.sh index 6f57a3144..01b7b15ef 100755 --- a/src/anaconda/install.sh +++ b/src/anaconda/install.sh @@ -81,22 +81,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u "${CURRENT_USER}" > /dev/null 2>&1; then - USERNAME="${CURRENT_USER}" - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") architecture="$(uname -m)" # Normalize arm64 to aarch64 for consistency diff --git a/src/common-utils/main.sh b/src/common-utils/main.sh index b0fd2f3b0..481dc3c95 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -399,25 +399,15 @@ case "${ADJUSTED_ID}" in ;; esac -# If in automatic mode, determine if a user already exists, if not use vscode -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - if [ "${_REMOTE_USER}" != "root" ]; then - USERNAME="${_REMOTE_USER}" - else - USERNAME="" - POSSIBLE_USERS=("devcontainer" "vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=vscode - fi - fi -elif [ "${USERNAME}" = "none" ]; then - USERNAME=root +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + +# If in automatic mode, determine if a user already exists, if not use vscode (which will be created) +USERNAME=$(determine_user_from_input "${USERNAME}" "vscode") + +# Handle the special "none" case for common-utils +if [ "${USERNAME}" = "none" ]; then USER_UID=0 USER_GID=0 fi diff --git a/src/conda/install.sh b/src/conda/install.sh index 43ab82f54..20e4feeac 100644 --- a/src/conda/install.sh +++ b/src/conda/install.sh @@ -27,22 +27,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u "${CURRENT_USER}" > /dev/null 2>&1; then - USERNAME="${CURRENT_USER}" - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") architecture="$(uname -m)" if [ "${architecture}" != "x86_64" ]; then diff --git a/src/desktop-lite/install.sh b/src/desktop-lite/install.sh index 4575cc4f9..3f366b354 100755 --- a/src/desktop-lite/install.sh +++ b/src/desktop-lite/install.sh @@ -71,22 +71,12 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") # Add default Fluxbox config files if none are already present fluxbox_apps="$(cat \ << 'EOF' diff --git a/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index 3f30158e5..db365d394 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -44,22 +44,12 @@ fi # See: https://github.com/microsoft/vscode-dev-containers/blob/main/script-library/shared/utils.sh ################### +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") # Package manager update function pkg_mgr_update() { diff --git a/src/docker-outside-of-docker/install.sh b/src/docker-outside-of-docker/install.sh index 74fd63530..034dde234 100755 --- a/src/docker-outside-of-docker/install.sh +++ b/src/docker-outside-of-docker/install.sh @@ -38,22 +38,12 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") apt_get_update() { diff --git a/src/go/install.sh b/src/go/install.sh index 85fea5dc4..f4628b37e 100755 --- a/src/go/install.sh +++ b/src/go/install.sh @@ -174,22 +174,12 @@ if ! type awk >/dev/null 2>&1; then check_packages awk fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") export DEBIAN_FRONTEND=noninteractive diff --git a/src/hugo/install.sh b/src/hugo/install.sh index b268e384b..bbfd2bb13 100755 --- a/src/hugo/install.sh +++ b/src/hugo/install.sh @@ -29,22 +29,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") architecture="$(uname -m)" if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then diff --git a/src/java/install.sh b/src/java/install.sh index 62fd39462..f1d13d201 100644 --- a/src/java/install.sh +++ b/src/java/install.sh @@ -154,22 +154,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") updaterc() { local _bashrc diff --git a/src/kubectl-helm-minikube/install.sh b/src/kubectl-helm-minikube/install.sh index f0cf1c946..cbafb18e9 100755 --- a/src/kubectl-helm-minikube/install.sh +++ b/src/kubectl-helm-minikube/install.sh @@ -28,22 +28,12 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") USERHOME="/home/$USERNAME" if [ "$USERNAME" = "root" ]; then diff --git a/src/node/install.sh b/src/node/install.sh index 71d91ffe2..e241d47d7 100755 --- a/src/node/install.sh +++ b/src/node/install.sh @@ -247,22 +247,12 @@ if ! type awk >/dev/null 2>&1; then check_packages awk fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive diff --git a/src/oryx/install.sh b/src/oryx/install.sh index cf67db6b1..b43580b23 100755 --- a/src/oryx/install.sh +++ b/src/oryx/install.sh @@ -25,22 +25,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") function updaterc() { if [ "${UPDATE_RC}" = "true" ]; then diff --git a/src/php/install.sh b/src/php/install.sh index 357395e88..4eea3a4ed 100755 --- a/src/php/install.sh +++ b/src/php/install.sh @@ -36,24 +36,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh -# If in automatic mode, determine if a user already exists, if not use vscode -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ]; then - USERNAME=root - USER_UID=0 - USER_GID=0 -fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + +# If in automatic mode, determine if a user already exists, if not use root +USERNAME=$(determine_user_from_input "${USERNAME}" "root") architecture="$(uname -m)" if [ "${architecture}" != "amd64" ] && [ "${architecture}" != "x86_64" ] && [ "${architecture}" != "arm64" ] && [ "${architecture}" != "aarch64" ]; then diff --git a/src/python/install.sh b/src/python/install.sh index be895927d..86345bd34 100755 --- a/src/python/install.sh +++ b/src/python/install.sh @@ -835,22 +835,12 @@ if ! type awk >/dev/null 2>&1; then check_packages awk fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") # Ensure apt is in non-interactive to avoid prompts export DEBIAN_FRONTEND=noninteractive diff --git a/src/ruby/install.sh b/src/ruby/install.sh index 39cb5be03..87a6d7384 100755 --- a/src/ruby/install.sh +++ b/src/ruby/install.sh @@ -39,22 +39,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") updaterc() { if [ "${UPDATE_RC}" = "true" ]; then diff --git a/src/rust/install.sh b/src/rust/install.sh index 99a7ba8f5..54bcd1748 100755 --- a/src/rust/install.sh +++ b/src/rust/install.sh @@ -101,22 +101,12 @@ rm -f /etc/profile.d/00-restore-env.sh echo "export PATH=${PATH//$(sh -lc 'echo $PATH')/\$PATH}" > /etc/profile.d/00-restore-env.sh chmod +x /etc/profile.d/00-restore-env.sh +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u "${CURRENT_USER}" > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u "${USERNAME}" > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") # Figure out correct version of a three part version number is not passed find_version_from_git_tags() { diff --git a/src/sshd/install.sh b/src/sshd/install.sh index 9b9ddedf2..281d095b4 100755 --- a/src/sshd/install.sh +++ b/src/sshd/install.sh @@ -25,22 +25,12 @@ if [ "$(id -u)" -ne 0 ]; then exit 1 fi +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + # Determine the appropriate non-root user -if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then - USERNAME="" - POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do - if id -u ${CURRENT_USER} > /dev/null 2>&1; then - USERNAME=${CURRENT_USER} - break - fi - done - if [ "${USERNAME}" = "" ]; then - USERNAME=root - fi -elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then - USERNAME=root -fi +USERNAME=$(determine_user_from_input "${USERNAME}" "root") apt_get_update() { From 79767c0edf4ff1d55d165b60b3c56e1f3abe14b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:48:10 +0000 Subject: [PATCH 4/6] Update java wrapper.sh to use common-setup.sh helper --- src/java/wrapper.sh | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/java/wrapper.sh b/src/java/wrapper.sh index bea343f96..dcd868955 100644 --- a/src/java/wrapper.sh +++ b/src/java/wrapper.sh @@ -24,18 +24,9 @@ if [ "${is_jdk_8}" = "true" ]; then jdk_11_folder="$(ls --format=single-column ${SDKMAN_DIR}/candidates/java | grep -oE -m 1 '11\..+')" ln -s "${SDKMAN_DIR}/candidates/java/${jdk_11_folder}" /extension-java-home - # Determine the appropriate non-root user - username="" - possible_users=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") - for current_user in "${POSSIBLE_USERS[@]}"; do - if id -u ${current_user} > /dev/null 2>&1; then - username=${current_user} - break - fi - done - if [ "${username}" = "" ]; then - username=root - fi + # Source common helper functions to determine the appropriate non-root user + source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../_common/common-setup.sh" + username=$(determine_user_from_input "${USERNAME}" "root") else ln -s ${SDKMAN_DIR}/candidates/java/current /extension-java-home fi From 5c4e3565464486924420cfa7eab9658c0b25d6a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:49:17 +0000 Subject: [PATCH 5/6] Add documentation for _common helper scripts Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/_common/README.md | 122 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/_common/README.md diff --git a/src/_common/README.md b/src/_common/README.md new file mode 100644 index 000000000..64ea15c66 --- /dev/null +++ b/src/_common/README.md @@ -0,0 +1,122 @@ +# Common Helper Scripts + +This directory contains common helper scripts that can be shared across multiple features to avoid code duplication. + +## common-setup.sh + +A helper script that provides common setup functions used across multiple features. + +### Functions + +#### `determine_user_from_input` + +Determines the appropriate non-root user based on the input username. + +**Usage:** +```bash +# Source the helper script +source "${SCRIPT_DIR}/../_common/common-setup.sh" + +# Determine the user +USERNAME=$(determine_user_from_input "${USERNAME}" "root") +``` + +**Parameters:** +- `$1` (required): Input username from feature configuration (e.g., "automatic", "auto", "none", or a specific username) +- `$2` (optional): Fallback username when no user is found in automatic mode (defaults to "root") + +**Behavior:** +- **"auto" or "automatic"**: + - First checks if `_REMOTE_USER` environment variable is set and is not "root" + - If `_REMOTE_USER` is root or not set, searches for an existing user from the priority list: + 1. `devcontainer` + 2. `vscode` + 3. `node` + 4. `codespace` + 5. User with UID 1000 (from `/etc/passwd`) + - If no user is found, returns the fallback user (default: "root") + +- **"none"**: Always returns "root" + +- **Specific username**: + - Validates the user exists using `id -u` + - If the user exists, returns that username + - If the user doesn't exist, returns "root" + +**Examples:** + +```bash +# Basic usage with default fallback (root) +USERNAME=$(determine_user_from_input "automatic") + +# With custom fallback +USERNAME=$(determine_user_from_input "automatic" "vscode") + +# Explicit user +USERNAME=$(determine_user_from_input "myuser") + +# None (always returns root) +USERNAME=$(determine_user_from_input "none") +``` + +**Return Value:** +Prints the resolved username to stdout, which can be captured using command substitution. + +## Testing + +Tests for the helper scripts are located in `/test/_common/`. Run the tests with: + +```bash +bash test/_common/test-common-setup.sh +``` + +## Edge Cases + +The helper handles several edge cases: + +1. **Missing awk**: Some systems (like Mariner) don't have awk by default. Features should install it before sourcing the helper if needed. + +2. **UID 1000 lookup**: The user with UID 1000 is included in the search as it's commonly the first non-system user created. + +3. **_REMOTE_USER behavior**: When `_REMOTE_USER` is set to a non-root user, it takes priority over all other detection methods in automatic mode. + +4. **Empty user list entries**: The helper safely handles empty entries in the user detection loop. + +## Migration Guide + +To migrate an existing feature to use the common helper: + +### Before: +```bash +# Determine the appropriate non-root user +if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then + USERNAME="" + POSSIBLE_USERS=("vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd)") + for CURRENT_USER in "${POSSIBLE_USERS[@]}"; do + if id -u ${CURRENT_USER} > /dev/null 2>&1; then + USERNAME=${CURRENT_USER} + break + fi + done + if [ "${USERNAME}" = "" ]; then + USERNAME=root + fi +elif [ "${USERNAME}" = "none" ] || ! id -u ${USERNAME} > /dev/null 2>&1; then + USERNAME=root +fi +``` + +### After: +```bash +# Source common helper functions +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "${SCRIPT_DIR}/../_common/common-setup.sh" + +# Determine the appropriate non-root user +USERNAME=$(determine_user_from_input "${USERNAME}" "root") +``` + +**Note:** For features like `common-utils` that create users and need a different fallback, use: +```bash +USERNAME=$(determine_user_from_input "${USERNAME}" "vscode") +``` From 964dccffa668fd1e2f24e86e051e3af0550e8c6c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 29 Jan 2026 13:53:02 +0000 Subject: [PATCH 6/6] Fix code review issues: validate _REMOTE_USER, fix common-utils none handling, remove dead code Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/_common/common-setup.sh | 13 +++++++++++-- src/common-utils/main.sh | 13 ++++++++----- src/java/wrapper.sh | 4 ---- test/_common/test-common-setup.sh | 26 ++++++++++++++++++++++---- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/_common/common-setup.sh b/src/_common/common-setup.sh index 550fd9bed..d2ac866cf 100644 --- a/src/_common/common-setup.sh +++ b/src/_common/common-setup.sh @@ -37,8 +37,17 @@ determine_user_from_input() { # First, check if _REMOTE_USER is set and is not root if [ -n "${_REMOTE_USER:-}" ] && [ "${_REMOTE_USER}" != "root" ]; then - resolved_username="${_REMOTE_USER}" - else + # Verify the user exists before using it + if id -u "${_REMOTE_USER}" > /dev/null 2>&1; then + resolved_username="${_REMOTE_USER}" + else + # _REMOTE_USER doesn't exist, fall through to normal detection + resolved_username="" + fi + fi + + # If we didn't resolve via _REMOTE_USER, try to find a non-root user + if [ -z "${resolved_username}" ]; then # Try to find a non-root user from a list of common usernames # The list includes: devcontainer, vscode, node, codespace, and the user with UID 1000 local possible_users=("devcontainer" "vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '')") diff --git a/src/common-utils/main.sh b/src/common-utils/main.sh index 481dc3c95..eaa4cb512 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -403,13 +403,16 @@ esac SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "${SCRIPT_DIR}/../_common/common-setup.sh" -# If in automatic mode, determine if a user already exists, if not use vscode (which will be created) -USERNAME=$(determine_user_from_input "${USERNAME}" "vscode") - -# Handle the special "none" case for common-utils -if [ "${USERNAME}" = "none" ]; then +# Handle the special "none" case for common-utils before user determination +# The "none" case sets USER_UID and USER_GID to 0 +ORIGINAL_USERNAME="${USERNAME}" +if [ "${ORIGINAL_USERNAME}" = "none" ]; then + USERNAME="root" USER_UID=0 USER_GID=0 +else + # If in automatic mode, determine if a user already exists, if not use vscode (which will be created) + USERNAME=$(determine_user_from_input "${USERNAME}" "vscode") fi # Create or update a non-root user to match UID/GID. group_name="${USERNAME}" diff --git a/src/java/wrapper.sh b/src/java/wrapper.sh index dcd868955..202a1856d 100644 --- a/src/java/wrapper.sh +++ b/src/java/wrapper.sh @@ -23,10 +23,6 @@ if [ "${is_jdk_8}" = "true" ]; then ./install.sh "${ADDITIONAL_JAVA_VERSION}" "${SDKMAN_DIR}" "${USERNAME}" "${UPDATE_RC}" jdk_11_folder="$(ls --format=single-column ${SDKMAN_DIR}/candidates/java | grep -oE -m 1 '11\..+')" ln -s "${SDKMAN_DIR}/candidates/java/${jdk_11_folder}" /extension-java-home - - # Source common helper functions to determine the appropriate non-root user - source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/../_common/common-setup.sh" - username=$(determine_user_from_input "${USERNAME}" "root") else ln -s ${SDKMAN_DIR}/candidates/java/current /extension-java-home fi diff --git a/test/_common/test-common-setup.sh b/test/_common/test-common-setup.sh index e2a5a6dcb..149571e50 100755 --- a/test/_common/test-common-setup.sh +++ b/test/_common/test-common-setup.sh @@ -113,14 +113,17 @@ test_remote_user_root() { # Test 10: Finding vscode user if it exists test_find_vscode_user() { - # Check if vscode user exists - if id -u vscode > /dev/null 2>&1; then + # Check if vscode user exists and no higher priority users exist + if id -u vscode > /dev/null 2>&1 && \ + ! id -u devcontainer > /dev/null 2>&1; then + # Unset _REMOTE_USER to ensure it doesn't interfere + unset _REMOTE_USER local result=$(determine_user_from_input "automatic") # Should find vscode (it's second in priority after devcontainer) run_test "Finds vscode user in automatic mode" "vscode" "${result}" else - # Skip this test if vscode user doesn't exist - run_test "Finds vscode user in automatic mode (SKIPPED - user doesn't exist)" "SKIP" "SKIP" + # Skip this test if vscode user doesn't exist or higher priority user exists + run_test "Finds vscode user in automatic mode (SKIPPED - conditions not met)" "SKIP" "SKIP" fi } @@ -128,6 +131,8 @@ test_find_vscode_user() { test_find_devcontainer_user() { # Check if devcontainer user exists if id -u devcontainer > /dev/null 2>&1; then + # Unset _REMOTE_USER to ensure it doesn't interfere + unset _REMOTE_USER local result=$(determine_user_from_input "automatic") # Should find devcontainer (highest priority) run_test "Finds devcontainer user (highest priority)" "devcontainer" "${result}" @@ -165,6 +170,18 @@ test_empty_input() { run_test "Empty input treated as automatic and finds UID 1000 or uses fallback" "${expected}" "${result}" } +# Test 14: _REMOTE_USER set to non-existent user should use fallback +test_remote_user_nonexistent() { + export _REMOTE_USER="nonexistentuser99999" + local result=$(determine_user_from_input "automatic" "mydefault") + unset _REMOTE_USER + + # Should fall through to normal detection - find UID 1000 or use fallback + local uid_1000_user=$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '') + local expected="${uid_1000_user:-mydefault}" + run_test "_REMOTE_USER set to non-existent user falls back to detection" "${expected}" "${result}" +} + # Run all tests echo "Running tests for common-setup.sh..." echo "======================================" @@ -183,6 +200,7 @@ test_find_vscode_user test_find_devcontainer_user test_find_uid_1000 test_empty_input +test_remote_user_nonexistent # Print summary echo ""