From a9cb1ceb9a523c5889c88291db689bbe361ef192 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:14:19 +0000 Subject: [PATCH 1/5] Initial plan From 624b1e8c8e8e6922617377c0005b324fd9a6575f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:22:13 +0000 Subject: [PATCH 2/5] Copy common helpers into each feature directory Renamed `.common` to `_lib` and copied it into each feature directory that uses common-setup.sh. This ensures the helper scripts are available in the feature build context during testing. The devcontainer CLI only packages files from within the feature directory, so external directories (even if in src/) are not included in the build. Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/anaconda/_lib/README.md | 122 ++++++++++++++++++ src/anaconda/_lib/common-setup.sh | 87 +++++++++++++ src/anaconda/install.sh | 2 +- src/common-utils/_lib/README.md | 122 ++++++++++++++++++ src/common-utils/_lib/common-setup.sh | 87 +++++++++++++ src/common-utils/main.sh | 2 +- src/conda/_lib/README.md | 122 ++++++++++++++++++ src/conda/_lib/common-setup.sh | 87 +++++++++++++ src/conda/install.sh | 2 +- src/desktop-lite/_lib/README.md | 122 ++++++++++++++++++ src/desktop-lite/_lib/common-setup.sh | 87 +++++++++++++ src/desktop-lite/install.sh | 2 +- src/docker-in-docker/_lib/README.md | 122 ++++++++++++++++++ src/docker-in-docker/_lib/common-setup.sh | 87 +++++++++++++ src/docker-in-docker/install.sh | 2 +- src/docker-outside-of-docker/_lib/README.md | 122 ++++++++++++++++++ .../_lib/common-setup.sh | 87 +++++++++++++ src/docker-outside-of-docker/install.sh | 2 +- src/go/_lib/README.md | 122 ++++++++++++++++++ src/go/_lib/common-setup.sh | 87 +++++++++++++ src/go/install.sh | 2 +- src/hugo/_lib/README.md | 122 ++++++++++++++++++ src/hugo/_lib/common-setup.sh | 87 +++++++++++++ src/hugo/install.sh | 2 +- src/java/_lib/README.md | 122 ++++++++++++++++++ src/java/_lib/common-setup.sh | 87 +++++++++++++ src/java/install.sh | 2 +- src/kubectl-helm-minikube/_lib/README.md | 122 ++++++++++++++++++ .../_lib/common-setup.sh | 87 +++++++++++++ src/kubectl-helm-minikube/install.sh | 2 +- src/node/_lib/README.md | 122 ++++++++++++++++++ src/node/_lib/common-setup.sh | 87 +++++++++++++ src/node/install.sh | 2 +- src/oryx/_lib/README.md | 122 ++++++++++++++++++ src/oryx/_lib/common-setup.sh | 87 +++++++++++++ src/oryx/install.sh | 2 +- src/php/_lib/README.md | 122 ++++++++++++++++++ src/php/_lib/common-setup.sh | 87 +++++++++++++ src/php/install.sh | 2 +- src/python/_lib/README.md | 122 ++++++++++++++++++ src/python/_lib/common-setup.sh | 87 +++++++++++++ src/python/install.sh | 2 +- src/ruby/_lib/README.md | 122 ++++++++++++++++++ src/ruby/_lib/common-setup.sh | 87 +++++++++++++ src/ruby/install.sh | 2 +- src/rust/_lib/README.md | 122 ++++++++++++++++++ src/rust/_lib/common-setup.sh | 87 +++++++++++++ src/rust/install.sh | 2 +- src/sshd/_lib/README.md | 122 ++++++++++++++++++ src/sshd/_lib/common-setup.sh | 87 +++++++++++++ src/sshd/install.sh | 2 +- 51 files changed, 3570 insertions(+), 17 deletions(-) create mode 100644 src/anaconda/_lib/README.md create mode 100644 src/anaconda/_lib/common-setup.sh create mode 100644 src/common-utils/_lib/README.md create mode 100644 src/common-utils/_lib/common-setup.sh create mode 100644 src/conda/_lib/README.md create mode 100644 src/conda/_lib/common-setup.sh create mode 100644 src/desktop-lite/_lib/README.md create mode 100644 src/desktop-lite/_lib/common-setup.sh create mode 100644 src/docker-in-docker/_lib/README.md create mode 100644 src/docker-in-docker/_lib/common-setup.sh create mode 100644 src/docker-outside-of-docker/_lib/README.md create mode 100644 src/docker-outside-of-docker/_lib/common-setup.sh create mode 100644 src/go/_lib/README.md create mode 100644 src/go/_lib/common-setup.sh create mode 100644 src/hugo/_lib/README.md create mode 100644 src/hugo/_lib/common-setup.sh create mode 100644 src/java/_lib/README.md create mode 100644 src/java/_lib/common-setup.sh create mode 100644 src/kubectl-helm-minikube/_lib/README.md create mode 100644 src/kubectl-helm-minikube/_lib/common-setup.sh create mode 100644 src/node/_lib/README.md create mode 100644 src/node/_lib/common-setup.sh create mode 100644 src/oryx/_lib/README.md create mode 100644 src/oryx/_lib/common-setup.sh create mode 100644 src/php/_lib/README.md create mode 100644 src/php/_lib/common-setup.sh create mode 100644 src/python/_lib/README.md create mode 100644 src/python/_lib/common-setup.sh create mode 100644 src/ruby/_lib/README.md create mode 100644 src/ruby/_lib/common-setup.sh create mode 100644 src/rust/_lib/README.md create mode 100644 src/rust/_lib/common-setup.sh create mode 100644 src/sshd/_lib/README.md create mode 100644 src/sshd/_lib/common-setup.sh diff --git a/src/anaconda/_lib/README.md b/src/anaconda/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/anaconda/_lib/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") +``` diff --git a/src/anaconda/_lib/common-setup.sh b/src/anaconda/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/anaconda/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/anaconda/install.sh b/src/anaconda/install.sh index 2fc5d411a..dd2e523be 100755 --- a/src/anaconda/install.sh +++ b/src/anaconda/install.sh @@ -83,7 +83,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/common-utils/_lib/README.md b/src/common-utils/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/common-utils/_lib/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") +``` diff --git a/src/common-utils/_lib/common-setup.sh b/src/common-utils/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/common-utils/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/common-utils/main.sh b/src/common-utils/main.sh index 0374a9302..42a41bc81 100644 --- a/src/common-utils/main.sh +++ b/src/common-utils/main.sh @@ -401,7 +401,7 @@ esac # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Handle the special "none" case for common-utils before user determination # The "none" case sets USER_UID and USER_GID to 0 diff --git a/src/conda/_lib/README.md b/src/conda/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/conda/_lib/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") +``` diff --git a/src/conda/_lib/common-setup.sh b/src/conda/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/conda/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/conda/install.sh b/src/conda/install.sh index 1e7b49089..4d1cf2237 100644 --- a/src/conda/install.sh +++ b/src/conda/install.sh @@ -29,7 +29,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/desktop-lite/_lib/README.md b/src/desktop-lite/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/desktop-lite/_lib/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") +``` diff --git a/src/desktop-lite/_lib/common-setup.sh b/src/desktop-lite/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/desktop-lite/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/desktop-lite/install.sh b/src/desktop-lite/install.sh index f704cd7e2..47ae44857 100755 --- a/src/desktop-lite/install.sh +++ b/src/desktop-lite/install.sh @@ -73,7 +73,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/docker-in-docker/_lib/README.md b/src/docker-in-docker/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/docker-in-docker/_lib/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") +``` diff --git a/src/docker-in-docker/_lib/common-setup.sh b/src/docker-in-docker/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/docker-in-docker/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/docker-in-docker/install.sh b/src/docker-in-docker/install.sh index d0197c644..e484e40a2 100755 --- a/src/docker-in-docker/install.sh +++ b/src/docker-in-docker/install.sh @@ -46,7 +46,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/docker-outside-of-docker/_lib/README.md b/src/docker-outside-of-docker/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/docker-outside-of-docker/_lib/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") +``` diff --git a/src/docker-outside-of-docker/_lib/common-setup.sh b/src/docker-outside-of-docker/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/docker-outside-of-docker/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/docker-outside-of-docker/install.sh b/src/docker-outside-of-docker/install.sh index a81970d51..1b555b522 100755 --- a/src/docker-outside-of-docker/install.sh +++ b/src/docker-outside-of-docker/install.sh @@ -40,7 +40,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/go/_lib/README.md b/src/go/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/go/_lib/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") +``` diff --git a/src/go/_lib/common-setup.sh b/src/go/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/go/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/go/install.sh b/src/go/install.sh index 290a1555b..825bae28e 100755 --- a/src/go/install.sh +++ b/src/go/install.sh @@ -176,7 +176,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/hugo/_lib/README.md b/src/hugo/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/hugo/_lib/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") +``` diff --git a/src/hugo/_lib/common-setup.sh b/src/hugo/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/hugo/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/hugo/install.sh b/src/hugo/install.sh index 67de944f8..4c605a473 100755 --- a/src/hugo/install.sh +++ b/src/hugo/install.sh @@ -31,7 +31,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/java/_lib/README.md b/src/java/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/java/_lib/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") +``` diff --git a/src/java/_lib/common-setup.sh b/src/java/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/java/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/java/install.sh b/src/java/install.sh index 097996f60..39a2d77c6 100644 --- a/src/java/install.sh +++ b/src/java/install.sh @@ -156,7 +156,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/kubectl-helm-minikube/_lib/README.md b/src/kubectl-helm-minikube/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/kubectl-helm-minikube/_lib/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") +``` diff --git a/src/kubectl-helm-minikube/_lib/common-setup.sh b/src/kubectl-helm-minikube/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/kubectl-helm-minikube/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/kubectl-helm-minikube/install.sh b/src/kubectl-helm-minikube/install.sh index 6d7ffae07..ce97193d1 100755 --- a/src/kubectl-helm-minikube/install.sh +++ b/src/kubectl-helm-minikube/install.sh @@ -30,7 +30,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/node/_lib/README.md b/src/node/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/node/_lib/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") +``` diff --git a/src/node/_lib/common-setup.sh b/src/node/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/node/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/node/install.sh b/src/node/install.sh index ef33289bd..ac04bbed1 100755 --- a/src/node/install.sh +++ b/src/node/install.sh @@ -243,7 +243,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/oryx/_lib/README.md b/src/oryx/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/oryx/_lib/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") +``` diff --git a/src/oryx/_lib/common-setup.sh b/src/oryx/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/oryx/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/oryx/install.sh b/src/oryx/install.sh index 6242b7782..47260748e 100755 --- a/src/oryx/install.sh +++ b/src/oryx/install.sh @@ -27,7 +27,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/php/_lib/README.md b/src/php/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/php/_lib/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") +``` diff --git a/src/php/_lib/common-setup.sh b/src/php/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/php/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/php/install.sh b/src/php/install.sh index 4b84bc1b4..a71f2ad3a 100755 --- a/src/php/install.sh +++ b/src/php/install.sh @@ -38,7 +38,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # If in automatic mode, determine if a user already exists, if not use root USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/python/_lib/README.md b/src/python/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/python/_lib/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") +``` diff --git a/src/python/_lib/common-setup.sh b/src/python/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/python/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/python/install.sh b/src/python/install.sh index 540a9c3c2..75c18c4eb 100755 --- a/src/python/install.sh +++ b/src/python/install.sh @@ -837,7 +837,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/ruby/_lib/README.md b/src/ruby/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/ruby/_lib/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") +``` diff --git a/src/ruby/_lib/common-setup.sh b/src/ruby/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/ruby/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/ruby/install.sh b/src/ruby/install.sh index 1063d3377..124070952 100755 --- a/src/ruby/install.sh +++ b/src/ruby/install.sh @@ -41,7 +41,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/rust/_lib/README.md b/src/rust/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/rust/_lib/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") +``` diff --git a/src/rust/_lib/common-setup.sh b/src/rust/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/rust/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/rust/install.sh b/src/rust/install.sh index c46b8f50e..5a6410e6d 100755 --- a/src/rust/install.sh +++ b/src/rust/install.sh @@ -103,7 +103,7 @@ 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" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/sshd/_lib/README.md b/src/sshd/_lib/README.md new file mode 100644 index 000000000..e614bc005 --- /dev/null +++ b/src/sshd/_lib/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") +``` diff --git a/src/sshd/_lib/common-setup.sh b/src/sshd/_lib/common-setup.sh new file mode 100644 index 000000000..d2ac866cf --- /dev/null +++ b/src/sshd/_lib/common-setup.sh @@ -0,0 +1,87 @@ +#!/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 + # 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 '')") + + 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/src/sshd/install.sh b/src/sshd/install.sh index 2bfbdaa4f..f72e1ef48 100755 --- a/src/sshd/install.sh +++ b/src/sshd/install.sh @@ -27,7 +27,7 @@ fi # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") From 447e6a575e38584b2510bc98695ad5c88ceef61e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:22:54 +0000 Subject: [PATCH 3/5] Remove src/.common directory as it's no longer needed Each feature now has its own copy of the common helpers in _lib/, so the shared .common directory is no longer needed. Removing it also fixes the validation issue that was originally present. Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/.common/README.md | 122 ------------------------------------ src/.common/common-setup.sh | 87 ------------------------- 2 files changed, 209 deletions(-) delete mode 100644 src/.common/README.md delete mode 100644 src/.common/common-setup.sh diff --git a/src/.common/README.md b/src/.common/README.md deleted file mode 100644 index e614bc005..000000000 --- a/src/.common/README.md +++ /dev/null @@ -1,122 +0,0 @@ -# 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") -``` diff --git a/src/.common/common-setup.sh b/src/.common/common-setup.sh deleted file mode 100644 index d2ac866cf..000000000 --- a/src/.common/common-setup.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/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 - # 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 '')") - - 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}" -} From 487355b83600cbcba8d489a8254e6671acdc8fd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:24:00 +0000 Subject: [PATCH 4/5] Update _lib README files to reference local path Updated all README.md files in _lib directories to show the correct path (_lib/common-setup.sh) instead of the old path (../.common/common-setup.sh). Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- src/anaconda/_lib/README.md | 4 ++-- src/common-utils/_lib/README.md | 4 ++-- src/conda/_lib/README.md | 4 ++-- src/desktop-lite/_lib/README.md | 4 ++-- src/docker-in-docker/_lib/README.md | 4 ++-- src/docker-outside-of-docker/_lib/README.md | 4 ++-- src/go/_lib/README.md | 4 ++-- src/hugo/_lib/README.md | 4 ++-- src/java/_lib/README.md | 4 ++-- src/kubectl-helm-minikube/_lib/README.md | 4 ++-- src/node/_lib/README.md | 4 ++-- src/oryx/_lib/README.md | 4 ++-- src/php/_lib/README.md | 4 ++-- src/python/_lib/README.md | 4 ++-- src/ruby/_lib/README.md | 4 ++-- src/rust/_lib/README.md | 4 ++-- src/sshd/_lib/README.md | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/anaconda/_lib/README.md b/src/anaconda/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/anaconda/_lib/README.md +++ b/src/anaconda/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/common-utils/_lib/README.md b/src/common-utils/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/common-utils/_lib/README.md +++ b/src/common-utils/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/conda/_lib/README.md b/src/conda/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/conda/_lib/README.md +++ b/src/conda/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/desktop-lite/_lib/README.md b/src/desktop-lite/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/desktop-lite/_lib/README.md +++ b/src/desktop-lite/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/docker-in-docker/_lib/README.md b/src/docker-in-docker/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/docker-in-docker/_lib/README.md +++ b/src/docker-in-docker/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/docker-outside-of-docker/_lib/README.md b/src/docker-outside-of-docker/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/docker-outside-of-docker/_lib/README.md +++ b/src/docker-outside-of-docker/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/go/_lib/README.md b/src/go/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/go/_lib/README.md +++ b/src/go/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/hugo/_lib/README.md b/src/hugo/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/hugo/_lib/README.md +++ b/src/hugo/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/java/_lib/README.md b/src/java/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/java/_lib/README.md +++ b/src/java/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/kubectl-helm-minikube/_lib/README.md b/src/kubectl-helm-minikube/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/kubectl-helm-minikube/_lib/README.md +++ b/src/kubectl-helm-minikube/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/node/_lib/README.md b/src/node/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/node/_lib/README.md +++ b/src/node/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/oryx/_lib/README.md b/src/oryx/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/oryx/_lib/README.md +++ b/src/oryx/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/php/_lib/README.md b/src/php/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/php/_lib/README.md +++ b/src/php/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/python/_lib/README.md b/src/python/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/python/_lib/README.md +++ b/src/python/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/ruby/_lib/README.md b/src/ruby/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/ruby/_lib/README.md +++ b/src/ruby/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/rust/_lib/README.md b/src/rust/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/rust/_lib/README.md +++ b/src/rust/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") diff --git a/src/sshd/_lib/README.md b/src/sshd/_lib/README.md index e614bc005..4273e8865 100644 --- a/src/sshd/_lib/README.md +++ b/src/sshd/_lib/README.md @@ -15,7 +15,7 @@ Determines the appropriate non-root user based on the input username. **Usage:** ```bash # Source the helper script -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the user USERNAME=$(determine_user_from_input "${USERNAME}" "root") @@ -110,7 +110,7 @@ fi ```bash # Source common helper functions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../.common/common-setup.sh" +source "${SCRIPT_DIR}/_lib/common-setup.sh" # Determine the appropriate non-root user USERNAME=$(determine_user_from_input "${USERNAME}" "root") From 5e2ff2b337f5487ada9bab829049d33f094bc0fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:25:14 +0000 Subject: [PATCH 5/5] Update test file to source from feature _lib directory Updated test/.common/test-common-setup.sh to source from src/anaconda/_lib/common-setup.sh since src/.common/ was removed. All features have identical copies of the helper. Co-authored-by: abdurriq <137001048+abdurriq@users.noreply.github.com> --- test/.common/test-common-setup.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/.common/test-common-setup.sh b/test/.common/test-common-setup.sh index c8f79a1da..2f662bda9 100755 --- a/test/.common/test-common-setup.sh +++ b/test/.common/test-common-setup.sh @@ -6,9 +6,9 @@ set -e -# Source the helper script +# Source the helper script from anaconda feature (all features have identical copies) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -source "${SCRIPT_DIR}/../../src/.common/common-setup.sh" +source "${SCRIPT_DIR}/../../src/anaconda/_lib/common-setup.sh" # Test counters PASSED=0