From d19e20bd95c9bc4faa73824ba6282775c36e89e3 Mon Sep 17 00:00:00 2001 From: Greg Magolan Date: Wed, 24 Nov 2021 01:16:11 -0800 Subject: [PATCH] fix: don't symlink execroot node_modules when under bazel run --- .circleci/bazel.rc | 3 + internal/linker/index.js | 54 ++++++++---- internal/linker/link_node_modules.ts | 82 ++++++++++------- internal/node/launcher.sh | 62 ++++++------- internal/node/node_patches.js | 13 ++- internal/node/test/env.spec.js | 111 ++++++++++++------------ packages/node-patches/src/fs.ts | 13 ++- packages/node-patches/test/fs/escape.ts | 15 ++++ 8 files changed, 207 insertions(+), 146 deletions(-) diff --git a/.circleci/bazel.rc b/.circleci/bazel.rc index a2c3a1779b..619f1e0f59 100644 --- a/.circleci/bazel.rc +++ b/.circleci/bazel.rc @@ -23,3 +23,6 @@ test --flaky_test_attempts=2 # Expose the SSH_AUTH_SOCK variable to integration tests that need to run git_repository repository rules # This is needed by the CircleCI git-over-ssh environment test --test_env=SSH_AUTH_SOCK + +# Keep going after first failure +build --keep_going diff --git a/internal/linker/index.js b/internal/linker/index.js index 9a60a9552d..8461f1d7d1 100644 --- a/internal/linker/index.js +++ b/internal/linker/index.js @@ -289,6 +289,8 @@ function main(args, runfiles) { log_verbose('execroot:', execroot ? execroot : 'not found'); const isExecroot = startCwd == execroot; log_verbose('isExecroot:', isExecroot.toString()); + const isBazelRun = !!process.env['BUILD_WORKSPACE_DIRECTORY']; + log_verbose('isBazelRun:', isBazelRun.toString()); if (!isExecroot && execroot) { process.chdir(execroot); log_verbose('changed directory to execroot', execroot); @@ -327,32 +329,46 @@ function main(args, runfiles) { else { workspaceNodeModules = undefined; } + let primaryNodeModules; if (packagePath) { - yield mkdirp(packagePath); - } - const execrootNodeModules = path.posix.join(packagePath, `node_modules`); - if (workspaceNodeModules) { - yield symlinkWithUnlink(workspaceNodeModules, execrootNodeModules); + const binNodeModules = path.posix.join(bin, packagePath, 'node_modules'); + yield mkdirp(path.dirname(binNodeModules)); + if (workspaceNodeModules) { + yield symlinkWithUnlink(workspaceNodeModules, binNodeModules); + primaryNodeModules = workspaceNodeModules; + } + else { + yield mkdirp(binNodeModules); + primaryNodeModules = binNodeModules; + } + if (!isBazelRun) { + const execrootNodeModules = path.posix.join(packagePath, 'node_modules'); + yield mkdirp(path.dirname(execrootNodeModules)); + yield symlinkWithUnlink(primaryNodeModules, execrootNodeModules); + } } else { - yield mkdirp(execrootNodeModules); - } - if (packagePath) { - const packagePathBin = path.posix.join(bin, packagePath); - yield mkdirp(`${packagePathBin}`); - yield symlinkWithUnlink(execrootNodeModules, `${packagePathBin}/node_modules`); + const execrootNodeModules = 'node_modules'; + if (workspaceNodeModules) { + yield symlinkWithUnlink(workspaceNodeModules, execrootNodeModules); + primaryNodeModules = workspaceNodeModules; + } + else { + yield mkdirp(execrootNodeModules); + primaryNodeModules = execrootNodeModules; + } } if (!isExecroot) { - const runfilesPackagePath = path.posix.join(startCwd, packagePath); - yield mkdirp(`${runfilesPackagePath}`); - yield symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`); + const runfilesNodeModules = path.posix.join(startCwd, packagePath, 'node_modules'); + yield mkdirp(path.dirname(runfilesNodeModules)); + yield symlinkWithUnlink(primaryNodeModules, runfilesNodeModules); } if (process.env['RUNFILES']) { const stat = yield gracefulLstat(process.env['RUNFILES']); if (stat && stat.isDirectory()) { - const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], workspace, packagePath); - yield mkdirp(`${runfilesPackagePath}`); - yield symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`); + const runfilesNodeModules = path.posix.join(process.env['RUNFILES'], workspace, 'node_modules'); + yield mkdirp(path.dirname(runfilesNodeModules)); + yield symlinkWithUnlink(primaryNodeModules, runfilesNodeModules); } } } @@ -393,7 +409,9 @@ function main(args, runfiles) { } function linkModules(package_path, m) { return __awaiter(this, void 0, void 0, function* () { - const symlinkIn = package_path ? `${package_path}/node_modules` : 'node_modules'; + const symlinkIn = package_path ? + path.posix.join(bin, package_path, 'node_modules') : + 'node_modules'; if (path.dirname(m.name)) { yield mkdirp(`${symlinkIn}/${path.dirname(m.name)}`); } diff --git a/internal/linker/link_node_modules.ts b/internal/linker/link_node_modules.ts index 104f8655d4..da0d7c2325 100644 --- a/internal/linker/link_node_modules.ts +++ b/internal/linker/link_node_modules.ts @@ -424,6 +424,9 @@ export async function main(args: string[], runfiles: Runfiles) { const isExecroot = startCwd == execroot; log_verbose('isExecroot:', isExecroot.toString()); + const isBazelRun = !!process.env['BUILD_WORKSPACE_DIRECTORY'] + log_verbose('isBazelRun:', isBazelRun.toString()); + if (!isExecroot && execroot) { // If we're not in the execroot and we've found one then change to the execroot // directory to create the node_modules symlinks @@ -481,55 +484,66 @@ export async function main(args: string[], runfiles: Runfiles) { workspaceNodeModules = undefined; } + let primaryNodeModules; if (packagePath) { - // In some cases packagePath may not exist in sandbox if there are no source deps - // and only generated file deps. we create it so that we that we can link to - // packagePath/node_modules since packagePathBin/node_modules is a symlink to - // packagePath/node_modules and is unguarded in launcher.sh as we allow symlinks to fall - // through to from output tree to source tree to prevent resolving the same npm package to - // multiple locations on disk - await mkdirp(packagePath); - } - - // There are third party modules at this package path - const execrootNodeModules = path.posix.join(packagePath, `node_modules`); + const binNodeModules = path.posix.join(bin, packagePath, 'node_modules'); + await mkdirp(path.dirname(binNodeModules)); + + // Create bin//node_modules symlink + // (or empty directory if there are no 3rd party deps to symlink to) + if (workspaceNodeModules) { + await symlinkWithUnlink(workspaceNodeModules, binNodeModules); + primaryNodeModules = workspaceNodeModules; + } else { + await mkdirp(binNodeModules); + primaryNodeModules = binNodeModules; + } - if (workspaceNodeModules) { - // Execroot symlink -> external workspace node_modules - await symlinkWithUnlink(workspaceNodeModules, execrootNodeModules); + if (!isBazelRun) { + // Special case under bazel run where we don't want to create node_modules + // in an execroot under a package path as this will end up in the user's + // workspace via the package path folder symlink + const execrootNodeModules = path.posix.join(packagePath, 'node_modules'); + await mkdirp(path.dirname(execrootNodeModules)); + await symlinkWithUnlink(primaryNodeModules, execrootNodeModules); + } } else { - // Create an execroot node_modules directory since there are no third party node_modules to symlink to - await mkdirp(execrootNodeModules); - } + const execrootNodeModules = 'node_modules'; - if (packagePath) { - // Bin symlink -> execroot node_modules - // NB: don't do this for the root of the bin tree since standard node_modules resolution - // will fall back to the execroot node_modules naturally - // See https://github.com/bazelbuild/rules_nodejs/issues/3054 - const packagePathBin = path.posix.join(bin, packagePath); - await mkdirp(`${packagePathBin}`); - await symlinkWithUnlink(execrootNodeModules, `${packagePathBin}/node_modules`); + // Create execroot/node_modules symlink (or empty directory if there are + // no 3rd party deps to symlink to) + if (workspaceNodeModules) { + await symlinkWithUnlink(workspaceNodeModules, execrootNodeModules); + primaryNodeModules = workspaceNodeModules; + } else { + await mkdirp(execrootNodeModules); + primaryNodeModules = execrootNodeModules; + } + + // NB: Don't create a bin/node_modules since standard node_modules + // resolution will fall back to the execroot node_modules naturally. See + // https://github.com/bazelbuild/rules_nodejs/issues/3054 } - // Start CWD symlink -> execroot node_modules + // If start cwd was in runfiles then create + // start/cwd if (!isExecroot) { - const runfilesPackagePath = path.posix.join(startCwd, packagePath); - await mkdirp(`${runfilesPackagePath}`); + const runfilesNodeModules = path.posix.join(startCwd, packagePath, 'node_modules'); + await mkdirp(path.dirname(runfilesNodeModules)); // Don't link to the root execroot node_modules if there is a workspace node_modules. // Bazel will delete that symlink on rebuild in the ibazel run context. - await symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`); + await symlinkWithUnlink(primaryNodeModules, runfilesNodeModules); } // RUNFILES symlink -> execroot node_modules if (process.env['RUNFILES']) { const stat = await gracefulLstat(process.env['RUNFILES']); if (stat && stat.isDirectory()) { - const runfilesPackagePath = path.posix.join(process.env['RUNFILES'], workspace, packagePath); - await mkdirp(`${runfilesPackagePath}`); + const runfilesNodeModules = path.posix.join(process.env['RUNFILES'], workspace, 'node_modules'); + await mkdirp(path.dirname(runfilesNodeModules)); // Don't link to the root execroot node_modules if there is a workspace node_modules. // Bazel will delete that symlink on rebuild in the ibazel run context. - await symlinkWithUnlink(!packagePath && workspaceNodeModules ? workspaceNodeModules : execrootNodeModules, `${runfilesPackagePath}/node_modules`); + await symlinkWithUnlink(primaryNodeModules, runfilesNodeModules); } } } @@ -593,7 +607,9 @@ export async function main(args: string[], runfiles: Runfiles) { } async function linkModules(package_path: string, m: LinkerTreeElement) { - const symlinkIn = package_path ? `${package_path}/node_modules` : 'node_modules'; + const symlinkIn = package_path ? + path.posix.join(bin, package_path, 'node_modules') : + 'node_modules'; // ensure the parent directory exist if (path.dirname(m.name)) { diff --git a/internal/node/launcher.sh b/internal/node/launcher.sh index 007b7279c4..fd832d0203 100644 --- a/internal/node/launcher.sh +++ b/internal/node/launcher.sh @@ -271,29 +271,24 @@ else RUNFILES_ROOT= fi -# Tell the node_patches_script that programs should not escape the execroot -export BAZEL_PATCH_ROOTS="${EXECROOT}" -# Set all bazel managed node_modules directories as guarded so no symlinks may -# escape and no symlinks may enter. -# We always guard against the root node_modules where 1st party deps go. -# (e.g., /private/.../execroot/build_bazel_rules_nodejs/node_modules) -export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${EXECROOT}/node_modules" +# Tell the node_patches_script that programs should not escape the execroot or +# root node_modules +# For example, +# .../execroot/build_bazel_rules_nodejs +# .../execroot/build_bazel_rules_nodejs/node_modules +export BAZEL_PATCH_ROOTS="${EXECROOT},${EXECROOT}/node_modules" if [[ "${RUNFILES_ROOT}" ]]; then - # If in runfiles, guard the runfiles root itself - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}" - # If in runfiles guard the node_modules location in runfiles as well - # (e.g., /private/.../execroot/build_bazel_rules_nodejs/bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/build_bazel_rules_nodejs/node_modules) - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}/${BAZEL_WORKSPACE}/node_modules" + # Guard the RUNFILES_ROOT & RUNFILES_ROOT/workspace/node_modules + export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT},${RUNFILES_ROOT}/${BAZEL_WORKSPACE}/node_modules" fi -if [[ "${RUNFILES}" ]]; then - # If in runfiles, guard the RUNFILES root itself - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES}" - # If RUNFILES is set, guard the RUNFILES node_modules as well - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES}/${BAZEL_WORKSPACE}/node_modules" +if [[ "${RUNFILES}" ]] && [[ "${RUNFILES}" != "${RUNFILES_ROOT}" ]]; then + # Same as above but for RUNFILES is not equal to RUNFILES_ROOT + export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES},${RUNFILES}/${BAZEL_WORKSPACE}/node_modules" fi if [[ -n "${BAZEL_NODE_MODULES_ROOTS:-}" ]]; then - # BAZEL_NODE_MODULES_ROOTS is in the format ":,:" - # (e.g., "internal/linker/test:npm_internal_linker_test,:npm") + # BAZEL_NODE_MODULES_ROOTS is in the format ":,:". + # For example, + # "internal/linker/test:npm_internal_linker_test,:npm" if [[ -n "${VERBOSE_LOGS:-}" ]]; then echo "BAZEL_NODE_MODULES_ROOTS=${BAZEL_NODE_MODULES_ROOTS}" >&2 fi @@ -310,27 +305,20 @@ if [[ -n "${BAZEL_NODE_MODULES_ROOTS:-}" ]]; then root_path="${root_pair[0]}" root_workspace="${root_pair[1]:-}" if [[ "${root_path}" ]]; then - # Guard non-root node_modules as well - # (e.g., /private/.../execroot/build_bazel_rules_nodejs/internal/linker/test/node_modules) - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${EXECROOT}/${root_path}/node_modules" + # Guard non-root execroot & bazel-out node_modules as well. + # For example, + # .../execroot/build_bazel_rules_nodejs/internal/linker/test/node_modules + # .../execroot/build_bazel_rules_nodejs/bazel-out/*/bin/internal/linker/test/node_modules + export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${EXECROOT}/${root_path}/node_modules,${EXECROOT}/bazel-out/*/bin/${root_path}/node_modules" if [[ "${RUNFILES_ROOT}" ]]; then - # If in runfiles guard the node_modules location in runfiles as well - # (e.g., /private/.../execroot/build_bazel_rules_nodejs/bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/build_bazel_rules_nodejs/internal/linker/test/node_modules) + # If in runfiles guard the node_modules location in runfiles as well. + # For example, + # .../execroot/build_bazel_rules_nodejs/bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/build_bazel_rules_nodejs/internal/linker/test/node_modules export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}/${BAZEL_WORKSPACE}/${root_path}/node_modules" fi - fi - # TODO: the following guards on the external workspaces may not be necessary and could be removed in the future with care - if [[ "${root_workspace}" ]] && [[ "${root_workspace}" != "${BAZEL_WORKSPACE}" ]]; then - # Guard the external workspaces if they are not the user workspace - # (e.g., /private/.../execroot/build_bazel_rules_nodejs/external/npm_internal_linker_test/node_modules) - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${EXECROOT}/external/${root_workspace}/node_modules" - if [[ "${RUNFILES_ROOT}" ]]; then - # If in runfiles guard the external workspace location in runfiles as well - # (e.g., /private/.../execroot/build_bazel_rules_nodejs/bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/npm_internal_linker_test/node_modules) - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}/${root_workspace}/node_modules" - # and include the legacy runfiles location incase legacy runfiles are enabled - # (e.g., /private/.../bazel-out/darwin-fastbuild/bin/internal/linker/test/multi_linker/test.sh.runfiles/build_bazel_rules_nodejs/external/npm_internal_linker_test/node_modules) - export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES_ROOT}/${BAZEL_WORKSPACE}/external/${root_workspace}/node_modules" + if [[ "${RUNFILES}" ]] && [[ "${RUNFILES}" != "${RUNFILES_ROOT}" ]]; then + # Same as above but for RUNFILES is not equal to RUNFILES_ROOT + export BAZEL_PATCH_ROOTS="${BAZEL_PATCH_ROOTS},${RUNFILES}/${BAZEL_WORKSPACE}/${root_path}/node_modules" fi fi fi diff --git a/internal/node/node_patches.js b/internal/node/node_patches.js index 360bff24f9..7b13064769 100644 --- a/internal/node/node_patches.js +++ b/internal/node/node_patches.js @@ -486,7 +486,18 @@ const patcher = (fs = fs__default['default'], roots) => { }; exports.patcher = patcher; function isOutPath(root, str) { - return !root || (!str.startsWith(root + path__default['default'].sep) && str !== root); + if (!root) + return true; + let strParts = str.split(path__default['default'].sep); + let rootParts = root.split(path__default['default'].sep); + let i = 0; + for (; i < rootParts.length && i < strParts.length; i++) { + if (rootParts[i] === strParts[i] || rootParts[i] === '*') { + continue; + } + break; + } + return i < rootParts.length; } exports.isOutPath = isOutPath; const escapeFunction = (roots) => { diff --git a/internal/node/test/env.spec.js b/internal/node/test/env.spec.js index 88d92cbaae..8a5a410654 100644 --- a/internal/node/test/env.spec.js +++ b/internal/node/test/env.spec.js @@ -13,6 +13,9 @@ function normPath(p) { if (/^[a-z]\:/.test(result)) { // Handle c:/ and /c/ mismatch result = `/${result[0]}${result.slice(2)}`; + } else if (/^[a-z];[a-z]\:\/msys64/.test(result)) { + // Handle c;b:/msys64/ and /b/ mismatch + result = `/${result[2]}${result.slice(11)}`; } else if (/^[a-z];[a-z]\:/.test(result)) { // Handle c;b:/ and /c/b/ mismatch result = `/${result[0]}/${result[2]}${result.slice(4)}`; @@ -43,73 +46,69 @@ describe('launcher.sh environment', function() { expectPathsToMatch(process.cwd(), `${process.env['RUNFILES_DIR']}/build_bazel_rules_nodejs`); expectPathsToMatch(process.env['PWD'], `${process.env['RUNFILES_DIR']}/build_bazel_rules_nodejs`); expectPathsToMatch(process.env['BAZEL_NODE_MODULES_ROOTS'], ':npm'); - console.log(process.env['RUNFILES']) const expectedRoots = [ `${execroot}`, `${execroot}/node_modules`, `${runfilesRoot}`, `${runfilesRoot}/build_bazel_rules_nodejs/node_modules`, - `${process.env['RUNFILES']}`, - `${process.env['RUNFILES']}/${process.env['BAZEL_WORKSPACE']}/node_modules`, - `${execroot}/external/npm/node_modules`, - `${runfilesRoot}/npm/node_modules`, - `${runfilesRoot}/build_bazel_rules_nodejs/external/npm/node_modules`, ] + if (isWindows) { + expectedRoots.push( + process.env['RUNFILES'], + `${process.env['RUNFILES']}/build_bazel_rules_nodejs/node_modules`, + ) + } expectPathsToMatch(process.env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots); }); - - it('should setup correct bazel environment variables when in execroot with no third party deps', - function() { - const env = require(runfiles.resolvePackageRelative('dump_build_env.json')); - // On Windows, the RUNFILES path ends in a /MANIFEST segment in this context - const runfilesRoot = normPath(isWindows ? path.dirname(env['RUNFILES']) : env['RUNFILES']); - const match = runfilesRoot.match(/\/bazel-out\//); - expect(!!match).toBe(true); - const execroot = runfilesRoot.slice(0, match.index); - expectPathsToMatch(path.basename(runfilesRoot), `dump_build_env.${runfilesExt}.runfiles`); - expectPathsToMatch(env['BAZEL_WORKSPACE'], 'build_bazel_rules_nodejs'); - expectPathsToMatch(env['BAZEL_TARGET'], '//internal/node/test:dump_build_env'); - expectPathsToMatch(env['PWD'], execroot); - // On Windows, an empty string value for 'BAZEL_NODE_MODULES_ROOTS' does not make it into - // dump_build_env.json - expectPathsToMatch( - env['BAZEL_NODE_MODULES_ROOTS'] ? env['BAZEL_NODE_MODULES_ROOTS'] : '', ''); - const expectedRoots = [ - `${execroot}`, - `${execroot}/node_modules`, - `${env['RUNFILES']}`, - `${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`, - ] - expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots); - }); - - it('should setup correct bazel environment variables when in execroot with third party deps', - function() { - const env = require(runfiles.resolvePackageRelative('dump_build_env_alt.json')); - // On Windows, the RUNFILES path ends in a /MANIFEST segment in this context - const runfilesRoot = normPath(isWindows ? path.dirname(env['RUNFILES']) : env['RUNFILES']); - const match = runfilesRoot.match(/\/bazel-out\//); - expect(!!match).toBe(true); - const execroot = runfilesRoot.slice(0, match.index); - expectPathsToMatch( - path.basename(runfilesRoot), `dump_build_env_alt.${runfilesExt}.runfiles`); - expectPathsToMatch(env['BAZEL_WORKSPACE'], 'build_bazel_rules_nodejs'); - expectPathsToMatch(env['BAZEL_TARGET'], '//internal/node/test:dump_build_env_alt'); - expectPathsToMatch(env['PWD'], execroot); - expectPathsToMatch(env['BAZEL_NODE_MODULES_ROOTS'], ':npm'); - const expectedRoots = [ - `${execroot}`, - `${execroot}/node_modules`, - `${env['RUNFILES']}`, - `${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`, - `${execroot}/external/npm/node_modules`, - ] - expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots); - }); - + + it('should setup correct bazel environment variables when in execroot with no third party deps', function() { + const env = require(runfiles.resolvePackageRelative('dump_build_env.json')); + // On Windows, the RUNFILES path ends in a /MANIFEST segment in this context + const runfilesRoot = normPath(isWindows ? path.dirname(env['RUNFILES']) : env['RUNFILES']); + const match = runfilesRoot.match(/\/bazel-out\//); + expect(!!match).toBe(true); + const execroot = runfilesRoot.slice(0, match.index); + expectPathsToMatch(path.basename(runfilesRoot), `dump_build_env.${runfilesExt}.runfiles`); + expectPathsToMatch(env['BAZEL_WORKSPACE'], 'build_bazel_rules_nodejs'); + expectPathsToMatch(env['BAZEL_TARGET'], '//internal/node/test:dump_build_env'); + expectPathsToMatch(env['PWD'], execroot); + // On Windows, an empty string value for 'BAZEL_NODE_MODULES_ROOTS' does not make it into + // dump_build_env.json + expectPathsToMatch(env['BAZEL_NODE_MODULES_ROOTS'] ? env['BAZEL_NODE_MODULES_ROOTS'] : '', ''); + const expectedRoots = [ + `${execroot}`, + `${execroot}/node_modules`, + `${env['RUNFILES']}`, + `${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`, + ]; + expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots); + }); + + it('should setup correct bazel environment variables when in execroot with third party deps', function() { + const env = require(runfiles.resolvePackageRelative('dump_build_env_alt.json')); + // On Windows, the RUNFILES path ends in a /MANIFEST segment in this context + const runfilesRoot = normPath(isWindows ? path.dirname(env['RUNFILES']) : env['RUNFILES']); + const match = runfilesRoot.match(/\/bazel-out\//); + expect(!!match).toBe(true); + const execroot = runfilesRoot.slice(0, match.index); + expectPathsToMatch(path.basename(runfilesRoot), `dump_build_env_alt.${runfilesExt}.runfiles`); + expectPathsToMatch(env['BAZEL_WORKSPACE'], 'build_bazel_rules_nodejs'); + expectPathsToMatch(env['BAZEL_TARGET'], '//internal/node/test:dump_build_env_alt'); + expectPathsToMatch(env['PWD'], execroot); + expectPathsToMatch(env['BAZEL_NODE_MODULES_ROOTS'], ':npm'); + const expectedRoots = [ + `${execroot}`, + `${execroot}/node_modules`, + `${env['RUNFILES']}`, + `${env['RUNFILES']}/${env['BAZEL_WORKSPACE']}/node_modules`, + ]; + expectPathsToMatch(env['BAZEL_PATCH_ROOTS'].split(','), expectedRoots); + }); + it('should setup correct bazel environment variables from env attr', function() { const env = require(runfiles.resolvePackageRelative('dump_build_env_attr.json')); expect(env['FOO']).toBe('BAR'); expect(env['LOC']).toBe('build_bazel_rules_nodejs/internal/node/test/dump_build_env.js'); }); }); + \ No newline at end of file diff --git a/packages/node-patches/src/fs.ts b/packages/node-patches/src/fs.ts index 48d5e3fa10..fa4e79625d 100644 --- a/packages/node-patches/src/fs.ts +++ b/packages/node-patches/src/fs.ts @@ -471,7 +471,18 @@ export const patcher = (fs: any = _fs, roots: string[]) => { }; export function isOutPath(root: string, str: string) { - return !root || (!str.startsWith(root + path.sep) && str !== root); + if (!root) + return true; + let strParts = str.split(path.sep); + let rootParts = root.split(path.sep); + let i=0; + for (; i { diff --git a/packages/node-patches/test/fs/escape.ts b/packages/node-patches/test/fs/escape.ts index 96df7d54d2..c76b2fcae5 100644 --- a/packages/node-patches/test/fs/escape.ts +++ b/packages/node-patches/test/fs/escape.ts @@ -27,6 +27,21 @@ describe('escape function', () => { assert.ok(!isOutPath('/a/b', '/a/b/c/d')); }); + it('isOutPath with wildcard is correct', () => { + assert.ok(isOutPath('/a/*', '/a')); + assert.ok(!isOutPath('/a/*', '/a/c/b')); + assert.ok(!isOutPath('/a/*', '/a/b')); + assert.ok(!isOutPath('/a/*', '/a/b/c/d')); + assert.ok(isOutPath('/a/*', '/b/b')); + assert.ok(isOutPath('/a/*', '/b/b/c/d')); + assert.ok(isOutPath('/*/b', '/a')); + assert.ok(isOutPath('/*/b', '/a/c/b')); + assert.ok(!isOutPath('/*/b', '/a/b')); + assert.ok(!isOutPath('/*/b', '/a/b/c/d')); + assert.ok(!isOutPath('/*/b', '/b/b')); + assert.ok(!isOutPath('/*/b', '/b/b/c/d')); + }); + it('isEscape is correct', () => { const roots = [ '/a/b',