From c298b9955801e92f91a5b0f1a7ec728bcf43bb85 Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Thu, 4 Dec 2025 16:25:57 +0000 Subject: [PATCH 1/8] Fix for wrapper scripts not being applied in non-interactive environments These environments do not respect the BASH_ENV variable so installing these alias hooks does not work --- src/artifacts-helper/NOTES.md | 30 +--------- .../devcontainer-feature.json | 8 +-- src/artifacts-helper/install.sh | 60 +++++-------------- src/artifacts-helper/scripts/auth-ado.sh | 4 ++ src/artifacts-helper/scripts/dotnet | 11 ++++ src/artifacts-helper/scripts/npm | 6 ++ src/artifacts-helper/scripts/npx | 6 ++ src/artifacts-helper/scripts/nuget | 11 ++++ src/artifacts-helper/scripts/pnpm | 6 ++ src/artifacts-helper/scripts/pnpx | 6 ++ src/artifacts-helper/scripts/resolve-shim.sh | 9 +++ src/artifacts-helper/scripts/run-dotnet.sh | 25 -------- src/artifacts-helper/scripts/run-npm.sh | 18 ------ src/artifacts-helper/scripts/run-npx.sh | 18 ------ src/artifacts-helper/scripts/run-nuget.sh | 25 -------- src/artifacts-helper/scripts/run-pnpm.sh | 18 ------ src/artifacts-helper/scripts/run-pnpx.sh | 18 ------ src/artifacts-helper/scripts/run-rush-pnpm.sh | 18 ------ src/artifacts-helper/scripts/run-rush.sh | 18 ------ src/artifacts-helper/scripts/run-yarn.sh | 18 ------ src/artifacts-helper/scripts/rush | 6 ++ src/artifacts-helper/scripts/rush-pnpm | 6 ++ src/artifacts-helper/scripts/yarn | 6 ++ 23 files changed, 99 insertions(+), 252 deletions(-) create mode 100644 src/artifacts-helper/scripts/auth-ado.sh create mode 100755 src/artifacts-helper/scripts/dotnet create mode 100755 src/artifacts-helper/scripts/npm create mode 100755 src/artifacts-helper/scripts/npx create mode 100755 src/artifacts-helper/scripts/nuget create mode 100755 src/artifacts-helper/scripts/pnpm create mode 100755 src/artifacts-helper/scripts/pnpx create mode 100644 src/artifacts-helper/scripts/resolve-shim.sh delete mode 100755 src/artifacts-helper/scripts/run-dotnet.sh delete mode 100755 src/artifacts-helper/scripts/run-npm.sh delete mode 100755 src/artifacts-helper/scripts/run-npx.sh delete mode 100755 src/artifacts-helper/scripts/run-nuget.sh delete mode 100644 src/artifacts-helper/scripts/run-pnpm.sh delete mode 100644 src/artifacts-helper/scripts/run-pnpx.sh delete mode 100644 src/artifacts-helper/scripts/run-rush-pnpm.sh delete mode 100644 src/artifacts-helper/scripts/run-rush.sh delete mode 100755 src/artifacts-helper/scripts/run-yarn.sh create mode 100755 src/artifacts-helper/scripts/rush create mode 100755 src/artifacts-helper/scripts/rush-pnpm create mode 100755 src/artifacts-helper/scripts/yarn 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/devcontainer-feature.json b/src/artifacts-helper/devcontainer-feature.json index 389127a..000514e 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,10 +49,10 @@ "default": true, "description": "Create alias for pnpm" }, - "targetFiles": { + "shimDirectory": { "type": "string", - "default": "DEFAULT", - "description": "Comma separated list of files to write to. Default is '/etc/bash.bashrc,/etc/zsh/zshrc' for root and '~/.bashrc,~/.zshrc' for non-root" + "default": "/usr/local/bin", + "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." }, "python": { "type": "boolean", diff --git a/src/artifacts-helper/install.sh b/src/artifacts-helper/install.sh index d460f94..880e174 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/bin"}" ALIASES_ARR=() @@ -78,31 +78,23 @@ 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 + +# Install helper scripts in ${SHIM_DIRECTORY} +cp "./scripts/auth-ado.sh" "${SHIM_DIRECTORY}" +cp "./scripts/resolve-shim.sh" "${SHIM_DIRECTORY}" + +# 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 @@ -116,28 +108,6 @@ if command -v sudo >/dev/null 2>&1; then fi fi -if [ "${COMMA_SEP_TARGET_FILES}" = "DEFAULT" ]; then - if [ "${INSTALL_WITH_SUDO}" = "true" ]; then - COMMA_SEP_TARGET_FILES="~/.bashrc,~/.zshrc" - else - COMMA_SEP_TARGET_FILES="/etc/bash.bashrc,/etc/zsh/zshrc" - fi -fi - -IFS=',' read -r -a TARGET_FILES_ARR <<< "$COMMA_SEP_TARGET_FILES" - -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 -done - if [ "${INSTALL_WITH_SUDO}" = "true" ]; then sudo -u ${_REMOTE_USER} bash -c "/tmp/install-provider.sh ${USENET6}" fi diff --git a/src/artifacts-helper/scripts/auth-ado.sh b/src/artifacts-helper/scripts/auth-ado.sh new file mode 100644 index 0000000..9095103 --- /dev/null +++ b/src/artifacts-helper/scripts/auth-ado.sh @@ -0,0 +1,4 @@ +#!/bin/bash +if [ -f "${HOME}/ado-auth-helper" ]; then + ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) +fi \ 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..cfe83ed --- /dev/null +++ b/src/artifacts-helper/scripts/dotnet @@ -0,0 +1,11 @@ +#!/bin/bash +source auth-ado.sh +source 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 ${NUGET_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..61c529b --- /dev/null +++ b/src/artifacts-helper/scripts/npm @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..4564dfc --- /dev/null +++ b/src/artifacts-helper/scripts/npx @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..2fd07f6 --- /dev/null +++ b/src/artifacts-helper/scripts/nuget @@ -0,0 +1,11 @@ +#!/bin/bash +source auth-ado.sh +source 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..48df9fb --- /dev/null +++ b/src/artifacts-helper/scripts/pnpm @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..741248c --- /dev/null +++ b/src/artifacts-helper/scripts/pnpx @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..a1b0388 --- /dev/null +++ b/src/artifacts-helper/scripts/resolve-shim.sh @@ -0,0 +1,9 @@ +#!/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]}") + echo $(which -a dotnet | grep -vx "$shim_file" | head -n 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..7eb80e7 --- /dev/null +++ b/src/artifacts-helper/scripts/rush @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..e01b887 --- /dev/null +++ b/src/artifacts-helper/scripts/rush-pnpm @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source 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..19d384e --- /dev/null +++ b/src/artifacts-helper/scripts/yarn @@ -0,0 +1,6 @@ +#!/bin/bash +source auth-ado.sh +source resolve-shim.sh + +YARN_EXE="$(resolve_shim)" +ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${YARN_EXE} "$@" \ No newline at end of file From 12248b767043d3f4d5579b9f44d9de336805b6e4 Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Fri, 5 Dec 2025 11:25:04 +0000 Subject: [PATCH 2/8] Improve resolution of backing executable by iterating through all items in PATH --- src/artifacts-helper/scripts/dotnet | 2 +- src/artifacts-helper/scripts/resolve-shim.sh | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/artifacts-helper/scripts/dotnet b/src/artifacts-helper/scripts/dotnet index cfe83ed..5127da8 100755 --- a/src/artifacts-helper/scripts/dotnet +++ b/src/artifacts-helper/scripts/dotnet @@ -8,4 +8,4 @@ if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then fi DOTNET_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 +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/resolve-shim.sh b/src/artifacts-helper/scripts/resolve-shim.sh index a1b0388..a115049 100644 --- a/src/artifacts-helper/scripts/resolve-shim.sh +++ b/src/artifacts-helper/scripts/resolve-shim.sh @@ -4,6 +4,19 @@ 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]}") - echo $(which -a dotnet | grep -vx "$shim_file" | head -n 1) + 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 From e34adf27f647de5fd70ef83be4fc4bb56b9928ee Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Fri, 5 Dec 2025 12:18:11 +0000 Subject: [PATCH 3/8] Backward-compatible approach, with functions still added to targetFiles --- .../devcontainer-feature.json | 13 ++++++++-- src/artifacts-helper/install.sh | 25 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/src/artifacts-helper/devcontainer-feature.json b/src/artifacts-helper/devcontainer-feature.json index 000514e..d2fdecc 100644 --- a/src/artifacts-helper/devcontainer-feature.json +++ b/src/artifacts-helper/devcontainer-feature.json @@ -51,18 +51,27 @@ }, "shimDirectory": { "type": "string", - "default": "/usr/local/bin", + "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", + "description": "Comma separated list of files to write to. Default is '/etc/bash.bashrc,/etc/zsh/zshrc' for root and '~/.bashrc,~/.zshrc' for non-root" + }, "python": { "type": "boolean", "default": false, "description": "Install Python keyring helper for pip" } }, + "containerEnv": { + "PATH": "${shimDirectory}:${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 880e174..f40549d 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"}" -SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/bin"}" +SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/share/codespace-shims"}" ALIASES_ARR=() @@ -108,6 +108,29 @@ if command -v sudo >/dev/null 2>&1; then fi fi +if [ "${COMMA_SEP_TARGET_FILES}" = "DEFAULT" ]; then + if [ "${INSTALL_WITH_SUDO}" = "true" ]; then + COMMA_SEP_TARGET_FILES="~/.bashrc,~/.zshrc" + else + COMMA_SEP_TARGET_FILES="/etc/bash.bashrc,/etc/zsh/zshrc" + fi +fi + +IFS=',' read -r -a TARGET_FILES_ARR <<< "$COMMA_SEP_TARGET_FILES" + +ALIASES_BLOCK="" +for ALIAS in "${ALIASES_ARR[@]}"; do + 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 sudo -u ${_REMOTE_USER} bash -c "/tmp/install-provider.sh ${USENET6}" fi From 47b524b45899d8b3671cef6c5ecf2dc61df79e65 Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Mon, 8 Dec 2025 15:34:43 +0000 Subject: [PATCH 4/8] Use absolute path for sourced sh files so that commands work even if not in a PATH directory --- src/artifacts-helper/install.sh | 5 ++++- src/artifacts-helper/scripts/dotnet | 4 ++-- src/artifacts-helper/scripts/npm | 4 ++-- src/artifacts-helper/scripts/npx | 4 ++-- src/artifacts-helper/scripts/nuget | 4 ++-- src/artifacts-helper/scripts/pnpm | 4 ++-- src/artifacts-helper/scripts/pnpx | 4 ++-- src/artifacts-helper/scripts/rush | 4 ++-- src/artifacts-helper/scripts/rush-pnpm | 4 ++-- src/artifacts-helper/scripts/yarn | 4 ++-- 10 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/artifacts-helper/install.sh b/src/artifacts-helper/install.sh index f40549d..73ccbb6 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"}" -SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/share/codespace-shims"}" +SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/share/codespace-shims/"}" ALIASES_ARR=() @@ -86,6 +86,9 @@ chmod +rx /tmp/install-python-keyring.sh 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}" diff --git a/src/artifacts-helper/scripts/dotnet b/src/artifacts-helper/scripts/dotnet index 5127da8..7b765e1 100755 --- a/src/artifacts-helper/scripts/dotnet +++ b/src/artifacts-helper/scripts/dotnet @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 diff --git a/src/artifacts-helper/scripts/npm b/src/artifacts-helper/scripts/npm index 61c529b..deef49f 100755 --- a/src/artifacts-helper/scripts/npm +++ b/src/artifacts-helper/scripts/npm @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 index 4564dfc..1879135 100755 --- a/src/artifacts-helper/scripts/npx +++ b/src/artifacts-helper/scripts/npx @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 index 2fd07f6..756c918 100755 --- a/src/artifacts-helper/scripts/nuget +++ b/src/artifacts-helper/scripts/nuget @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 diff --git a/src/artifacts-helper/scripts/pnpm b/src/artifacts-helper/scripts/pnpm index 48df9fb..7cf2e95 100755 --- a/src/artifacts-helper/scripts/pnpm +++ b/src/artifacts-helper/scripts/pnpm @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 index 741248c..5f865e0 100755 --- a/src/artifacts-helper/scripts/pnpx +++ b/src/artifacts-helper/scripts/pnpx @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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/rush b/src/artifacts-helper/scripts/rush index 7eb80e7..f1c262b 100755 --- a/src/artifacts-helper/scripts/rush +++ b/src/artifacts-helper/scripts/rush @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 index e01b887..8370d83 100755 --- a/src/artifacts-helper/scripts/rush-pnpm +++ b/src/artifacts-helper/scripts/rush-pnpm @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 index 19d384e..5453431 100755 --- a/src/artifacts-helper/scripts/yarn +++ b/src/artifacts-helper/scripts/yarn @@ -1,6 +1,6 @@ #!/bin/bash -source auth-ado.sh -source resolve-shim.sh +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 From 16c0297c102dcc7b9dd566e6be58995f1267cca6 Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Fri, 12 Dec 2025 14:41:23 +0000 Subject: [PATCH 5/8] Add wait for ado-helper in case it isn't installed immediately --- .../devcontainer-feature.json | 2 +- src/artifacts-helper/scripts/auth-ado.sh | 31 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/artifacts-helper/devcontainer-feature.json b/src/artifacts-helper/devcontainer-feature.json index d2fdecc..e805008 100644 --- a/src/artifacts-helper/devcontainer-feature.json +++ b/src/artifacts-helper/devcontainer-feature.json @@ -66,7 +66,7 @@ } }, "containerEnv": { - "PATH": "${shimDirectory}:${PATH}" + "PATH": "/usr/local/share/codespace-shims:${PATH}" }, "installsAfter": [ "ghcr.io/devcontainers/features/common-utils", diff --git a/src/artifacts-helper/scripts/auth-ado.sh b/src/artifacts-helper/scripts/auth-ado.sh index 9095103..2fb4f4e 100644 --- a/src/artifacts-helper/scripts/auth-ado.sh +++ b/src/artifacts-helper/scripts/auth-ado.sh @@ -1,4 +1,29 @@ #!/bin/bash -if [ -f "${HOME}/ado-auth-helper" ]; then - ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token) -fi \ No newline at end of file + +echo "::step::Waiting for AzDO Authentication Helper..." + +# Wait up to 3 minutes for the ado-auth-helper to be installed +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 +echo "::error::AzDO Authentication Helper not found after ${MAX_WAIT} seconds" +echo "Expected location: ${HOME}/ado-auth-helper" +echo "Restore cannot proceed without authentication" +return 1 \ No newline at end of file From f959bcc20b6171c443d0f2c000507e83a750f32f Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Fri, 12 Dec 2025 16:25:10 +0000 Subject: [PATCH 6/8] Add tests --- src/artifacts-helper/README.md | 30 ++++ src/artifacts-helper/install.sh | 2 + test/artifacts-helper/README.md | 143 ++++++++++++++++++ test/artifacts-helper/run-tests.sh | 26 ++++ test/artifacts-helper/scenarios.json | 16 ++ test/artifacts-helper/test.sh | 6 +- test/artifacts-helper/test_auth_wait.sh | 25 +++ .../artifacts-helper/test_shim_integration.sh | 28 ++++ 8 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 test/artifacts-helper/README.md create mode 100755 test/artifacts-helper/run-tests.sh create mode 100755 test/artifacts-helper/test_auth_wait.sh create mode 100755 test/artifacts-helper/test_shim_integration.sh diff --git a/src/artifacts-helper/README.md b/src/artifacts-helper/README.md index 16f8bcb..5972b0b 100644 --- a/src/artifacts-helper/README.md +++ b/src/artifacts-helper/README.md @@ -73,10 +73,40 @@ 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 +2. Display progress indicators every 20 seconds while waiting +3. Continue execution once authentication is successful +4. Return an error (but not terminate dependent scripts) if the helper 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. + +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 that can be handled by the calling script. + ## 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 +``` + +The test suite includes: +- **test_auth_wait.sh**: Verifies that auth-ado.sh can be sourced without terminating the shell +- **test_shim_integration.sh**: Tests that shim scripts properly handle missing authentication helper +- **Python keyring tests**: Validates Python package installation with Azure Artifacts authentication + ## 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/install.sh b/src/artifacts-helper/install.sh index 73ccbb6..5386350 100755 --- a/src/artifacts-helper/install.sh +++ b/src/artifacts-helper/install.sh @@ -92,6 +92,8 @@ 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 diff --git a/test/artifacts-helper/README.md b/test/artifacts-helper/README.md new file mode 100644 index 0000000..752613c --- /dev/null +++ b/test/artifacts-helper/README.md @@ -0,0 +1,143 @@ +# Testing Azure Artifacts Helper + +This document describes how to test the artifacts-helper feature, particularly the authentication wait behavior and shim script resilience. + +## Test Scenarios + +### 1. Authentication Wait Test (`test_auth_wait`) + +**Purpose**: Verify that the auth-ado.sh script can be sourced without terminating the parent shell. + +**What it tests**: +- Shim scripts exist in `/usr/local/share/codespace-shims/` +- `auth-ado.sh` can be sourced multiple times without crashing +- Sourcing the script doesn't terminate the parent shell even on error + +**Expected behavior**: +- Scripts are executable and in the correct location +- Sourcing auth-ado.sh returns control to the caller +- Parent shell continues executing after sourcing fails + +### 2. Shim Integration Test (`test_shim_integration`) + +**Purpose**: Test that shim scripts properly handle missing authentication helper. + +**What it tests**: +- Shim scripts source auth-ado.sh correctly +- Scripts handle timeout gracefully when ado-auth-helper is missing +- Shim directory is in PATH +- Scripts don't crash when authentication fails + +**Expected behavior**: +- Shims wait for up to 3 minutes for authentication +- Timeout error is returned but doesn't crash the script +- Calling scripts can continue or handle error appropriately + +### 3. Python Keyring Tests + +Multiple scenarios test Python integration: +- `python38_and_keyring_debian`: Python 3.8 on Debian with keyring +- `python38_and_keyring_ubuntu`: Python 3.8 on Ubuntu with keyring +- `python312_and_keyring_debian`: Python 3.12 on Debian with keyring +- `python_and_no_keyring`: Python without keyring helper + +## Running Tests + +### Run All Tests + +```bash +cd /path/to/codespace-features +devcontainer features test -f artifacts-helper +``` + +### Run Specific Test Scenario + +```bash +devcontainer features test -f artifacts-helper --scenario test_auth_wait +devcontainer features test -f artifacts-helper --scenario test_shim_integration +``` + +### Run Individual Test Script + +If you want to test a specific script in an already-built container: + +```bash +# Inside a devcontainer with artifacts-helper installed +bash /path/to/test/artifacts-helper/test_auth_wait.sh +``` + +## Manual Testing + +### Test Authentication Wait Behavior + +1. Create a test devcontainer with artifacts-helper feature +2. Remove or delay the ado-auth-helper installation +3. Try to run a package manager command (e.g., `dotnet restore`) +4. Observe that the script waits and shows progress +5. Verify the script eventually times out with error but doesn't crash + +```bash +# Mock a scenario where ado-auth-helper is missing +rm -f ~/ado-auth-helper + +# Try to run dotnet - should wait and timeout gracefully +timeout 10 dotnet --version +echo "Exit code: $?" # Should be non-zero but script continues + +# Verify we can still run commands +echo "Shell is still active" +``` + +### Test Shim Sourcing Behavior + +```bash +# Test that sourcing doesn't exit the shell +bash -c ' + source /usr/local/share/codespace-shims/auth-ado.sh 2>/dev/null || echo "Returned with error" + echo "Shell still running" +' +``` + +### Test with Actual Authentication + +1. Set up a codespace with the ADO Codespaces Auth extension +2. Configure an Azure Artifacts feed +3. Wait for ado-auth-helper to be installed +4. Run package restore commands +5. Verify authentication succeeds + +## What Changed in PR #85 + +The key changes improve resilience when the authentication helper isn't immediately available: + +1. **Added wait loop**: Scripts now wait up to 3 minutes for ado-auth-helper +2. **Removed `set -e`**: Prevents sourced script from terminating parent shell +3. **Changed `exit` to `return`**: Allows error handling in calling scripts +4. **Added progress indicators**: Shows wait progress every 20 seconds +5. **Fixed PATH**: Uses hardcoded `/usr/local/share/codespace-shims` instead of variable + +## Troubleshooting Test Failures + +### "auth-ado.sh terminates shell" +- Check that `set -e` is removed from auth-ado.sh +- Verify `return 1` is used instead of `exit 1` + +### "Shim scripts not found" +- Verify PATH includes `/usr/local/share/codespace-shims` +- Check that install.sh properly creates the shim scripts +- Ensure containerEnv in devcontainer-feature.json is correct + +### "Tests timeout" +- Reduce MAX_WAIT in auth-ado.sh for faster testing +- Use `timeout` command to limit test duration +- Check that progress indicators are working + +## CI/CD Integration + +These tests are designed to work with the devcontainer features test framework and can be integrated into CI/CD pipelines: + +```yaml +- name: Test artifacts-helper feature + run: | + devcontainer features test -f artifacts-helper +``` 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..bc6cf5c 100644 --- a/test/artifacts-helper/scenarios.json +++ b/test/artifacts-helper/scenarios.json @@ -42,5 +42,21 @@ "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 + } + } } } \ 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..7543d8e --- /dev/null +++ b/test/artifacts-helper/test_auth_wait.sh @@ -0,0 +1,25 @@ +#!/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 +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_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 From 69a51743589afcb709dc7cd06b5446c4558c4ef5 Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal Date: Fri, 12 Dec 2025 16:52:51 +0000 Subject: [PATCH 7/8] Add test to verify that scripts are still executed even if ado-helper fails --- src/artifacts-helper/README.md | 13 ++---- src/artifacts-helper/scripts/auth-ado.sh | 9 ++-- test/artifacts-helper/scenarios.json | 12 +++++ test/artifacts-helper/test_auth_wait.sh | 1 + .../test_fallback_execution.sh | 46 +++++++++++++++++++ 5 files changed, 68 insertions(+), 13 deletions(-) create mode 100755 test/artifacts-helper/test_fallback_execution.sh diff --git a/src/artifacts-helper/README.md b/src/artifacts-helper/README.md index 5972b0b..5363da9 100644 --- a/src/artifacts-helper/README.md +++ b/src/artifacts-helper/README.md @@ -77,14 +77,14 @@ to download the package. 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 +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. Return an error (but not terminate dependent scripts) if the helper is not available after the timeout +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. +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 that can be handled by the calling script. +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 @@ -102,11 +102,6 @@ devcontainer features test -f artifacts-helper devcontainer features test -f artifacts-helper --scenario test_auth_wait ``` -The test suite includes: -- **test_auth_wait.sh**: Verifies that auth-ado.sh can be sourced without terminating the shell -- **test_shim_integration.sh**: Tests that shim scripts properly handle missing authentication helper -- **Python keyring tests**: Validates Python package installation with Azure Artifacts authentication - ## 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/scripts/auth-ado.sh b/src/artifacts-helper/scripts/auth-ado.sh index 2fb4f4e..db54205 100644 --- a/src/artifacts-helper/scripts/auth-ado.sh +++ b/src/artifacts-helper/scripts/auth-ado.sh @@ -3,7 +3,8 @@ echo "::step::Waiting for AzDO Authentication Helper..." # Wait up to 3 minutes for the ado-auth-helper to be installed -MAX_WAIT=180 +# Can be overridden via environment variable for testing +MAX_WAIT=${MAX_WAIT:-180} ELAPSED=0 while [ $ELAPSED -lt $MAX_WAIT ]; do @@ -22,8 +23,8 @@ while [ $ELAPSED -lt $MAX_WAIT ]; do fi done -# Timeout reached -echo "::error::AzDO Authentication Helper not found after ${MAX_WAIT} seconds" +# Timeout reached - continue without authentication +echo "::warning::AzDO Authentication Helper not found after ${MAX_WAIT} seconds" echo "Expected location: ${HOME}/ado-auth-helper" -echo "Restore cannot proceed without authentication" +echo "Continuing without Azure Artifacts authentication..." return 1 \ No newline at end of file diff --git a/test/artifacts-helper/scenarios.json b/test/artifacts-helper/scenarios.json index bc6cf5c..516c9af 100644 --- a/test/artifacts-helper/scenarios.json +++ b/test/artifacts-helper/scenarios.json @@ -58,5 +58,17 @@ "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_auth_wait.sh b/test/artifacts-helper/test_auth_wait.sh index 7543d8e..1f5cb55 100755 --- a/test/artifacts-helper/test_auth_wait.sh +++ b/test/artifacts-helper/test_auth_wait.sh @@ -9,6 +9,7 @@ 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 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 From 0507d687552d6ee8e195d1513541965c6eb9e67f Mon Sep 17 00:00:00 2001 From: Abdurrahmaan Iqbal <137001048+abdurriq@users.noreply.github.com> Date: Fri, 12 Dec 2025 17:02:24 +0000 Subject: [PATCH 8/8] Delete test/artifacts-helper/README.md --- test/artifacts-helper/README.md | 143 -------------------------------- 1 file changed, 143 deletions(-) delete mode 100644 test/artifacts-helper/README.md diff --git a/test/artifacts-helper/README.md b/test/artifacts-helper/README.md deleted file mode 100644 index 752613c..0000000 --- a/test/artifacts-helper/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# Testing Azure Artifacts Helper - -This document describes how to test the artifacts-helper feature, particularly the authentication wait behavior and shim script resilience. - -## Test Scenarios - -### 1. Authentication Wait Test (`test_auth_wait`) - -**Purpose**: Verify that the auth-ado.sh script can be sourced without terminating the parent shell. - -**What it tests**: -- Shim scripts exist in `/usr/local/share/codespace-shims/` -- `auth-ado.sh` can be sourced multiple times without crashing -- Sourcing the script doesn't terminate the parent shell even on error - -**Expected behavior**: -- Scripts are executable and in the correct location -- Sourcing auth-ado.sh returns control to the caller -- Parent shell continues executing after sourcing fails - -### 2. Shim Integration Test (`test_shim_integration`) - -**Purpose**: Test that shim scripts properly handle missing authentication helper. - -**What it tests**: -- Shim scripts source auth-ado.sh correctly -- Scripts handle timeout gracefully when ado-auth-helper is missing -- Shim directory is in PATH -- Scripts don't crash when authentication fails - -**Expected behavior**: -- Shims wait for up to 3 minutes for authentication -- Timeout error is returned but doesn't crash the script -- Calling scripts can continue or handle error appropriately - -### 3. Python Keyring Tests - -Multiple scenarios test Python integration: -- `python38_and_keyring_debian`: Python 3.8 on Debian with keyring -- `python38_and_keyring_ubuntu`: Python 3.8 on Ubuntu with keyring -- `python312_and_keyring_debian`: Python 3.12 on Debian with keyring -- `python_and_no_keyring`: Python without keyring helper - -## Running Tests - -### Run All Tests - -```bash -cd /path/to/codespace-features -devcontainer features test -f artifacts-helper -``` - -### Run Specific Test Scenario - -```bash -devcontainer features test -f artifacts-helper --scenario test_auth_wait -devcontainer features test -f artifacts-helper --scenario test_shim_integration -``` - -### Run Individual Test Script - -If you want to test a specific script in an already-built container: - -```bash -# Inside a devcontainer with artifacts-helper installed -bash /path/to/test/artifacts-helper/test_auth_wait.sh -``` - -## Manual Testing - -### Test Authentication Wait Behavior - -1. Create a test devcontainer with artifacts-helper feature -2. Remove or delay the ado-auth-helper installation -3. Try to run a package manager command (e.g., `dotnet restore`) -4. Observe that the script waits and shows progress -5. Verify the script eventually times out with error but doesn't crash - -```bash -# Mock a scenario where ado-auth-helper is missing -rm -f ~/ado-auth-helper - -# Try to run dotnet - should wait and timeout gracefully -timeout 10 dotnet --version -echo "Exit code: $?" # Should be non-zero but script continues - -# Verify we can still run commands -echo "Shell is still active" -``` - -### Test Shim Sourcing Behavior - -```bash -# Test that sourcing doesn't exit the shell -bash -c ' - source /usr/local/share/codespace-shims/auth-ado.sh 2>/dev/null || echo "Returned with error" - echo "Shell still running" -' -``` - -### Test with Actual Authentication - -1. Set up a codespace with the ADO Codespaces Auth extension -2. Configure an Azure Artifacts feed -3. Wait for ado-auth-helper to be installed -4. Run package restore commands -5. Verify authentication succeeds - -## What Changed in PR #85 - -The key changes improve resilience when the authentication helper isn't immediately available: - -1. **Added wait loop**: Scripts now wait up to 3 minutes for ado-auth-helper -2. **Removed `set -e`**: Prevents sourced script from terminating parent shell -3. **Changed `exit` to `return`**: Allows error handling in calling scripts -4. **Added progress indicators**: Shows wait progress every 20 seconds -5. **Fixed PATH**: Uses hardcoded `/usr/local/share/codespace-shims` instead of variable - -## Troubleshooting Test Failures - -### "auth-ado.sh terminates shell" -- Check that `set -e` is removed from auth-ado.sh -- Verify `return 1` is used instead of `exit 1` - -### "Shim scripts not found" -- Verify PATH includes `/usr/local/share/codespace-shims` -- Check that install.sh properly creates the shim scripts -- Ensure containerEnv in devcontainer-feature.json is correct - -### "Tests timeout" -- Reduce MAX_WAIT in auth-ado.sh for faster testing -- Use `timeout` command to limit test duration -- Check that progress indicators are working - -## CI/CD Integration - -These tests are designed to work with the devcontainer features test framework and can be integrated into CI/CD pipelines: - -```yaml -- name: Test artifacts-helper feature - run: | - devcontainer features test -f artifacts-helper -```