From babf688a8df4e4855b30734688fe55dd7418a73f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:34:26 +0000 Subject: [PATCH 01/14] Initial plan From 32cf333df14bbc73a3a6dfe0725a0cb7e64c08db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:42:46 +0000 Subject: [PATCH 02/14] Upgrade tar from 6.2.1 to 7.5.6 to fix security vulnerability Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- libraries/rush-lib/package.json | 3 +-- libraries/rush-lib/src/logic/TempProjectHelper.ts | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/rush-lib/package.json b/libraries/rush-lib/package.json index e8ae5199e1..d0274ed073 100644 --- a/libraries/rush-lib/package.json +++ b/libraries/rush-lib/package.json @@ -66,7 +66,7 @@ "ssri": "~8.0.0", "strict-uri-encode": "~2.0.0", "tapable": "2.2.1", - "tar": "~6.2.1", + "tar": "~7.5.6", "true-case-path": "~2.2.1" }, "devDependencies": { @@ -85,7 +85,6 @@ "@types/semver": "7.5.0", "@types/ssri": "~7.1.0", "@types/strict-uri-encode": "2.0.0", - "@types/tar": "6.1.6", "@types/webpack-env": "1.18.8", "eslint": "~9.37.0", "local-node-rig": "workspace:*", diff --git a/libraries/rush-lib/src/logic/TempProjectHelper.ts b/libraries/rush-lib/src/logic/TempProjectHelper.ts index 8cd0bcc94e..10c27d2e84 100644 --- a/libraries/rush-lib/src/logic/TempProjectHelper.ts +++ b/libraries/rush-lib/src/logic/TempProjectHelper.ts @@ -2,6 +2,7 @@ // See LICENSE in the project root for license information. import * as path from 'node:path'; +import type { Stats } from 'node:fs'; import * as tar from 'tar'; @@ -46,7 +47,7 @@ export class TempProjectHelper { noPax: true, sync: true, prefix: npmPackageFolder, - filter: (tarPath: string, stat: tar.FileStat): boolean => { + filter: (tarPath: string, stat: Stats): boolean => { if ( !this._rushConfiguration.experimentsConfiguration.configuration.noChmodFieldInTarHeaderNormalization ) { From 58eb627e71b60ac9655f34b5bb7d32bbc998ccfa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:24:33 +0000 Subject: [PATCH 03/14] Add integration tests for npm and yarn modes with tar 7.x Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../.gitignore | 2 + .../README.md | 84 +++++++++ .../run-all-tests.sh | 90 +++++++++ .../test-npm-mode.sh | 171 ++++++++++++++++++ .../test-yarn-mode.sh | 171 ++++++++++++++++++ 5 files changed, 518 insertions(+) create mode 100644 build-tests/rush-package-manager-integration-test/.gitignore create mode 100644 build-tests/rush-package-manager-integration-test/README.md create mode 100755 build-tests/rush-package-manager-integration-test/run-all-tests.sh create mode 100755 build-tests/rush-package-manager-integration-test/test-npm-mode.sh create mode 100755 build-tests/rush-package-manager-integration-test/test-yarn-mode.sh diff --git a/build-tests/rush-package-manager-integration-test/.gitignore b/build-tests/rush-package-manager-integration-test/.gitignore new file mode 100644 index 0000000000..a906da7cf5 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/.gitignore @@ -0,0 +1,2 @@ +# Temporary test repositories created during test runs +temp/ diff --git a/build-tests/rush-package-manager-integration-test/README.md b/build-tests/rush-package-manager-integration-test/README.md new file mode 100644 index 0000000000..18b20bba90 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/README.md @@ -0,0 +1,84 @@ +# Rush Package Manager Integration Tests + +This directory contains integration tests for verifying Rush works correctly with different package managers after the tar 7.x upgrade. + +## Background + +Rush's npm and yarn modes use temp project tarballs (stored in `common/temp/projects/`) to simulate package installations. The tar library is used to: +1. **Create** tarballs from temp project folders (`TempProjectHelper.createTempProjectTarball`) +2. **Extract** tarballs during the linking process (`NpmLinkManager._linkProjectAsync`) + +These tests ensure the tar 7.x upgrade works correctly with these workflows. + +## Tests + +### test-npm-mode.sh +Tests Rush npm mode by: +- Initializing a Rush repo with `npmVersion` configured +- Creating two projects with dependencies +- Running `rush update` (creates tarballs) +- Running `rush install` (extracts tarballs) +- Running `rush build` (verifies everything works end-to-end) + +### test-yarn-mode.sh +Tests Rush yarn mode by: +- Initializing a Rush repo with `yarnVersion` configured +- Creating two projects with dependencies +- Running `rush update` (creates tarballs) +- Running `rush install` (extracts tarballs) +- Running `rush build` (verifies everything works end-to-end) + +## Prerequisites + +Before running these tests: +1. Build Rush locally: `rush build --to rush` +2. Ensure you have Node.js 18+ installed + +## Running the Tests + +### Run all tests: +```bash +cd build-tests/rush-package-manager-integration-test +./run-all-tests.sh +``` + +### Run individual tests: +```bash +# Test npm mode +./test-npm-mode.sh + +# Test yarn mode +./test-yarn-mode.sh +``` + +## What Gets Tested + +These integration tests verify: +- ✓ Temp project tarballs are created correctly using tar 7.x +- ✓ Tarballs are extracted correctly during `rush install` +- ✓ File permissions are preserved (tar filter function works) +- ✓ Dependencies are linked properly between projects +- ✓ The complete workflow (update → install → build) succeeds +- ✓ Built code executes correctly + +## Test Output + +Each test creates a temporary Rush repository in the `temp/` directory: +- `temp/npm-test-repo/` - npm mode test repository +- `temp/yarn-test-repo/` - yarn mode test repository + +These directories are cleaned up at the start of each test run. + +## Related Code + +The tar library is used in: +- `libraries/rush-lib/src/logic/TempProjectHelper.ts` - Creates tarballs +- `libraries/rush-lib/src/logic/npm/NpmLinkManager.ts` - Extracts tarballs + +## Troubleshooting + +If tests fail: +1. Check that Rush built successfully: `rush build --to rush` +2. Verify Node.js version: `node --version` (should be 18+) +3. Look for error messages in the test output +4. Inspect the temp test repo: `ls -la temp/npm-test-repo/common/temp/projects/` diff --git a/build-tests/rush-package-manager-integration-test/run-all-tests.sh b/build-tests/rush-package-manager-integration-test/run-all-tests.sh new file mode 100755 index 0000000000..44f8672a00 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/run-all-tests.sh @@ -0,0 +1,90 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +# See LICENSE in the project root for license information. + +# Master test script that runs all package manager integration tests + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +echo "==========================================" +echo "Rush Package Manager Integration Tests" +echo "==========================================" +echo "" +echo "These tests verify that the tar 7.x upgrade works correctly" +echo "with different Rush package managers (npm, yarn)." +echo "" +echo "Tests will:" +echo " 1. Create Rush repos using locally-built Rush" +echo " 2. Add projects with dependencies" +echo " 3. Run rush update (creates temp tarballs)" +echo " 4. Run rush install (extracts tarballs)" +echo " 5. Run rush build (end-to-end verification)" +echo "" + +# Make scripts executable +chmod +x "$SCRIPT_DIR/test-npm-mode.sh" +chmod +x "$SCRIPT_DIR/test-yarn-mode.sh" + +# Track test results +TESTS_PASSED=0 +TESTS_FAILED=0 +FAILED_TESTS=() + +# Run npm mode test +echo "==========================================" +echo "Running NPM mode test..." +echo "==========================================" +if "$SCRIPT_DIR/test-npm-mode.sh"; then + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo "" +else + TESTS_FAILED=$((TESTS_FAILED + 1)) + FAILED_TESTS+=("NPM mode") + echo "" + echo "⚠️ NPM mode test FAILED" + echo "" +fi + +# Run yarn mode test +echo "==========================================" +echo "Running Yarn mode test..." +echo "==========================================" +if "$SCRIPT_DIR/test-yarn-mode.sh"; then + TESTS_PASSED=$((TESTS_PASSED + 1)) + echo "" +else + TESTS_FAILED=$((TESTS_FAILED + 1)) + FAILED_TESTS+=("Yarn mode") + echo "" + echo "⚠️ Yarn mode test FAILED" + echo "" +fi + +# Print summary +echo "==========================================" +echo "Test Summary" +echo "==========================================" +echo "" +echo "Tests passed: $TESTS_PASSED" +echo "Tests failed: $TESTS_FAILED" +echo "" + +if [ $TESTS_FAILED -gt 0 ]; then + echo "Failed tests:" + for test in "${FAILED_TESTS[@]}"; do + echo " - $test" + done + echo "" + echo "❌ Some tests failed" + exit 1 +else + echo "✅ All tests passed!" + echo "" + echo "The tar 7.x upgrade is working correctly with:" + echo " - NPM package manager" + echo " - Yarn package manager" + echo "" + exit 0 +fi diff --git a/build-tests/rush-package-manager-integration-test/test-npm-mode.sh b/build-tests/rush-package-manager-integration-test/test-npm-mode.sh new file mode 100755 index 0000000000..bbe407c150 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/test-npm-mode.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +# See LICENSE in the project root for license information. + +# Integration test for Rush npm mode with tar 7.x +# This script verifies that temp project tarballs work correctly with npm package manager + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TEST_REPO_DIR="$SCRIPT_DIR/temp/npm-test-repo" +RUSHSTACK_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )" + +echo "==========================================" +echo "Rush NPM Mode Integration Test" +echo "==========================================" +echo "" +echo "This test verifies that tar 7.x changes work correctly with npm package manager" +echo "by creating temp project tarballs and extracting them during rush install." +echo "" + +# Clean up previous test runs +if [ -d "$TEST_REPO_DIR" ]; then + echo "Cleaning up previous test run..." + rm -rf "$TEST_REPO_DIR" +fi + +# Create test repo directory +echo "Creating test repository at $TEST_REPO_DIR..." +mkdir -p "$TEST_REPO_DIR" +cd "$TEST_REPO_DIR" + +# Use locally built rush to initialize the repo +echo "Initializing Rush repo with npm mode..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" init --overwrite-existing + +# Configure rush.json to use npm +echo "Configuring rush.json for npm mode..." +cat > rush.json << 'EOF' +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "rushVersion": "5.166.0", + "npmVersion": "8.19.4", + "nodeSupportedVersionRange": ">=18.0.0", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 2, + "projects": [ + { + "packageName": "test-project-a", + "projectFolder": "projects/test-project-a" + }, + { + "packageName": "test-project-b", + "projectFolder": "projects/test-project-b" + } + ] +} +EOF + +# Create project A (dependency) +echo "Creating test-project-a..." +mkdir -p projects/test-project-a +cat > projects/test-project-a/package.json << 'EOF' +{ + "name": "test-project-a", + "version": "1.0.0", + "main": "lib/index.js", + "scripts": { + "build": "node -e \"require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\\"Hello from A\\\" };');\"" + }, + "dependencies": { + "lodash": "^4.17.21" + } +} +EOF + +# Create project B (depends on A) +echo "Creating test-project-b..." +mkdir -p projects/test-project-b +cat > projects/test-project-b/package.json << 'EOF' +{ + "name": "test-project-b", + "version": "1.0.0", + "main": "lib/index.js", + "scripts": { + "build": "node -e \"const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\\"Using: \\\" + require(\\'test-project-a\\').greet() };');\"" + }, + "dependencies": { + "test-project-a": "1.0.0", + "moment": "^2.29.4" + } +} +EOF + +# Run rush update (this will create temp project tarballs using tar.create) +echo "" +echo "Running 'rush update' (creates temp project tarballs using tar 7.x)..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" update + +# Verify temp project tarballs were created +echo "" +echo "Verifying temp project tarballs were created..." +if [ ! -f "common/temp/projects/test-project-a.tgz" ]; then + echo "ERROR: test-project-a.tgz was not created!" + exit 1 +fi +if [ ! -f "common/temp/projects/test-project-b.tgz" ]; then + echo "ERROR: test-project-b.tgz was not created!" + exit 1 +fi +echo "✓ Temp project tarballs created successfully" + +# Run rush install (this will extract temp project tarballs using tar.extract) +echo "" +echo "Running 'rush install' (extracts temp project tarballs using tar 7.x)..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" install + +# Verify node_modules were populated correctly +echo "" +echo "Verifying node_modules structure..." +if [ ! -d "projects/test-project-a/node_modules/lodash" ]; then + echo "ERROR: lodash not installed in test-project-a!" + exit 1 +fi +if [ ! -L "projects/test-project-b/node_modules/test-project-a" ]; then + echo "ERROR: test-project-a not linked in test-project-b!" + exit 1 +fi +echo "✓ Dependencies installed correctly" + +# Run rush build to verify everything works end-to-end +echo "" +echo "Running 'rush build'..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" build + +# Verify build outputs +echo "" +echo "Verifying build outputs..." +if [ ! -f "projects/test-project-a/lib/index.js" ]; then + echo "ERROR: test-project-a build output not found!" + exit 1 +fi +if [ ! -f "projects/test-project-b/lib/index.js" ]; then + echo "ERROR: test-project-b build output not found!" + exit 1 +fi +echo "✓ Build completed successfully" + +# Test that the built code actually works +echo "" +echo "Testing built code..." +cd projects/test-project-b +node -e "const b = require('./lib/index.js'); console.log(b.test());" | grep -q "Using: Hello from A" +if [ $? -eq 0 ]; then + echo "✓ Built code executes correctly" +else + echo "ERROR: Built code did not execute as expected!" + exit 1 +fi + +echo "" +echo "==========================================" +echo "✓ NPM Mode Integration Test PASSED" +echo "==========================================" +echo "" +echo "The tar 7.x changes work correctly with npm mode:" +echo " - Temp project tarballs created successfully" +echo " - Tarballs extracted correctly during install" +echo " - Dependencies linked properly" +echo " - Build completed successfully" +echo "" diff --git a/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh b/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh new file mode 100755 index 0000000000..dfb52e2d02 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +# See LICENSE in the project root for license information. + +# Integration test for Rush yarn mode with tar 7.x +# This script verifies that temp project tarballs work correctly with yarn package manager + +set -e + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +TEST_REPO_DIR="$SCRIPT_DIR/temp/yarn-test-repo" +RUSHSTACK_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )" + +echo "==========================================" +echo "Rush Yarn Mode Integration Test" +echo "==========================================" +echo "" +echo "This test verifies that tar 7.x changes work correctly with yarn package manager" +echo "by creating temp project tarballs and extracting them during rush install." +echo "" + +# Clean up previous test runs +if [ -d "$TEST_REPO_DIR" ]; then + echo "Cleaning up previous test run..." + rm -rf "$TEST_REPO_DIR" +fi + +# Create test repo directory +echo "Creating test repository at $TEST_REPO_DIR..." +mkdir -p "$TEST_REPO_DIR" +cd "$TEST_REPO_DIR" + +# Use locally built rush to initialize the repo +echo "Initializing Rush repo with yarn mode..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" init --overwrite-existing + +# Configure rush.json to use yarn +echo "Configuring rush.json for yarn mode..." +cat > rush.json << 'EOF' +{ + "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", + "rushVersion": "5.166.0", + "yarnVersion": "1.22.22", + "nodeSupportedVersionRange": ">=18.0.0", + "projectFolderMinDepth": 1, + "projectFolderMaxDepth": 2, + "projects": [ + { + "packageName": "test-project-a", + "projectFolder": "projects/test-project-a" + }, + { + "packageName": "test-project-b", + "projectFolder": "projects/test-project-b" + } + ] +} +EOF + +# Create project A (dependency) +echo "Creating test-project-a..." +mkdir -p projects/test-project-a +cat > projects/test-project-a/package.json << 'EOF' +{ + "name": "test-project-a", + "version": "1.0.0", + "main": "lib/index.js", + "scripts": { + "build": "node -e \"require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\\"Hello from A\\\" };');\"" + }, + "dependencies": { + "lodash": "^4.17.21" + } +} +EOF + +# Create project B (depends on A) +echo "Creating test-project-b..." +mkdir -p projects/test-project-b +cat > projects/test-project-b/package.json << 'EOF' +{ + "name": "test-project-b", + "version": "1.0.0", + "main": "lib/index.js", + "scripts": { + "build": "node -e \"const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\\"Using: \\\" + require(\\'test-project-a\\').greet() };');\"" + }, + "dependencies": { + "test-project-a": "1.0.0", + "moment": "^2.29.4" + } +} +EOF + +# Run rush update (this will create temp project tarballs using tar.create) +echo "" +echo "Running 'rush update' (creates temp project tarballs using tar 7.x)..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" update + +# Verify temp project tarballs were created +echo "" +echo "Verifying temp project tarballs were created..." +if [ ! -f "common/temp/projects/test-project-a.tgz" ]; then + echo "ERROR: test-project-a.tgz was not created!" + exit 1 +fi +if [ ! -f "common/temp/projects/test-project-b.tgz" ]; then + echo "ERROR: test-project-b.tgz was not created!" + exit 1 +fi +echo "✓ Temp project tarballs created successfully" + +# Run rush install (this will extract temp project tarballs using tar.extract) +echo "" +echo "Running 'rush install' (extracts temp project tarballs using tar 7.x)..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" install + +# Verify node_modules were populated correctly +echo "" +echo "Verifying node_modules structure..." +if [ ! -d "projects/test-project-a/node_modules/lodash" ]; then + echo "ERROR: lodash not installed in test-project-a!" + exit 1 +fi +if [ ! -L "projects/test-project-b/node_modules/test-project-a" ]; then + echo "ERROR: test-project-a not linked in test-project-b!" + exit 1 +fi +echo "✓ Dependencies installed correctly" + +# Run rush build to verify everything works end-to-end +echo "" +echo "Running 'rush build'..." +node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" build + +# Verify build outputs +echo "" +echo "Verifying build outputs..." +if [ ! -f "projects/test-project-a/lib/index.js" ]; then + echo "ERROR: test-project-a build output not found!" + exit 1 +fi +if [ ! -f "projects/test-project-b/lib/index.js" ]; then + echo "ERROR: test-project-b build output not found!" + exit 1 +fi +echo "✓ Build completed successfully" + +# Test that the built code actually works +echo "" +echo "Testing built code..." +cd projects/test-project-b +node -e "const b = require('./lib/index.js'); console.log(b.test());" | grep -q "Using: Hello from A" +if [ $? -eq 0 ]; then + echo "✓ Built code executes correctly" +else + echo "ERROR: Built code did not execute as expected!" + exit 1 +fi + +echo "" +echo "==========================================" +echo "✓ Yarn Mode Integration Test PASSED" +echo "==========================================" +echo "" +echo "The tar 7.x changes work correctly with yarn mode:" +echo " - Temp project tarballs created successfully" +echo " - Tarballs extracted correctly during install" +echo " - Dependencies linked properly" +echo " - Build completed successfully" +echo "" From fde5bc4bc761a6b765e71d4ba95137db675b98e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 21:51:19 +0000 Subject: [PATCH 04/14] Translate bash test scripts to TypeScript using @rushstack/node-core-library Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../README.md | 41 ++-- .../config/heft.json | 5 + .../eslint.config.js | 20 ++ .../package.json | 21 ++ .../run-all-tests.sh | 90 --------- .../src/TestHelper.ts | 190 ++++++++++++++++++ .../src/runTests.ts | 91 +++++++++ .../src/testNpmMode.ts | 89 ++++++++ .../src/testYarnMode.ts | 89 ++++++++ .../test-npm-mode.sh | 171 ---------------- .../test-yarn-mode.sh | 171 ---------------- .../tsconfig.json | 23 +++ rush.json | 6 + 13 files changed, 561 insertions(+), 446 deletions(-) create mode 100644 build-tests/rush-package-manager-integration-test/config/heft.json create mode 100644 build-tests/rush-package-manager-integration-test/eslint.config.js create mode 100644 build-tests/rush-package-manager-integration-test/package.json delete mode 100755 build-tests/rush-package-manager-integration-test/run-all-tests.sh create mode 100644 build-tests/rush-package-manager-integration-test/src/TestHelper.ts create mode 100644 build-tests/rush-package-manager-integration-test/src/runTests.ts create mode 100644 build-tests/rush-package-manager-integration-test/src/testNpmMode.ts create mode 100644 build-tests/rush-package-manager-integration-test/src/testYarnMode.ts delete mode 100755 build-tests/rush-package-manager-integration-test/test-npm-mode.sh delete mode 100755 build-tests/rush-package-manager-integration-test/test-yarn-mode.sh create mode 100644 build-tests/rush-package-manager-integration-test/tsconfig.json diff --git a/build-tests/rush-package-manager-integration-test/README.md b/build-tests/rush-package-manager-integration-test/README.md index 18b20bba90..ecfd24605a 100644 --- a/build-tests/rush-package-manager-integration-test/README.md +++ b/build-tests/rush-package-manager-integration-test/README.md @@ -12,7 +12,9 @@ These tests ensure the tar 7.x upgrade works correctly with these workflows. ## Tests -### test-npm-mode.sh +The test suite is written in TypeScript using `@rushstack/node-core-library` for cross-platform compatibility. + +### testNpmMode.ts Tests Rush npm mode by: - Initializing a Rush repo with `npmVersion` configured - Creating two projects with dependencies @@ -20,7 +22,7 @@ Tests Rush npm mode by: - Running `rush install` (extracts tarballs) - Running `rush build` (verifies everything works end-to-end) -### test-yarn-mode.sh +### testYarnMode.ts Tests Rush yarn mode by: - Initializing a Rush repo with `yarnVersion` configured - Creating two projects with dependencies @@ -32,23 +34,25 @@ Tests Rush yarn mode by: Before running these tests: 1. Build Rush locally: `rush build --to rush` -2. Ensure you have Node.js 18+ installed +2. Build this test project: `rush build --to rush-package-manager-integration-test` +3. Ensure you have Node.js 18+ installed ## Running the Tests -### Run all tests: ```bash +# Build the test project first cd build-tests/rush-package-manager-integration-test -./run-all-tests.sh +rush build + +# Run all tests +npm run test ``` -### Run individual tests: +Or from the root of the repo: ```bash -# Test npm mode -./test-npm-mode.sh - -# Test yarn mode -./test-yarn-mode.sh +rush build --to rush-package-manager-integration-test +cd build-tests/rush-package-manager-integration-test +npm run test ``` ## What Gets Tested @@ -69,6 +73,14 @@ Each test creates a temporary Rush repository in the `temp/` directory: These directories are cleaned up at the start of each test run. +## Implementation + +The tests use: +- **TypeScript** for type safety and better IDE support +- **@rushstack/node-core-library** for cross-platform file operations and process execution +- **TestHelper class** to encapsulate common test operations +- Modular test functions that can be run independently or together + ## Related Code The tar library is used in: @@ -79,6 +91,7 @@ The tar library is used in: If tests fail: 1. Check that Rush built successfully: `rush build --to rush` -2. Verify Node.js version: `node --version` (should be 18+) -3. Look for error messages in the test output -4. Inspect the temp test repo: `ls -la temp/npm-test-repo/common/temp/projects/` +2. Check that the test project built: `rush build --to rush-package-manager-integration-test` +3. Verify Node.js version: `node --version` (should be 18+) +4. Look for error messages in the test output +5. Inspect the temp test repo: `ls -la temp/npm-test-repo/common/temp/projects/` diff --git a/build-tests/rush-package-manager-integration-test/config/heft.json b/build-tests/rush-package-manager-integration-test/config/heft.json new file mode 100644 index 0000000000..48b379aa56 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/config/heft.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", + + "extends": "local-node-rig/profiles/default/config/heft.json" +} diff --git a/build-tests/rush-package-manager-integration-test/eslint.config.js b/build-tests/rush-package-manager-integration-test/eslint.config.js new file mode 100644 index 0000000000..bc19ee055d --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/eslint.config.js @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +const nodeProfile = require('local-node-rig/profiles/default/includes/eslint/flat/profile/node'); + +module.exports = [ + ...nodeProfile, + { + files: ['**/*.ts', '**/*.tsx'], + languageOptions: { + parserOptions: { + tsconfigRootDir: __dirname + } + }, + rules: { + // Allow console.log in test scripts + 'no-console': 'off' + } + } +]; diff --git a/build-tests/rush-package-manager-integration-test/package.json b/build-tests/rush-package-manager-integration-test/package.json new file mode 100644 index 0000000000..db94ace02f --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/package.json @@ -0,0 +1,21 @@ +{ + "name": "rush-package-manager-integration-test", + "version": "1.0.0", + "private": true, + "description": "Integration tests for Rush package managers with tar 7.x", + "license": "MIT", + "scripts": { + "_phase:build": "heft build --clean", + "build": "heft build --clean", + "test": "node lib/runTests.js" + }, + "devDependencies": { + "@microsoft/rush-lib": "workspace:*", + "@rushstack/heft": "workspace:*", + "@rushstack/node-core-library": "workspace:*", + "@types/node": "20.17.19", + "eslint": "~9.37.0", + "local-node-rig": "workspace:*", + "typescript": "~5.8.2" + } +} diff --git a/build-tests/rush-package-manager-integration-test/run-all-tests.sh b/build-tests/rush-package-manager-integration-test/run-all-tests.sh deleted file mode 100755 index 44f8672a00..0000000000 --- a/build-tests/rush-package-manager-integration-test/run-all-tests.sh +++ /dev/null @@ -1,90 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -# See LICENSE in the project root for license information. - -# Master test script that runs all package manager integration tests - -set -e - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -echo "==========================================" -echo "Rush Package Manager Integration Tests" -echo "==========================================" -echo "" -echo "These tests verify that the tar 7.x upgrade works correctly" -echo "with different Rush package managers (npm, yarn)." -echo "" -echo "Tests will:" -echo " 1. Create Rush repos using locally-built Rush" -echo " 2. Add projects with dependencies" -echo " 3. Run rush update (creates temp tarballs)" -echo " 4. Run rush install (extracts tarballs)" -echo " 5. Run rush build (end-to-end verification)" -echo "" - -# Make scripts executable -chmod +x "$SCRIPT_DIR/test-npm-mode.sh" -chmod +x "$SCRIPT_DIR/test-yarn-mode.sh" - -# Track test results -TESTS_PASSED=0 -TESTS_FAILED=0 -FAILED_TESTS=() - -# Run npm mode test -echo "==========================================" -echo "Running NPM mode test..." -echo "==========================================" -if "$SCRIPT_DIR/test-npm-mode.sh"; then - TESTS_PASSED=$((TESTS_PASSED + 1)) - echo "" -else - TESTS_FAILED=$((TESTS_FAILED + 1)) - FAILED_TESTS+=("NPM mode") - echo "" - echo "⚠️ NPM mode test FAILED" - echo "" -fi - -# Run yarn mode test -echo "==========================================" -echo "Running Yarn mode test..." -echo "==========================================" -if "$SCRIPT_DIR/test-yarn-mode.sh"; then - TESTS_PASSED=$((TESTS_PASSED + 1)) - echo "" -else - TESTS_FAILED=$((TESTS_FAILED + 1)) - FAILED_TESTS+=("Yarn mode") - echo "" - echo "⚠️ Yarn mode test FAILED" - echo "" -fi - -# Print summary -echo "==========================================" -echo "Test Summary" -echo "==========================================" -echo "" -echo "Tests passed: $TESTS_PASSED" -echo "Tests failed: $TESTS_FAILED" -echo "" - -if [ $TESTS_FAILED -gt 0 ]; then - echo "Failed tests:" - for test in "${FAILED_TESTS[@]}"; do - echo " - $test" - done - echo "" - echo "❌ Some tests failed" - exit 1 -else - echo "✅ All tests passed!" - echo "" - echo "The tar 7.x upgrade is working correctly with:" - echo " - NPM package manager" - echo " - Yarn package manager" - echo "" - exit 0 -fi diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts new file mode 100644 index 0000000000..10c9d961e1 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -0,0 +1,190 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; +import type * as child_process from 'node:child_process'; + +import { FileSystem, Executable } from '@rushstack/node-core-library'; + +/** + * Helper class for running integration tests with Rush package managers + */ +export class TestHelper { + private readonly _rushstackRoot: string; + private readonly _rushBinPath: string; + + public constructor() { + // Get rushstack root (two levels up from build-tests/rush-package-manager-integration-test) + this._rushstackRoot = path.resolve(__dirname, '../../..'); + this._rushBinPath = path.join(this._rushstackRoot, 'apps/rush/lib/start.js'); + } + + public get rushstackRoot(): string { + return this._rushstackRoot; + } + + /** + * Execute a Rush command using the locally-built Rush + */ + public async executeRushAsync(args: string[], workingDirectory: string): Promise { + console.log(`Executing: node ${this._rushBinPath} ${args.join(' ')}`); + + const childProcess: child_process.ChildProcess = Executable.spawn('node', [this._rushBinPath, ...args], { + currentWorkingDirectory: workingDirectory, + stdio: 'inherit' + }); + + const result = await Executable.waitForExitAsync(childProcess); + if (result.exitCode !== 0) { + throw new Error(`Command failed with exit code ${result.exitCode}`); + } + } + + /** + * Create a test Rush repository with the specified package manager + */ + public async createTestRepoAsync( + testRepoPath: string, + packageManagerType: 'npm' | 'yarn', + packageManagerVersion: string + ): Promise { + // Clean up previous test run + if (FileSystem.exists(testRepoPath)) { + console.log('Cleaning up previous test run...'); + FileSystem.deleteFolder(testRepoPath); + } + + // Create test repo directory + console.log(`Creating test repository at ${testRepoPath}...`); + FileSystem.ensureFolder(testRepoPath); + + // Initialize Rush repo + console.log('Initializing Rush repo...'); + await this.executeRushAsync(['init', '--overwrite-existing'], testRepoPath); + + // Configure rush.json for the specified package manager + console.log(`Configuring rush.json for ${packageManagerType} mode...`); + const rushJsonPath: string = path.join(testRepoPath, 'rush.json'); + const rushJsonContent: string = FileSystem.readFile(rushJsonPath); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rushJson: any = JSON.parse(rushJsonContent); + + // Update package manager configuration + if (packageManagerType === 'npm') { + delete rushJson.pnpmVersion; + delete rushJson.yarnVersion; + rushJson.npmVersion = packageManagerVersion; + } else if (packageManagerType === 'yarn') { + delete rushJson.pnpmVersion; + delete rushJson.npmVersion; + rushJson.yarnVersion = packageManagerVersion; + } + + // Add test projects + rushJson.projects = [ + { + packageName: 'test-project-a', + projectFolder: 'projects/test-project-a' + }, + { + packageName: 'test-project-b', + projectFolder: 'projects/test-project-b' + } + ]; + + FileSystem.writeFile(rushJsonPath, JSON.stringify(rushJson, null, 2)); + } + + /** + * Create a test project with the specified configuration + */ + public createTestProject( + testRepoPath: string, + projectName: string, + version: string, + dependencies: Record, + buildScript: string + ): void { + const projectPath: string = path.join(testRepoPath, 'projects', projectName); + FileSystem.ensureFolder(projectPath); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const packageJson: any = { + name: projectName, + version: version, + main: 'lib/index.js', + scripts: { + build: buildScript + }, + dependencies: dependencies + }; + + FileSystem.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)); + } + + /** + * Verify that temp project tarballs were created + */ + public verifyTempTarballs(testRepoPath: string, projectNames: string[]): void { + console.log('\nVerifying temp project tarballs were created...'); + for (const projectName of projectNames) { + const tarballPath: string = path.join(testRepoPath, 'common/temp/projects', `${projectName}.tgz`); + if (!FileSystem.exists(tarballPath)) { + throw new Error(`ERROR: ${projectName}.tgz was not created!`); + } + } + console.log('✓ Temp project tarballs created successfully'); + } + + /** + * Verify that dependencies are installed correctly + */ + public verifyDependencies(testRepoPath: string, projectName: string, expectedDependencies: string[]): void { + console.log('\nVerifying node_modules structure...'); + const projectNodeModules: string = path.join(testRepoPath, 'projects', projectName, 'node_modules'); + + for (const dep of expectedDependencies) { + const depPath: string = path.join(projectNodeModules, dep); + if (!FileSystem.exists(depPath)) { + throw new Error(`ERROR: ${dep} not found in ${projectName}!`); + } + } + console.log('✓ Dependencies installed correctly'); + } + + /** + * Verify that build outputs were created + */ + public verifyBuildOutputs(testRepoPath: string, projectNames: string[]): void { + console.log('\nVerifying build outputs...'); + for (const projectName of projectNames) { + const outputPath: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); + if (!FileSystem.exists(outputPath)) { + throw new Error(`ERROR: ${projectName} build output not found!`); + } + } + console.log('✓ Build completed successfully'); + } + + /** + * Test that the built code executes correctly + */ + public async testBuiltCodeAsync(testRepoPath: string, projectName: string): Promise { + console.log('\nTesting built code...'); + const projectLib: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); + + // Use Executable.spawnSync to capture output + const result: string = Executable.spawnSync( + 'node', + ['-e', `const b = require('${projectLib}'); console.log(b.test());`], + { + currentWorkingDirectory: testRepoPath + } + ).stdout.toString(); + + if (!result.includes('Using: Hello from A')) { + throw new Error('ERROR: Built code did not execute as expected!'); + } + console.log('✓ Built code executes correctly'); + } +} diff --git a/build-tests/rush-package-manager-integration-test/src/runTests.ts b/build-tests/rush-package-manager-integration-test/src/runTests.ts new file mode 100644 index 0000000000..1572e41341 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/runTests.ts @@ -0,0 +1,91 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import { testNpmModeAsync } from './testNpmMode'; +import { testYarnModeAsync } from './testYarnMode'; + +/** + * Main test runner that executes all package manager integration tests + */ +async function runTestsAsync(): Promise { + console.log('=========================================='); + console.log('Rush Package Manager Integration Tests'); + console.log('=========================================='); + console.log(''); + console.log('These tests verify that the tar 7.x upgrade works correctly'); + console.log('with different Rush package managers (npm, yarn).'); + console.log(''); + console.log('Tests will:'); + console.log(' 1. Create Rush repos using locally-built Rush'); + console.log(' 2. Add projects with dependencies'); + console.log(' 3. Run rush update (creates temp tarballs)'); + console.log(' 4. Run rush install (extracts tarballs)'); + console.log(' 5. Run rush build (end-to-end verification)'); + console.log(''); + + let testsPassed: number = 0; + let testsFailed: number = 0; + const failedTests: string[] = []; + + // Run npm mode test + console.log('=========================================='); + console.log('Running NPM mode test...'); + console.log('=========================================='); + try { + await testNpmModeAsync(); + testsPassed++; + } catch (error) { + testsFailed++; + failedTests.push('NPM mode'); + console.error('⚠️ NPM mode test FAILED'); + console.error(error); + } + + // Run yarn mode test + console.log('=========================================='); + console.log('Running Yarn mode test...'); + console.log('=========================================='); + try { + await testYarnModeAsync(); + testsPassed++; + } catch (error) { + testsFailed++; + failedTests.push('Yarn mode'); + console.error('⚠️ Yarn mode test FAILED'); + console.error(error); + } + + // Print summary + console.log('=========================================='); + console.log('Test Summary'); + console.log('=========================================='); + console.log(''); + console.log(`Tests passed: ${testsPassed}`); + console.log(`Tests failed: ${testsFailed}`); + console.log(''); + + if (testsFailed > 0) { + console.log('Failed tests:'); + for (const test of failedTests) { + console.log(` - ${test}`); + } + console.log(''); + console.log('❌ Some tests failed'); + process.exit(1); + } else { + console.log('✅ All tests passed!'); + console.log(''); + console.log('The tar 7.x upgrade is working correctly with:'); + console.log(' - NPM package manager'); + console.log(' - Yarn package manager'); + console.log(''); + process.exit(0); + } +} + +// Run tests and handle errors +runTestsAsync().catch((error) => { + console.error('Fatal error running tests:'); + console.error(error); + process.exit(1); +}); diff --git a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts new file mode 100644 index 0000000000..cc67053b19 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { TestHelper } from './TestHelper'; + +/** + * Integration test for Rush npm mode with tar 7.x + * This test verifies that temp project tarballs work correctly with npm package manager + */ +export async function testNpmModeAsync(): Promise { + const helper: TestHelper = new TestHelper(); + const testRepoPath: string = path.join(__dirname, '../temp/npm-test-repo'); + + console.log('=========================================='); + console.log('Rush NPM Mode Integration Test'); + console.log('=========================================='); + console.log(''); + console.log('This test verifies that tar 7.x changes work correctly with npm package manager'); + console.log('by creating temp project tarballs and extracting them during rush install.'); + console.log(''); + + // Create test repository + await helper.createTestRepoAsync(testRepoPath, 'npm', '8.19.4'); + + // Create project A (dependency) + console.log('Creating test-project-a...'); + helper.createTestProject( + testRepoPath, + 'test-project-a', + '1.0.0', + { lodash: '^4.17.21' }, + `node -e "require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + ); + + // Create project B (depends on A) + console.log('Creating test-project-b...'); + helper.createTestProject( + testRepoPath, + 'test-project-b', + '1.0.0', + { + 'test-project-a': '1.0.0', + moment: '^2.29.4' + }, + `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + ); + + // Run rush update (creates temp project tarballs using tar.create) + console.log(''); + console.log("Running 'rush update' (creates temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['update'], testRepoPath); + + // Verify temp project tarballs were created + helper.verifyTempTarballs(testRepoPath, ['test-project-a', 'test-project-b']); + + // Run rush install (extracts temp project tarballs using tar.extract) + console.log(''); + console.log("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['install'], testRepoPath); + + // Verify node_modules were populated correctly + helper.verifyDependencies(testRepoPath, 'test-project-a', ['lodash']); + helper.verifyDependencies(testRepoPath, 'test-project-b', ['test-project-a']); + + // Run rush build + console.log(''); + console.log("Running 'rush build'..."); + await helper.executeRushAsync(['build'], testRepoPath); + + // Verify build outputs + helper.verifyBuildOutputs(testRepoPath, ['test-project-a', 'test-project-b']); + + // Test that the built code actually works + await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); + + console.log(''); + console.log('=========================================='); + console.log('✓ NPM Mode Integration Test PASSED'); + console.log('=========================================='); + console.log(''); + console.log('The tar 7.x changes work correctly with npm mode:'); + console.log(' - Temp project tarballs created successfully'); + console.log(' - Tarballs extracted correctly during install'); + console.log(' - Dependencies linked properly'); + console.log(' - Build completed successfully'); + console.log(''); +} diff --git a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts new file mode 100644 index 0000000000..0c99724e95 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as path from 'node:path'; + +import { TestHelper } from './TestHelper'; + +/** + * Integration test for Rush yarn mode with tar 7.x + * This test verifies that temp project tarballs work correctly with yarn package manager + */ +export async function testYarnModeAsync(): Promise { + const helper: TestHelper = new TestHelper(); + const testRepoPath: string = path.join(__dirname, '../temp/yarn-test-repo'); + + console.log('=========================================='); + console.log('Rush Yarn Mode Integration Test'); + console.log('=========================================='); + console.log(''); + console.log('This test verifies that tar 7.x changes work correctly with yarn package manager'); + console.log('by creating temp project tarballs and extracting them during rush install.'); + console.log(''); + + // Create test repository + await helper.createTestRepoAsync(testRepoPath, 'yarn', '1.22.22'); + + // Create project A (dependency) + console.log('Creating test-project-a...'); + helper.createTestProject( + testRepoPath, + 'test-project-a', + '1.0.0', + { lodash: '^4.17.21' }, + `node -e "require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + ); + + // Create project B (depends on A) + console.log('Creating test-project-b...'); + helper.createTestProject( + testRepoPath, + 'test-project-b', + '1.0.0', + { + 'test-project-a': '1.0.0', + moment: '^2.29.4' + }, + `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + ); + + // Run rush update (creates temp project tarballs using tar.create) + console.log(''); + console.log("Running 'rush update' (creates temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['update'], testRepoPath); + + // Verify temp project tarballs were created + helper.verifyTempTarballs(testRepoPath, ['test-project-a', 'test-project-b']); + + // Run rush install (extracts temp project tarballs using tar.extract) + console.log(''); + console.log("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + await helper.executeRushAsync(['install'], testRepoPath); + + // Verify node_modules were populated correctly + helper.verifyDependencies(testRepoPath, 'test-project-a', ['lodash']); + helper.verifyDependencies(testRepoPath, 'test-project-b', ['test-project-a']); + + // Run rush build + console.log(''); + console.log("Running 'rush build'..."); + await helper.executeRushAsync(['build'], testRepoPath); + + // Verify build outputs + helper.verifyBuildOutputs(testRepoPath, ['test-project-a', 'test-project-b']); + + // Test that the built code actually works + await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); + + console.log(''); + console.log('=========================================='); + console.log('✓ Yarn Mode Integration Test PASSED'); + console.log('=========================================='); + console.log(''); + console.log('The tar 7.x changes work correctly with yarn mode:'); + console.log(' - Temp project tarballs created successfully'); + console.log(' - Tarballs extracted correctly during install'); + console.log(' - Dependencies linked properly'); + console.log(' - Build completed successfully'); + console.log(''); +} diff --git a/build-tests/rush-package-manager-integration-test/test-npm-mode.sh b/build-tests/rush-package-manager-integration-test/test-npm-mode.sh deleted file mode 100755 index bbe407c150..0000000000 --- a/build-tests/rush-package-manager-integration-test/test-npm-mode.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -# See LICENSE in the project root for license information. - -# Integration test for Rush npm mode with tar 7.x -# This script verifies that temp project tarballs work correctly with npm package manager - -set -e - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -TEST_REPO_DIR="$SCRIPT_DIR/temp/npm-test-repo" -RUSHSTACK_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )" - -echo "==========================================" -echo "Rush NPM Mode Integration Test" -echo "==========================================" -echo "" -echo "This test verifies that tar 7.x changes work correctly with npm package manager" -echo "by creating temp project tarballs and extracting them during rush install." -echo "" - -# Clean up previous test runs -if [ -d "$TEST_REPO_DIR" ]; then - echo "Cleaning up previous test run..." - rm -rf "$TEST_REPO_DIR" -fi - -# Create test repo directory -echo "Creating test repository at $TEST_REPO_DIR..." -mkdir -p "$TEST_REPO_DIR" -cd "$TEST_REPO_DIR" - -# Use locally built rush to initialize the repo -echo "Initializing Rush repo with npm mode..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" init --overwrite-existing - -# Configure rush.json to use npm -echo "Configuring rush.json for npm mode..." -cat > rush.json << 'EOF' -{ - "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", - "rushVersion": "5.166.0", - "npmVersion": "8.19.4", - "nodeSupportedVersionRange": ">=18.0.0", - "projectFolderMinDepth": 1, - "projectFolderMaxDepth": 2, - "projects": [ - { - "packageName": "test-project-a", - "projectFolder": "projects/test-project-a" - }, - { - "packageName": "test-project-b", - "projectFolder": "projects/test-project-b" - } - ] -} -EOF - -# Create project A (dependency) -echo "Creating test-project-a..." -mkdir -p projects/test-project-a -cat > projects/test-project-a/package.json << 'EOF' -{ - "name": "test-project-a", - "version": "1.0.0", - "main": "lib/index.js", - "scripts": { - "build": "node -e \"require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\\"Hello from A\\\" };');\"" - }, - "dependencies": { - "lodash": "^4.17.21" - } -} -EOF - -# Create project B (depends on A) -echo "Creating test-project-b..." -mkdir -p projects/test-project-b -cat > projects/test-project-b/package.json << 'EOF' -{ - "name": "test-project-b", - "version": "1.0.0", - "main": "lib/index.js", - "scripts": { - "build": "node -e \"const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\\"Using: \\\" + require(\\'test-project-a\\').greet() };');\"" - }, - "dependencies": { - "test-project-a": "1.0.0", - "moment": "^2.29.4" - } -} -EOF - -# Run rush update (this will create temp project tarballs using tar.create) -echo "" -echo "Running 'rush update' (creates temp project tarballs using tar 7.x)..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" update - -# Verify temp project tarballs were created -echo "" -echo "Verifying temp project tarballs were created..." -if [ ! -f "common/temp/projects/test-project-a.tgz" ]; then - echo "ERROR: test-project-a.tgz was not created!" - exit 1 -fi -if [ ! -f "common/temp/projects/test-project-b.tgz" ]; then - echo "ERROR: test-project-b.tgz was not created!" - exit 1 -fi -echo "✓ Temp project tarballs created successfully" - -# Run rush install (this will extract temp project tarballs using tar.extract) -echo "" -echo "Running 'rush install' (extracts temp project tarballs using tar 7.x)..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" install - -# Verify node_modules were populated correctly -echo "" -echo "Verifying node_modules structure..." -if [ ! -d "projects/test-project-a/node_modules/lodash" ]; then - echo "ERROR: lodash not installed in test-project-a!" - exit 1 -fi -if [ ! -L "projects/test-project-b/node_modules/test-project-a" ]; then - echo "ERROR: test-project-a not linked in test-project-b!" - exit 1 -fi -echo "✓ Dependencies installed correctly" - -# Run rush build to verify everything works end-to-end -echo "" -echo "Running 'rush build'..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" build - -# Verify build outputs -echo "" -echo "Verifying build outputs..." -if [ ! -f "projects/test-project-a/lib/index.js" ]; then - echo "ERROR: test-project-a build output not found!" - exit 1 -fi -if [ ! -f "projects/test-project-b/lib/index.js" ]; then - echo "ERROR: test-project-b build output not found!" - exit 1 -fi -echo "✓ Build completed successfully" - -# Test that the built code actually works -echo "" -echo "Testing built code..." -cd projects/test-project-b -node -e "const b = require('./lib/index.js'); console.log(b.test());" | grep -q "Using: Hello from A" -if [ $? -eq 0 ]; then - echo "✓ Built code executes correctly" -else - echo "ERROR: Built code did not execute as expected!" - exit 1 -fi - -echo "" -echo "==========================================" -echo "✓ NPM Mode Integration Test PASSED" -echo "==========================================" -echo "" -echo "The tar 7.x changes work correctly with npm mode:" -echo " - Temp project tarballs created successfully" -echo " - Tarballs extracted correctly during install" -echo " - Dependencies linked properly" -echo " - Build completed successfully" -echo "" diff --git a/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh b/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh deleted file mode 100755 index dfb52e2d02..0000000000 --- a/build-tests/rush-package-manager-integration-test/test-yarn-mode.sh +++ /dev/null @@ -1,171 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. -# See LICENSE in the project root for license information. - -# Integration test for Rush yarn mode with tar 7.x -# This script verifies that temp project tarballs work correctly with yarn package manager - -set -e - -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -TEST_REPO_DIR="$SCRIPT_DIR/temp/yarn-test-repo" -RUSHSTACK_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )" - -echo "==========================================" -echo "Rush Yarn Mode Integration Test" -echo "==========================================" -echo "" -echo "This test verifies that tar 7.x changes work correctly with yarn package manager" -echo "by creating temp project tarballs and extracting them during rush install." -echo "" - -# Clean up previous test runs -if [ -d "$TEST_REPO_DIR" ]; then - echo "Cleaning up previous test run..." - rm -rf "$TEST_REPO_DIR" -fi - -# Create test repo directory -echo "Creating test repository at $TEST_REPO_DIR..." -mkdir -p "$TEST_REPO_DIR" -cd "$TEST_REPO_DIR" - -# Use locally built rush to initialize the repo -echo "Initializing Rush repo with yarn mode..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" init --overwrite-existing - -# Configure rush.json to use yarn -echo "Configuring rush.json for yarn mode..." -cat > rush.json << 'EOF' -{ - "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json", - "rushVersion": "5.166.0", - "yarnVersion": "1.22.22", - "nodeSupportedVersionRange": ">=18.0.0", - "projectFolderMinDepth": 1, - "projectFolderMaxDepth": 2, - "projects": [ - { - "packageName": "test-project-a", - "projectFolder": "projects/test-project-a" - }, - { - "packageName": "test-project-b", - "projectFolder": "projects/test-project-b" - } - ] -} -EOF - -# Create project A (dependency) -echo "Creating test-project-a..." -mkdir -p projects/test-project-a -cat > projects/test-project-a/package.json << 'EOF' -{ - "name": "test-project-a", - "version": "1.0.0", - "main": "lib/index.js", - "scripts": { - "build": "node -e \"require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\\"Hello from A\\\" };');\"" - }, - "dependencies": { - "lodash": "^4.17.21" - } -} -EOF - -# Create project B (depends on A) -echo "Creating test-project-b..." -mkdir -p projects/test-project-b -cat > projects/test-project-b/package.json << 'EOF' -{ - "name": "test-project-b", - "version": "1.0.0", - "main": "lib/index.js", - "scripts": { - "build": "node -e \"const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\\"Using: \\\" + require(\\'test-project-a\\').greet() };');\"" - }, - "dependencies": { - "test-project-a": "1.0.0", - "moment": "^2.29.4" - } -} -EOF - -# Run rush update (this will create temp project tarballs using tar.create) -echo "" -echo "Running 'rush update' (creates temp project tarballs using tar 7.x)..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" update - -# Verify temp project tarballs were created -echo "" -echo "Verifying temp project tarballs were created..." -if [ ! -f "common/temp/projects/test-project-a.tgz" ]; then - echo "ERROR: test-project-a.tgz was not created!" - exit 1 -fi -if [ ! -f "common/temp/projects/test-project-b.tgz" ]; then - echo "ERROR: test-project-b.tgz was not created!" - exit 1 -fi -echo "✓ Temp project tarballs created successfully" - -# Run rush install (this will extract temp project tarballs using tar.extract) -echo "" -echo "Running 'rush install' (extracts temp project tarballs using tar 7.x)..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" install - -# Verify node_modules were populated correctly -echo "" -echo "Verifying node_modules structure..." -if [ ! -d "projects/test-project-a/node_modules/lodash" ]; then - echo "ERROR: lodash not installed in test-project-a!" - exit 1 -fi -if [ ! -L "projects/test-project-b/node_modules/test-project-a" ]; then - echo "ERROR: test-project-a not linked in test-project-b!" - exit 1 -fi -echo "✓ Dependencies installed correctly" - -# Run rush build to verify everything works end-to-end -echo "" -echo "Running 'rush build'..." -node "$RUSHSTACK_ROOT/apps/rush/lib/start.js" build - -# Verify build outputs -echo "" -echo "Verifying build outputs..." -if [ ! -f "projects/test-project-a/lib/index.js" ]; then - echo "ERROR: test-project-a build output not found!" - exit 1 -fi -if [ ! -f "projects/test-project-b/lib/index.js" ]; then - echo "ERROR: test-project-b build output not found!" - exit 1 -fi -echo "✓ Build completed successfully" - -# Test that the built code actually works -echo "" -echo "Testing built code..." -cd projects/test-project-b -node -e "const b = require('./lib/index.js'); console.log(b.test());" | grep -q "Using: Hello from A" -if [ $? -eq 0 ]; then - echo "✓ Built code executes correctly" -else - echo "ERROR: Built code did not execute as expected!" - exit 1 -fi - -echo "" -echo "==========================================" -echo "✓ Yarn Mode Integration Test PASSED" -echo "==========================================" -echo "" -echo "The tar 7.x changes work correctly with yarn mode:" -echo " - Temp project tarballs created successfully" -echo " - Tarballs extracted correctly during install" -echo " - Dependencies linked properly" -echo " - Build completed successfully" -echo "" diff --git a/build-tests/rush-package-manager-integration-test/tsconfig.json b/build-tests/rush-package-manager-integration-test/tsconfig.json new file mode 100644 index 0000000000..6040469f92 --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/tsconfig.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json.schemastore.org/tsconfig", + + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "declarationMap": true, + "inlineSources": true, + "strictNullChecks": true, + "noUnusedLocals": true, + "types": ["node"], + + "module": "commonjs", + "target": "es2017", + "lib": ["es2017"] + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "lib", "temp"] +} diff --git a/rush.json b/rush.json index 6cfc842f2f..12aee24970 100644 --- a/rush.json +++ b/rush.json @@ -1058,6 +1058,12 @@ "reviewCategory": "tests", "shouldPublish": false }, + { + "packageName": "rush-package-manager-integration-test", + "projectFolder": "build-tests/rush-package-manager-integration-test", + "reviewCategory": "tests", + "shouldPublish": false + }, { "packageName": "rush-redis-cobuild-plugin-integration-test", "projectFolder": "build-tests/rush-redis-cobuild-plugin-integration-test", From 79f01d2b0d7657c74bc62743e266e1fad26981a8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 22:30:23 +0000 Subject: [PATCH 05/14] Address code review feedback - use rig.json, Terminal, async operations, and correct package manager versions Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../.gitignore | 2 - .../README.md | 6 +- .../config/heft.json | 5 - .../config/rig.json | 7 ++ .../eslint.config.js | 4 - .../package.json | 8 +- .../src/TestHelper.ts | 113 ++++++++++-------- .../src/runTests.ts | 92 +++++++------- .../src/testNpmMode.ts | 81 +++++++------ .../src/testYarnMode.ts | 81 +++++++------ .../tsconfig.json | 23 +--- 11 files changed, 209 insertions(+), 213 deletions(-) delete mode 100644 build-tests/rush-package-manager-integration-test/.gitignore delete mode 100644 build-tests/rush-package-manager-integration-test/config/heft.json create mode 100644 build-tests/rush-package-manager-integration-test/config/rig.json diff --git a/build-tests/rush-package-manager-integration-test/.gitignore b/build-tests/rush-package-manager-integration-test/.gitignore deleted file mode 100644 index a906da7cf5..0000000000 --- a/build-tests/rush-package-manager-integration-test/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# Temporary test repositories created during test runs -temp/ diff --git a/build-tests/rush-package-manager-integration-test/README.md b/build-tests/rush-package-manager-integration-test/README.md index ecfd24605a..3d52ea674a 100644 --- a/build-tests/rush-package-manager-integration-test/README.md +++ b/build-tests/rush-package-manager-integration-test/README.md @@ -67,9 +67,9 @@ These integration tests verify: ## Test Output -Each test creates a temporary Rush repository in the `temp/` directory: -- `temp/npm-test-repo/` - npm mode test repository -- `temp/yarn-test-repo/` - yarn mode test repository +Each test creates a temporary Rush repository in `/tmp/rush-package-manager-test/`: +- `/tmp/rush-package-manager-test/npm-test-repo/` - npm mode test repository +- `/tmp/rush-package-manager-test/yarn-test-repo/` - yarn mode test repository These directories are cleaned up at the start of each test run. diff --git a/build-tests/rush-package-manager-integration-test/config/heft.json b/build-tests/rush-package-manager-integration-test/config/heft.json deleted file mode 100644 index 48b379aa56..0000000000 --- a/build-tests/rush-package-manager-integration-test/config/heft.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json", - - "extends": "local-node-rig/profiles/default/config/heft.json" -} diff --git a/build-tests/rush-package-manager-integration-test/config/rig.json b/build-tests/rush-package-manager-integration-test/config/rig.json new file mode 100644 index 0000000000..165ffb001f --- /dev/null +++ b/build-tests/rush-package-manager-integration-test/config/rig.json @@ -0,0 +1,7 @@ +{ + // The "rig.json" file directs tools to look for their config files in an external package. + // Documentation for this system: https://www.npmjs.com/package/@rushstack/rig-package + "$schema": "https://developer.microsoft.com/json-schemas/rig-package/rig.schema.json", + + "rigPackageName": "local-node-rig" +} diff --git a/build-tests/rush-package-manager-integration-test/eslint.config.js b/build-tests/rush-package-manager-integration-test/eslint.config.js index bc19ee055d..95db6d06e1 100644 --- a/build-tests/rush-package-manager-integration-test/eslint.config.js +++ b/build-tests/rush-package-manager-integration-test/eslint.config.js @@ -11,10 +11,6 @@ module.exports = [ parserOptions: { tsconfigRootDir: __dirname } - }, - rules: { - // Allow console.log in test scripts - 'no-console': 'off' } } ]; diff --git a/build-tests/rush-package-manager-integration-test/package.json b/build-tests/rush-package-manager-integration-test/package.json index db94ace02f..02ac89c950 100644 --- a/build-tests/rush-package-manager-integration-test/package.json +++ b/build-tests/rush-package-manager-integration-test/package.json @@ -2,7 +2,7 @@ "name": "rush-package-manager-integration-test", "version": "1.0.0", "private": true, - "description": "Integration tests for Rush package managers with tar 7.x", + "description": "Integration tests for non-pnpm package managers in Rush.", "license": "MIT", "scripts": { "_phase:build": "heft build --clean", @@ -13,9 +13,7 @@ "@microsoft/rush-lib": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", - "@types/node": "20.17.19", - "eslint": "~9.37.0", - "local-node-rig": "workspace:*", - "typescript": "~5.8.2" + "@rushstack/terminal": "workspace:*", + "local-node-rig": "workspace:*" } } diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts index 10c9d961e1..d57ae4901d 100644 --- a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -4,40 +4,43 @@ import * as path from 'node:path'; import type * as child_process from 'node:child_process'; -import { FileSystem, Executable } from '@rushstack/node-core-library'; +import { FileSystem, Executable, JsonFile, type JsonObject } from '@rushstack/node-core-library'; +import type { ITerminal } from '@rushstack/terminal'; /** * Helper class for running integration tests with Rush package managers */ export class TestHelper { - private readonly _rushstackRoot: string; + public readonly rushstackRoot: string; private readonly _rushBinPath: string; - - public constructor() { - // Get rushstack root (two levels up from build-tests/rush-package-manager-integration-test) - this._rushstackRoot = path.resolve(__dirname, '../../..'); - this._rushBinPath = path.join(this._rushstackRoot, 'apps/rush/lib/start.js'); - } - - public get rushstackRoot(): string { - return this._rushstackRoot; + private readonly _terminal: ITerminal; + + public constructor(terminal: ITerminal) { + this._terminal = terminal; + // Resolve rushstack root and rush bin path + this.rushstackRoot = path.resolve(__dirname, '../../..'); + // Use the locally built rush from apps/rush + this._rushBinPath = path.join(this.rushstackRoot, 'apps/rush/lib/start.js'); } /** * Execute a Rush command using the locally-built Rush */ public async executeRushAsync(args: string[], workingDirectory: string): Promise { - console.log(`Executing: node ${this._rushBinPath} ${args.join(' ')}`); + this._terminal.writeLine(`Executing: ${process.argv0} ${this._rushBinPath} ${args.join(' ')}`); - const childProcess: child_process.ChildProcess = Executable.spawn('node', [this._rushBinPath, ...args], { - currentWorkingDirectory: workingDirectory, - stdio: 'inherit' - }); + const childProcess: child_process.ChildProcess = Executable.spawn( + process.argv0, + [this._rushBinPath, ...args], + { + currentWorkingDirectory: workingDirectory, + stdio: 'inherit' + } + ); - const result = await Executable.waitForExitAsync(childProcess); - if (result.exitCode !== 0) { - throw new Error(`Command failed with exit code ${result.exitCode}`); - } + await Executable.waitForExitAsync(childProcess, { + throwOnNonZeroExitCode: true + }); } /** @@ -49,25 +52,23 @@ export class TestHelper { packageManagerVersion: string ): Promise { // Clean up previous test run - if (FileSystem.exists(testRepoPath)) { - console.log('Cleaning up previous test run...'); - FileSystem.deleteFolder(testRepoPath); + if (await FileSystem.existsAsync(testRepoPath)) { + this._terminal.writeLine('Cleaning up previous test run...'); + await FileSystem.deleteFolderAsync(testRepoPath); } // Create test repo directory - console.log(`Creating test repository at ${testRepoPath}...`); - FileSystem.ensureFolder(testRepoPath); + this._terminal.writeLine(`Creating test repository at ${testRepoPath}...`); + await FileSystem.ensureFolderAsync(testRepoPath); // Initialize Rush repo - console.log('Initializing Rush repo...'); - await this.executeRushAsync(['init', '--overwrite-existing'], testRepoPath); + this._terminal.writeLine('Initializing Rush repo...'); + await this.executeRushAsync(['init'], testRepoPath); // Configure rush.json for the specified package manager - console.log(`Configuring rush.json for ${packageManagerType} mode...`); + this._terminal.writeLine(`Configuring rush.json for ${packageManagerType} mode...`); const rushJsonPath: string = path.join(testRepoPath, 'rush.json'); - const rushJsonContent: string = FileSystem.readFile(rushJsonPath); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const rushJson: any = JSON.parse(rushJsonContent); + const rushJson: JsonObject = await JsonFile.loadAsync(rushJsonPath); // Update package manager configuration if (packageManagerType === 'npm') { @@ -92,24 +93,26 @@ export class TestHelper { } ]; - FileSystem.writeFile(rushJsonPath, JSON.stringify(rushJson, null, 2)); + // Update nodeSupportedVersionRange to match current environment + rushJson.nodeSupportedVersionRange = '>=18.0.0'; + + await JsonFile.saveAsync(rushJson, rushJsonPath, { updateExistingFile: true }); } /** * Create a test project with the specified configuration */ - public createTestProject( + public async createTestProjectAsync( testRepoPath: string, projectName: string, version: string, dependencies: Record, buildScript: string - ): void { + ): Promise { const projectPath: string = path.join(testRepoPath, 'projects', projectName); - FileSystem.ensureFolder(projectPath); + await FileSystem.ensureFolderAsync(projectPath); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const packageJson: any = { + const packageJson: JsonObject = { name: projectName, version: version, main: 'lib/index.js', @@ -119,63 +122,67 @@ export class TestHelper { dependencies: dependencies }; - FileSystem.writeFile(path.join(projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)); + await JsonFile.saveAsync(packageJson, path.join(projectPath, 'package.json')); } /** * Verify that temp project tarballs were created */ - public verifyTempTarballs(testRepoPath: string, projectNames: string[]): void { - console.log('\nVerifying temp project tarballs were created...'); + public async verifyTempTarballsAsync(testRepoPath: string, projectNames: string[]): Promise { + this._terminal.writeLine('\nVerifying temp project tarballs were created...'); for (const projectName of projectNames) { const tarballPath: string = path.join(testRepoPath, 'common/temp/projects', `${projectName}.tgz`); - if (!FileSystem.exists(tarballPath)) { + if (!(await FileSystem.existsAsync(tarballPath))) { throw new Error(`ERROR: ${projectName}.tgz was not created!`); } } - console.log('✓ Temp project tarballs created successfully'); + this._terminal.writeLine('✓ Temp project tarballs created successfully'); } /** * Verify that dependencies are installed correctly */ - public verifyDependencies(testRepoPath: string, projectName: string, expectedDependencies: string[]): void { - console.log('\nVerifying node_modules structure...'); + public async verifyDependenciesAsync( + testRepoPath: string, + projectName: string, + expectedDependencies: string[] + ): Promise { + this._terminal.writeLine('\nVerifying node_modules structure...'); const projectNodeModules: string = path.join(testRepoPath, 'projects', projectName, 'node_modules'); for (const dep of expectedDependencies) { const depPath: string = path.join(projectNodeModules, dep); - if (!FileSystem.exists(depPath)) { + if (!(await FileSystem.existsAsync(depPath))) { throw new Error(`ERROR: ${dep} not found in ${projectName}!`); } } - console.log('✓ Dependencies installed correctly'); + this._terminal.writeLine('✓ Dependencies installed correctly'); } /** * Verify that build outputs were created */ - public verifyBuildOutputs(testRepoPath: string, projectNames: string[]): void { - console.log('\nVerifying build outputs...'); + public async verifyBuildOutputsAsync(testRepoPath: string, projectNames: string[]): Promise { + this._terminal.writeLine('\nVerifying build outputs...'); for (const projectName of projectNames) { const outputPath: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); - if (!FileSystem.exists(outputPath)) { + if (!(await FileSystem.existsAsync(outputPath))) { throw new Error(`ERROR: ${projectName} build output not found!`); } } - console.log('✓ Build completed successfully'); + this._terminal.writeLine('✓ Build completed successfully'); } /** * Test that the built code executes correctly */ public async testBuiltCodeAsync(testRepoPath: string, projectName: string): Promise { - console.log('\nTesting built code...'); + this._terminal.writeLine('\nTesting built code...'); const projectLib: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); // Use Executable.spawnSync to capture output const result: string = Executable.spawnSync( - 'node', + process.argv0, ['-e', `const b = require('${projectLib}'); console.log(b.test());`], { currentWorkingDirectory: testRepoPath @@ -185,6 +192,6 @@ export class TestHelper { if (!result.includes('Using: Hello from A')) { throw new Error('ERROR: Built code did not execute as expected!'); } - console.log('✓ Built code executes correctly'); + this._terminal.writeLine('✓ Built code executes correctly'); } } diff --git a/build-tests/rush-package-manager-integration-test/src/runTests.ts b/build-tests/rush-package-manager-integration-test/src/runTests.ts index 1572e41341..e531c6251b 100644 --- a/build-tests/rush-package-manager-integration-test/src/runTests.ts +++ b/build-tests/rush-package-manager-integration-test/src/runTests.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import { Terminal, ConsoleTerminalProvider } from '@rushstack/terminal'; + import { testNpmModeAsync } from './testNpmMode'; import { testYarnModeAsync } from './testYarnMode'; @@ -8,84 +10,88 @@ import { testYarnModeAsync } from './testYarnMode'; * Main test runner that executes all package manager integration tests */ async function runTestsAsync(): Promise { - console.log('=========================================='); - console.log('Rush Package Manager Integration Tests'); - console.log('=========================================='); - console.log(''); - console.log('These tests verify that the tar 7.x upgrade works correctly'); - console.log('with different Rush package managers (npm, yarn).'); - console.log(''); - console.log('Tests will:'); - console.log(' 1. Create Rush repos using locally-built Rush'); - console.log(' 2. Add projects with dependencies'); - console.log(' 3. Run rush update (creates temp tarballs)'); - console.log(' 4. Run rush install (extracts tarballs)'); - console.log(' 5. Run rush build (end-to-end verification)'); - console.log(''); + const terminal: Terminal = new Terminal(new ConsoleTerminalProvider()); + + terminal.writeLine('=========================================='); + terminal.writeLine('Rush Package Manager Integration Tests'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('These tests verify that the tar 7.x upgrade works correctly'); + terminal.writeLine('with different Rush package managers (npm, yarn).'); + terminal.writeLine(''); + terminal.writeLine('Tests will:'); + terminal.writeLine(' 1. Create Rush repos using locally-built Rush'); + terminal.writeLine(' 2. Add projects with dependencies'); + terminal.writeLine(' 3. Run rush update (creates and extracts temp tarballs)'); + terminal.writeLine(' 4. Run rush install (extracts tarballs)'); + terminal.writeLine(' 5. Run rush build (end-to-end verification)'); + terminal.writeLine(''); let testsPassed: number = 0; let testsFailed: number = 0; const failedTests: string[] = []; // Run npm mode test - console.log('=========================================='); - console.log('Running NPM mode test...'); - console.log('=========================================='); + terminal.writeLine('=========================================='); + terminal.writeLine('Running NPM mode test...'); + terminal.writeLine('=========================================='); try { - await testNpmModeAsync(); + await testNpmModeAsync(terminal); testsPassed++; } catch (error) { testsFailed++; failedTests.push('NPM mode'); - console.error('⚠️ NPM mode test FAILED'); - console.error(error); + terminal.writeErrorLine('⚠️ NPM mode test FAILED'); + terminal.writeErrorLine(String(error)); } // Run yarn mode test - console.log('=========================================='); - console.log('Running Yarn mode test...'); - console.log('=========================================='); + terminal.writeLine('=========================================='); + terminal.writeLine('Running Yarn mode test...'); + terminal.writeLine('=========================================='); try { - await testYarnModeAsync(); + await testYarnModeAsync(terminal); testsPassed++; } catch (error) { testsFailed++; failedTests.push('Yarn mode'); - console.error('⚠️ Yarn mode test FAILED'); - console.error(error); + terminal.writeErrorLine('⚠️ Yarn mode test FAILED'); + terminal.writeErrorLine(String(error)); } // Print summary - console.log('=========================================='); - console.log('Test Summary'); - console.log('=========================================='); - console.log(''); - console.log(`Tests passed: ${testsPassed}`); - console.log(`Tests failed: ${testsFailed}`); - console.log(''); + terminal.writeLine('=========================================='); + terminal.writeLine('Test Summary'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine(`Tests passed: ${testsPassed}`); + terminal.writeLine(`Tests failed: ${testsFailed}`); + terminal.writeLine(''); if (testsFailed > 0) { - console.log('Failed tests:'); + terminal.writeLine('Failed tests:'); for (const test of failedTests) { - console.log(` - ${test}`); + terminal.writeLine(` - ${test}`); } - console.log(''); - console.log('❌ Some tests failed'); + terminal.writeLine(''); + terminal.writeLine('❌ Some tests failed'); process.exit(1); } else { - console.log('✅ All tests passed!'); - console.log(''); - console.log('The tar 7.x upgrade is working correctly with:'); - console.log(' - NPM package manager'); - console.log(' - Yarn package manager'); - console.log(''); + terminal.writeLine('✅ All tests passed!'); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x upgrade is working correctly with:'); + terminal.writeLine(' - NPM package manager'); + terminal.writeLine(' - Yarn package manager'); + terminal.writeLine(''); process.exit(0); } } // Run tests and handle errors runTestsAsync().catch((error) => { + // eslint-disable-next-line no-console console.error('Fatal error running tests:'); + // eslint-disable-next-line no-console console.error(error); process.exit(1); }); diff --git a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts index cc67053b19..e925ed4357 100644 --- a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts @@ -3,30 +3,33 @@ import * as path from 'node:path'; +import type { ITerminal } from '@rushstack/terminal'; + import { TestHelper } from './TestHelper'; /** * Integration test for Rush npm mode with tar 7.x * This test verifies that temp project tarballs work correctly with npm package manager */ -export async function testNpmModeAsync(): Promise { - const helper: TestHelper = new TestHelper(); - const testRepoPath: string = path.join(__dirname, '../temp/npm-test-repo'); +export async function testNpmModeAsync(terminal: ITerminal): Promise { + const helper: TestHelper = new TestHelper(terminal); + // Use system temp directory to avoid rush init detecting parent rush.json + const testRepoPath: string = path.join('/tmp', 'rush-package-manager-test', 'npm-test-repo'); - console.log('=========================================='); - console.log('Rush NPM Mode Integration Test'); - console.log('=========================================='); - console.log(''); - console.log('This test verifies that tar 7.x changes work correctly with npm package manager'); - console.log('by creating temp project tarballs and extracting them during rush install.'); - console.log(''); + terminal.writeLine('=========================================='); + terminal.writeLine('Rush NPM Mode Integration Test'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('This test verifies that tar 7.x changes work correctly with npm package manager'); + terminal.writeLine('by creating temp project tarballs and extracting them during rush install.'); + terminal.writeLine(''); - // Create test repository - await helper.createTestRepoAsync(testRepoPath, 'npm', '8.19.4'); + // Create test repository with npm 6.14.15 (the version from rush init template) + await helper.createTestRepoAsync(testRepoPath, 'npm', '6.14.15'); // Create project A (dependency) - console.log('Creating test-project-a...'); - helper.createTestProject( + terminal.writeLine('Creating test-project-a...'); + await helper.createTestProjectAsync( testRepoPath, 'test-project-a', '1.0.0', @@ -35,8 +38,8 @@ export async function testNpmModeAsync(): Promise { ); // Create project B (depends on A) - console.log('Creating test-project-b...'); - helper.createTestProject( + terminal.writeLine('Creating test-project-b...'); + await helper.createTestProjectAsync( testRepoPath, 'test-project-b', '1.0.0', @@ -47,43 +50,43 @@ export async function testNpmModeAsync(): Promise { `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` ); - // Run rush update (creates temp project tarballs using tar.create) - console.log(''); - console.log("Running 'rush update' (creates temp project tarballs using tar 7.x)..."); + // Run rush update (creates and extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush update' (creates and extracts temp project tarballs using tar 7.x)..."); await helper.executeRushAsync(['update'], testRepoPath); // Verify temp project tarballs were created - helper.verifyTempTarballs(testRepoPath, ['test-project-a', 'test-project-b']); + await helper.verifyTempTarballsAsync(testRepoPath, ['test-project-a', 'test-project-b']); - // Run rush install (extracts temp project tarballs using tar.extract) - console.log(''); - console.log("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + // Run rush install (extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); await helper.executeRushAsync(['install'], testRepoPath); // Verify node_modules were populated correctly - helper.verifyDependencies(testRepoPath, 'test-project-a', ['lodash']); - helper.verifyDependencies(testRepoPath, 'test-project-b', ['test-project-a']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-a', ['lodash']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-b', ['test-project-a']); // Run rush build - console.log(''); - console.log("Running 'rush build'..."); + terminal.writeLine(''); + terminal.writeLine("Running 'rush build'..."); await helper.executeRushAsync(['build'], testRepoPath); // Verify build outputs - helper.verifyBuildOutputs(testRepoPath, ['test-project-a', 'test-project-b']); + await helper.verifyBuildOutputsAsync(testRepoPath, ['test-project-a', 'test-project-b']); // Test that the built code actually works await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); - console.log(''); - console.log('=========================================='); - console.log('✓ NPM Mode Integration Test PASSED'); - console.log('=========================================='); - console.log(''); - console.log('The tar 7.x changes work correctly with npm mode:'); - console.log(' - Temp project tarballs created successfully'); - console.log(' - Tarballs extracted correctly during install'); - console.log(' - Dependencies linked properly'); - console.log(' - Build completed successfully'); - console.log(''); + terminal.writeLine(''); + terminal.writeLine('=========================================='); + terminal.writeLine('✓ NPM Mode Integration Test PASSED'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x changes work correctly with npm mode:'); + terminal.writeLine(' - Temp project tarballs created successfully'); + terminal.writeLine(' - Tarballs extracted correctly during install'); + terminal.writeLine(' - Dependencies linked properly'); + terminal.writeLine(' - Build completed successfully'); + terminal.writeLine(''); } diff --git a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts index 0c99724e95..a21b07f408 100644 --- a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts @@ -3,30 +3,33 @@ import * as path from 'node:path'; +import type { ITerminal } from '@rushstack/terminal'; + import { TestHelper } from './TestHelper'; /** * Integration test for Rush yarn mode with tar 7.x * This test verifies that temp project tarballs work correctly with yarn package manager */ -export async function testYarnModeAsync(): Promise { - const helper: TestHelper = new TestHelper(); - const testRepoPath: string = path.join(__dirname, '../temp/yarn-test-repo'); +export async function testYarnModeAsync(terminal: ITerminal): Promise { + const helper: TestHelper = new TestHelper(terminal); + // Use system temp directory to avoid rush init detecting parent rush.json + const testRepoPath: string = path.join('/tmp', 'rush-package-manager-test', 'yarn-test-repo'); - console.log('=========================================='); - console.log('Rush Yarn Mode Integration Test'); - console.log('=========================================='); - console.log(''); - console.log('This test verifies that tar 7.x changes work correctly with yarn package manager'); - console.log('by creating temp project tarballs and extracting them during rush install.'); - console.log(''); + terminal.writeLine('=========================================='); + terminal.writeLine('Rush Yarn Mode Integration Test'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('This test verifies that tar 7.x changes work correctly with yarn package manager'); + terminal.writeLine('by creating temp project tarballs and extracting them during rush install.'); + terminal.writeLine(''); - // Create test repository - await helper.createTestRepoAsync(testRepoPath, 'yarn', '1.22.22'); + // Create test repository with yarn 1.9.4 (the version from rush init template) + await helper.createTestRepoAsync(testRepoPath, 'yarn', '1.9.4'); // Create project A (dependency) - console.log('Creating test-project-a...'); - helper.createTestProject( + terminal.writeLine('Creating test-project-a...'); + await helper.createTestProjectAsync( testRepoPath, 'test-project-a', '1.0.0', @@ -35,8 +38,8 @@ export async function testYarnModeAsync(): Promise { ); // Create project B (depends on A) - console.log('Creating test-project-b...'); - helper.createTestProject( + terminal.writeLine('Creating test-project-b...'); + await helper.createTestProjectAsync( testRepoPath, 'test-project-b', '1.0.0', @@ -47,43 +50,43 @@ export async function testYarnModeAsync(): Promise { `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` ); - // Run rush update (creates temp project tarballs using tar.create) - console.log(''); - console.log("Running 'rush update' (creates temp project tarballs using tar 7.x)..."); + // Run rush update (creates and extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush update' (creates and extracts temp project tarballs using tar 7.x)..."); await helper.executeRushAsync(['update'], testRepoPath); // Verify temp project tarballs were created - helper.verifyTempTarballs(testRepoPath, ['test-project-a', 'test-project-b']); + await helper.verifyTempTarballsAsync(testRepoPath, ['test-project-a', 'test-project-b']); - // Run rush install (extracts temp project tarballs using tar.extract) - console.log(''); - console.log("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); + // Run rush install (extracts temp project tarballs) + terminal.writeLine(''); + terminal.writeLine("Running 'rush install' (extracts temp project tarballs using tar 7.x)..."); await helper.executeRushAsync(['install'], testRepoPath); // Verify node_modules were populated correctly - helper.verifyDependencies(testRepoPath, 'test-project-a', ['lodash']); - helper.verifyDependencies(testRepoPath, 'test-project-b', ['test-project-a']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-a', ['lodash']); + await helper.verifyDependenciesAsync(testRepoPath, 'test-project-b', ['test-project-a']); // Run rush build - console.log(''); - console.log("Running 'rush build'..."); + terminal.writeLine(''); + terminal.writeLine("Running 'rush build'..."); await helper.executeRushAsync(['build'], testRepoPath); // Verify build outputs - helper.verifyBuildOutputs(testRepoPath, ['test-project-a', 'test-project-b']); + await helper.verifyBuildOutputsAsync(testRepoPath, ['test-project-a', 'test-project-b']); // Test that the built code actually works await helper.testBuiltCodeAsync(testRepoPath, 'test-project-b'); - console.log(''); - console.log('=========================================='); - console.log('✓ Yarn Mode Integration Test PASSED'); - console.log('=========================================='); - console.log(''); - console.log('The tar 7.x changes work correctly with yarn mode:'); - console.log(' - Temp project tarballs created successfully'); - console.log(' - Tarballs extracted correctly during install'); - console.log(' - Dependencies linked properly'); - console.log(' - Build completed successfully'); - console.log(''); + terminal.writeLine(''); + terminal.writeLine('=========================================='); + terminal.writeLine('✓ Yarn Mode Integration Test PASSED'); + terminal.writeLine('=========================================='); + terminal.writeLine(''); + terminal.writeLine('The tar 7.x changes work correctly with yarn mode:'); + terminal.writeLine(' - Temp project tarballs created successfully'); + terminal.writeLine(' - Tarballs extracted correctly during install'); + terminal.writeLine(' - Dependencies linked properly'); + terminal.writeLine(' - Build completed successfully'); + terminal.writeLine(''); } diff --git a/build-tests/rush-package-manager-integration-test/tsconfig.json b/build-tests/rush-package-manager-integration-test/tsconfig.json index 6040469f92..d42a8c9f2b 100644 --- a/build-tests/rush-package-manager-integration-test/tsconfig.json +++ b/build-tests/rush-package-manager-integration-test/tsconfig.json @@ -1,23 +1,6 @@ { - "$schema": "http://json.schemastore.org/tsconfig", - + "extends": "local-node-rig/profiles/default/tsconfig-base.json", "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - - "forceConsistentCasingInFileNames": true, - "declaration": true, - "sourceMap": true, - "declarationMap": true, - "inlineSources": true, - "strictNullChecks": true, - "noUnusedLocals": true, - "types": ["node"], - - "module": "commonjs", - "target": "es2017", - "lib": ["es2017"] - }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules", "lib", "temp"] + "types": ["node"] + } } From 8abfd2612ef8f011fc42b15aebfefe9c96f623a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:28:14 +0000 Subject: [PATCH 06/14] Address final code review feedback and add tests to CI/CD pipelines Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .github/workflows/ci.yml | 7 +++++++ .../rush-package-manager-integration-test/README.md | 8 ++++---- .../rush-package-manager-integration-test/package.json | 1 + .../src/TestHelper.ts | 7 ++----- .../rush-package-manager-integration-test/tsconfig.json | 5 +---- common/config/azure-pipelines/templates/build.yaml | 6 ++++++ common/config/rush/browser-approved-packages.json | 4 ++++ 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bb38332fe..8afa6ceba8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,6 +79,13 @@ jobs: run: node common/scripts/install-run-rush.js retest --verbose --production working-directory: repo-a + - name: Run package manager integration tests + run: | + node ${{ github.workspace }}/repo-a/apps/rush/lib/start-dev.js build --to rush-package-manager-integration-test + cd build-tests/rush-package-manager-integration-test + npm run test + working-directory: repo-a + - name: Ensure repo README is up-to-date run: node repo-scripts/repo-toolbox/lib/start.js readme --verify working-directory: repo-a diff --git a/build-tests/rush-package-manager-integration-test/README.md b/build-tests/rush-package-manager-integration-test/README.md index 3d52ea674a..5563850776 100644 --- a/build-tests/rush-package-manager-integration-test/README.md +++ b/build-tests/rush-package-manager-integration-test/README.md @@ -18,16 +18,16 @@ The test suite is written in TypeScript using `@rushstack/node-core-library` for Tests Rush npm mode by: - Initializing a Rush repo with `npmVersion` configured - Creating two projects with dependencies -- Running `rush update` (creates tarballs) -- Running `rush install` (extracts tarballs) +- Running `rush update` +- Running `rush install` - Running `rush build` (verifies everything works end-to-end) ### testYarnMode.ts Tests Rush yarn mode by: - Initializing a Rush repo with `yarnVersion` configured - Creating two projects with dependencies -- Running `rush update` (creates tarballs) -- Running `rush install` (extracts tarballs) +- Running `rush update` +- Running `rush install` - Running `rush build` (verifies everything works end-to-end) ## Prerequisites diff --git a/build-tests/rush-package-manager-integration-test/package.json b/build-tests/rush-package-manager-integration-test/package.json index 02ac89c950..a8c688eb31 100644 --- a/build-tests/rush-package-manager-integration-test/package.json +++ b/build-tests/rush-package-manager-integration-test/package.json @@ -10,6 +10,7 @@ "test": "node lib/runTests.js" }, "devDependencies": { + "@microsoft/rush": "workspace:*", "@microsoft/rush-lib": "workspace:*", "@rushstack/heft": "workspace:*", "@rushstack/node-core-library": "workspace:*", diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts index d57ae4901d..38501b1b1a 100644 --- a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -11,16 +11,13 @@ import type { ITerminal } from '@rushstack/terminal'; * Helper class for running integration tests with Rush package managers */ export class TestHelper { - public readonly rushstackRoot: string; private readonly _rushBinPath: string; private readonly _terminal: ITerminal; public constructor(terminal: ITerminal) { this._terminal = terminal; - // Resolve rushstack root and rush bin path - this.rushstackRoot = path.resolve(__dirname, '../../..'); - // Use the locally built rush from apps/rush - this._rushBinPath = path.join(this.rushstackRoot, 'apps/rush/lib/start.js'); + // Resolve rush bin path from @microsoft/rush dependency + this._rushBinPath = require.resolve('@microsoft/rush/lib/start-dev'); } /** diff --git a/build-tests/rush-package-manager-integration-test/tsconfig.json b/build-tests/rush-package-manager-integration-test/tsconfig.json index d42a8c9f2b..b98cc4ab57 100644 --- a/build-tests/rush-package-manager-integration-test/tsconfig.json +++ b/build-tests/rush-package-manager-integration-test/tsconfig.json @@ -1,6 +1,3 @@ { - "extends": "local-node-rig/profiles/default/tsconfig-base.json", - "compilerOptions": { - "types": ["node"] - } + "extends": "local-node-rig/profiles/default/tsconfig-base.json" } diff --git a/common/config/azure-pipelines/templates/build.yaml b/common/config/azure-pipelines/templates/build.yaml index 05f11e3d67..52d3b6cd37 100644 --- a/common/config/azure-pipelines/templates/build.yaml +++ b/common/config/azure-pipelines/templates/build.yaml @@ -24,3 +24,9 @@ steps: - script: 'node common/scripts/install-run-rush.js retest --verbose --production ${{ parameters.BuildParameters }}' displayName: 'Rush retest (install-run-rush)' + + - script: | + node apps/rush/lib/start-dev.js build --to rush-package-manager-integration-test + cd build-tests/rush-package-manager-integration-test + npm run test + displayName: 'Run package manager integration tests' diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index 23c585145b..fb9444b5d9 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -14,6 +14,10 @@ "name": "@lifaon/path", "allowedCategories": [ "libraries" ] }, + { + "name": "@microsoft/rush", + "allowedCategories": [ "tests" ] + }, { "name": "@radix-ui/colors", "allowedCategories": [ "libraries" ] From 42c22f8e5c4f068e2e67f0104e2c7135cf62e529 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:45:24 +0000 Subject: [PATCH 07/14] Fix CI pipeline steps and move @microsoft/rush to nonbrowser-approved-packages.json Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .github/workflows/ci.yml | 7 ++----- common/config/azure-pipelines/templates/build.yaml | 6 ++---- common/config/rush/browser-approved-packages.json | 4 ---- common/config/rush/nonbrowser-approved-packages.json | 4 ++++ 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8afa6ceba8..94dc5a2bbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,11 +80,8 @@ jobs: working-directory: repo-a - name: Run package manager integration tests - run: | - node ${{ github.workspace }}/repo-a/apps/rush/lib/start-dev.js build --to rush-package-manager-integration-test - cd build-tests/rush-package-manager-integration-test - npm run test - working-directory: repo-a + run: npm run test + working-directory: repo-a/build-tests/rush-package-manager-integration-test - name: Ensure repo README is up-to-date run: node repo-scripts/repo-toolbox/lib/start.js readme --verify diff --git a/common/config/azure-pipelines/templates/build.yaml b/common/config/azure-pipelines/templates/build.yaml index 52d3b6cd37..0d1204c016 100644 --- a/common/config/azure-pipelines/templates/build.yaml +++ b/common/config/azure-pipelines/templates/build.yaml @@ -25,8 +25,6 @@ steps: - script: 'node common/scripts/install-run-rush.js retest --verbose --production ${{ parameters.BuildParameters }}' displayName: 'Rush retest (install-run-rush)' - - script: | - node apps/rush/lib/start-dev.js build --to rush-package-manager-integration-test - cd build-tests/rush-package-manager-integration-test - npm run test + - script: 'npm run test' + workingDirectory: 'build-tests/rush-package-manager-integration-test' displayName: 'Run package manager integration tests' diff --git a/common/config/rush/browser-approved-packages.json b/common/config/rush/browser-approved-packages.json index fb9444b5d9..23c585145b 100644 --- a/common/config/rush/browser-approved-packages.json +++ b/common/config/rush/browser-approved-packages.json @@ -14,10 +14,6 @@ "name": "@lifaon/path", "allowedCategories": [ "libraries" ] }, - { - "name": "@microsoft/rush", - "allowedCategories": [ "tests" ] - }, { "name": "@radix-ui/colors", "allowedCategories": [ "libraries" ] diff --git a/common/config/rush/nonbrowser-approved-packages.json b/common/config/rush/nonbrowser-approved-packages.json index 6a51100d23..3f6756df1a 100644 --- a/common/config/rush/nonbrowser-approved-packages.json +++ b/common/config/rush/nonbrowser-approved-packages.json @@ -62,6 +62,10 @@ "name": "@microsoft/load-themed-styles", "allowedCategories": [ "libraries" ] }, + { + "name": "@microsoft/rush", + "allowedCategories": [ "tests" ] + }, { "name": "@microsoft/rush-lib", "allowedCategories": [ "libraries", "tests", "vscode-extensions" ] From 53c74ef92153eca6c0a8197dae1502f8c12f10df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:53:23 +0000 Subject: [PATCH 08/14] Add changelog entry for tar upgrade Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../upgrade-tar-security_2026-01-26-23-51.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json diff --git a/common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json b/common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json new file mode 100644 index 0000000000..8dfd80f452 --- /dev/null +++ b/common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@microsoft/rush-lib", + "comment": "Upgrade tar dependency from 6.2.1 to 7.5.6 to fix security vulnerability GHSA-8qq5-rm4j-mr97", + "type": "patch" + } + ], + "packageName": "@microsoft/rush-lib", + "email": "copilot@github.com" +} From c7e297ae360f5ba275c2124c3d4c2fb29bd48a25 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 23:56:41 +0000 Subject: [PATCH 09/14] Fix changelog entry - use @microsoft/rush with type 'none' for lockstep versioning Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../filter-npmrc-properties_2026-01-26-23-12.json | 0 .../upgrade-tar-security_2026-01-26-23-56.json} | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename common/changes/@microsoft/{rush-lib => rush}/filter-npmrc-properties_2026-01-26-23-12.json (100%) rename common/changes/@microsoft/{rush-lib/upgrade-tar-security_2026-01-26-23-51.json => rush/upgrade-tar-security_2026-01-26-23-56.json} (63%) diff --git a/common/changes/@microsoft/rush-lib/filter-npmrc-properties_2026-01-26-23-12.json b/common/changes/@microsoft/rush/filter-npmrc-properties_2026-01-26-23-12.json similarity index 100% rename from common/changes/@microsoft/rush-lib/filter-npmrc-properties_2026-01-26-23-12.json rename to common/changes/@microsoft/rush/filter-npmrc-properties_2026-01-26-23-12.json diff --git a/common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json b/common/changes/@microsoft/rush/upgrade-tar-security_2026-01-26-23-56.json similarity index 63% rename from common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json rename to common/changes/@microsoft/rush/upgrade-tar-security_2026-01-26-23-56.json index 8dfd80f452..102132590c 100644 --- a/common/changes/@microsoft/rush-lib/upgrade-tar-security_2026-01-26-23-51.json +++ b/common/changes/@microsoft/rush/upgrade-tar-security_2026-01-26-23-56.json @@ -1,11 +1,11 @@ { "changes": [ { - "packageName": "@microsoft/rush-lib", + "packageName": "@microsoft/rush", "comment": "Upgrade tar dependency from 6.2.1 to 7.5.6 to fix security vulnerability GHSA-8qq5-rm4j-mr97", - "type": "patch" + "type": "none" } ], - "packageName": "@microsoft/rush-lib", + "packageName": "@microsoft/rush", "email": "copilot@github.com" } From 35391fd68d55751f2247e283f5dce8e2944b7752 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 00:35:34 +0000 Subject: [PATCH 10/14] Update repo README with new integration test project Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 82f7b7c8a3..dbd5622956 100644 --- a/README.md +++ b/README.md @@ -218,6 +218,7 @@ These GitHub repositories provide supplementary resources for Rush Stack: | [/build-tests/rush-amazon-s3-build-cache-plugin-integration-test](./build-tests/rush-amazon-s3-build-cache-plugin-integration-test/) | Tests connecting to an amazon S3 endpoint | | [/build-tests/rush-lib-declaration-paths-test](./build-tests/rush-lib-declaration-paths-test/) | This project ensures all of the paths in rush-lib/lib/... have imports that resolve correctly. If this project builds, all `lib/**/*.d.ts` files in the `@microsoft/rush-lib` package are valid. | | [/build-tests/rush-mcp-example-plugin](./build-tests/rush-mcp-example-plugin/) | Example showing how to create a plugin for @rushstack/mcp-server | +| [/build-tests/rush-package-manager-integration-test](./build-tests/rush-package-manager-integration-test/) | Integration tests for non-pnpm package managers in Rush. | | [/build-tests/rush-project-change-analyzer-test](./build-tests/rush-project-change-analyzer-test/) | This is an example project that uses rush-lib's ProjectChangeAnalyzer to | | [/build-tests/rush-redis-cobuild-plugin-integration-test](./build-tests/rush-redis-cobuild-plugin-integration-test/) | Tests connecting to an redis server | | [/build-tests/set-webpack-public-path-plugin-test](./build-tests/set-webpack-public-path-plugin-test/) | Building this project tests the set-webpack-public-path-plugin | From 9497ef0afd66630e56647d8927b98e5912758aa0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 01:18:10 +0000 Subject: [PATCH 11/14] Fix Windows path handling in testBuiltCodeAsync Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../rush-package-manager-integration-test/src/TestHelper.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts index 38501b1b1a..d4e548b892 100644 --- a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -177,10 +177,13 @@ export class TestHelper { this._terminal.writeLine('\nTesting built code...'); const projectLib: string = path.join(testRepoPath, 'projects', projectName, 'lib/index.js'); + // Use forward slashes for require() path on all platforms + const projectLibPosix: string = projectLib.split(path.sep).join(path.posix.sep); + // Use Executable.spawnSync to capture output const result: string = Executable.spawnSync( process.argv0, - ['-e', `const b = require('${projectLib}'); console.log(b.test());`], + ['-e', `const b = require('${projectLibPosix}'); console.log(b.test());`], { currentWorkingDirectory: testRepoPath } From c3f406c8603e046c9ade2adb66938089e3e01609 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 01:34:12 +0000 Subject: [PATCH 12/14] Address code review feedback - use ensureEmptyFolderAsync, verify symlinks, and consolidate require statements Co-authored-by: dmichon-msft <26827560+dmichon-msft@users.noreply.github.com> --- .../src/TestHelper.ts | 26 ++++++++++++------- .../src/testNpmMode.ts | 4 +-- .../src/testYarnMode.ts | 4 +-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts index d4e548b892..e711014973 100644 --- a/build-tests/rush-package-manager-integration-test/src/TestHelper.ts +++ b/build-tests/rush-package-manager-integration-test/src/TestHelper.ts @@ -48,15 +48,9 @@ export class TestHelper { packageManagerType: 'npm' | 'yarn', packageManagerVersion: string ): Promise { - // Clean up previous test run - if (await FileSystem.existsAsync(testRepoPath)) { - this._terminal.writeLine('Cleaning up previous test run...'); - await FileSystem.deleteFolderAsync(testRepoPath); - } - - // Create test repo directory + // Clean up previous test run and create empty test repo directory this._terminal.writeLine(`Creating test repository at ${testRepoPath}...`); - await FileSystem.ensureFolderAsync(testRepoPath); + await FileSystem.ensureEmptyFolderAsync(testRepoPath); // Initialize Rush repo this._terminal.writeLine('Initializing Rush repo...'); @@ -145,13 +139,27 @@ export class TestHelper { expectedDependencies: string[] ): Promise { this._terminal.writeLine('\nVerifying node_modules structure...'); - const projectNodeModules: string = path.join(testRepoPath, 'projects', projectName, 'node_modules'); + const projectPath: string = path.join(testRepoPath, 'projects', projectName); + const projectNodeModules: string = path.join(projectPath, 'node_modules'); for (const dep of expectedDependencies) { const depPath: string = path.join(projectNodeModules, dep); if (!(await FileSystem.existsAsync(depPath))) { throw new Error(`ERROR: ${dep} not found in ${projectName}!`); } + + // Verify symlinks resolve correctly for local dependencies + if (dep.startsWith('test-project-')) { + const depRealPath: string = await FileSystem.getRealPathAsync(depPath); + const expectedRealPath: string = path.join(testRepoPath, 'projects', dep); + if (depRealPath !== expectedRealPath) { + throw new Error( + `ERROR: Symlink for ${dep} does not resolve correctly!\n` + + `Expected: ${expectedRealPath}\n` + + `Actual: ${depRealPath}` + ); + } + } } this._terminal.writeLine('✓ Dependencies installed correctly'); } diff --git a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts index e925ed4357..3f7143e576 100644 --- a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts @@ -34,7 +34,7 @@ export async function testNpmModeAsync(terminal: ITerminal): Promise { 'test-project-a', '1.0.0', { lodash: '^4.17.21' }, - `node -e "require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + `node -e "const fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` ); // Create project B (depends on A) @@ -47,7 +47,7 @@ export async function testNpmModeAsync(terminal: ITerminal): Promise { 'test-project-a': '1.0.0', moment: '^2.29.4' }, - `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + `node -e "const a = require('test-project-a'), fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` ); // Run rush update (creates and extracts temp project tarballs) diff --git a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts index a21b07f408..2942f233ab 100644 --- a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts @@ -34,7 +34,7 @@ export async function testYarnModeAsync(terminal: ITerminal): Promise { 'test-project-a', '1.0.0', { lodash: '^4.17.21' }, - `node -e "require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` + `node -e "const fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { greet: () => \\"Hello from A\\" };');"` ); // Create project B (depends on A) @@ -47,7 +47,7 @@ export async function testYarnModeAsync(terminal: ITerminal): Promise { 'test-project-a': '1.0.0', moment: '^2.29.4' }, - `node -e "const a = require('test-project-a'); require('fs').mkdirSync('lib', {recursive: true}); require('fs').writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` + `node -e "const a = require('test-project-a'), fs = require('fs'); fs.mkdirSync('lib', {recursive: true}); fs.writeFileSync('lib/index.js', 'module.exports = { test: () => \\"Using: \\" + require(\\'test-project-a\\').greet() };');"` ); // Run rush update (creates and extracts temp project tarballs) From 78a8208f66f6fbaa47a8b9b80394be69be3cfd98 Mon Sep 17 00:00:00 2001 From: Ian Clanton-Thuon Date: Mon, 26 Jan 2026 17:55:31 -0800 Subject: [PATCH 13/14] Rush update. --- .../build-tests-subspace/pnpm-lock.yaml | 79 +++++++++--------- .../build-tests-subspace/repo-state.json | 4 +- .../config/subspaces/default/pnpm-lock.yaml | 82 ++++++++++++++----- .../config/subspaces/default/repo-state.json | 2 +- 4 files changed, 104 insertions(+), 63 deletions(-) diff --git a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml index d5a0bc83b4..6de7624d79 100644 --- a/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml +++ b/common/config/subspaces/build-tests-subspace/pnpm-lock.yaml @@ -429,6 +429,10 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -1471,9 +1475,9 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} @@ -1985,10 +1989,6 @@ packages: resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2772,17 +2772,13 @@ packages: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} @@ -3447,10 +3443,9 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + tar@7.5.6: + resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==} + engines: {node: '>=18'} terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} @@ -3704,6 +3699,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@2.4.1: resolution: {integrity: sha512-pIXzoImaqmfOrL7teGUBt/T7ZDnyeGBWyXQBvOVhLkWLN37GXv8NMLK406UY6dS51JfcQHsmcW5cJ441bHg6Lg==} engines: {node: '>= 14'} @@ -4010,6 +4009,10 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -4334,7 +4337,7 @@ snapshots: ssri: 8.0.1 strict-uri-encode: 2.0.0 tapable: 2.2.1 - tar: 6.2.1 + tar: 7.5.6 true-case-path: 2.2.1 transitivePeerDependencies: - '@types/node' @@ -4411,7 +4414,7 @@ snapshots: '@pnpm/crypto.base32-hash': 2.0.0 '@pnpm/types': 9.4.2 encode-registry: 3.0.1 - semver: 7.5.4 + semver: 7.7.3 '@pnpm/dependency-path@5.1.7': dependencies: @@ -4477,7 +4480,7 @@ snapshots: js-yaml: '@zkochan/js-yaml@0.0.6' normalize-path: 3.0.0 ramda: '@pnpm/ramda@0.28.1' - semver: 7.5.4 + semver: 7.7.3 sort-keys: 4.2.0 strip-bom: 4.0.0 write-file-atomic: 5.0.1 @@ -4556,7 +4559,7 @@ snapshots: '@pnpm/lockfile-types': 5.1.5 comver-to-semver: 1.0.0 ramda: '@pnpm/ramda@0.28.1' - semver: 7.5.4 + semver: 7.7.3 '@pnpm/object.key-sorting@1000.0.1': dependencies: @@ -5719,7 +5722,7 @@ snapshots: chardet@2.1.1: {} - chownr@2.0.0: {} + chownr@3.0.0: {} chrome-trace-event@1.0.4: {} @@ -6409,10 +6412,6 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -7459,14 +7458,11 @@ snapshots: dependencies: yallist: 4.0.0 - minipass@5.0.0: {} - minipass@7.1.2: {} - minizlib@2.1.2: + minizlib@3.1.0: dependencies: - minipass: 3.3.6 - yallist: 4.0.0 + minipass: 7.1.2 mkdirp@0.5.6: dependencies: @@ -7516,7 +7512,7 @@ snapshots: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.16.1 - semver: 7.5.4 + semver: 7.7.3 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -7675,7 +7671,7 @@ snapshots: got: 11.8.6 registry-auth-token: 4.2.2 registry-url: 5.1.0 - semver: 7.5.4 + semver: 7.7.3 pako@1.0.11: {} @@ -8207,14 +8203,13 @@ snapshots: tapable@2.3.0: {} - tar@6.2.1: + tar@7.5.6: dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 terser-webpack-plugin@5.3.16(webpack@5.103.0): dependencies: @@ -8533,6 +8528,8 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@2.4.1: {} yocto-queue@0.1.0: {} diff --git a/common/config/subspaces/build-tests-subspace/repo-state.json b/common/config/subspaces/build-tests-subspace/repo-state.json index 6c578d03e7..6c5fab479b 100644 --- a/common/config/subspaces/build-tests-subspace/repo-state.json +++ b/common/config/subspaces/build-tests-subspace/repo-state.json @@ -1,6 +1,6 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "3821e72a3e032f9d3bfc8da6115cea4275a0fe2b", + "pnpmShrinkwrapHash": "da80c128380df78244e113861884b5e6a38ad7c5", "preferredVersionsHash": "550b4cee0bef4e97db6c6aad726df5149d20e7d9", - "packageJsonInjectedDependenciesHash": "f0c6fee692b330a1de08fb064f17fa7612920cd4" + "packageJsonInjectedDependenciesHash": "1c312688ef85bfdb64079cc271a46b18d816d411" } diff --git a/common/config/subspaces/default/pnpm-lock.yaml b/common/config/subspaces/default/pnpm-lock.yaml index 330c74bd4c..38034be545 100644 --- a/common/config/subspaces/default/pnpm-lock.yaml +++ b/common/config/subspaces/default/pnpm-lock.yaml @@ -2761,6 +2761,27 @@ importers: specifier: workspace:* version: link:../../rigs/local-node-rig + ../../../build-tests/rush-package-manager-integration-test: + devDependencies: + '@microsoft/rush': + specifier: workspace:* + version: link:../../apps/rush + '@microsoft/rush-lib': + specifier: workspace:* + version: link:../../libraries/rush-lib + '@rushstack/heft': + specifier: workspace:* + version: link:../../apps/heft + '@rushstack/node-core-library': + specifier: workspace:* + version: link:../../libraries/node-core-library + '@rushstack/terminal': + specifier: workspace:* + version: link:../../libraries/terminal + local-node-rig: + specifier: workspace:* + version: link:../../rigs/local-node-rig + ../../../build-tests/rush-project-change-analyzer-test: dependencies: '@microsoft/rush-lib': @@ -4112,8 +4133,8 @@ importers: specifier: 2.2.1 version: 2.2.1 tar: - specifier: ~6.2.1 - version: 6.2.1 + specifier: ~7.5.6 + version: 7.5.6 true-case-path: specifier: ~2.2.1 version: 2.2.1 @@ -4163,9 +4184,6 @@ importers: '@types/strict-uri-encode': specifier: 2.0.0 version: 2.0.0 - '@types/tar': - specifier: 6.1.6 - version: 6.1.6 '@types/webpack-env': specifier: 1.18.8 version: 1.18.8 @@ -7936,6 +7954,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/load-nyc-config@1.1.0': resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} @@ -10190,9 +10212,6 @@ packages: '@types/tapable@1.0.6': resolution: {integrity: sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==} - '@types/tar@6.1.6': - resolution: {integrity: sha512-HQ06kiiDXz9uqtmE9ksQUn1ovcPr1gGV9EgaCWo6FGYKD0onNBCetBzL0kfcS8Kbj1EFxJWY9jL2W4ZvvtGI8Q==} - '@types/through@0.0.33': resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} @@ -11502,6 +11521,10 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} @@ -15166,10 +15189,6 @@ packages: resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} engines: {node: '>=8'} - minipass@4.2.8: - resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==} - engines: {node: '>=8'} - minipass@5.0.0: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} @@ -15182,6 +15201,10 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + mississippi@3.0.0: resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==} engines: {node: '>=4.0.0'} @@ -17623,6 +17646,10 @@ packages: engines: {node: '>=10'} deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + tar@7.5.6: + resolution: {integrity: sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==} + engines: {node: '>=18'} + telejson@5.3.3: resolution: {integrity: sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA==} @@ -18626,6 +18653,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -22293,6 +22324,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/load-nyc-config@1.1.0': dependencies: camelcase: 5.3.1 @@ -26292,11 +26327,6 @@ snapshots: '@types/tapable@1.0.6': {} - '@types/tar@6.1.6': - dependencies: - '@types/node': 22.9.3 - minipass: 4.2.8 - '@types/through@0.0.33': dependencies: '@types/node': 22.9.3 @@ -28323,6 +28353,8 @@ snapshots: chownr@2.0.0: {} + chownr@3.0.0: {} + chrome-trace-event@1.0.4: {} ci-info@2.0.0: {} @@ -33286,8 +33318,6 @@ snapshots: dependencies: yallist: 4.0.0 - minipass@4.2.8: {} - minipass@5.0.0: {} minipass@7.1.2: {} @@ -33297,6 +33327,10 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + mississippi@3.0.0: dependencies: concat-stream: 1.6.2 @@ -36134,6 +36168,14 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + tar@7.5.6: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + telejson@5.3.3: dependencies: '@types/is-function': 1.0.3 @@ -37405,6 +37447,8 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + yaml@1.10.2: {} yaml@2.4.1: {} diff --git a/common/config/subspaces/default/repo-state.json b/common/config/subspaces/default/repo-state.json index cb27decae5..6f7b9aabb5 100644 --- a/common/config/subspaces/default/repo-state.json +++ b/common/config/subspaces/default/repo-state.json @@ -1,5 +1,5 @@ // DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush. { - "pnpmShrinkwrapHash": "627024553ddfaf140f96522acefe4e7a4895a0b3", + "pnpmShrinkwrapHash": "aebea9e28d4e012d8a37fc57c92bd28eb386df5b", "preferredVersionsHash": "a9b67c38568259823f9cfb8270b31bf6d8470b27" } From 55d7166b046ff6efa2977a20d9ca03ae8a03c3da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 05:16:45 +0000 Subject: [PATCH 14/14] Fix Windows path issue - use os.tmpdir() instead of hardcoded /tmp Co-authored-by: iclanton <5010588+iclanton@users.noreply.github.com> --- .../rush-package-manager-integration-test/src/testNpmMode.ts | 3 ++- .../rush-package-manager-integration-test/src/testYarnMode.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts index 3f7143e576..3392e90eac 100644 --- a/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testNpmMode.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as os from 'node:os'; import * as path from 'node:path'; import type { ITerminal } from '@rushstack/terminal'; @@ -14,7 +15,7 @@ import { TestHelper } from './TestHelper'; export async function testNpmModeAsync(terminal: ITerminal): Promise { const helper: TestHelper = new TestHelper(terminal); // Use system temp directory to avoid rush init detecting parent rush.json - const testRepoPath: string = path.join('/tmp', 'rush-package-manager-test', 'npm-test-repo'); + const testRepoPath: string = path.join(os.tmpdir(), 'rush-package-manager-test', 'npm-test-repo'); terminal.writeLine('=========================================='); terminal.writeLine('Rush NPM Mode Integration Test'); diff --git a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts index 2942f233ab..5a097c6168 100644 --- a/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts +++ b/build-tests/rush-package-manager-integration-test/src/testYarnMode.ts @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as os from 'node:os'; import * as path from 'node:path'; import type { ITerminal } from '@rushstack/terminal'; @@ -14,7 +15,7 @@ import { TestHelper } from './TestHelper'; export async function testYarnModeAsync(terminal: ITerminal): Promise { const helper: TestHelper = new TestHelper(terminal); // Use system temp directory to avoid rush init detecting parent rush.json - const testRepoPath: string = path.join('/tmp', 'rush-package-manager-test', 'yarn-test-repo'); + const testRepoPath: string = path.join(os.tmpdir(), 'rush-package-manager-test', 'yarn-test-repo'); terminal.writeLine('=========================================='); terminal.writeLine('Rush Yarn Mode Integration Test');