Skip to content

Commit 25ecd5e

Browse files
authored
Merge pull request #85 from abdurriq/azure-artifacts-shims-fix
Fix for wrapper scripts not being applied in non-interactive environments
2 parents 57969aa + 0507d68 commit 25ecd5e

30 files changed

+342
-240
lines changed

src/artifacts-helper/NOTES.md

Lines changed: 3 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
This installs [Azure Artifacts Credential Provider](https://github.com/microsoft/artifacts-credprovider)
2-
and optionally configures functions which shadow `dotnet`, `nuget`, `npm`, `yarn`, `rush`, and `pnpm` which dynamically sets an authentication token
3-
for pulling artifacts from a feed before running the command.
2+
and optionally configures shims which shadow `dotnet`, `nuget`, `npm`, `yarn`, `rush`, and `pnpm`.
3+
These dynamically sets an authentication token for pulling artifacts from a feed before running the command.
44

55
For `npm`, `yarn`, `rush`, and `pnpm` this requires that your `~/.npmrc` file is configured to use the ${ARTIFACTS_ACCESSTOKEN}
66
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.
4040

4141
## OS Support
4242

43-
This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0
44-
45-
## Changing where functions are configured
46-
47-
By default, the functions are defined in `/etc/bash.bashrc` and `/etc/zsh/zshrc` if the container user is `root`, otherwise `~/.bashrc` and `~/.zshrc`.
48-
This default configuration ensures that the functions are always available for any interactive shells.
49-
50-
In some cases it can be useful to have the functions written to a non-default location. For example:
51-
- the configuration file of a shell other than `bash` and `zsh`
52-
- a custom file which is not a shell configuration script (so that it can be `source`d in non-interactive shells and scripts)
53-
54-
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
55-
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:
56-
57-
```bash
58-
# .devcontainer/devcontainer.json
59-
{
60-
// ...
61-
"targetFiles": "/custom/path/to/auth-helper.sh"
62-
}
63-
64-
# ~/.bashrc
65-
66-
source /custom/path/to/auth-helper.sh
67-
```
43+
This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0

src/artifacts-helper/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,35 @@ pip install <package_name> --index-url https://pkgs.dev.azure.com/<org_name>/_pa
7373
When the feed URL is an Azure Artifacts feed pip will use the keyring helper to provide the credentials needed
7474
to download the package.
7575

76+
## Authentication Helper Wait Behavior
77+
78+
The shim scripts (e.g., `dotnet`, `npm`, `nuget`) now include a wait mechanism for the Azure DevOps authentication helper. When invoked, these scripts will:
79+
80+
1. Wait up to 3 minutes for the `ado-auth-helper` to become available (configurable via `MAX_WAIT` environment variable)
81+
2. Display progress indicators every 20 seconds while waiting
82+
3. Continue execution once authentication is successful
83+
4. **Continue with the underlying command even if authentication is not available** after the timeout
84+
85+
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.
86+
87+
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.
88+
7689
## OS Support
7790

7891
This feature is tested to work on Debian/Ubuntu and Mariner CBL 2.0
7992

93+
## Testing
94+
95+
To test this feature locally, you can use the devcontainer CLI:
96+
97+
```bash
98+
# Test all scenarios
99+
devcontainer features test -f artifacts-helper
100+
101+
# Test specific scenario
102+
devcontainer features test -f artifacts-helper --scenario test_auth_wait
103+
```
104+
80105
## Changing where functions are configured
81106

82107
By default, the functions are defined in `/etc/bash.bashrc` and `/etc/zsh/zshrc` if the container user is `root`, otherwise `~/.bashrc` and `~/.zshrc`.

src/artifacts-helper/devcontainer-feature.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "Azure Artifacts Credential Helper",
33
"id": "artifacts-helper",
4-
"version": "2.0.3",
4+
"version": "3.0.0",
55
"description": "Configures Codespace to authenticate with Azure Artifact feeds",
66
"options": {
77
"nugetURIPrefixes": {
@@ -49,6 +49,11 @@
4949
"default": true,
5050
"description": "Create alias for pnpm"
5151
},
52+
"shimDirectory": {
53+
"type": "string",
54+
"default": "/usr/local/share/codespace-shims",
55+
"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."
56+
},
5257
"targetFiles": {
5358
"type": "string",
5459
"default": "DEFAULT",
@@ -60,9 +65,13 @@
6065
"description": "Install Python keyring helper for pip"
6166
}
6267
},
68+
"containerEnv": {
69+
"PATH": "/usr/local/share/codespace-shims:${PATH}"
70+
},
6371
"installsAfter": [
6472
"ghcr.io/devcontainers/features/common-utils",
65-
"ghcr.io/devcontainers/features/python"
73+
"ghcr.io/devcontainers/features/python",
74+
"ghcr.io/devcontainers/features/node"
6675
],
6776
"customizations": {
6877
"vscode": {

src/artifacts-helper/install.sh

Lines changed: 30 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ ALIAS_NPX="${NPXALIAS:-"true"}"
1212
ALIAS_RUSH="${RUSHALIAS:-"true"}"
1313
ALIAS_PNPM="${PNPMALIAS:-"true"}"
1414
INSTALL_PIP_HELPER="${PYTHON:-"false"}"
15-
COMMA_SEP_TARGET_FILES="${TARGETFILES:-"DEFAULT"}"
15+
SHIM_DIRECTORY="${SHIMDIRECTORY:-"/usr/local/share/codespace-shims/"}"
1616

1717
ALIASES_ARR=()
1818

@@ -78,31 +78,28 @@ cd "$(dirname "$0")"
7878

7979
cp ./scripts/install-provider.sh /tmp
8080
chmod +rx /tmp/install-provider.sh
81+
8182
cp ./scripts/install-python-keyring.sh /tmp
8283
chmod +rx /tmp/install-python-keyring.sh
8384

84-
sed "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/run-dotnet.sh > /usr/local/bin/run-dotnet.sh
85-
chmod +rx /usr/local/bin/run-dotnet.sh
86-
sed "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/run-nuget.sh > /usr/local/bin/run-nuget.sh
87-
chmod +rx /usr/local/bin/run-nuget.sh
88-
cp ./scripts/run-npm.sh /usr/local/bin/run-npm.sh
89-
chmod +rx /usr/local/bin/run-npm.sh
90-
cp ./scripts/run-yarn.sh /usr/local/bin/run-yarn.sh
91-
chmod +rx /usr/local/bin/run-yarn.sh
92-
cp ./scripts/write-npm.sh /usr/local/bin/write-npm.sh
93-
chmod +rx /usr/local/bin/write-npm.sh
94-
cp ./scripts/run-npx.sh /usr/local/bin/run-npx.sh
95-
chmod +rx /usr/local/bin/run-npx.sh
96-
97-
cp ./scripts/run-rush.sh /usr/local/bin/run-rush.sh
98-
chmod +rx /usr/local/bin/run-rush.sh
99-
cp ./scripts/run-rush-pnpm.sh /usr/local/bin/run-rush-pnpm.sh
100-
chmod +rx /usr/local/bin/run-rush-pnpm.sh
101-
102-
cp ./scripts/run-pnpm.sh /usr/local/bin/run-pnpm.sh
103-
chmod +rx /usr/local/bin/run-pnpm.sh
104-
cp ./scripts/run-pnpx.sh /usr/local/bin/run-pnpx.sh
105-
chmod +rx /usr/local/bin/run-pnpx.sh
85+
# Replace AZURE_DEVOPS_NUGET_FEED_URL_PREFIX in scripts that require it
86+
sed -i "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/dotnet
87+
sed -i "s|REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX|${PREFIXES}|g" ./scripts/nuget
88+
89+
# Create ${SHIM_DIRECTORY}
90+
mkdir -p "${SHIM_DIRECTORY}"
91+
92+
# Install helper scripts in ${SHIM_DIRECTORY}
93+
cp "./scripts/auth-ado.sh" "${SHIM_DIRECTORY}"
94+
cp "./scripts/resolve-shim.sh" "${SHIM_DIRECTORY}"
95+
cp "./scripts/write-npm.sh" "${SHIM_DIRECTORY}"
96+
chmod +rx "${SHIM_DIRECTORY}/write-npm.sh"
97+
98+
# Install selected shim scripts in ${SHIM_DIRECTORY}
99+
for alias in "${ALIASES_ARR[@]}"; do
100+
chmod +rx "./scripts/${alias}"
101+
cp "./scripts/${alias}" "${SHIM_DIRECTORY}"
102+
done
106103

107104
if [ "${INSTALL_PIP_HELPER}" = "true" ]; then
108105
USER="${_REMOTE_USER}" /tmp/install-python-keyring.sh
@@ -126,16 +123,17 @@ fi
126123

127124
IFS=',' read -r -a TARGET_FILES_ARR <<< "$COMMA_SEP_TARGET_FILES"
128125

126+
ALIASES_BLOCK=""
129127
for ALIAS in "${ALIASES_ARR[@]}"; do
130-
for TARGET_FILE in "${TARGET_FILES_ARR[@]}"; do
131-
CMD="$ALIAS() { /usr/local/bin/run-$ALIAS.sh \"\$@\"; }"
132-
133-
if [ "${INSTALL_WITH_SUDO}" = "true" ]; then
134-
sudo -u ${_REMOTE_USER} bash -c "echo '$CMD' >> $TARGET_FILE"
135-
else
136-
echo $CMD >> $TARGET_FILE || true
137-
fi
138-
done
128+
ALIASES_BLOCK+="$ALIAS() { \"${SHIM_DIRECTORY}/$ALIAS\" \"\$@\"; }\n"
129+
done
130+
131+
for TARGET_FILE in "${TARGET_FILES_ARR[@]}"; do
132+
if [ "${INSTALL_WITH_SUDO}" = "true" ]; then
133+
sudo -u ${_REMOTE_USER} bash -c "printf '%s' \"$ALIASES_BLOCK\" >> $TARGET_FILE"
134+
else
135+
printf '%s' "$ALIASES_BLOCK" >> "$TARGET_FILE" || true
136+
fi
139137
done
140138

141139
if [ "${INSTALL_WITH_SUDO}" = "true" ]; then
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/bash
2+
3+
echo "::step::Waiting for AzDO Authentication Helper..."
4+
5+
# Wait up to 3 minutes for the ado-auth-helper to be installed
6+
# Can be overridden via environment variable for testing
7+
MAX_WAIT=${MAX_WAIT:-180}
8+
ELAPSED=0
9+
10+
while [ $ELAPSED -lt $MAX_WAIT ]; do
11+
if [ -f "${HOME}/ado-auth-helper" ]; then
12+
echo "::step::Running ado-auth-helper get-access-token..."
13+
ARTIFACTS_ACCESSTOKEN=$(${HOME}/ado-auth-helper get-access-token)
14+
echo "::step::✓ Access token retrieved successfully"
15+
return 0
16+
fi
17+
sleep 2
18+
ELAPSED=$((ELAPSED + 2))
19+
20+
# Progress indicator every 20 seconds
21+
if [ $((ELAPSED % 20)) -eq 0 ]; then
22+
echo " Still waiting... (${ELAPSED}s elapsed)"
23+
fi
24+
done
25+
26+
# Timeout reached - continue without authentication
27+
echo "::warning::AzDO Authentication Helper not found after ${MAX_WAIT} seconds"
28+
echo "Expected location: ${HOME}/ado-auth-helper"
29+
echo "Continuing without Azure Artifacts authentication..."
30+
return 1
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
source "$(dirname $0)"/auth-ado.sh
3+
source "$(dirname $0)"/resolve-shim.sh
4+
5+
# Install artifact credential provider if it is not already installed
6+
if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then
7+
wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash
8+
fi
9+
10+
DOTNET_EXE="$(resolve_shim)"
11+
VSS_NUGET_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX ${DOTNET_EXE} "$@"

src/artifacts-helper/scripts/npm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
source "$(dirname $0)"/auth-ado.sh
3+
source "$(dirname $0)"/resolve-shim.sh
4+
5+
NPM_EXE="$(resolve_shim)"
6+
ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${NPM_EXE} "$@"

src/artifacts-helper/scripts/npx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
source "$(dirname $0)"/auth-ado.sh
3+
source "$(dirname $0)"/resolve-shim.sh
4+
5+
NPX_EXE="$(resolve_shim)"
6+
ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${NPX_EXE} "$@"

src/artifacts-helper/scripts/nuget

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
source "$(dirname $0)"/auth-ado.sh
3+
source "$(dirname $0)"/resolve-shim.sh
4+
5+
# Install artifact credential provider if it is not already installed
6+
if [ ! -d "${HOME}/.nuget/plugins/netcore" ]; then
7+
wget -qO- https://aka.ms/install-artifacts-credprovider.sh | bash
8+
fi
9+
10+
NUGET_EXE="$(resolve_shim)"
11+
VSS_NUGET_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" VSS_NUGET_URI_PREFIXES=REPLACE_WITH_AZURE_DEVOPS_NUGET_FEED_URL_PREFIX ${NUGET_EXE} "$@"

src/artifacts-helper/scripts/pnpm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/bash
2+
source "$(dirname $0)"/auth-ado.sh
3+
source "$(dirname $0)"/resolve-shim.sh
4+
5+
PNPM_EXE="$(resolve_shim)"
6+
ARTIFACTS_ACCESSTOKEN="${ARTIFACTS_ACCESSTOKEN:-}" ${PNPM_EXE} "$@"

0 commit comments

Comments
 (0)