diff --git a/pre_commit/languages/node.py b/pre_commit/languages/node.py index 5f5d9fdd..4779db50 100644 --- a/pre_commit/languages/node.py +++ b/pre_commit/languages/node.py @@ -7,6 +7,7 @@ import sys from pre_commit.envcontext import envcontext from pre_commit.envcontext import Var from pre_commit.languages import helpers +from pre_commit.languages.python import bin_dir from pre_commit.util import clean_path_on_failure from pre_commit.util import cmd_output from pre_commit.xargs import xargs @@ -17,10 +18,17 @@ get_default_version = helpers.basic_get_default_version healthy = helpers.basic_healthy -def get_env_patch(venv): # pragma: windows no cover +def _envdir(prefix, version): + directory = helpers.environment_dir(ENVIRONMENT_DIR, version) + return prefix.path(directory) + + +def get_env_patch(venv): if sys.platform == 'cygwin': # pragma: no cover _, win_venv, _ = cmd_output('cygpath', '-w', venv) install_prefix = r'{}\bin'.format(win_venv.strip()) + elif sys.platform == 'win32': # pragma: no cover + install_prefix = bin_dir(venv) else: install_prefix = venv return ( @@ -28,29 +36,26 @@ def get_env_patch(venv): # pragma: windows no cover ('NPM_CONFIG_PREFIX', install_prefix), ('npm_config_prefix', install_prefix), ('NODE_PATH', os.path.join(venv, 'lib', 'node_modules')), - ('PATH', (os.path.join(venv, 'bin'), os.pathsep, Var('PATH'))), + ('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))), ) @contextlib.contextmanager -def in_env(prefix, language_version): # pragma: windows no cover - envdir = prefix.path( - helpers.environment_dir(ENVIRONMENT_DIR, language_version), - ) - with envcontext(get_env_patch(envdir)): +def in_env(prefix, language_version): + with envcontext(get_env_patch(_envdir(prefix, language_version))): yield -def install_environment( - prefix, version, additional_dependencies, -): # pragma: windows no cover +def install_environment(prefix, version, additional_dependencies): additional_dependencies = tuple(additional_dependencies) assert prefix.exists('package.json') - directory = helpers.environment_dir(ENVIRONMENT_DIR, version) + envdir = _envdir(prefix, version) - env_dir = prefix.path(directory) - with clean_path_on_failure(env_dir): - cmd = [sys.executable, '-m', 'nodeenv', '--prebuilt', env_dir] + # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx?f=255&MSPPError=-2147217396#maxpath + if sys.platform == 'win32': # pragma: no cover + envdir = '\\\\?\\' + os.path.normpath(envdir) + with clean_path_on_failure(envdir): + cmd = [sys.executable, '-m', 'nodeenv', '--prebuilt', envdir] if version != 'default': cmd.extend(['-n', version]) cmd_output(*cmd) @@ -62,6 +67,6 @@ def install_environment( ) -def run_hook(prefix, hook, file_args): # pragma: windows no cover +def run_hook(prefix, hook, file_args): with in_env(prefix, hook['language_version']): return xargs(helpers.to_cmd(hook), file_args) diff --git a/testing/resources/node_0_11_8_hooks_repo/.pre-commit-hooks.yaml b/testing/resources/node_0_11_8_hooks_repo/.pre-commit-hooks.yaml deleted file mode 100644 index 005a1e3b..00000000 --- a/testing/resources/node_0_11_8_hooks_repo/.pre-commit-hooks.yaml +++ /dev/null @@ -1,6 +0,0 @@ -- id: node-11-8-hook - name: Node 0.11.8 hook - entry: node-11-8-hook - language: node - language_version: 0.11.8 - files: \.js$ diff --git a/testing/resources/node_0_11_8_hooks_repo/package.json b/testing/resources/node_0_11_8_hooks_repo/package.json deleted file mode 100644 index 911a3ed9..00000000 --- a/testing/resources/node_0_11_8_hooks_repo/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "name": "node-11-8-hook", - "version": "0.0.1", - "bin": {"node-11-8-hook": "./bin/main.js"} -} diff --git a/testing/resources/node_versioned_hooks_repo/.pre-commit-hooks.yaml b/testing/resources/node_versioned_hooks_repo/.pre-commit-hooks.yaml new file mode 100644 index 00000000..e7ad5ea7 --- /dev/null +++ b/testing/resources/node_versioned_hooks_repo/.pre-commit-hooks.yaml @@ -0,0 +1,6 @@ +- id: versioned-node-hook + name: Versioned node hook + entry: versioned-node-hook + language: node + language_version: 9.3.0 + files: \.js$ diff --git a/testing/resources/node_0_11_8_hooks_repo/bin/main.js b/testing/resources/node_versioned_hooks_repo/bin/main.js similarity index 100% rename from testing/resources/node_0_11_8_hooks_repo/bin/main.js rename to testing/resources/node_versioned_hooks_repo/bin/main.js diff --git a/testing/resources/node_versioned_hooks_repo/package.json b/testing/resources/node_versioned_hooks_repo/package.json new file mode 100644 index 00000000..18c7787c --- /dev/null +++ b/testing/resources/node_versioned_hooks_repo/package.json @@ -0,0 +1,5 @@ +{ + "name": "versioned-node-hook", + "version": "0.0.1", + "bin": {"versioned-node-hook": "./bin/main.js"} +} diff --git a/testing/util.py b/testing/util.py index 357968fb..aa4b76f5 100644 --- a/testing/util.py +++ b/testing/util.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import os.path +import sys import pytest @@ -42,9 +43,21 @@ xfailif_windows_no_ruby = pytest.mark.xfail( reason='Ruby support not yet implemented on windows.', ) -xfailif_windows_no_node = pytest.mark.xfail( - os.name == 'nt', - reason='Node support not yet implemented on windows.', + +def broken_deep_listdir(): # pragma: no cover (platform specific) + if sys.platform != 'win32': + return False + try: + os.listdir(str('\\\\?\\') + os.path.abspath(str('.'))) + except OSError: + return True + else: + return False + + +xfailif_broken_deep_listdir = pytest.mark.xfail( + broken_deep_listdir(), + reason='Node on windows requires deep listdir', ) diff --git a/tests/repository_test.py b/tests/repository_test.py index 1c518eba..c160581e 100644 --- a/tests/repository_test.py +++ b/tests/repository_test.py @@ -31,8 +31,8 @@ from testing.fixtures import modify_manifest from testing.util import get_resource_path from testing.util import skipif_cant_run_docker from testing.util import skipif_cant_run_swift +from testing.util import xfailif_broken_deep_listdir from testing.util import xfailif_no_pcre_support -from testing.util import xfailif_windows_no_node from testing.util import xfailif_windows_no_ruby @@ -186,7 +186,7 @@ def test_run_a_docker_image_hook(tempdir_factory, store, hook_id): ) -@xfailif_windows_no_node +@xfailif_broken_deep_listdir @pytest.mark.integration def test_run_a_node_hook(tempdir_factory, store): _test_hook_repo( @@ -195,12 +195,12 @@ def test_run_a_node_hook(tempdir_factory, store): ) -@xfailif_windows_no_node +@xfailif_broken_deep_listdir @pytest.mark.integration def test_run_versioned_node_hook(tempdir_factory, store): _test_hook_repo( - tempdir_factory, store, 'node_0_11_8_hooks_repo', - 'node-11-8-hook', [os.devnull], b'v0.11.8\nHello World\n', + tempdir_factory, store, 'node_versioned_hooks_repo', + 'versioned-node-hook', [os.devnull], b'v9.3.0\nHello World\n', ) @@ -505,7 +505,7 @@ def test_additional_ruby_dependencies_installed( assert 'tins' in output -@xfailif_windows_no_node +@xfailif_broken_deep_listdir @pytest.mark.integration def test_additional_node_dependencies_installed( tempdir_factory, store, diff --git a/tox.ini b/tox.ini index 872b4c35..a254c369 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,7 @@ envlist = py27,py35,py36,pypy [testenv] deps = -rrequirements-dev.txt -passenv = GOROOT HOME HOMEPATH PROGRAMDATA TERM +passenv = GOROOT HOME HOMEPATH PROCESSOR_ARCHITECTURE PROGRAMDATA TERM commands = coverage erase coverage run -m pytest {posargs:tests}