From 6e46d6ae75072ae2266408868cff07338507359f Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 11 Jan 2018 22:25:39 -0800 Subject: [PATCH] Support node on windows with long path hack --- pre_commit/languages/node.py | 35 ++++++++++++++++++++--------------- testing/util.py | 19 ++++++++++++++++--- tests/repository_test.py | 8 ++++---- tox.ini | 2 +- 4 files changed, 41 insertions(+), 23 deletions(-) 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/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 0e43e728..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,7 +195,7 @@ 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( @@ -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}