Merging upstream version 2.21.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
08d9b01ff9
commit
54d13e9018
48 changed files with 534 additions and 210 deletions
2
.github/ISSUE_TEMPLATE/bug.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug.yaml
vendored
|
@ -12,7 +12,7 @@ body:
|
||||||
- type: input
|
- type: input
|
||||||
id: search
|
id: search
|
||||||
attributes:
|
attributes:
|
||||||
label: search tried in the issue tracker
|
label: search you tried in the issue tracker
|
||||||
placeholder: ...
|
placeholder: ...
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.3.0
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
|
@ -10,35 +10,35 @@ repos:
|
||||||
- id: name-tests-test
|
- id: name-tests-test
|
||||||
- id: requirements-txt-fixer
|
- id: requirements-txt-fixer
|
||||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||||
rev: v1.20.1
|
rev: v2.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: setup-cfg-fmt
|
- id: setup-cfg-fmt
|
||||||
- repo: https://github.com/asottile/reorder_python_imports
|
- repo: https://github.com/asottile/reorder_python_imports
|
||||||
rev: v3.3.0
|
rev: v3.9.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: reorder-python-imports
|
- id: reorder-python-imports
|
||||||
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
|
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
|
||||||
args: [--py37-plus, --add-import, 'from __future__ import annotations']
|
args: [--py37-plus, --add-import, 'from __future__ import annotations']
|
||||||
- repo: https://github.com/asottile/add-trailing-comma
|
- repo: https://github.com/asottile/add-trailing-comma
|
||||||
rev: v2.2.3
|
rev: v2.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: add-trailing-comma
|
- id: add-trailing-comma
|
||||||
args: [--py36-plus]
|
args: [--py36-plus]
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v2.34.0
|
rev: v3.3.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py37-plus]
|
args: [--py37-plus]
|
||||||
- repo: https://github.com/pre-commit/mirrors-autopep8
|
- repo: https://github.com/pre-commit/mirrors-autopep8
|
||||||
rev: v1.6.0
|
rev: v2.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: autopep8
|
- id: autopep8
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 4.0.1
|
rev: 6.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||||
rev: v0.961
|
rev: v0.991
|
||||||
hooks:
|
hooks:
|
||||||
- id: mypy
|
- id: mypy
|
||||||
additional_dependencies: [types-all]
|
additional_dependencies: [types-all]
|
||||||
|
|
40
CHANGELOG.md
40
CHANGELOG.md
|
@ -1,3 +1,43 @@
|
||||||
|
2.21.0 - 2022-12-25
|
||||||
|
===================
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Require new-enough virtualenv to prevent 3.10 breakage
|
||||||
|
- #2467 PR by @asottile.
|
||||||
|
- Respect aliases with `SKIP` for environment install.
|
||||||
|
- #2480 PR by @kmARC.
|
||||||
|
- #2478 issue by @kmARC.
|
||||||
|
- Allow `pre-commit run --files` against unmerged paths.
|
||||||
|
- #2484 PR by @asottile.
|
||||||
|
- Also apply regex warnings to `repo: local` hooks.
|
||||||
|
- #2524 PR by @chrisRedwine.
|
||||||
|
- #2521 issue by @asottile.
|
||||||
|
- `rust` is now a "first class" language -- supporting `language_version` and
|
||||||
|
installation when not present.
|
||||||
|
- #2534 PR by @Holzhaus.
|
||||||
|
- `r` now uses more-reliable binary installation.
|
||||||
|
- #2460 PR by @lorenzwalthert.
|
||||||
|
- `GIT_ALLOW_PROTOCOL` is now passed through for git operations.
|
||||||
|
- #2555 PR by @asottile.
|
||||||
|
- `GIT_ASKPASS` is now passed through for git operations.
|
||||||
|
- #2564 PR by @mattp-.
|
||||||
|
- Remove `toml` dependency by using `cargo add` directly.
|
||||||
|
- #2568 PR by @m-rsha.
|
||||||
|
- Support `dotnet` hooks which have dotted prefixes.
|
||||||
|
- #2641 PR by @rkm.
|
||||||
|
- #2629 issue by @rkm.
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Properly adjust `--commit-msg-filename` if run from a sub directory.
|
||||||
|
- #2459 PR by @asottile.
|
||||||
|
- Simplify `--intent-to-add` detection by using `git diff`.
|
||||||
|
- #2580 PR by @m-rsha.
|
||||||
|
- Fix `R.exe` selection on windows.
|
||||||
|
- #2605 PR by @lorenzwalthert.
|
||||||
|
- #2599 issue by @SInginc.
|
||||||
|
- Skip default `nuget` source when installing `dotnet` packages.
|
||||||
|
- #2642 PR by @rkm.
|
||||||
|
|
||||||
2.20.0 - 2022-07-10
|
2.20.0 - 2022-07-10
|
||||||
===================
|
===================
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
- The complete test suite depends on having at least the following installed
|
- The complete test suite depends on having at least the following installed
|
||||||
(possibly not a complete list)
|
(possibly not a complete list)
|
||||||
- git (Version 2.24.0 or above is required to run pre-merge-commit tests)
|
- git (Version 2.24.0 or above is required to run pre-merge-commit tests)
|
||||||
- python2 (Required by a test which checks different python versions)
|
|
||||||
- python3 (Required by a test which checks different python versions)
|
- python3 (Required by a test which checks different python versions)
|
||||||
- tox (or virtualenv)
|
- tox (or virtualenv)
|
||||||
- ruby + gem
|
- ruby + gem
|
||||||
|
@ -65,9 +64,9 @@ to implement. The current implemented languages are at varying levels:
|
||||||
- 0th class - pre-commit does not require any dependencies for these languages
|
- 0th class - pre-commit does not require any dependencies for these languages
|
||||||
as they're not actually languages (current examples: fail, pygrep)
|
as they're not actually languages (current examples: fail, pygrep)
|
||||||
- 1st class - pre-commit will bootstrap a full interpreter requiring nothing to
|
- 1st class - pre-commit will bootstrap a full interpreter requiring nothing to
|
||||||
be installed globally (current examples: node, ruby)
|
be installed globally (current examples: node, ruby, rust)
|
||||||
- 2nd class - pre-commit requires the user to install the language globally but
|
- 2nd class - pre-commit requires the user to install the language globally but
|
||||||
will install tools in an isolated fashion (current examples: python, go, rust,
|
will install tools in an isolated fashion (current examples: python, go,
|
||||||
swift, docker).
|
swift, docker).
|
||||||
- 3rd class - pre-commit requires the user to install both the tool and the
|
- 3rd class - pre-commit requires the user to install both the tool and the
|
||||||
language globally (current examples: script, system)
|
language globally (current examples: script, system)
|
||||||
|
|
|
@ -17,6 +17,8 @@ jobs:
|
||||||
parameters:
|
parameters:
|
||||||
toxenvs: [py37]
|
toxenvs: [py37]
|
||||||
os: windows
|
os: windows
|
||||||
|
additional_variables:
|
||||||
|
TEMP: C:\Temp
|
||||||
pre_test:
|
pre_test:
|
||||||
- task: UseRubyVersion@0
|
- task: UseRubyVersion@0
|
||||||
- powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts"
|
- powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts"
|
||||||
|
|
|
@ -298,6 +298,14 @@ CONFIG_HOOK_DICT = cfgv.Map(
|
||||||
OptionalSensibleRegexAtHook('files', cfgv.check_string),
|
OptionalSensibleRegexAtHook('files', cfgv.check_string),
|
||||||
OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
|
OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
|
||||||
)
|
)
|
||||||
|
LOCAL_HOOK_DICT = cfgv.Map(
|
||||||
|
'Hook', 'id',
|
||||||
|
|
||||||
|
*MANIFEST_HOOK_DICT.items,
|
||||||
|
|
||||||
|
OptionalSensibleRegexAtHook('files', cfgv.check_string),
|
||||||
|
OptionalSensibleRegexAtHook('exclude', cfgv.check_string),
|
||||||
|
)
|
||||||
CONFIG_REPO_DICT = cfgv.Map(
|
CONFIG_REPO_DICT = cfgv.Map(
|
||||||
'Repository', 'repo',
|
'Repository', 'repo',
|
||||||
|
|
||||||
|
@ -308,7 +316,7 @@ CONFIG_REPO_DICT = cfgv.Map(
|
||||||
'repo', cfgv.NotIn(LOCAL, META),
|
'repo', cfgv.NotIn(LOCAL, META),
|
||||||
),
|
),
|
||||||
cfgv.ConditionalRecurse(
|
cfgv.ConditionalRecurse(
|
||||||
'hooks', cfgv.Array(MANIFEST_HOOK_DICT),
|
'hooks', cfgv.Array(LOCAL_HOOK_DICT),
|
||||||
'repo', LOCAL,
|
'repo', LOCAL,
|
||||||
),
|
),
|
||||||
cfgv.ConditionalRecurse(
|
cfgv.ConditionalRecurse(
|
||||||
|
|
|
@ -263,7 +263,7 @@ def _all_filenames(args: argparse.Namespace) -> Collection[str]:
|
||||||
|
|
||||||
def _get_diff() -> bytes:
|
def _get_diff() -> bytes:
|
||||||
_, out, _ = cmd_output_b(
|
_, out, _ = cmd_output_b(
|
||||||
'git', 'diff', '--no-ext-diff', '--ignore-submodules', retcode=None,
|
'git', 'diff', '--no-ext-diff', '--ignore-submodules', check=False,
|
||||||
)
|
)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ def _has_unmerged_paths() -> bool:
|
||||||
def _has_unstaged_config(config_file: str) -> bool:
|
def _has_unstaged_config(config_file: str) -> bool:
|
||||||
retcode, _, _ = cmd_output_b(
|
retcode, _, _ = cmd_output_b(
|
||||||
'git', 'diff', '--no-ext-diff', '--exit-code', config_file,
|
'git', 'diff', '--no-ext-diff', '--exit-code', config_file,
|
||||||
retcode=None,
|
check=False,
|
||||||
)
|
)
|
||||||
# be explicit, other git errors don't mean it has an unstaged config.
|
# be explicit, other git errors don't mean it has an unstaged config.
|
||||||
return retcode == 1
|
return retcode == 1
|
||||||
|
@ -333,7 +333,7 @@ def run(
|
||||||
stash = not args.all_files and not args.files
|
stash = not args.all_files and not args.files
|
||||||
|
|
||||||
# Check if we have unresolved merge conflict files and fail fast.
|
# Check if we have unresolved merge conflict files and fail fast.
|
||||||
if _has_unmerged_paths():
|
if stash and _has_unmerged_paths():
|
||||||
logger.error('Unmerged files. Resolve before committing.')
|
logger.error('Unmerged files. Resolve before committing.')
|
||||||
return 1
|
return 1
|
||||||
if bool(args.from_ref) != bool(args.to_ref):
|
if bool(args.from_ref) != bool(args.to_ref):
|
||||||
|
@ -420,7 +420,11 @@ def run(
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
skips = _get_skips(environ)
|
skips = _get_skips(environ)
|
||||||
to_install = [hook for hook in hooks if hook.id not in skips]
|
to_install = [
|
||||||
|
hook
|
||||||
|
for hook in hooks
|
||||||
|
if hook.id not in skips and hook.alias not in skips
|
||||||
|
]
|
||||||
install_hook_envs(to_install, store)
|
install_hook_envs(to_install, store)
|
||||||
|
|
||||||
return _run_hooks(config, hooks, skips, args)
|
return _run_hooks(config, hooks, skips, args)
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
# TODO: maybe `git ls-remote git://github.com/pre-commit/pre-commit-hooks` to
|
|
||||||
# determine the latest revision? This adds ~200ms from my tests (and is
|
|
||||||
# significantly faster than https:// or http://). For now, periodically
|
|
||||||
# manually updating the revision is fine.
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
SAMPLE_CONFIG = '''\
|
SAMPLE_CONFIG = '''\
|
||||||
# See https://pre-commit.com for more information
|
# See https://pre-commit.com for more information
|
||||||
|
|
|
@ -25,7 +25,7 @@ def _log_and_exit(
|
||||||
error_msg = f'{msg}: {type(exc).__name__}: '.encode() + force_bytes(exc)
|
error_msg = f'{msg}: {type(exc).__name__}: '.encode() + force_bytes(exc)
|
||||||
output.write_line_b(error_msg)
|
output.write_line_b(error_msg)
|
||||||
|
|
||||||
_, git_version_b, _ = cmd_output_b('git', '--version', retcode=None)
|
_, git_version_b, _ = cmd_output_b('git', '--version', check=False)
|
||||||
git_version = git_version_b.decode(errors='backslashreplace').rstrip()
|
git_version = git_version_b.decode(errors='backslashreplace').rstrip()
|
||||||
|
|
||||||
storedir = Store().directory
|
storedir = Store().directory
|
||||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||||
import logging
|
import logging
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
from typing import MutableMapping
|
from typing import Mapping
|
||||||
|
|
||||||
from pre_commit.errors import FatalError
|
from pre_commit.errors import FatalError
|
||||||
from pre_commit.util import CalledProcessError
|
from pre_commit.util import CalledProcessError
|
||||||
|
@ -24,9 +24,7 @@ def zsplit(s: str) -> list[str]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def no_git_env(
|
def no_git_env(_env: Mapping[str, str] | None = None) -> dict[str, str]:
|
||||||
_env: MutableMapping[str, str] | None = None,
|
|
||||||
) -> dict[str, str]:
|
|
||||||
# Too many bugs dealing with environment variables and GIT:
|
# Too many bugs dealing with environment variables and GIT:
|
||||||
# https://github.com/pre-commit/pre-commit/issues/300
|
# https://github.com/pre-commit/pre-commit/issues/300
|
||||||
# In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
|
# In git 2.6.3 (maybe others), git exports GIT_WORK_TREE while running
|
||||||
|
@ -44,6 +42,8 @@ def no_git_env(
|
||||||
'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',
|
'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',
|
||||||
'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',
|
'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',
|
||||||
'GIT_HTTP_PROXY_AUTHMETHOD',
|
'GIT_HTTP_PROXY_AUTHMETHOD',
|
||||||
|
'GIT_ALLOW_PROTOCOL',
|
||||||
|
'GIT_ASKPASS',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,18 +150,10 @@ def get_staged_files(cwd: str | None = None) -> list[str]:
|
||||||
|
|
||||||
def intent_to_add_files() -> list[str]:
|
def intent_to_add_files() -> list[str]:
|
||||||
_, stdout, _ = cmd_output(
|
_, stdout, _ = cmd_output(
|
||||||
'git', 'status', '--ignore-submodules', '--porcelain', '-z',
|
'git', 'diff', '--no-ext-diff', '--ignore-submodules',
|
||||||
|
'--diff-filter=A', '--name-only', '-z',
|
||||||
)
|
)
|
||||||
parts = list(reversed(zsplit(stdout)))
|
return zsplit(stdout)
|
||||||
intent_to_add = []
|
|
||||||
while parts:
|
|
||||||
line = parts.pop()
|
|
||||||
status, filename = line[:3], line[3:]
|
|
||||||
if status[0] in {'C', 'R'}: # renames / moves have an additional arg
|
|
||||||
parts.pop()
|
|
||||||
if status[1] == 'A':
|
|
||||||
intent_to_add.append(filename)
|
|
||||||
return intent_to_add
|
|
||||||
|
|
||||||
|
|
||||||
def get_all_files() -> list[str]:
|
def get_all_files() -> list[str]:
|
||||||
|
@ -187,11 +179,11 @@ def head_rev(remote: str) -> str:
|
||||||
|
|
||||||
def has_diff(*args: str, repo: str = '.') -> bool:
|
def has_diff(*args: str, repo: str = '.') -> bool:
|
||||||
cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)
|
cmd = ('git', 'diff', '--quiet', '--no-ext-diff', *args)
|
||||||
return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1
|
return cmd_output_b(*cmd, cwd=repo, check=False)[0] == 1
|
||||||
|
|
||||||
|
|
||||||
def has_core_hookpaths_set() -> bool:
|
def has_core_hookpaths_set() -> bool:
|
||||||
_, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', retcode=None)
|
_, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', check=False)
|
||||||
return bool(out.strip())
|
return bool(out.strip())
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,10 @@ from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os.path
|
import os.path
|
||||||
|
import re
|
||||||
|
import tempfile
|
||||||
|
import xml.etree.ElementTree
|
||||||
|
import zipfile
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
|
@ -35,6 +39,22 @@ def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def _nuget_config_no_sources() -> Generator[str, None, None]:
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
nuget_config = os.path.join(tmpdir, 'nuget.config')
|
||||||
|
with open(nuget_config, 'w') as f:
|
||||||
|
f.write(
|
||||||
|
'<?xml version="1.0" encoding="utf-8"?>'
|
||||||
|
'<configuration>'
|
||||||
|
' <packageSources>'
|
||||||
|
' <clear />'
|
||||||
|
' </packageSources>'
|
||||||
|
'</configuration>',
|
||||||
|
)
|
||||||
|
yield nuget_config
|
||||||
|
|
||||||
|
|
||||||
def install_environment(
|
def install_environment(
|
||||||
prefix: Prefix,
|
prefix: Prefix,
|
||||||
version: str,
|
version: str,
|
||||||
|
@ -57,19 +77,40 @@ def install_environment(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Determine tool from the packaged file <tool_name>.<version>.nupkg
|
nupkg_dir = prefix.path(build_dir)
|
||||||
build_outputs = os.listdir(os.path.join(prefix.prefix_dir, build_dir))
|
nupkgs = [x for x in os.listdir(nupkg_dir) if x.endswith('.nupkg')]
|
||||||
for output in build_outputs:
|
|
||||||
tool_name = output.split('.')[0]
|
if not nupkgs:
|
||||||
|
raise AssertionError('could not find any build outputs to install')
|
||||||
|
|
||||||
|
for nupkg in nupkgs:
|
||||||
|
with zipfile.ZipFile(os.path.join(nupkg_dir, nupkg)) as f:
|
||||||
|
nuspec, = (x for x in f.namelist() if x.endswith('.nuspec'))
|
||||||
|
with f.open(nuspec) as spec:
|
||||||
|
tree = xml.etree.ElementTree.parse(spec)
|
||||||
|
|
||||||
|
namespace = re.match(r'{.*}', tree.getroot().tag)
|
||||||
|
if not namespace:
|
||||||
|
raise AssertionError('could not parse namespace from nuspec')
|
||||||
|
|
||||||
|
tool_id_element = tree.find(f'.//{namespace[0]}id')
|
||||||
|
if tool_id_element is None:
|
||||||
|
raise AssertionError('expected to find an "id" element')
|
||||||
|
|
||||||
|
tool_id = tool_id_element.text
|
||||||
|
if not tool_id:
|
||||||
|
raise AssertionError('"id" element missing tool name')
|
||||||
|
|
||||||
# Install to bin dir
|
# Install to bin dir
|
||||||
|
with _nuget_config_no_sources() as nuget_config:
|
||||||
helpers.run_setup_cmd(
|
helpers.run_setup_cmd(
|
||||||
prefix,
|
prefix,
|
||||||
(
|
(
|
||||||
'dotnet', 'tool', 'install',
|
'dotnet', 'tool', 'install',
|
||||||
|
'--configfile', nuget_config,
|
||||||
'--tool-path', os.path.join(envdir, BIN_DIR),
|
'--tool-path', os.path.join(envdir, BIN_DIR),
|
||||||
'--add-source', build_dir,
|
'--add-source', build_dir,
|
||||||
tool_name,
|
tool_id,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ def in_env(
|
||||||
|
|
||||||
def health_check(prefix: Prefix, language_version: str) -> str | None:
|
def health_check(prefix: Prefix, language_version: str) -> str | None:
|
||||||
with in_env(prefix, language_version):
|
with in_env(prefix, language_version):
|
||||||
retcode, _, _ = cmd_output_b('node', '--version', retcode=None)
|
retcode, _, _ = cmd_output_b('node', '--version', check=False)
|
||||||
if retcode != 0: # pragma: win32 no cover
|
if retcode != 0: # pragma: win32 no cover
|
||||||
return f'`node --version` returned {retcode}'
|
return f'`node --version` returned {retcode}'
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -15,6 +15,7 @@ from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
from pre_commit.util import cmd_output_b
|
from pre_commit.util import cmd_output_b
|
||||||
|
from pre_commit.util import win_exe
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'renv'
|
ENVIRONMENT_DIR = 'renv'
|
||||||
RSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')
|
RSCRIPT_OPTS = ('--no-save', '--no-restore', '--no-site-file', '--no-environ')
|
||||||
|
@ -63,7 +64,7 @@ def _rscript_exec() -> str:
|
||||||
if r_home is None:
|
if r_home is None:
|
||||||
return 'Rscript'
|
return 'Rscript'
|
||||||
else:
|
else:
|
||||||
return os.path.join(r_home, 'bin', 'Rscript')
|
return os.path.join(r_home, 'bin', win_exe('Rscript'))
|
||||||
|
|
||||||
|
|
||||||
def _entry_validate(entry: Sequence[str]) -> None:
|
def _entry_validate(entry: Sequence[str]) -> None:
|
||||||
|
@ -158,7 +159,7 @@ def _inline_r_setup(code: str) -> str:
|
||||||
only be configured via R options once R has started. These are set here.
|
only be configured via R options once R has started. These are set here.
|
||||||
"""
|
"""
|
||||||
with_option = f"""\
|
with_option = f"""\
|
||||||
options(install.packages.compile.from.source = "never")
|
options(install.packages.compile.from.source = "never", pkgType = "binary")
|
||||||
{code}
|
{code}
|
||||||
"""
|
"""
|
||||||
return with_option
|
return with_option
|
||||||
|
|
|
@ -1,13 +1,17 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import functools
|
||||||
import os.path
|
import os.path
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import urllib.request
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import toml
|
|
||||||
|
|
||||||
import pre_commit.constants as C
|
import pre_commit.constants as C
|
||||||
|
from pre_commit import parse_shebang
|
||||||
from pre_commit.envcontext import envcontext
|
from pre_commit.envcontext import envcontext
|
||||||
from pre_commit.envcontext import PatchesT
|
from pre_commit.envcontext import PatchesT
|
||||||
from pre_commit.envcontext import Var
|
from pre_commit.envcontext import Var
|
||||||
|
@ -16,40 +20,105 @@ from pre_commit.languages import helpers
|
||||||
from pre_commit.prefix import Prefix
|
from pre_commit.prefix import Prefix
|
||||||
from pre_commit.util import clean_path_on_failure
|
from pre_commit.util import clean_path_on_failure
|
||||||
from pre_commit.util import cmd_output_b
|
from pre_commit.util import cmd_output_b
|
||||||
|
from pre_commit.util import make_executable
|
||||||
|
from pre_commit.util import win_exe
|
||||||
|
|
||||||
ENVIRONMENT_DIR = 'rustenv'
|
ENVIRONMENT_DIR = 'rustenv'
|
||||||
get_default_version = helpers.basic_get_default_version
|
|
||||||
health_check = helpers.basic_health_check
|
health_check = helpers.basic_health_check
|
||||||
|
|
||||||
|
|
||||||
def get_env_patch(target_dir: str) -> PatchesT:
|
@functools.lru_cache(maxsize=1)
|
||||||
|
def get_default_version() -> str:
|
||||||
|
# If rust is already installed, we can save a bunch of setup time by
|
||||||
|
# using the installed version.
|
||||||
|
#
|
||||||
|
# Just detecting the executable does not suffice, because if rustup is
|
||||||
|
# installed but no toolchain is available, then `cargo` exists but
|
||||||
|
# cannot be used without installing a toolchain first.
|
||||||
|
if cmd_output_b('cargo', '--version', check=False)[0] == 0:
|
||||||
|
return 'system'
|
||||||
|
else:
|
||||||
|
return C.DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
def _rust_toolchain(language_version: str) -> str:
|
||||||
|
"""Transform the language version into a rust toolchain version."""
|
||||||
|
if language_version == C.DEFAULT:
|
||||||
|
return 'stable'
|
||||||
|
else:
|
||||||
|
return language_version
|
||||||
|
|
||||||
|
|
||||||
|
def _envdir(prefix: Prefix, version: str) -> str:
|
||||||
|
directory = helpers.environment_dir(ENVIRONMENT_DIR, version)
|
||||||
|
return prefix.path(directory)
|
||||||
|
|
||||||
|
|
||||||
|
def get_env_patch(target_dir: str, version: str) -> PatchesT:
|
||||||
return (
|
return (
|
||||||
|
('CARGO_HOME', target_dir),
|
||||||
('PATH', (os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH'))),
|
('PATH', (os.path.join(target_dir, 'bin'), os.pathsep, Var('PATH'))),
|
||||||
|
# Only set RUSTUP_TOOLCHAIN if we don't want use the system's default
|
||||||
|
# toolchain
|
||||||
|
*(
|
||||||
|
(('RUSTUP_TOOLCHAIN', _rust_toolchain(version)),)
|
||||||
|
if version != 'system' else ()
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def in_env(prefix: Prefix) -> Generator[None, None, None]:
|
def in_env(
|
||||||
target_dir = prefix.path(
|
prefix: Prefix,
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
language_version: str,
|
||||||
)
|
) -> Generator[None, None, None]:
|
||||||
with envcontext(get_env_patch(target_dir)):
|
with envcontext(
|
||||||
|
get_env_patch(_envdir(prefix, language_version), language_version),
|
||||||
|
):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
|
|
||||||
def _add_dependencies(
|
def _add_dependencies(
|
||||||
cargo_toml_path: str,
|
prefix: Prefix,
|
||||||
additional_dependencies: set[str],
|
additional_dependencies: set[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
with open(cargo_toml_path, 'r+') as f:
|
crates = []
|
||||||
cargo_toml = toml.load(f)
|
|
||||||
cargo_toml.setdefault('dependencies', {})
|
|
||||||
for dep in additional_dependencies:
|
for dep in additional_dependencies:
|
||||||
name, _, spec = dep.partition(':')
|
name, _, spec = dep.partition(':')
|
||||||
cargo_toml['dependencies'][name] = spec or '*'
|
crate = f'{name}@{spec or "*"}'
|
||||||
f.seek(0)
|
crates.append(crate)
|
||||||
toml.dump(cargo_toml, f)
|
|
||||||
f.truncate()
|
helpers.run_setup_cmd(prefix, ('cargo', 'add', *crates))
|
||||||
|
|
||||||
|
|
||||||
|
def install_rust_with_toolchain(toolchain: str) -> None:
|
||||||
|
with tempfile.TemporaryDirectory() as rustup_dir:
|
||||||
|
with envcontext((('RUSTUP_HOME', rustup_dir),)):
|
||||||
|
# acquire `rustup` if not present
|
||||||
|
if parse_shebang.find_executable('rustup') is None:
|
||||||
|
# We did not detect rustup and need to download it first.
|
||||||
|
if sys.platform == 'win32': # pragma: win32 cover
|
||||||
|
url = 'https://win.rustup.rs/x86_64'
|
||||||
|
else: # pragma: win32 no cover
|
||||||
|
url = 'https://sh.rustup.rs'
|
||||||
|
|
||||||
|
resp = urllib.request.urlopen(url)
|
||||||
|
|
||||||
|
rustup_init = os.path.join(rustup_dir, win_exe('rustup-init'))
|
||||||
|
with open(rustup_init, 'wb') as f:
|
||||||
|
shutil.copyfileobj(resp, f)
|
||||||
|
make_executable(rustup_init)
|
||||||
|
|
||||||
|
# install rustup into `$CARGO_HOME/bin`
|
||||||
|
cmd_output_b(
|
||||||
|
rustup_init, '-y', '--quiet', '--no-modify-path',
|
||||||
|
'--default-toolchain', 'none',
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd_output_b(
|
||||||
|
'rustup', 'toolchain', 'install', '--no-self-update',
|
||||||
|
toolchain,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def install_environment(
|
def install_environment(
|
||||||
|
@ -57,10 +126,7 @@ def install_environment(
|
||||||
version: str,
|
version: str,
|
||||||
additional_dependencies: Sequence[str],
|
additional_dependencies: Sequence[str],
|
||||||
) -> None:
|
) -> None:
|
||||||
helpers.assert_version_default('rust', version)
|
directory = _envdir(prefix, version)
|
||||||
directory = prefix.path(
|
|
||||||
helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT),
|
|
||||||
)
|
|
||||||
|
|
||||||
# There are two cases where we might want to specify more dependencies:
|
# There are two cases where we might want to specify more dependencies:
|
||||||
# as dependencies for the library being built, and as binary packages
|
# as dependencies for the library being built, and as binary packages
|
||||||
|
@ -77,19 +143,23 @@ def install_environment(
|
||||||
}
|
}
|
||||||
lib_deps = set(additional_dependencies) - cli_deps
|
lib_deps = set(additional_dependencies) - cli_deps
|
||||||
|
|
||||||
if len(lib_deps) > 0:
|
|
||||||
_add_dependencies(prefix.path('Cargo.toml'), lib_deps)
|
|
||||||
|
|
||||||
with clean_path_on_failure(directory):
|
with clean_path_on_failure(directory):
|
||||||
packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
|
packages_to_install: set[tuple[str, ...]] = {('--path', '.')}
|
||||||
for cli_dep in cli_deps:
|
for cli_dep in cli_deps:
|
||||||
cli_dep = cli_dep[len('cli:'):]
|
cli_dep = cli_dep[len('cli:'):]
|
||||||
package, _, version = cli_dep.partition(':')
|
package, _, crate_version = cli_dep.partition(':')
|
||||||
if version != '':
|
if crate_version != '':
|
||||||
packages_to_install.add((package, '--version', version))
|
packages_to_install.add((package, '--version', crate_version))
|
||||||
else:
|
else:
|
||||||
packages_to_install.add((package,))
|
packages_to_install.add((package,))
|
||||||
|
|
||||||
|
with in_env(prefix, version):
|
||||||
|
if version != 'system':
|
||||||
|
install_rust_with_toolchain(_rust_toolchain(version))
|
||||||
|
|
||||||
|
if len(lib_deps) > 0:
|
||||||
|
_add_dependencies(prefix, lib_deps)
|
||||||
|
|
||||||
for args in packages_to_install:
|
for args in packages_to_install:
|
||||||
cmd_output_b(
|
cmd_output_b(
|
||||||
'cargo', 'install', '--bins', '--root', directory, *args,
|
'cargo', 'install', '--bins', '--root', directory, *args,
|
||||||
|
@ -102,5 +172,5 @@ def run_hook(
|
||||||
file_args: Sequence[str],
|
file_args: Sequence[str],
|
||||||
color: bool,
|
color: bool,
|
||||||
) -> tuple[int, bytes]:
|
) -> tuple[int, bytes]:
|
||||||
with in_env(hook.prefix):
|
with in_env(hook.prefix, hook.language_version):
|
||||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
||||||
|
|
|
@ -155,6 +155,10 @@ def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
|
||||||
args.config = os.path.abspath(args.config)
|
args.config = os.path.abspath(args.config)
|
||||||
if args.command in {'run', 'try-repo'}:
|
if args.command in {'run', 'try-repo'}:
|
||||||
args.files = [os.path.abspath(filename) for filename in args.files]
|
args.files = [os.path.abspath(filename) for filename in args.files]
|
||||||
|
if args.commit_msg_filename is not None:
|
||||||
|
args.commit_msg_filename = os.path.abspath(
|
||||||
|
args.commit_msg_filename,
|
||||||
|
)
|
||||||
if args.command == 'try-repo' and os.path.exists(args.repo):
|
if args.command == 'try-repo' and os.path.exists(args.repo):
|
||||||
args.repo = os.path.abspath(args.repo)
|
args.repo = os.path.abspath(args.repo)
|
||||||
|
|
||||||
|
@ -164,6 +168,10 @@ def _adjust_args_and_chdir(args: argparse.Namespace) -> None:
|
||||||
args.config = os.path.relpath(args.config)
|
args.config = os.path.relpath(args.config)
|
||||||
if args.command in {'run', 'try-repo'}:
|
if args.command in {'run', 'try-repo'}:
|
||||||
args.files = [os.path.relpath(filename) for filename in args.files]
|
args.files = [os.path.relpath(filename) for filename in args.files]
|
||||||
|
if args.commit_msg_filename is not None:
|
||||||
|
args.commit_msg_filename = os.path.relpath(
|
||||||
|
args.commit_msg_filename,
|
||||||
|
)
|
||||||
if args.command == 'try-repo' and os.path.exists(args.repo):
|
if args.command == 'try-repo' and os.path.exists(args.repo):
|
||||||
args.repo = os.path.relpath(args.repo)
|
args.repo = os.path.relpath(args.repo)
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -52,7 +52,7 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
|
||||||
retcode, diff_stdout_binary, _ = cmd_output_b(
|
retcode, diff_stdout_binary, _ = cmd_output_b(
|
||||||
'git', 'diff-index', '--ignore-submodules', '--binary',
|
'git', 'diff-index', '--ignore-submodules', '--binary',
|
||||||
'--exit-code', '--no-color', '--no-ext-diff', tree, '--',
|
'--exit-code', '--no-color', '--no-ext-diff', tree, '--',
|
||||||
retcode=None,
|
check=False,
|
||||||
)
|
)
|
||||||
if retcode and diff_stdout_binary.strip():
|
if retcode and diff_stdout_binary.strip():
|
||||||
patch_filename = f'patch{int(time.time())}-{os.getpid()}'
|
patch_filename = f'patch{int(time.time())}-{os.getpid()}'
|
||||||
|
|
|
@ -83,14 +83,12 @@ class CalledProcessError(RuntimeError):
|
||||||
self,
|
self,
|
||||||
returncode: int,
|
returncode: int,
|
||||||
cmd: tuple[str, ...],
|
cmd: tuple[str, ...],
|
||||||
expected_returncode: int,
|
|
||||||
stdout: bytes,
|
stdout: bytes,
|
||||||
stderr: bytes | None,
|
stderr: bytes | None,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(returncode, cmd, expected_returncode, stdout, stderr)
|
super().__init__(returncode, cmd, stdout, stderr)
|
||||||
self.returncode = returncode
|
self.returncode = returncode
|
||||||
self.cmd = cmd
|
self.cmd = cmd
|
||||||
self.expected_returncode = expected_returncode
|
|
||||||
self.stdout = stdout
|
self.stdout = stdout
|
||||||
self.stderr = stderr
|
self.stderr = stderr
|
||||||
|
|
||||||
|
@ -104,7 +102,6 @@ class CalledProcessError(RuntimeError):
|
||||||
return b''.join((
|
return b''.join((
|
||||||
f'command: {self.cmd!r}\n'.encode(),
|
f'command: {self.cmd!r}\n'.encode(),
|
||||||
f'return code: {self.returncode}\n'.encode(),
|
f'return code: {self.returncode}\n'.encode(),
|
||||||
f'expected return code: {self.expected_returncode}\n'.encode(),
|
|
||||||
b'stdout:', _indent_or_none(self.stdout), b'\n',
|
b'stdout:', _indent_or_none(self.stdout), b'\n',
|
||||||
b'stderr:', _indent_or_none(self.stderr),
|
b'stderr:', _indent_or_none(self.stderr),
|
||||||
))
|
))
|
||||||
|
@ -124,7 +121,7 @@ def _oserror_to_output(e: OSError) -> tuple[int, bytes, None]:
|
||||||
|
|
||||||
def cmd_output_b(
|
def cmd_output_b(
|
||||||
*cmd: str,
|
*cmd: str,
|
||||||
retcode: int | None = 0,
|
check: bool = True,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> tuple[int, bytes, bytes | None]:
|
) -> tuple[int, bytes, bytes | None]:
|
||||||
_setdefault_kwargs(kwargs)
|
_setdefault_kwargs(kwargs)
|
||||||
|
@ -142,8 +139,8 @@ def cmd_output_b(
|
||||||
stdout_b, stderr_b = proc.communicate()
|
stdout_b, stderr_b = proc.communicate()
|
||||||
returncode = proc.returncode
|
returncode = proc.returncode
|
||||||
|
|
||||||
if retcode is not None and retcode != returncode:
|
if check and returncode:
|
||||||
raise CalledProcessError(returncode, cmd, retcode, stdout_b, stderr_b)
|
raise CalledProcessError(returncode, cmd, stdout_b, stderr_b)
|
||||||
|
|
||||||
return returncode, stdout_b, stderr_b
|
return returncode, stdout_b, stderr_b
|
||||||
|
|
||||||
|
@ -196,10 +193,10 @@ if os.name != 'nt': # pragma: win32 no cover
|
||||||
|
|
||||||
def cmd_output_p(
|
def cmd_output_p(
|
||||||
*cmd: str,
|
*cmd: str,
|
||||||
retcode: int | None = 0,
|
check: bool = True,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> tuple[int, bytes, bytes | None]:
|
) -> tuple[int, bytes, bytes | None]:
|
||||||
assert retcode is None
|
assert check is False
|
||||||
assert kwargs['stderr'] == subprocess.STDOUT, kwargs['stderr']
|
assert kwargs['stderr'] == subprocess.STDOUT, kwargs['stderr']
|
||||||
_setdefault_kwargs(kwargs)
|
_setdefault_kwargs(kwargs)
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ def xargs(
|
||||||
run_cmd: tuple[str, ...],
|
run_cmd: tuple[str, ...],
|
||||||
) -> tuple[int, bytes, bytes | None]:
|
) -> tuple[int, bytes, bytes | None]:
|
||||||
return cmd_fn(
|
return cmd_fn(
|
||||||
*run_cmd, retcode=None, stderr=subprocess.STDOUT, **kwargs,
|
*run_cmd, check=False, stderr=subprocess.STDOUT, **kwargs,
|
||||||
)
|
)
|
||||||
|
|
||||||
threads = min(len(partitions), target_concurrency)
|
threads = min(len(partitions), target_concurrency)
|
||||||
|
|
10
setup.cfg
10
setup.cfg
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = pre_commit
|
name = pre_commit
|
||||||
version = 2.20.0
|
version = 2.21.0
|
||||||
description = A framework for managing and maintaining multi-language pre-commit hooks.
|
description = A framework for managing and maintaining multi-language pre-commit hooks.
|
||||||
long_description = file: README.md
|
long_description = file: README.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
|
@ -13,10 +13,6 @@ classifiers =
|
||||||
License :: OSI Approved :: MIT License
|
License :: OSI Approved :: MIT License
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3 :: Only
|
Programming Language :: Python :: 3 :: Only
|
||||||
Programming Language :: Python :: 3.7
|
|
||||||
Programming Language :: Python :: 3.8
|
|
||||||
Programming Language :: Python :: 3.9
|
|
||||||
Programming Language :: Python :: 3.10
|
|
||||||
Programming Language :: Python :: Implementation :: CPython
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
Programming Language :: Python :: Implementation :: PyPy
|
Programming Language :: Python :: Implementation :: PyPy
|
||||||
|
|
||||||
|
@ -27,8 +23,7 @@ install_requires =
|
||||||
identify>=1.0.0
|
identify>=1.0.0
|
||||||
nodeenv>=0.11.1
|
nodeenv>=0.11.1
|
||||||
pyyaml>=5.1
|
pyyaml>=5.1
|
||||||
toml
|
virtualenv>=20.10.0
|
||||||
virtualenv>=20.0.8
|
|
||||||
importlib-metadata;python_version<"3.8"
|
importlib-metadata;python_version<"3.8"
|
||||||
python_requires = >=3.7
|
python_requires = >=3.7
|
||||||
|
|
||||||
|
@ -61,7 +56,6 @@ check_untyped_defs = true
|
||||||
disallow_any_generics = true
|
disallow_any_generics = true
|
||||||
disallow_incomplete_defs = true
|
disallow_incomplete_defs = true
|
||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
no_implicit_optional = true
|
|
||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
warn_unused_ignores = true
|
warn_unused_ignores = true
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
. /etc/lsb-release
|
. /etc/lsb-release
|
||||||
if [ "$DISTRIB_CODENAME" = "focal" ]; then
|
if [ "$DISTRIB_CODENAME" = "jammy" ]; then
|
||||||
SWIFT_URL='https://download.swift.org/swift-5.6.1-release/ubuntu2004/swift-5.6.1-RELEASE/swift-5.6.1-RELEASE-ubuntu20.04.tar.gz'
|
SWIFT_URL='https://download.swift.org/swift-5.7.1-release/ubuntu2204/swift-5.7.1-RELEASE/swift-5.7.1-RELEASE-ubuntu22.04.tar.gz'
|
||||||
SWIFT_HASH='2b4f22d4a8b59fe8e050f0b7f020f8d8f12553cbda56709b2340a4a3bb90cfea'
|
SWIFT_HASH='7f60291f5088d3e77b0c2364beaabd29616ee7b37260b7b06bdbeb891a7fe161'
|
||||||
else
|
else
|
||||||
echo "unknown dist: ${DISTRIB_CODENAME}" 1>&2
|
echo "unknown dist: ${DISTRIB_CODENAME}" 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|
|
@ -17,7 +17,7 @@ from typing import Sequence
|
||||||
|
|
||||||
REPOS = (
|
REPOS = (
|
||||||
('rbenv', 'https://github.com/rbenv/rbenv', '38e1fbb'),
|
('rbenv', 'https://github.com/rbenv/rbenv', '38e1fbb'),
|
||||||
('ruby-build', 'https://github.com/rbenv/ruby-build', '2004fd7'),
|
('ruby-build', 'https://github.com/rbenv/ruby-build', '98c0337'),
|
||||||
(
|
(
|
||||||
'ruby-download',
|
'ruby-download',
|
||||||
'https://github.com/garnieretienne/rvm-download',
|
'https://github.com/garnieretienne/rvm-download',
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6</TargetFramework>
|
||||||
|
|
||||||
<PackAsTool>true</PackAsTool>
|
<PackAsTool>true</PackAsTool>
|
||||||
<ToolCommandName>proj1</ToolCommandName>
|
<ToolCommandName>proj1</ToolCommandName>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net6</TargetFramework>
|
||||||
|
|
||||||
<PackAsTool>true</PackAsTool>
|
<PackAsTool>true</PackAsTool>
|
||||||
<ToolCommandName>proj2</ToolCommandName>
|
<ToolCommandName>proj2</ToolCommandName>
|
||||||
|
|
3
testing/resources/dotnet_hooks_csproj_prefix_repo/.gitignore
vendored
Normal file
3
testing/resources/dotnet_hooks_csproj_prefix_repo/.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
nupkg/
|
|
@ -0,0 +1,5 @@
|
||||||
|
- id: dotnet-example-hook
|
||||||
|
name: dotnet example hook
|
||||||
|
entry: testeroni.tool
|
||||||
|
language: dotnet
|
||||||
|
files: ''
|
12
testing/resources/dotnet_hooks_csproj_prefix_repo/Program.cs
Normal file
12
testing/resources/dotnet_hooks_csproj_prefix_repo/Program.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace dotnet_hooks_repo
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Hello from dotnet!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
<PackAsTool>true</PackAsTool>
|
||||||
|
<ToolCommandName>testeroni.tool</ToolCommandName>
|
||||||
|
<PackageOutputPath>./nupkg</PackageOutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net6</TargetFramework>
|
||||||
<PackAsTool>true</PackAsTool>
|
<PackAsTool>true</PackAsTool>
|
||||||
<ToolCommandName>testeroni</ToolCommandName>
|
<ToolCommandName>testeroni</ToolCommandName>
|
||||||
<PackageOutputPath>./nupkg</PackageOutputPath>
|
<PackageOutputPath>./nupkg</PackageOutputPath>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net6</TargetFramework>
|
||||||
<PackAsTool>true</PackAsTool>
|
<PackAsTool>true</PackAsTool>
|
||||||
<ToolCommandName>testeroni</ToolCommandName>
|
<ToolCommandName>testeroni</ToolCommandName>
|
||||||
<PackageOutputPath>./nupkg</PackageOutputPath>
|
<PackageOutputPath>./nupkg</PackageOutputPath>
|
||||||
|
|
|
@ -2,4 +2,5 @@
|
||||||
name: Python 3 Hook
|
name: Python 3 Hook
|
||||||
entry: python3-hook
|
entry: python3-hook
|
||||||
language: python
|
language: python
|
||||||
|
language_version: python3
|
||||||
files: \.py$
|
files: \.py$
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
name: Ruby Hook
|
name: Ruby Hook
|
||||||
entry: ruby_hook
|
entry: ruby_hook
|
||||||
language: ruby
|
language: ruby
|
||||||
language_version: 2.5.1
|
language_version: 3.1.0
|
||||||
files: \.rb$
|
files: \.rb$
|
||||||
|
|
|
@ -15,6 +15,8 @@ from pre_commit.clientlib import DEFAULT_LANGUAGE_VERSION
|
||||||
from pre_commit.clientlib import MANIFEST_SCHEMA
|
from pre_commit.clientlib import MANIFEST_SCHEMA
|
||||||
from pre_commit.clientlib import META_HOOK_DICT
|
from pre_commit.clientlib import META_HOOK_DICT
|
||||||
from pre_commit.clientlib import MigrateShaToRev
|
from pre_commit.clientlib import MigrateShaToRev
|
||||||
|
from pre_commit.clientlib import OptionalSensibleRegexAtHook
|
||||||
|
from pre_commit.clientlib import OptionalSensibleRegexAtTop
|
||||||
from pre_commit.clientlib import validate_config_main
|
from pre_commit.clientlib import validate_config_main
|
||||||
from pre_commit.clientlib import validate_manifest_main
|
from pre_commit.clientlib import validate_manifest_main
|
||||||
from testing.fixtures import sample_local_config
|
from testing.fixtures import sample_local_config
|
||||||
|
@ -261,6 +263,27 @@ def test_warn_mutable_rev_conditional():
|
||||||
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'validator_cls',
|
||||||
|
(
|
||||||
|
OptionalSensibleRegexAtHook,
|
||||||
|
OptionalSensibleRegexAtTop,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
def test_sensible_regex_validators_dont_pass_none(validator_cls):
|
||||||
|
key = 'files'
|
||||||
|
with pytest.raises(cfgv.ValidationError) as excinfo:
|
||||||
|
validator = validator_cls(key, cfgv.check_string)
|
||||||
|
validator.check({key: None})
|
||||||
|
|
||||||
|
assert str(excinfo.value) == (
|
||||||
|
'\n'
|
||||||
|
f'==> At key: {key}'
|
||||||
|
'\n'
|
||||||
|
'=====> Expected string got NoneType'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('regex', 'warning'),
|
('regex', 'warning'),
|
||||||
(
|
(
|
||||||
|
@ -296,6 +319,22 @@ def test_validate_optional_sensible_regex_at_hook(caplog, regex, warning):
|
||||||
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_optional_sensible_regex_at_local_hook(caplog):
|
||||||
|
config_obj = sample_local_config()
|
||||||
|
config_obj['hooks'][0]['files'] = 'dir/*.py'
|
||||||
|
|
||||||
|
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||||
|
|
||||||
|
assert caplog.record_tuples == [
|
||||||
|
(
|
||||||
|
'pre_commit',
|
||||||
|
logging.WARNING,
|
||||||
|
"The 'files' field in hook 'do_not_commit' is a regex, not a glob "
|
||||||
|
"-- matching '/*' probably isn't what you want here",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
('regex', 'warning'),
|
('regex', 'warning'),
|
||||||
(
|
(
|
||||||
|
|
|
@ -135,7 +135,7 @@ def test_init_templatedir_skip_on_missing_config(
|
||||||
retcode, output = git_commit(
|
retcode, output = git_commit(
|
||||||
fn=cmd_output_mocked_pre_commit_home,
|
fn=cmd_output_mocked_pre_commit_home,
|
||||||
tempdir_factory=tempdir_factory,
|
tempdir_factory=tempdir_factory,
|
||||||
retcode=None,
|
check=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert retcode == commit_retcode
|
assert retcode == commit_retcode
|
||||||
|
|
|
@ -126,7 +126,7 @@ def _get_commit_output(tempdir_factory, touch_file='foo', **kwargs):
|
||||||
cmd_output('git', 'add', touch_file)
|
cmd_output('git', 'add', touch_file)
|
||||||
return git_commit(
|
return git_commit(
|
||||||
fn=cmd_output_mocked_pre_commit_home,
|
fn=cmd_output_mocked_pre_commit_home,
|
||||||
retcode=None,
|
check=False,
|
||||||
tempdir_factory=tempdir_factory,
|
tempdir_factory=tempdir_factory,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
)
|
)
|
||||||
|
@ -286,7 +286,7 @@ def test_environment_not_sourced(tempdir_factory, store):
|
||||||
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
|
'GIT_AUTHOR_EMAIL': os.environ['GIT_AUTHOR_EMAIL'],
|
||||||
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
|
'GIT_COMMITTER_EMAIL': os.environ['GIT_COMMITTER_EMAIL'],
|
||||||
},
|
},
|
||||||
retcode=None,
|
check=False,
|
||||||
)
|
)
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert out == (
|
assert out == (
|
||||||
|
@ -551,7 +551,7 @@ def _get_push_output(tempdir_factory, remote='origin', opts=()):
|
||||||
return cmd_output_mocked_pre_commit_home(
|
return cmd_output_mocked_pre_commit_home(
|
||||||
'git', 'push', remote, 'HEAD:new_branch', *opts,
|
'git', 'push', remote, 'HEAD:new_branch', *opts,
|
||||||
tempdir_factory=tempdir_factory,
|
tempdir_factory=tempdir_factory,
|
||||||
retcode=None,
|
check=False,
|
||||||
)[:2]
|
)[:2]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -536,6 +536,13 @@ def test_merge_conflict(cap_out, store, in_merge_conflict):
|
||||||
assert b'Unmerged files. Resolve before committing.' in printed
|
assert b'Unmerged files. Resolve before committing.' in printed
|
||||||
|
|
||||||
|
|
||||||
|
def test_files_during_merge_conflict(cap_out, store, in_merge_conflict):
|
||||||
|
opts = run_opts(files=['placeholder'])
|
||||||
|
ret, printed = _do_run(cap_out, store, in_merge_conflict, opts)
|
||||||
|
assert ret == 0
|
||||||
|
assert b'Bash hook' in printed
|
||||||
|
|
||||||
|
|
||||||
def test_merge_conflict_modified(cap_out, store, in_merge_conflict):
|
def test_merge_conflict_modified(cap_out, store, in_merge_conflict):
|
||||||
# Touch another file so we have unstaged non-conflicting things
|
# Touch another file so we have unstaged non-conflicting things
|
||||||
assert os.path.exists('placeholder')
|
assert os.path.exists('placeholder')
|
||||||
|
@ -635,6 +642,32 @@ def test_skip_bypasses_installation(cap_out, store, repo_with_passing_hook):
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_skip_alias_bypasses_installation(
|
||||||
|
cap_out, store, repo_with_passing_hook,
|
||||||
|
):
|
||||||
|
config = {
|
||||||
|
'repo': 'local',
|
||||||
|
'hooks': [
|
||||||
|
{
|
||||||
|
'id': 'skipme',
|
||||||
|
'name': 'skipme-1',
|
||||||
|
'alias': 'skipme-1',
|
||||||
|
'entry': 'skipme',
|
||||||
|
'language': 'python',
|
||||||
|
'additional_dependencies': ['/pre-commit-does-not-exist'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
add_config_to_repo(repo_with_passing_hook, config)
|
||||||
|
|
||||||
|
ret, printed = _do_run(
|
||||||
|
cap_out, store, repo_with_passing_hook,
|
||||||
|
run_opts(all_files=True),
|
||||||
|
{'SKIP': 'skipme-1'},
|
||||||
|
)
|
||||||
|
assert ret == 0
|
||||||
|
|
||||||
|
|
||||||
def test_hook_id_not_in_non_verbose_output(
|
def test_hook_id_not_in_non_verbose_output(
|
||||||
cap_out, store, repo_with_passing_hook,
|
cap_out, store, repo_with_passing_hook,
|
||||||
):
|
):
|
||||||
|
@ -685,7 +718,7 @@ def test_non_ascii_hook_id(repo_with_passing_hook, tempdir_factory):
|
||||||
with cwd(repo_with_passing_hook):
|
with cwd(repo_with_passing_hook):
|
||||||
_, stdout, _ = cmd_output_mocked_pre_commit_home(
|
_, stdout, _ = cmd_output_mocked_pre_commit_home(
|
||||||
sys.executable, '-m', 'pre_commit.main', 'run', '☃',
|
sys.executable, '-m', 'pre_commit.main', 'run', '☃',
|
||||||
retcode=None, tempdir_factory=tempdir_factory,
|
check=False, tempdir_factory=tempdir_factory,
|
||||||
)
|
)
|
||||||
assert 'UnicodeDecodeError' not in stdout
|
assert 'UnicodeDecodeError' not in stdout
|
||||||
# Doesn't actually happen, but a reasonable assertion
|
# Doesn't actually happen, but a reasonable assertion
|
||||||
|
@ -704,7 +737,7 @@ def test_stdout_write_bug_py26(repo_with_failing_hook, store, tempdir_factory):
|
||||||
_, out = git_commit(
|
_, out = git_commit(
|
||||||
fn=cmd_output_mocked_pre_commit_home,
|
fn=cmd_output_mocked_pre_commit_home,
|
||||||
tempdir_factory=tempdir_factory,
|
tempdir_factory=tempdir_factory,
|
||||||
retcode=None,
|
check=False,
|
||||||
)
|
)
|
||||||
assert 'UnicodeEncodeError' not in out
|
assert 'UnicodeEncodeError' not in out
|
||||||
# Doesn't actually happen, but a reasonable assertion
|
# Doesn't actually happen, but a reasonable assertion
|
||||||
|
|
|
@ -68,7 +68,7 @@ def _make_conflict():
|
||||||
bar_only_file.write('bar')
|
bar_only_file.write('bar')
|
||||||
cmd_output('git', 'add', 'bar_only_file')
|
cmd_output('git', 'add', 'bar_only_file')
|
||||||
git_commit(msg=_make_conflict.__name__)
|
git_commit(msg=_make_conflict.__name__)
|
||||||
cmd_output('git', 'merge', 'foo', retcode=None)
|
cmd_output('git', 'merge', 'foo', check=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
|
|
@ -162,7 +162,7 @@ def test_error_handler_non_ascii_exception(mock_store_dir):
|
||||||
def test_error_handler_non_utf8_exception(mock_store_dir):
|
def test_error_handler_non_utf8_exception(mock_store_dir):
|
||||||
with pytest.raises(SystemExit):
|
with pytest.raises(SystemExit):
|
||||||
with error_handler.error_handler():
|
with error_handler.error_handler():
|
||||||
raise CalledProcessError(1, ('exe',), 0, b'error: \xa0\xe1', b'')
|
raise CalledProcessError(1, ('exe',), b'error: \xa0\xe1', b'')
|
||||||
|
|
||||||
|
|
||||||
def test_error_handler_non_stringable_exception(mock_store_dir):
|
def test_error_handler_non_stringable_exception(mock_store_dir):
|
||||||
|
@ -183,10 +183,11 @@ def test_error_handler_no_tty(tempdir_factory):
|
||||||
'from pre_commit.error_handler import error_handler\n'
|
'from pre_commit.error_handler import error_handler\n'
|
||||||
'with error_handler():\n'
|
'with error_handler():\n'
|
||||||
' raise ValueError("\\u2603")\n',
|
' raise ValueError("\\u2603")\n',
|
||||||
retcode=3,
|
check=False,
|
||||||
tempdir_factory=tempdir_factory,
|
tempdir_factory=tempdir_factory,
|
||||||
pre_commit_home=pre_commit_home,
|
pre_commit_home=pre_commit_home,
|
||||||
)
|
)
|
||||||
|
assert ret == 3
|
||||||
log_file = os.path.join(pre_commit_home, 'pre-commit.log')
|
log_file = os.path.join(pre_commit_home, 'pre-commit.log')
|
||||||
out_lines = out.splitlines()
|
out_lines = out.splitlines()
|
||||||
assert out_lines[-2] == 'An unexpected error has occurred: ValueError: ☃'
|
assert out_lines[-2] == 'An unexpected error has occurred: ValueError: ☃'
|
||||||
|
|
|
@ -104,7 +104,7 @@ def test_is_in_merge_conflict_submodule(in_conflicting_submodule):
|
||||||
def test_cherry_pick_conflict(in_merge_conflict):
|
def test_cherry_pick_conflict(in_merge_conflict):
|
||||||
cmd_output('git', 'merge', '--abort')
|
cmd_output('git', 'merge', '--abort')
|
||||||
foo_ref = cmd_output('git', 'rev-parse', 'foo')[1].strip()
|
foo_ref = cmd_output('git', 'rev-parse', 'foo')[1].strip()
|
||||||
cmd_output('git', 'cherry-pick', foo_ref, retcode=None)
|
cmd_output('git', 'cherry-pick', foo_ref, check=False)
|
||||||
assert git.is_in_merge_conflict() is False
|
assert git.is_in_merge_conflict() is False
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -178,6 +178,6 @@ def test_get_docker_path_in_docker_windows(in_docker):
|
||||||
|
|
||||||
def test_get_docker_path_in_docker_docker_in_docker(in_docker):
|
def test_get_docker_path_in_docker_docker_in_docker(in_docker):
|
||||||
# won't be able to discover "self" container in true docker-in-docker
|
# won't be able to discover "self" container in true docker-in-docker
|
||||||
err = CalledProcessError(1, (), 0, b'', b'')
|
err = CalledProcessError(1, (), b'', b'')
|
||||||
with mock.patch.object(docker, 'cmd_output_b', side_effect=err):
|
with mock.patch.object(docker, 'cmd_output_b', side_effect=err):
|
||||||
assert docker._get_docker_path('/project') == '/project'
|
assert docker._get_docker_path('/project') == '/project'
|
||||||
|
|
|
@ -6,6 +6,7 @@ import pytest
|
||||||
|
|
||||||
from pre_commit import envcontext
|
from pre_commit import envcontext
|
||||||
from pre_commit.languages import r
|
from pre_commit.languages import r
|
||||||
|
from pre_commit.util import win_exe
|
||||||
from testing.fixtures import make_config_from_repo
|
from testing.fixtures import make_config_from_repo
|
||||||
from testing.fixtures import make_repo
|
from testing.fixtures import make_repo
|
||||||
from tests.repository_test import _get_hook_no_install
|
from tests.repository_test import _get_hook_no_install
|
||||||
|
@ -133,7 +134,7 @@ def test_r_parsing_file_local(tempdir_factory, store):
|
||||||
|
|
||||||
|
|
||||||
def test_rscript_exec_relative_to_r_home():
|
def test_rscript_exec_relative_to_r_home():
|
||||||
expected = os.path.join('r_home_dir', 'bin', 'Rscript')
|
expected = os.path.join('r_home_dir', 'bin', win_exe('Rscript'))
|
||||||
with envcontext.envcontext((('R_HOME', 'r_home_dir'),)):
|
with envcontext.envcontext((('R_HOME', 'r_home_dir'),)):
|
||||||
assert r._rscript_exec() == expected
|
assert r._rscript_exec() == expected
|
||||||
|
|
||||||
|
|
|
@ -71,10 +71,10 @@ def test_install_ruby_default(fake_gem_prefix):
|
||||||
|
|
||||||
@xfailif_windows # pragma: win32 no cover
|
@xfailif_windows # pragma: win32 no cover
|
||||||
def test_install_ruby_with_version(fake_gem_prefix):
|
def test_install_ruby_with_version(fake_gem_prefix):
|
||||||
ruby.install_environment(fake_gem_prefix, '2.7.2', ())
|
ruby.install_environment(fake_gem_prefix, '3.1.0', ())
|
||||||
|
|
||||||
# Should be able to activate and use rbenv install
|
# Should be able to activate and use rbenv install
|
||||||
with ruby.in_env(fake_gem_prefix, '2.7.2'):
|
with ruby.in_env(fake_gem_prefix, '3.1.0'):
|
||||||
cmd_output('rbenv', 'install', '--help')
|
cmd_output('rbenv', 'install', '--help')
|
||||||
|
|
||||||
|
|
||||||
|
|
90
tests/languages/rust_test.py
Normal file
90
tests/languages/rust_test.py
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
import pre_commit.constants as C
|
||||||
|
from pre_commit import parse_shebang
|
||||||
|
from pre_commit.languages import rust
|
||||||
|
from pre_commit.prefix import Prefix
|
||||||
|
from pre_commit.util import cmd_output
|
||||||
|
|
||||||
|
ACTUAL_GET_DEFAULT_VERSION = rust.get_default_version.__wrapped__
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def cmd_output_b_mck():
|
||||||
|
with mock.patch.object(rust, 'cmd_output_b') as mck:
|
||||||
|
yield mck
|
||||||
|
|
||||||
|
|
||||||
|
def test_sets_system_when_rust_is_available(cmd_output_b_mck):
|
||||||
|
cmd_output_b_mck.return_value = (0, b'', b'')
|
||||||
|
assert ACTUAL_GET_DEFAULT_VERSION() == 'system'
|
||||||
|
|
||||||
|
|
||||||
|
def test_uses_default_when_rust_is_not_available(cmd_output_b_mck):
|
||||||
|
cmd_output_b_mck.return_value = (127, b'', b'error: not found')
|
||||||
|
assert ACTUAL_GET_DEFAULT_VERSION() == C.DEFAULT
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('language_version', (C.DEFAULT, '1.56.0'))
|
||||||
|
def test_installs_with_bootstrapped_rustup(tmpdir, language_version):
|
||||||
|
tmpdir.join('src', 'main.rs').ensure().write(
|
||||||
|
'fn main() {\n'
|
||||||
|
' println!("Hello, world!");\n'
|
||||||
|
'}\n',
|
||||||
|
)
|
||||||
|
tmpdir.join('Cargo.toml').ensure().write(
|
||||||
|
'[package]\n'
|
||||||
|
'name = "hello_world"\n'
|
||||||
|
'version = "0.1.0"\n'
|
||||||
|
'edition = "2021"\n',
|
||||||
|
)
|
||||||
|
prefix = Prefix(str(tmpdir))
|
||||||
|
|
||||||
|
find_executable_exes = []
|
||||||
|
|
||||||
|
original_find_executable = parse_shebang.find_executable
|
||||||
|
|
||||||
|
def mocked_find_executable(exe: str) -> str | None:
|
||||||
|
"""
|
||||||
|
Return `None` the first time `find_executable` is called to ensure
|
||||||
|
that the bootstrapping code is executed, then just let the function
|
||||||
|
work as normal.
|
||||||
|
|
||||||
|
Also log the arguments to ensure that everything works as expected.
|
||||||
|
"""
|
||||||
|
find_executable_exes.append(exe)
|
||||||
|
if len(find_executable_exes) == 1:
|
||||||
|
return None
|
||||||
|
return original_find_executable(exe)
|
||||||
|
|
||||||
|
with mock.patch.object(parse_shebang, 'find_executable') as find_exe_mck:
|
||||||
|
find_exe_mck.side_effect = mocked_find_executable
|
||||||
|
rust.install_environment(prefix, language_version, ())
|
||||||
|
assert find_executable_exes == ['rustup', 'rustup', 'cargo']
|
||||||
|
|
||||||
|
with rust.in_env(prefix, language_version):
|
||||||
|
assert cmd_output('hello_world')[1] == 'Hello, world!\n'
|
||||||
|
|
||||||
|
|
||||||
|
def test_installs_with_existing_rustup(tmpdir):
|
||||||
|
tmpdir.join('src', 'main.rs').ensure().write(
|
||||||
|
'fn main() {\n'
|
||||||
|
' println!("Hello, world!");\n'
|
||||||
|
'}\n',
|
||||||
|
)
|
||||||
|
tmpdir.join('Cargo.toml').ensure().write(
|
||||||
|
'[package]\n'
|
||||||
|
'name = "hello_world"\n'
|
||||||
|
'version = "0.1.0"\n'
|
||||||
|
'edition = "2021"\n',
|
||||||
|
)
|
||||||
|
prefix = Prefix(str(tmpdir))
|
||||||
|
|
||||||
|
assert parse_shebang.find_executable('rustup') is not None
|
||||||
|
rust.install_environment(prefix, '1.56.0', ())
|
||||||
|
with rust.in_env(prefix, '1.56.0'):
|
||||||
|
assert cmd_output('hello_world')[1] == 'Hello, world!\n'
|
|
@ -17,6 +17,8 @@ from testing.util import cwd
|
||||||
def _args(**kwargs):
|
def _args(**kwargs):
|
||||||
kwargs.setdefault('command', 'help')
|
kwargs.setdefault('command', 'help')
|
||||||
kwargs.setdefault('config', C.CONFIG_FILE)
|
kwargs.setdefault('config', C.CONFIG_FILE)
|
||||||
|
if kwargs['command'] in {'run', 'try-repo'}:
|
||||||
|
kwargs.setdefault('commit_msg_filename', None)
|
||||||
return argparse.Namespace(**kwargs)
|
return argparse.Namespace(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,13 +37,24 @@ def test_adjust_args_and_chdir_noop(in_git_dir):
|
||||||
|
|
||||||
def test_adjust_args_and_chdir_relative_things(in_git_dir):
|
def test_adjust_args_and_chdir_relative_things(in_git_dir):
|
||||||
in_git_dir.join('foo/cfg.yaml').ensure()
|
in_git_dir.join('foo/cfg.yaml').ensure()
|
||||||
in_git_dir.join('foo').chdir()
|
with in_git_dir.join('foo').as_cwd():
|
||||||
|
|
||||||
args = _args(command='run', files=['f1', 'f2'], config='cfg.yaml')
|
args = _args(command='run', files=['f1', 'f2'], config='cfg.yaml')
|
||||||
main._adjust_args_and_chdir(args)
|
main._adjust_args_and_chdir(args)
|
||||||
assert os.getcwd() == in_git_dir
|
assert os.getcwd() == in_git_dir
|
||||||
assert args.config == os.path.join('foo', 'cfg.yaml')
|
assert args.config == os.path.join('foo', 'cfg.yaml')
|
||||||
assert args.files == [os.path.join('foo', 'f1'), os.path.join('foo', 'f2')]
|
assert args.files == [
|
||||||
|
os.path.join('foo', 'f1'),
|
||||||
|
os.path.join('foo', 'f2'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_adjust_args_and_chdir_relative_commit_msg(in_git_dir):
|
||||||
|
in_git_dir.join('foo/cfg.yaml').ensure()
|
||||||
|
with in_git_dir.join('foo').as_cwd():
|
||||||
|
args = _args(command='run', files=[], commit_msg_filename='t.txt')
|
||||||
|
main._adjust_args_and_chdir(args)
|
||||||
|
assert os.getcwd() == in_git_dir
|
||||||
|
assert args.commit_msg_filename == os.path.join('foo', 't.txt')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(os.name != 'nt', reason='windows feature')
|
@pytest.mark.skipif(os.name != 'nt', reason='windows feature')
|
||||||
|
@ -56,8 +69,7 @@ def test_install_on_subst(in_git_dir, store): # pragma: posix no cover
|
||||||
|
|
||||||
|
|
||||||
def test_adjust_args_and_chdir_non_relative_config(in_git_dir):
|
def test_adjust_args_and_chdir_non_relative_config(in_git_dir):
|
||||||
in_git_dir.join('foo').ensure_dir().chdir()
|
with in_git_dir.join('foo').ensure_dir().as_cwd():
|
||||||
|
|
||||||
args = _args()
|
args = _args()
|
||||||
main._adjust_args_and_chdir(args)
|
main._adjust_args_and_chdir(args)
|
||||||
assert os.getcwd() == in_git_dir
|
assert os.getcwd() == in_git_dir
|
||||||
|
@ -65,8 +77,7 @@ def test_adjust_args_and_chdir_non_relative_config(in_git_dir):
|
||||||
|
|
||||||
|
|
||||||
def test_adjust_args_try_repo_repo_relative(in_git_dir):
|
def test_adjust_args_try_repo_repo_relative(in_git_dir):
|
||||||
in_git_dir.join('foo').ensure_dir().chdir()
|
with in_git_dir.join('foo').ensure_dir().as_cwd():
|
||||||
|
|
||||||
args = _args(command='try-repo', repo='../foo', files=[])
|
args = _args(command='try-repo', repo='../foo', files=[])
|
||||||
assert args.repo is not None
|
assert args.repo is not None
|
||||||
assert os.path.exists(args.repo)
|
assert os.path.exists(args.repo)
|
||||||
|
|
|
@ -173,24 +173,14 @@ def test_python_venv(tempdir_factory, store):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@xfailif_windows # pragma: win32 no cover # no python 2 in GHA
|
def test_language_versioned_python_hook(tempdir_factory, store):
|
||||||
def test_switch_language_versions_doesnt_clobber(tempdir_factory, store):
|
# we patch this force virtualenv executing with `-p` since we can't
|
||||||
# We're using the python3 repo because it prints the python version
|
# reliably have multiple pythons available in CI
|
||||||
path = make_repo(tempdir_factory, 'python3_hooks_repo')
|
with mock.patch.object(
|
||||||
|
python,
|
||||||
def run_on_version(version, expected_output):
|
'_sys_executable_matches',
|
||||||
config = make_config_from_repo(path)
|
return_value=False,
|
||||||
config['hooks'][0]['language_version'] = version
|
):
|
||||||
hook = _get_hook(config, store, 'python3-hook')
|
|
||||||
ret, out = _hook_run(hook, [], color=False)
|
|
||||||
assert ret == 0
|
|
||||||
assert _norm_out(out) == expected_output
|
|
||||||
|
|
||||||
run_on_version('python2', b'2\n[]\nHello World\n')
|
|
||||||
run_on_version('python3', b'3\n[]\nHello World\n')
|
|
||||||
|
|
||||||
|
|
||||||
def test_versioned_python_hook(tempdir_factory, store):
|
|
||||||
_test_hook_repo(
|
_test_hook_repo(
|
||||||
tempdir_factory, store, 'python3_hooks_repo',
|
tempdir_factory, store, 'python3_hooks_repo',
|
||||||
'python3-hook',
|
'python3-hook',
|
||||||
|
@ -345,7 +335,7 @@ def test_run_versioned_ruby_hook(tempdir_factory, store):
|
||||||
tempdir_factory, store, 'ruby_versioned_hooks_repo',
|
tempdir_factory, store, 'ruby_versioned_hooks_repo',
|
||||||
'ruby_hook',
|
'ruby_hook',
|
||||||
[os.devnull],
|
[os.devnull],
|
||||||
b'2.5.1\nHello world from a ruby hook\n',
|
b'3.1.0\nHello world from a ruby hook\n',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -367,7 +357,7 @@ def test_run_ruby_hook_with_disable_shared_gems(
|
||||||
tempdir_factory, store, 'ruby_versioned_hooks_repo',
|
tempdir_factory, store, 'ruby_versioned_hooks_repo',
|
||||||
'ruby_hook',
|
'ruby_hook',
|
||||||
[os.devnull],
|
[os.devnull],
|
||||||
b'2.5.1\nHello world from a ruby hook\n',
|
b'3.1.0\nHello world from a ruby hook\n',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -471,7 +461,7 @@ def test_additional_rust_cli_dependencies_installed(
|
||||||
hook = _get_hook(config, store, 'rust-hook')
|
hook = _get_hook(config, store, 'rust-hook')
|
||||||
binaries = os.listdir(
|
binaries = os.listdir(
|
||||||
hook.prefix.path(
|
hook.prefix.path(
|
||||||
helpers.environment_dir(rust.ENVIRONMENT_DIR, C.DEFAULT), 'bin',
|
helpers.environment_dir(rust.ENVIRONMENT_DIR, 'system'), 'bin',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# normalize for windows
|
# normalize for windows
|
||||||
|
@ -485,12 +475,12 @@ def test_additional_rust_lib_dependencies_installed(
|
||||||
path = make_repo(tempdir_factory, 'rust_hooks_repo')
|
path = make_repo(tempdir_factory, 'rust_hooks_repo')
|
||||||
config = make_config_from_repo(path)
|
config = make_config_from_repo(path)
|
||||||
# A small rust package with no dependencies.
|
# A small rust package with no dependencies.
|
||||||
deps = ['shellharden:3.1.0']
|
deps = ['shellharden:3.1.0', 'git-version']
|
||||||
config['hooks'][0]['additional_dependencies'] = deps
|
config['hooks'][0]['additional_dependencies'] = deps
|
||||||
hook = _get_hook(config, store, 'rust-hook')
|
hook = _get_hook(config, store, 'rust-hook')
|
||||||
binaries = os.listdir(
|
binaries = os.listdir(
|
||||||
hook.prefix.path(
|
hook.prefix.path(
|
||||||
helpers.environment_dir(rust.ENVIRONMENT_DIR, C.DEFAULT), 'bin',
|
helpers.environment_dir(rust.ENVIRONMENT_DIR, 'system'), 'bin',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
# normalize for windows
|
# normalize for windows
|
||||||
|
@ -883,7 +873,7 @@ def test_tags_on_repositories(in_tmpdir, tempdir_factory, store):
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def local_python_config():
|
def local_python_config():
|
||||||
# Make a "local" hooks repo that just installs our other hooks repo
|
# Make a "local" hooks repo that just installs our other hooks repo
|
||||||
repo_path = get_resource_path('python3_hooks_repo')
|
repo_path = get_resource_path('python_hooks_repo')
|
||||||
manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE))
|
manifest = load_manifest(os.path.join(repo_path, C.MANIFEST_FILE))
|
||||||
hooks = [
|
hooks = [
|
||||||
dict(hook, additional_dependencies=[repo_path]) for hook in manifest
|
dict(hook, additional_dependencies=[repo_path]) for hook in manifest
|
||||||
|
@ -892,23 +882,12 @@ def local_python_config():
|
||||||
|
|
||||||
|
|
||||||
def test_local_python_repo(store, local_python_config):
|
def test_local_python_repo(store, local_python_config):
|
||||||
hook = _get_hook(local_python_config, store, 'python3-hook')
|
hook = _get_hook(local_python_config, store, 'foo')
|
||||||
# language_version should have been adjusted to the interpreter version
|
# language_version should have been adjusted to the interpreter version
|
||||||
assert hook.language_version != C.DEFAULT
|
assert hook.language_version != C.DEFAULT
|
||||||
ret, out = _hook_run(hook, ('filename',), color=False)
|
ret, out = _hook_run(hook, ('filename',), color=False)
|
||||||
assert ret == 0
|
assert ret == 0
|
||||||
assert _norm_out(out) == b"3\n['filename']\nHello World\n"
|
assert _norm_out(out) == b"['filename']\nHello World\n"
|
||||||
|
|
||||||
|
|
||||||
@xfailif_windows # pragma: win32 no cover # no python2 in GHA
|
|
||||||
def test_local_python_repo_python2(store, local_python_config):
|
|
||||||
local_python_config['hooks'][0]['language_version'] = 'python2'
|
|
||||||
hook = _get_hook(local_python_config, store, 'python3-hook')
|
|
||||||
# language_version should have been adjusted to the interpreter version
|
|
||||||
assert hook.language_version != C.DEFAULT
|
|
||||||
ret, out = _hook_run(hook, ('filename',), color=False)
|
|
||||||
assert ret == 0
|
|
||||||
assert _norm_out(out) == b"2\n['filename']\nHello World\n"
|
|
||||||
|
|
||||||
|
|
||||||
def test_default_language_version(store, local_python_config):
|
def test_default_language_version(store, local_python_config):
|
||||||
|
@ -1052,6 +1031,7 @@ def test_local_perl_additional_dependencies(store):
|
||||||
'dotnet_hooks_csproj_repo',
|
'dotnet_hooks_csproj_repo',
|
||||||
'dotnet_hooks_sln_repo',
|
'dotnet_hooks_sln_repo',
|
||||||
'dotnet_hooks_combo_repo',
|
'dotnet_hooks_combo_repo',
|
||||||
|
'dotnet_hooks_csproj_prefix_repo',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
def test_dotnet_hook(tempdir_factory, store, repo):
|
def test_dotnet_hook(tempdir_factory, store, repo):
|
||||||
|
|
|
@ -127,7 +127,7 @@ def test_clone_shallow_failure_fallback_to_complete(
|
||||||
|
|
||||||
# Force shallow clone failure
|
# Force shallow clone failure
|
||||||
def fake_shallow_clone(self, *args, **kwargs):
|
def fake_shallow_clone(self, *args, **kwargs):
|
||||||
raise CalledProcessError(1, (), 0, b'', None)
|
raise CalledProcessError(1, (), b'', None)
|
||||||
store._shallow_clone = fake_shallow_clone
|
store._shallow_clone = fake_shallow_clone
|
||||||
|
|
||||||
ret = store.clone(path, rev)
|
ret = store.clone(path, rev)
|
||||||
|
|
|
@ -18,11 +18,10 @@ from pre_commit.util import tmpdir
|
||||||
|
|
||||||
|
|
||||||
def test_CalledProcessError_str():
|
def test_CalledProcessError_str():
|
||||||
error = CalledProcessError(1, ('exe',), 0, b'output', b'errors')
|
error = CalledProcessError(1, ('exe',), b'output', b'errors')
|
||||||
assert str(error) == (
|
assert str(error) == (
|
||||||
"command: ('exe',)\n"
|
"command: ('exe',)\n"
|
||||||
'return code: 1\n'
|
'return code: 1\n'
|
||||||
'expected return code: 0\n'
|
|
||||||
'stdout:\n'
|
'stdout:\n'
|
||||||
' output\n'
|
' output\n'
|
||||||
'stderr:\n'
|
'stderr:\n'
|
||||||
|
@ -31,11 +30,10 @@ def test_CalledProcessError_str():
|
||||||
|
|
||||||
|
|
||||||
def test_CalledProcessError_str_nooutput():
|
def test_CalledProcessError_str_nooutput():
|
||||||
error = CalledProcessError(1, ('exe',), 0, b'', b'')
|
error = CalledProcessError(1, ('exe',), b'', b'')
|
||||||
assert str(error) == (
|
assert str(error) == (
|
||||||
"command: ('exe',)\n"
|
"command: ('exe',)\n"
|
||||||
'return code: 1\n'
|
'return code: 1\n'
|
||||||
'expected return code: 0\n'
|
|
||||||
'stdout: (none)\n'
|
'stdout: (none)\n'
|
||||||
'stderr: (none)'
|
'stderr: (none)'
|
||||||
)
|
)
|
||||||
|
@ -83,14 +81,14 @@ def test_tmpdir():
|
||||||
|
|
||||||
|
|
||||||
def test_cmd_output_exe_not_found():
|
def test_cmd_output_exe_not_found():
|
||||||
ret, out, _ = cmd_output('dne', retcode=None)
|
ret, out, _ = cmd_output('dne', check=False)
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert out == 'Executable `dne` not found'
|
assert out == 'Executable `dne` not found'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('fn', (cmd_output_b, cmd_output_p))
|
@pytest.mark.parametrize('fn', (cmd_output_b, cmd_output_p))
|
||||||
def test_cmd_output_exe_not_found_bytes(fn):
|
def test_cmd_output_exe_not_found_bytes(fn):
|
||||||
ret, out, _ = fn('dne', retcode=None, stderr=subprocess.STDOUT)
|
ret, out, _ = fn('dne', check=False, stderr=subprocess.STDOUT)
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert out == b'Executable `dne` not found'
|
assert out == b'Executable `dne` not found'
|
||||||
|
|
||||||
|
@ -101,7 +99,7 @@ def test_cmd_output_no_shebang(tmpdir, fn):
|
||||||
make_executable(f)
|
make_executable(f)
|
||||||
|
|
||||||
# previously this raised `OSError` -- the output is platform specific
|
# previously this raised `OSError` -- the output is platform specific
|
||||||
ret, out, _ = fn(str(f), retcode=None, stderr=subprocess.STDOUT)
|
ret, out, _ = fn(str(f), check=False, stderr=subprocess.STDOUT)
|
||||||
assert ret == 1
|
assert ret == 1
|
||||||
assert isinstance(out, bytes)
|
assert isinstance(out, bytes)
|
||||||
assert out.endswith(b'\n')
|
assert out.endswith(b'\n')
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -3,7 +3,7 @@ envlist = py37,py38,pypy3,pre-commit
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
deps = -rrequirements-dev.txt
|
deps = -rrequirements-dev.txt
|
||||||
passenv = APPDATA HOME LOCALAPPDATA PROGRAMFILES RUSTUP_HOME
|
passenv = *
|
||||||
commands =
|
commands =
|
||||||
coverage erase
|
coverage erase
|
||||||
coverage run -m pytest {posargs:tests}
|
coverage run -m pytest {posargs:tests}
|
||||||
|
@ -23,5 +23,6 @@ env =
|
||||||
GIT_COMMITTER_NAME=test
|
GIT_COMMITTER_NAME=test
|
||||||
GIT_AUTHOR_EMAIL=test@example.com
|
GIT_AUTHOR_EMAIL=test@example.com
|
||||||
GIT_COMMITTER_EMAIL=test@example.com
|
GIT_COMMITTER_EMAIL=test@example.com
|
||||||
|
GIT_ALLOW_PROTOCOL=file
|
||||||
VIRTUALENV_NO_DOWNLOAD=1
|
VIRTUALENV_NO_DOWNLOAD=1
|
||||||
PRE_COMMIT_NO_CONCURRENCY=1
|
PRE_COMMIT_NO_CONCURRENCY=1
|
||||||
|
|
Loading…
Add table
Reference in a new issue