diff --git a/src/artifacts-helper/NOTES.md b/src/artifacts-helper/NOTES.md index 789b800..41cbed3 100644 --- a/src/artifacts-helper/NOTES.md +++ b/src/artifacts-helper/NOTES.md @@ -1,6 +1,6 @@ This installs [Azure Artifacts Credential Provider](https://github.com/microsoft/artifacts-credprovider) -and optionally configures functions which shadow `dotnet`, `nuget`, `npm`, `yarn`, `rush`, and `pnpm` which dynamically sets an authentication token -for pulling artifacts from a feed before running the command. +and optionally configures shims which shadow `dotnet`, `nuget`, `npm`, `yarn`, `rush`, and `pnpm`. +These dynamically sets an authentication token for pulling artifacts from a feed before running the command. For `npm`, `yarn`, `rush`, and `pnpm` this requires that your `~/.npmrc` file is configured to use the ${ARTIFACTS_ACCESSTOKEN} environment variable for the `authToken`. A helper script has been added that you can use to write your `~/.npmrc` @@ -40,28 +40,4 @@ to download the package. ## OS Support -This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0 - -## Changing where functions are configured - -By default, the functions are defined in `/etc/bash.bashrc` and `/etc/zsh/zshrc` if the container user is `root`, otherwise `~/.bashrc` and `~/.zshrc`. -This default configuration ensures that the functions are always available for any interactive shells. - -In some cases it can be useful to have the functions written to a non-default location. For example: -- the configuration file of a shell other than `bash` and `zsh` -- a custom file which is not a shell configuration script (so that it can be `source`d in non-interactive shells and scripts) - -To do this, set the `targetFiles` option to the path script path where the functions should be written. Note that the default paths WILL NOT be used -if the `targetFiles` option is provided, so you may want to include them in the overridden value, or add `source` the custom script in those configurations: - -```bash -# .devcontainer/devcontainer.json -{ - // ... - "targetFiles": "/custom/path/to/auth-helper.sh" -} - -# ~/.bashrc - -source /custom/path/to/auth-helper.sh -``` \ No newline at end of file +This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0 \ No newline at end of file diff --git a/src/artifacts-helper/README.md b/src/artifacts-helper/README.md index 16f8bcb..5363da9 100644 --- a/src/artifacts-helper/README.md +++ b/src/artifacts-helper/README.md @@ -73,10 +73,35 @@ pip install --index-url https://pkgs.dev.azure.com//_pa When the feed URL is an Azure Artifacts feed pip will use the keyring helper to provide the credentials needed to download the package. +## Authentication Helper Wait Behavior + +The shim scripts (e.g., `dotnet`, `npm`, `nuget`) now include a wait mechanism for the Azure DevOps authentication helper. When invoked, these scripts will: + +1. Wait up to 3 minutes for the `ado-auth-helper` to become available (configurable via `MAX_WAIT` environment variable) +2. Display progress indicators every 20 seconds while waiting +3. Continue execution once authentication is successful +4. **Continue with the underlying command even if authentication is not available** after the timeout + +This ensures that package restore operations can proceed even if there's a slight delay in the authentication helper installation, which can occur in some codespace initialization scenarios. Commands will still execute without authentication, though they may fail to access private Azure Artifacts feeds. + +The scripts are designed to be sourced safely, meaning they won't terminate the calling shell if authentication fails - they will simply return an error code and allow the underlying tool to execute. This allows you to work with public packages or other package sources even when Azure Artifacts authentication is unavailable. + ## OS Support This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0 +## Testing + +To test this feature locally, you can use the devcontainer CLI: + +```bash +# Test all scenarios +devcontainer features test -f artifacts-helper + +# Test specific scenario +devcontainer features test -f artifacts-helper --scenario test_auth_wait +``` + ## Changing where functions are configured By default, the functions are defined in `/etc/bash.bashrc` and `/etc/zsh/zshrc` if the container user is `root`, otherwise `~/.bashrc` and `~/.zshrc`. diff --git a/src/artifacts-helper/devcontainer-feature.json b/src/artifacts-helper/devcontainer-feature.json index 389127a..e805008 100644 --- a/src/artifacts-helper/devcontainer-feature.json +++ b/src/artifacts-helper/devcontainer-feature.json @@ -1,7 +1,7 @@ { "name": "Azure Artifacts Credential Helper", "id": "artifacts-helper", - "version": "2.0.3", + "version": "3.0.0", "description": "Configures Codespace to authenticate with Azure Artifact feeds", "options": { "nugetURIPrefixes": { @@ -49,6 +49,11 @@ "default": true, "description": "Create alias for pnpm" }, + "shimDirectory": { + "type": "string", + "default": "/usr/local/share/codespace-shims", + "description": "Directory where the shims will be installed. This must be in $PATH, and needs to be as early as possible in priority for the scripts to override the base executables." + }, "targetFiles": { "type": "string", "default": "DEFAULT", @@ -60,9 +65,13 @@ "description": "Install Python keyring helper for pip" } }, + "containerEnv": { + "PATH": "/usr/local/share/codespace-shims:${PATH}" + }, "installsAfter": [ "ghcr.io/devcontainers/features/common-utils", - "ghcr.io/devcontainers/features/python" + "ghcr.io/devcontainers/features/python", + "ghcr.io/devcontainers/features/node" ], "customizations": { "vscode": { diff --git a/src/artifacts-helper/install.sh b/src/artifacts-helper/install.sh index d460f94..5386350 100755 --- a/src/artifacts-helper/install.sh +++ b/src/artifacts-helper/install.sh @@ -12,7 +12,7 @@ ALIAS_NPX="${NPXALIAS:-"true"}" ALIAS_RUSH="${RUSHALIAS:-"true"}" ALIAS_PNPM="${PNPMALIAS:-"true"}" INSTALL_PIP_HELPER="${PYTHON:-"false"}" -COMMA_SEP_TARGET_FILES="${TARGETFILES:-"DEFAULT"}" +SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/share/codespace-shims/"}" ALIASES_ARR=() @@ -78,31 +78,28 @@ cd "$(dirname "$0")" cp ./scripts/install-provider.sh /tmp chmod +rx /tmp/install-provider.sh + cp ./scripts/install-python-keyring.sh /tmp chmod +rx /tmp/install-python-keyring.sh -sed "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/run-dotnet.sh > /usr/local/bin/run-dotnet.sh -chmod +rx /usr/local/bin/run-dotnet.sh -sed "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/run-nuget.sh > /usr/local/bin/run-nuget.sh -chmod +rx /usr/local/bin/run-nuget.sh -cp ./scripts/run-npm.sh /usr/local/bin/run-npm.sh -chmod +rx /usr/local/bin/run-npm.sh -cp ./scripts/run-yarn.sh /usr/local/bin/run-yarn.sh -chmod +rx /usr/local/bin/run-yarn.sh -cp ./scripts/write-npm.sh /usr/local/bin/write-npm.sh -chmod +rx /usr/local/bin/write-npm.sh -cp ./scripts/run-npx.sh /usr/local/bin/run-npx.sh -chmod +rx /usr/local/bin/run-npx.sh - -cp ./scripts/run-rush.sh /usr/local/bin/run-rush.sh -chmod +rx /usr/local/bin/run-rush.sh -cp ./scripts/run-rush-pnpm.sh /usr/local/bin/run-rush-pnpm.sh -chmod +rx /usr/local/bin/run-rush-pnpm.sh - -cp ./scripts/run-pnpm.sh /usr/local/bin/run-pnpm.sh -chmod +rx /usr/local/bin/run-pnpm.sh -cp ./scripts/run-pnpx.sh /usr/local/bin/run-pnpx.sh -chmod +rx /usr/local/bin/run-pnpx.sh +# Replace AZURE_DEVOPS_NUGET_FEED_URL_PREFIX in scripts that require it +sed -i "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/dotnet +sed -i "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/nuget + +# Create ${SHIM_DIRECTORY} +mkdir -p "${SHIM_DIRECTORY}" + +# Install helper scripts in ${SHIM_DIRECTORY} +cp "./scripts/auth-ado.sh" "${SHIM_DIRECTORY}" +cp "./scripts/resolve-shim.sh" "${SHIM_DIRECTORY}" +cp "./scripts/write-npm.sh" "${SHIM_DIRECTORY}" +chmod +rx "${SHIM_DIRECTORY}/write-npm.sh" + +# Install selected shim scripts in ${SHIM_DIRECTORY} +for alias in "${ALIASES_ARR[@]}"; do + chmod +rx "./scripts/${alias}" + cp "./scripts/${alias}" "${SHIM_DIRECTORY}" +done if [ "${INSTALL_PIP_HELPER}" = "true" ]; then USER="${_REMOTE_USER}" /tmp/install-python-keyring.sh @@ -126,16 +123,17 @@ fi IFS=',' read -r -a TARGET_FILES_ARR <<< "$COMMA_SEP_TARGET_FILES" +ALIASES_BLOCK="" for ALIAS in "${ALIASES_ARR[@]}"; do - for TARGET_FILE in "${TARGET_FILES_ARR[@]}"; do - CMD="$ALIAS() { /usr/local/bin/run-$ALIAS.sh \"\$@\"; }" - - if [ "${INSTALL_WITH_SUDO}" = "true" ]; then - sudo -u ${_REMOTE_USER} bash -c "echo '$CMD' >> $TARGET_FILE" - else - echo $CMD >> $TARGET_FILE || true - fi - done + ALIASES_BLOCK+="$ALIAS() { \"${SHIM_DIRECTORY}/$ALIAS\" \"\$@\"; }\n" +done + +for TARGET_FILE in "${TARGET_FILES_ARR[@]}"; do + if [ "${INSTALL_WITH_SUDO}" = "true" ]; then + sudo -u ${_REMOTE_USER} bash -c "printf '%s' \"$ALIASES_BLOCK\" >> $TARGET_FILE" + else + printf '%s' "$ALIASES_BLOCK" >> "$TARGET_FILE" || true + fi done if [ "${INSTALL_WITH_SUDO}" = "true" ]; then diff --git a/src/artifacts-helper/scripts/auth-ado.sh b/src/artifacts-helper/scripts/auth-ado.sh new file mode 100644 index 0000000..db54205 --- /dev/null +++ b/src/artifacts-helper/scripts/auth-ado.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +echo "::step::Waiting for AzDO Authentication Helper..." + +# Wait up to 3 minutes for the ado-auth-helper to be installed +# Can be overridden via environment variable for testing +MAX_WAIT=${MAX_WAIT:-180} +ELAPSED=0 + +while [ $ELAPSED -lt $MAX_WAIT ]; do + if [ -f "${HOME}/ado-auth-helper" ]; then + echo "::step::Running ado-auth-helper get-access-token..." + ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) + echo "::step::✓ Access token retrieved successfully" + return 0 + fi + sleep 2 + ELAPSED=$((ELAPSED + 2)) + + # Progress indicator every 20 seconds + if [ $((ELAPSED % 20)) -eq 0 ]; then + echo " Still waiting... (${ELAPSED}s elapsed)" + fi +done + +# Timeout reached - continue without authentication +echo "::warning::AzDO Authentication Helper not found after ${MAX_WAIT} seconds" +echo "Expected location: ${HOME}/ado-auth-helper" +echo "Continuing without Azure Artifacts authentication..." +return 1 \ No newline at end of file diff --git a/src/artifacts-helper/scripts/dotnet b/src/artifacts-helper/scripts/dotnet new file mode 100755 index 0000000..7b765e1 --- /dev/null +++ b/src/artifacts-helper/scripts/dotnet @@ -0,0 +1,11 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +# Install artifact credential provider if it is not already installed +if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then + wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash +fi + +DOTNET_EXE="$(resolve_shim)" +VSS_NUGET_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX ${DOTNET_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/npm b/src/artifacts-helper/scripts/npm new file mode 100755 index 0000000..deef49f --- /dev/null +++ b/src/artifacts-helper/scripts/npm @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +NPM_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${NPM_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/npx b/src/artifacts-helper/scripts/npx new file mode 100755 index 0000000..1879135 --- /dev/null +++ b/src/artifacts-helper/scripts/npx @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +NPX_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${NPX_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/nuget b/src/artifacts-helper/scripts/nuget new file mode 100755 index 0000000..756c918 --- /dev/null +++ b/src/artifacts-helper/scripts/nuget @@ -0,0 +1,11 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +# Install artifact credential provider if it is not already installed +if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then + wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash +fi + +NUGET_EXE="$(resolve_shim)" +VSS_NUGET_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX ${NUGET_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/pnpm b/src/artifacts-helper/scripts/pnpm new file mode 100755 index 0000000..7cf2e95 --- /dev/null +++ b/src/artifacts-helper/scripts/pnpm @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +PNPM_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${PNPM_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/pnpx b/src/artifacts-helper/scripts/pnpx new file mode 100755 index 0000000..5f865e0 --- /dev/null +++ b/src/artifacts-helper/scripts/pnpx @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +PNPX_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${PNPX_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/resolve-shim.sh b/src/artifacts-helper/scripts/resolve-shim.sh new file mode 100644 index 0000000..a115049 --- /dev/null +++ b/src/artifacts-helper/scripts/resolve-shim.sh @@ -0,0 +1,22 @@ +#!/bin/bash +[[ ${RESOLVE_SHIMS_IMPORTED} == "true" ]] && return +RESOLVE_SHIMS_IMPORTED=true + +resolve_shim() { + # Find the next non-shim executable in PATH so we do not run the shim again + shim_file="$(readlink -f "${BASH_SOURCE[1]}")" + executable="$(basename "$shim_file")" + + # Read into array first to handle spaces properly + readarray -t candidates < <(which -a "$executable" 2>/dev/null) + + for candidate in "${candidates[@]}"; do + # Skip any candidate which is a symlink to the shim file + [[ "$(readlink -f "$candidate")" != "$shim_file" ]] && { + echo "$candidate" + return 0 + } + done + + return 1 +} \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-dotnet.sh b/src/artifacts-helper/scripts/run-dotnet.sh deleted file mode 100755 index 667caa8..0000000 --- a/src/artifacts-helper/scripts/run-dotnet.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Install artifact credential provider if it is not already installed -if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then - wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash -fi - -if [ -f "${HOME}/ado-auth-helper" ]; then - export VSS_NUGET_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) - export VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX -fi - -# Find the dotnet executable so we do not run the bash alias again -DOTNET_EXE=$(which dotnet) - -${DOTNET_EXE} "$@" -EXIT_CODE=$? -unset DOTNET_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset VSS_NUGET_ACCESSTOKEN - unset VSS_NUGET_URI_PREFIXES -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-npm.sh b/src/artifacts-helper/scripts/run-npm.sh deleted file mode 100755 index 469c5a9..0000000 --- a/src/artifacts-helper/scripts/run-npm.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the npm executable so we do not run the bash alias again -NPM_EXE=$(which npm) - -${NPM_EXE} "$@" -EXIT_CODE=$? -unset NPM_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-npx.sh b/src/artifacts-helper/scripts/run-npx.sh deleted file mode 100755 index 932f621..0000000 --- a/src/artifacts-helper/scripts/run-npx.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the npm executable so we do not run the bash alias again -NPX_EXE=$(which npx) - -${NPX_EXE} "$@" -EXIT_CODE=$? -unset NPX_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-nuget.sh b/src/artifacts-helper/scripts/run-nuget.sh deleted file mode 100755 index 368dfcb..0000000 --- a/src/artifacts-helper/scripts/run-nuget.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -# Install artifact credential provider if it is not already installed -if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then - wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash -fi - -if [ -f "${HOME}/ado-auth-helper" ]; then - export VSS_NUGET_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) - export VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX -fi - -# Find the dotnet executable so we do not run the bash alias again -NUGET_EXE=$(which nuget) - -${NUGET_EXE} "$@" -EXIT_CODE=$? -unset NUGET_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset VSS_NUGET_ACCESSTOKEN - unset VSS_NUGET_URI_PREFIXES -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-pnpm.sh b/src/artifacts-helper/scripts/run-pnpm.sh deleted file mode 100644 index 9a2bad2..0000000 --- a/src/artifacts-helper/scripts/run-pnpm.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the pnpm executable so we do not run the bash alias again -PNPM_EXE=$(which pnpm) - -${PNPM_EXE} "$@" -EXIT_CODE=$? -unset PNPM_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-pnpx.sh b/src/artifacts-helper/scripts/run-pnpx.sh deleted file mode 100644 index 02572ad..0000000 --- a/src/artifacts-helper/scripts/run-pnpx.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the pnpx executable so we do not run the bash alias again -PNPX_EXE=$(which pnpx) - -${PNPX_EXE} "$@" -EXIT_CODE=$? -unset PNPX_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-rush-pnpm.sh b/src/artifacts-helper/scripts/run-rush-pnpm.sh deleted file mode 100644 index ec3cdc7..0000000 --- a/src/artifacts-helper/scripts/run-rush-pnpm.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the rush-pnpm executable so we do not run the bash alias again -RUSH_PNPM_EXE=$(which rush-pnpm) - -${RUSH_PNPM_EXE} "$@" -EXIT_CODE=$? -unset RUSH_PNPM_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-rush.sh b/src/artifacts-helper/scripts/run-rush.sh deleted file mode 100644 index ddc7b4f..0000000 --- a/src/artifacts-helper/scripts/run-rush.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the rush executable so we do not run the bash alias again -RUSH_EXE=$(which rush) - -${RUSH_EXE} "$@" -EXIT_CODE=$? -unset RUSH_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/run-yarn.sh b/src/artifacts-helper/scripts/run-yarn.sh deleted file mode 100755 index a414c02..0000000 --- a/src/artifacts-helper/scripts/run-yarn.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -if [ -f "${HOME}/ado-auth-helper" ]; then - export ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi - -# Find the yarn executable so we do not run the bash alias again -YARN_EXE=$(which yarn) - -${YARN_EXE} "$@" -EXIT_CODE=$? -unset YARN_EXE - -if [ -f "${HOME}/ado-auth-helper" ]; then - unset ARTIFACTS_ACCESSTOKEN -fi - -exit $EXIT_CODE \ No newline at end of file diff --git a/src/artifacts-helper/scripts/rush b/src/artifacts-helper/scripts/rush new file mode 100755 index 0000000..f1c262b --- /dev/null +++ b/src/artifacts-helper/scripts/rush @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +RUSH_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${RUSH_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/rush-pnpm b/src/artifacts-helper/scripts/rush-pnpm new file mode 100755 index 0000000..8370d83 --- /dev/null +++ b/src/artifacts-helper/scripts/rush-pnpm @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +RUSH_PNPM_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${RUSH_PNPM_EXE} "$@" \ No newline at end of file diff --git a/src/artifacts-helper/scripts/yarn b/src/artifacts-helper/scripts/yarn new file mode 100755 index 0000000..5453431 --- /dev/null +++ b/src/artifacts-helper/scripts/yarn @@ -0,0 +1,6 @@ +#!/bin/bash +source "$(dirname $0)"/auth-ado.sh +source "$(dirname $0)"/resolve-shim.sh + +YARN_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${YARN_EXE} "$@" \ No newline at end of file diff --git a/test/artifacts-helper/run-tests.sh b/test/artifacts-helper/run-tests.sh new file mode 100755 index 0000000..f6e6e1d --- /dev/null +++ b/test/artifacts-helper/run-tests.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Quick test runner for artifacts-helper feature +# Usage: ./run-tests.sh [scenario-name] + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FEATURE_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" + +echo "===================================" +echo "Testing artifacts-helper feature" +echo "===================================" +echo + +if [ -n "$1" ]; then + echo "Running scenario: $1" + devcontainer features test -f artifacts-helper --scenario "$1" +else + echo "Running all scenarios..." + devcontainer features test -f artifacts-helper +fi + +echo +echo "===================================" +echo "Tests completed!" +echo "===================================" diff --git a/test/artifacts-helper/scenarios.json b/test/artifacts-helper/scenarios.json index 67178f7..516c9af 100644 --- a/test/artifacts-helper/scenarios.json +++ b/test/artifacts-helper/scenarios.json @@ -42,5 +42,33 @@ "python": false } } + }, + "test_auth_wait": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "artifacts-helper": {} + } + }, + "test_shim_integration": { + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "artifacts-helper": { + "dotnetAlias": true, + "npmAlias": true, + "nugetAlias": true + } + } + }, + "test_fallback_execution": { + "image": "mcr.microsoft.com/devcontainers/base:debian", + "features": { + "ghcr.io/devcontainers/features/dotnet:1": {}, + "ghcr.io/devcontainers/features/node:1": {}, + "artifacts-helper": { + "dotnetAlias": true, + "npmAlias": true, + "nugetAlias": true + } + } } } \ No newline at end of file diff --git a/test/artifacts-helper/test.sh b/test/artifacts-helper/test.sh index 30dc3be..779e4d4 100755 --- a/test/artifacts-helper/test.sh +++ b/test/artifacts-helper/test.sh @@ -6,9 +6,9 @@ set -e source dev-container-features-test-lib # Feature-specific tests -check "dotnet" grep "pkgs.dev.azure.com" <(cat /usr/local/bin/run-dotnet.sh) -check "nuget" grep "pkgs.dev.azure.com" <(cat /usr/local/bin/run-nuget.sh) -check "write-npm" /usr/local/bin/write-npm.sh pkgs.dev.azure.com && grep "pkgs.dev.azure.com" <(cat ~/.npmrc) +check "dotnet" grep "pkgs.dev.azure.com" <(cat /usr/local/share/codespace-shims/dotnet) +check "nuget" grep "pkgs.dev.azure.com" <(cat /usr/local/share/codespace-shims/nuget) +check "write-npm" /usr/local/share/codespace-shims/write-npm.sh pkgs.dev.azure.com && grep "pkgs.dev.azure.com" <(cat ~/.npmrc) # Report results diff --git a/test/artifacts-helper/test_auth_wait.sh b/test/artifacts-helper/test_auth_wait.sh new file mode 100755 index 0000000..1f5cb55 --- /dev/null +++ b/test/artifacts-helper/test_auth_wait.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib || exit 1 + +# Test that the shim scripts exist and can be sourced +check "dotnet shim exists" test -f /usr/local/share/codespace-shims/dotnet +check "npm shim exists" test -f /usr/local/share/codespace-shims/npm +check "nuget shim exists" test -f /usr/local/share/codespace-shims/nuget + +# Test that auth-ado.sh can be sourced without exiting the shell +export MAX_WAIT=5 +check "auth-ado.sh can be sourced" bash -c 'source /usr/local/share/codespace-shims/auth-ado.sh 2>/dev/null || true; echo "still running"' + +# Test that sourcing auth-ado.sh doesn't terminate the parent shell +check "sourcing auth-ado.sh preserves shell" bash -c ' + source /usr/local/share/codespace-shims/auth-ado.sh 2>/dev/null || true + echo "completed" +' | grep -q "completed" + +# Test that the shim scripts can be executed +check "dotnet shim is executable" test -x /usr/local/share/codespace-shims/dotnet +check "npm shim is executable" test -x /usr/local/share/codespace-shims/npm + +# Report results +reportResults diff --git a/test/artifacts-helper/test_fallback_execution.sh b/test/artifacts-helper/test_fallback_execution.sh new file mode 100755 index 0000000..a3075f7 --- /dev/null +++ b/test/artifacts-helper/test_fallback_execution.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib || exit 1 + +# Test that shim scripts call the underlying command even when auth helper is not found + +# Create a temporary directory to simulate a clean environment +TEST_HOME=$(mktemp -d) + +# Mock scenario: ado-auth-helper doesn't exist, but the underlying command should still run +check "dotnet command executes without auth helper" bash -c ' + # Use a short timeout to avoid waiting 3 minutes + # The shim should call dotnet even if auth fails + export HOME='"$TEST_HOME"' + export MAX_WAIT=5 + + # Call dotnet --version which should work even without auth + timeout 10 /usr/local/share/codespace-shims/dotnet --version 2>&1 | grep -q "dotnet\|\.NET" && echo "SUCCESS" || echo "FAILED" +' | grep -q "SUCCESS" + +# Test with npm as well +check "npm command executes without auth helper" bash -c ' + export HOME='"$TEST_HOME"' + export MAX_WAIT=5 + + # npm --version should work without auth + timeout 10 /usr/local/share/codespace-shims/npm --version 2>&1 | grep -q "[0-9]\+\.[0-9]\+\.[0-9]\+" && echo "SUCCESS" || echo "FAILED" +' | grep -q "SUCCESS" + +# Test that nuget can be called (may not return version without auth, but should not crash) +check "nuget command attempts to execute without auth helper" bash -c ' + export HOME='"$TEST_HOME"' + export MAX_WAIT=5 + + # Try to call nuget - it should attempt to run even if auth fails + timeout 10 /usr/local/share/codespace-shims/nuget help 2>&1 || true + # Just verify we get here without the shell crashing + echo "completed" +' | grep -q "completed" + +# Cleanup +rm -rf "$TEST_HOME" + +# Report results +reportResults diff --git a/test/artifacts-helper/test_shim_integration.sh b/test/artifacts-helper/test_shim_integration.sh new file mode 100755 index 0000000..f09be98 --- /dev/null +++ b/test/artifacts-helper/test_shim_integration.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Import test library bundled with the devcontainer CLI +source dev-container-features-test-lib || exit 1 + +# Test that shims properly handle the case when ado-auth-helper is not available +# The shim should wait and eventually timeout, but not crash the script + +# Test dotnet shim can be invoked (will timeout waiting for auth but shouldn't crash) +check "dotnet shim handles missing auth helper" bash -c ' + # Mock a quick timeout scenario + export HOME=$(mktemp -d) + timeout 5 /usr/local/share/codespace-shims/dotnet --version 2>&1 || exit_code=$? + # Exit code 124 means timeout killed it, which is expected + # Exit code 1 means it returned error but script continued + # Exit code 0 means it succeeded (if auth helper was present) + [[ $exit_code -eq 124 || $exit_code -eq 1 || $exit_code -eq 0 ]] +' + +# Test that the shim scripts properly source auth-ado.sh +check "dotnet shim sources auth-ado.sh" grep -q "source.*auth-ado.sh" /usr/local/share/codespace-shims/dotnet +check "npm shim sources auth-ado.sh" grep -q "source.*auth-ado.sh" /usr/local/share/codespace-shims/npm + +# Verify the shim directory is in PATH +check "shim directory in PATH" bash -c '[[ ":$PATH:" == *":/usr/local/share/codespace-shims:"* ]]' + +# Report results +reportResults