From 06f907153d710c6497fb4e55e375f464416e1412 Mon Sep 17 00:00:00 2001 From: Tejesh Vaish <188414140+tridev-tej@users.noreply.github.com> Date: Mon, 26 Oct 2026 01:25:18 +0400 Subject: [PATCH 1/2] fix(nodeenv): escape glob characters in prebuilt paths --- CHANGES | 3 +++ nodeenv.py | 6 ++--- tests/nodeenv_test.py | 52 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index bbb0888..40e563d 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,9 @@ Version [unreleased] compatibility paths. Old Node.js source builds still require Python 2.7 as their build interpreter `#436 `_ +- Prebuilt node.js installs treat environment paths and versions literally, + with a trailing ``-`` separating the version from the platform suffix + `#441 `_ - `--source` builds of node.js 13 and later no longer need a `python2` command; 8.8 to 12.x still need one but point `python` at it on their own, so the `python` to python2 link in `PATH` is only set up for older diff --git a/nodeenv.py b/nodeenv.py index 118ce53..c92acfc 100644 --- a/nodeenv.py +++ b/nodeenv.py @@ -1018,9 +1018,9 @@ def copy_node_from_prebuilt(env_dir, src_dir, node_version): else: dest = env_dir - src_folder_tpl = src_dir + '/node-v%s-*' % node_version - src_folder, = glob.glob(src_folder_tpl) - copytree(src_folder, dest, True) + src_folder_tpl = glob.escape('node-v%s-' % node_version) + '*' + src_folder, = glob.glob(src_folder_tpl, root_dir=src_dir) + copytree(join(src_dir, src_folder), dest, True) if is_CYGWIN: for filename in ('npm', 'npx', 'node.exe'): diff --git a/tests/nodeenv_test.py b/tests/nodeenv_test.py index 0b90187..4245f69 100644 --- a/tests/nodeenv_test.py +++ b/tests/nodeenv_test.py @@ -1000,6 +1000,58 @@ def test_copytree_keeps_existing_symlink(tmpdir): assert os.readlink(tmpdir.join('dst', 'bin', 'npm').strpath) == target +@pytest.mark.parametrize( + 'dirname', + ( + 'proj[1]', + pytest.param('proj*', marks=pytest.mark.skipif( + nodeenv.is_WIN, reason='* is not a valid Windows filename')), + pytest.param('proj?', marks=pytest.mark.skipif( + nodeenv.is_WIN, reason='? is not a valid Windows filename')), + ), +) +def test_copy_node_from_prebuilt_literal_path(tmpdir, monkeypatch, dirname): + monkeypatch.setattr(nodeenv, 'is_WIN', False) + monkeypatch.setattr(nodeenv, 'is_CYGWIN', False) + env_dir = tmpdir.join(dirname, 'env').ensure(dir=True) + src_dir = env_dir.join('src').ensure(dir=True) + archive = 'node-v18.0.0-test-platform' + source = src_dir.join(archive, 'bin', 'node').ensure() + source.write('literal source') + env_dir.join('keep.txt').write('keep this file') + decoy = tmpdir.join('proj1', 'env', 'src', archive, 'bin', 'node').ensure() + decoy.write('neighbor source') + + nodeenv.copy_node_from_prebuilt(env_dir.strpath, src_dir.strpath, '18.0.0') + + assert env_dir.join('bin', 'node').read() == 'literal source' + assert env_dir.join('keep.txt').read() == 'keep this file' + assert source.read() == 'literal source' + assert decoy.read() == 'neighbor source' + + +def test_copy_node_from_prebuilt_literal_version(tmp_path, monkeypatch): + monkeypatch.setattr(nodeenv, 'is_WIN', False) + monkeypatch.setattr(nodeenv, 'is_CYGWIN', False) + version = '18.0.0+build[1]' + env_dir = tmp_path / 'env' + env_dir.mkdir() + src_dir = tmp_path / 'src' + archive = src_dir / ('node-v%s-linux-x64' % version) + neighbor = src_dir / 'node-v18.0.0+build1-linux-x64' + for directory, contents in ( + (archive, 'literal version'), + (neighbor, 'neighbor version')): + directory.mkdir(parents=True) + (directory / 'payload').write_text(contents, encoding='utf-8') + + nodeenv.copy_node_from_prebuilt(str(env_dir), str(src_dir), version) + + assert (env_dir / 'payload').read_text() == 'literal version' + assert (archive / 'payload').read_text() == 'literal version' + assert (neighbor / 'payload').read_text() == 'neighbor version' + + def test_parse_version(): assert nodeenv.parse_version("v21.7") == (21, 7) assert nodeenv.parse_version("v21.7.3") == (21, 7, 3) From 77c49a23f9238cd552d842f02b0b24e3063692b7 Mon Sep 17 00:00:00 2001 From: Eugene Kalinin Date: Sat, 31 Oct 2026 14:06:47 +0300 Subject: [PATCH 2/2] docs(changes): reword the #441 entry and move it to the top --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 78a9d02..d96adbf 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ Nodeenv changelog Version [unreleased] -------------------- +- `--prebuilt` installs no longer fail when the environment path or the + node.js version contains `[`, `*` or `?` + `#441 `_ - On Windows, Cygwin and MSYS the node.js zip archive is extracted 32 files at a time instead of one by one, so a security scanner that checks every new file no longer makes the install wait for each file in turn. @@ -35,9 +38,6 @@ Version [unreleased] compatibility paths. Old Node.js source builds still require Python 2.7 as their build interpreter `#436 `_ -- Prebuilt node.js installs treat environment paths and versions literally, - with a trailing ``-`` separating the version from the platform suffix - `#441 `_ - `--source` builds of node.js 13 and later no longer need a `python2` command; 8.8 to 12.x still need one but point `python` at it on their own, so the `python` to python2 link in `PATH` is only set up for older