|
8 | 8 | # Maintainer: The Dev Container spec maintainers |
9 | 9 |
|
10 | 10 | export NODE_VERSION="${VERSION:-"lts"}" |
| 11 | +export NPM_VERSION="${NPMVERSION:-"lts"}" |
11 | 12 | export PNPM_VERSION="${PNPMVERSION:-"latest"}" |
12 | 13 | export NVM_VERSION="${NVMVERSION:-"latest"}" |
13 | 14 | export NVM_DIR="${NVMINSTALLPATH:-"/usr/local/share/nvm"}" |
@@ -381,6 +382,101 @@ if [ ! -z "${ADDITIONAL_VERSIONS}" ]; then |
381 | 382 | IFS=$OLDIFS |
382 | 383 | fi |
383 | 384 |
|
| 385 | +# Install or update npm to specific version |
| 386 | +if [ -z "${NPM_VERSION}" ] || [ "${NPM_VERSION}" = "none" ]; then |
| 387 | + echo "Ignoring NPM version update" |
| 388 | +elif bash -c ". '${NVM_DIR}/nvm.sh' && type npm >/dev/null 2>&1"; then |
| 389 | + ( |
| 390 | + . "${NVM_DIR}/nvm.sh" |
| 391 | + [ ! -z "$http_proxy" ] && npm set proxy="$http_proxy" |
| 392 | + [ ! -z "$https_proxy" ] && npm set https-proxy="$https_proxy" |
| 393 | + [ ! -z "$no_proxy" ] && npm set noproxy="$no_proxy" |
| 394 | + echo "Installing npm version ${NPM_VERSION}..." |
| 395 | + |
| 396 | + CURRENT_NPM_VERSION=$(npm --version 2>/dev/null || echo 'unknown') |
| 397 | + echo "Current npm version: $CURRENT_NPM_VERSION" |
| 398 | + |
| 399 | + # Clear npm cache and extract version numbers |
| 400 | + npm cache clean --force 2>/dev/null || true |
| 401 | + CURRENT_MAJOR=$(echo "$CURRENT_NPM_VERSION" | cut -d. -f1 || echo "0") |
| 402 | + NODE_MAJOR=$(node --version 2>/dev/null | cut -d. -f1 | tr -d 'v' || echo "0") |
| 403 | + |
| 404 | + # Dynamically check npm's Node.js requirements and auto-fallback if incompatible |
| 405 | + ORIGINAL_NPM_VERSION="$NPM_VERSION" |
| 406 | + if [ "$NPM_VERSION" != "none" ]; then |
| 407 | + echo "Checking npm compatibility requirements..." |
| 408 | + NPM_NODE_REQUIREMENT=$(npm view npm@${NPM_VERSION} engines.node 2>/dev/null || echo "") |
| 409 | + |
| 410 | + if [ -n "$NPM_NODE_REQUIREMENT" ]; then |
| 411 | + echo "npm $NPM_VERSION requires Node.js: $NPM_NODE_REQUIREMENT" |
| 412 | + |
| 413 | + # Extract minimum required Node version from requirement string |
| 414 | + MIN_NODE=$(echo "$NPM_NODE_REQUIREMENT" | grep -oE '[0-9]+' | head -1 || echo "0") |
| 415 | + |
| 416 | + if [ "$MIN_NODE" -gt "0" ] && [ "$NODE_MAJOR" -lt "$MIN_NODE" ]; then |
| 417 | + echo "⚠️ WARNING: npm $NPM_VERSION requires Node.js $MIN_NODE+, you have $NODE_MAJOR.x" |
| 418 | + |
| 419 | + # Find compatible npm version dynamically using same logic |
| 420 | + echo "🔍 Finding compatible npm version for Node.js $NODE_MAJOR.x..." |
| 421 | + |
| 422 | + # Try npm major versions in descending order to find highest compatible version |
| 423 | + for npm_major in 10 9 8 7 6; do |
| 424 | + echo "Checking npm $npm_major compatibility..." |
| 425 | + FALLBACK_NODE_REQUIREMENT=$(npm view "npm@${npm_major}" engines.node 2>/dev/null || echo "") |
| 426 | + |
| 427 | + if [ -n "$FALLBACK_NODE_REQUIREMENT" ]; then |
| 428 | + MIN_NODE=$(echo "$FALLBACK_NODE_REQUIREMENT" | grep -oE '[0-9]+' | head -1 || echo "0") |
| 429 | + |
| 430 | + if [ "$MIN_NODE" -le "$NODE_MAJOR" ]; then |
| 431 | + # Get latest patch version for this compatible major version |
| 432 | + NPM_VERSION=$(npm view "npm@${npm_major}" version 2>/dev/null || echo "") |
| 433 | + if [ -n "$NPM_VERSION" ]; then |
| 434 | + echo "✓ Found compatible npm $NPM_VERSION (requires Node.js $MIN_NODE+)" |
| 435 | + echo "🔄 Auto-fallback: Installing compatible npm $NPM_VERSION instead" |
| 436 | + break |
| 437 | + fi |
| 438 | + fi |
| 439 | + fi |
| 440 | + done |
| 441 | + |
| 442 | + # If no compatible version found, skip npm installation |
| 443 | + if [ "$NPM_VERSION" = "$ORIGINAL_NPM_VERSION" ]; then |
| 444 | + echo "❌ Could not find compatible npm version, keeping current npm" |
| 445 | + NPM_VERSION="none" |
| 446 | + fi |
| 447 | + elif [ "$MIN_NODE" -gt "0" ]; then |
| 448 | + echo "✓ Node.js $NODE_MAJOR.x meets npm $NPM_VERSION requirement" |
| 449 | + fi |
| 450 | + else |
| 451 | + echo "Could not determine Node.js requirements for npm $NPM_VERSION, proceeding anyway..." |
| 452 | + fi |
| 453 | + fi |
| 454 | + |
| 455 | + # Check if npm installation was cancelled due to compatibility issues |
| 456 | + if [ "$NPM_VERSION" = "none" ]; then |
| 457 | + echo "Skipping npm installation due to compatibility issues." |
| 458 | + else |
| 459 | + # Try npm installation with retries |
| 460 | + for i in 1 2 3; do |
| 461 | + echo "Attempt $i: Running npm install -g npm@$NPM_VERSION" |
| 462 | + if npm install -g npm@$NPM_VERSION --force --no-audit --no-fund 2>&1; then |
| 463 | + NEW_VERSION=$(npm --version 2>/dev/null || echo 'unknown') |
| 464 | + echo "Successfully installed npm@${NPM_VERSION}, new version: $NEW_VERSION" |
| 465 | + break |
| 466 | + else |
| 467 | + echo "Attempt $i failed, retrying..." |
| 468 | + sleep 2 |
| 469 | + if [ $i -eq 3 ]; then |
| 470 | + echo "Failed to install npm@${NPM_VERSION} after 3 attempts. Keeping current npm version $(npm --version 2>/dev/null || echo 'unknown')." |
| 471 | + fi |
| 472 | + fi |
| 473 | + done |
| 474 | + fi |
| 475 | + ) |
| 476 | + else |
| 477 | + echo "Skip installing/updating npm because npm is not available" |
| 478 | + fi |
| 479 | + |
384 | 480 | # Install pnpm |
385 | 481 | if [ ! -z "${PNPM_VERSION}" ] && [ "${PNPM_VERSION}" = "none" ]; then |
386 | 482 | echo "Ignoring installation of PNPM" |
|
0 commit comments