Merging upstream version 2.16.0.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
2b79a68a34
commit
ada93679f9
34 changed files with 301 additions and 126 deletions
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
|
@ -1,2 +0,0 @@
|
|||
github: asottile
|
||||
open_collective: pre-commit
|
41
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug.yaml
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
name: bug report
|
||||
description: something went wrong
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
this is for issues for `pre-commit` (the framework).
|
||||
if you are reporting an issue for [pre-commit.ci] please report it at [pre-commit-ci/issues]
|
||||
|
||||
[pre-commit.ci]: https://pre-commit.ci
|
||||
[pre-commit-ci/issues]: https://github.com/pre-commit-ci/issues
|
||||
- type: textarea
|
||||
id: freeform
|
||||
attributes:
|
||||
label: describe your issue
|
||||
placeholder: 'I was doing ... I ran ... I expected ... I got ...'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: pre-commit --version
|
||||
placeholder: pre-commit x.x.x
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: configuration
|
||||
attributes:
|
||||
label: .pre-commit-config.yaml
|
||||
description: (auto-rendered as yaml, no need for backticks)
|
||||
placeholder: 'repos: ...'
|
||||
render: yaml
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: error-log
|
||||
attributes:
|
||||
label: '~/.cache/pre-commit/pre-commit.log (if present)'
|
||||
placeholder: "### version information\n..."
|
||||
validations:
|
||||
required: false
|
|
@ -12,7 +12,7 @@ repos:
|
|||
- id: requirements-txt-fixer
|
||||
- id: double-quote-string-fixer
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 3.9.2
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies: [flake8-typing-imports==1.10.0]
|
||||
|
@ -21,11 +21,11 @@ repos:
|
|||
hooks:
|
||||
- id: autopep8
|
||||
- repo: https://github.com/pre-commit/pre-commit
|
||||
rev: v2.15.0
|
||||
rev: v2.16.0
|
||||
hooks:
|
||||
- id: validate_manifest
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.25.0
|
||||
rev: v2.29.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py36-plus]
|
||||
|
@ -35,16 +35,16 @@ repos:
|
|||
- id: reorder-python-imports
|
||||
args: [--py3-plus]
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v2.1.0
|
||||
rev: v2.2.1
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
args: [--py36-plus]
|
||||
- repo: https://github.com/asottile/setup-cfg-fmt
|
||||
rev: v1.17.0
|
||||
rev: v1.20.0
|
||||
hooks:
|
||||
- id: setup-cfg-fmt
|
||||
- repo: https://github.com/pre-commit/mirrors-mypy
|
||||
rev: v0.910
|
||||
rev: v0.910-1
|
||||
hooks:
|
||||
- id: mypy
|
||||
additional_dependencies: [types-all]
|
||||
|
|
30
CHANGELOG.md
30
CHANGELOG.md
|
@ -1,3 +1,33 @@
|
|||
2.16.0 - 2021-11-30
|
||||
===================
|
||||
|
||||
### Features
|
||||
- add warning for regexes containing `[\/]` or `[/\\]`.
|
||||
- #2053 PR by @radek-sprta.
|
||||
- #2043 issue by @asottile.
|
||||
- move hook template back to `bash` resolving shebang-portability issues.
|
||||
- #2065 PR by @asottile.
|
||||
- add support for `fail_fast` at the individual hook level.
|
||||
- #2097 PR by @colens3.
|
||||
- #1143 issue by @potiuk.
|
||||
- allow passthrough of `GIT_CONFIG_KEY_*`, `GIT_CONFIG_VALUE_*`, and
|
||||
`GIT_CONFIG_COUNT`.
|
||||
- #2136 PR by @emzeat.
|
||||
|
||||
### Fixes
|
||||
- fix pre-commit autoupdate for `core.useBuiltinFSMonitor=true` on windows.
|
||||
- #2047 PR by @asottile.
|
||||
- #2046 issue by @lcnittl.
|
||||
- fix temporary file stashing with for `submodule.recurse=1`.
|
||||
- #2071 PR by @asottile.
|
||||
- #2063 issue by @a666.
|
||||
- ban broken importlib-resources versions.
|
||||
- #2098 PR by @asottile.
|
||||
- replace `exit(...)` with `raise SystemExit(...)` for portability.
|
||||
- #2103 PR by @asottile.
|
||||
- #2104 PR by @asottile.
|
||||
|
||||
|
||||
2.15.0 - 2021-09-02
|
||||
===================
|
||||
|
||||
|
|
|
@ -2,4 +2,4 @@ from pre_commit.main import main
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -70,6 +70,7 @@ MANIFEST_HOOK_DICT = cfgv.Map(
|
|||
),
|
||||
cfgv.Optional('args', cfgv.check_array(cfgv.check_string), []),
|
||||
cfgv.Optional('always_run', cfgv.check_bool, False),
|
||||
cfgv.Optional('fail_fast', cfgv.check_bool, False),
|
||||
cfgv.Optional('pass_filenames', cfgv.check_bool, True),
|
||||
cfgv.Optional('description', cfgv.check_string, ''),
|
||||
cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
|
||||
|
@ -143,6 +144,18 @@ class OptionalSensibleRegexAtHook(cfgv.OptionalNoDefault):
|
|||
f"regex, not a glob -- matching '/*' probably isn't what you "
|
||||
f'want here',
|
||||
)
|
||||
if r'[\/]' in dct.get(self.key, ''):
|
||||
logger.warning(
|
||||
fr'pre-commit normalizes slashes in the {self.key!r} field '
|
||||
fr'in hook {dct.get("id")!r} to forward slashes, so you '
|
||||
fr'can use / instead of [\/]',
|
||||
)
|
||||
if r'[/\\]' in dct.get(self.key, ''):
|
||||
logger.warning(
|
||||
fr'pre-commit normalizes slashes in the {self.key!r} field '
|
||||
fr'in hook {dct.get("id")!r} to forward slashes, so you '
|
||||
fr'can use / instead of [/\\]',
|
||||
)
|
||||
|
||||
|
||||
class OptionalSensibleRegexAtTop(cfgv.OptionalNoDefault):
|
||||
|
@ -154,6 +167,18 @@ class OptionalSensibleRegexAtTop(cfgv.OptionalNoDefault):
|
|||
f'The top-level {self.key!r} field is a regex, not a glob -- '
|
||||
f"matching '/*' probably isn't what you want here",
|
||||
)
|
||||
if r'[\/]' in dct.get(self.key, ''):
|
||||
logger.warning(
|
||||
fr'pre-commit normalizes the slashes in the top-level '
|
||||
fr'{self.key!r} field to forward slashes, so you can use / '
|
||||
fr'instead of [\/]',
|
||||
)
|
||||
if r'[/\\]' in dct.get(self.key, ''):
|
||||
logger.warning(
|
||||
fr'pre-commit normalizes the slashes in the top-level '
|
||||
fr'{self.key!r} field to forward slashes, so you can use / '
|
||||
fr'instead of [/\\]',
|
||||
)
|
||||
|
||||
|
||||
class MigrateShaToRev:
|
||||
|
|
|
@ -36,24 +36,36 @@ class RevInfo(NamedTuple):
|
|||
return cls(config['repo'], config['rev'], None)
|
||||
|
||||
def update(self, tags_only: bool, freeze: bool) -> 'RevInfo':
|
||||
git_cmd = ('git', *git.NO_FS_MONITOR)
|
||||
|
||||
if tags_only:
|
||||
tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--abbrev=0')
|
||||
tag_cmd = (
|
||||
*git_cmd, 'describe',
|
||||
'FETCH_HEAD', '--tags', '--abbrev=0',
|
||||
)
|
||||
else:
|
||||
tag_cmd = ('git', 'describe', 'FETCH_HEAD', '--tags', '--exact')
|
||||
tag_cmd = (
|
||||
*git_cmd, 'describe',
|
||||
'FETCH_HEAD', '--tags', '--exact',
|
||||
)
|
||||
|
||||
with tmpdir() as tmp:
|
||||
git.init_repo(tmp, self.repo)
|
||||
cmd_output_b('git', 'fetch', 'origin', 'HEAD', '--tags', cwd=tmp)
|
||||
cmd_output_b(
|
||||
*git_cmd, 'fetch', 'origin', 'HEAD', '--tags',
|
||||
cwd=tmp,
|
||||
)
|
||||
|
||||
try:
|
||||
rev = cmd_output(*tag_cmd, cwd=tmp)[1].strip()
|
||||
except CalledProcessError:
|
||||
cmd = ('git', 'rev-parse', 'FETCH_HEAD')
|
||||
cmd = (*git_cmd, 'rev-parse', 'FETCH_HEAD')
|
||||
rev = cmd_output(*cmd, cwd=tmp)[1].strip()
|
||||
|
||||
frozen = None
|
||||
if freeze:
|
||||
exact = cmd_output('git', 'rev-parse', rev, cwd=tmp)[1].strip()
|
||||
exact_rev_cmd = (*git_cmd, 'rev-parse', rev)
|
||||
exact = cmd_output(*exact_rev_cmd, cwd=tmp)[1].strip()
|
||||
if exact != rev:
|
||||
rev, frozen = exact, rev
|
||||
return self._replace(rev=rev, frozen=frozen)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import itertools
|
||||
import logging
|
||||
import os.path
|
||||
import shlex
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Optional
|
||||
|
@ -100,19 +101,17 @@ def _install_hook_script(
|
|||
args = ['hook-impl', f'--config={config_file}', f'--hook-type={hook_type}']
|
||||
if skip_on_missing_config:
|
||||
args.append('--skip-on-missing-config')
|
||||
params = {'INSTALL_PYTHON': sys.executable, 'ARGS': args}
|
||||
|
||||
with open(hook_path, 'w') as hook_file:
|
||||
contents = resource_text('hook-tmpl')
|
||||
before, rest = contents.split(TEMPLATE_START)
|
||||
to_template, after = rest.split(TEMPLATE_END)
|
||||
|
||||
before = before.replace('#!/usr/bin/env python3', shebang())
|
||||
_, after = rest.split(TEMPLATE_END)
|
||||
|
||||
hook_file.write(before + TEMPLATE_START)
|
||||
for line in to_template.splitlines():
|
||||
var = line.split()[0]
|
||||
hook_file.write(f'{var} = {params[var]!r}\n')
|
||||
hook_file.write(f'INSTALL_PYTHON={shlex.quote(sys.executable)}\n')
|
||||
# TODO: python3.8+: shlex.join
|
||||
args_s = ' '.join(shlex.quote(part) for part in args)
|
||||
hook_file.write(f'ARGS=({args_s})\n')
|
||||
hook_file.write(TEMPLATE_END + after)
|
||||
make_executable(hook_path)
|
||||
|
||||
|
|
|
@ -290,7 +290,7 @@ def _run_hooks(
|
|||
verbose=args.verbose, use_color=args.color,
|
||||
)
|
||||
retval |= current_retval
|
||||
if retval and config['fail_fast']:
|
||||
if retval and (config['fail_fast'] or hook.fail_fast):
|
||||
break
|
||||
if retval and args.show_diff_on_failure and prior_diff:
|
||||
if args.all_files:
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import sys
|
||||
|
||||
if sys.version_info < (3, 8): # pragma: no cover (<PY38)
|
||||
import importlib_metadata
|
||||
else: # pragma: no cover (PY38+)
|
||||
if sys.version_info >= (3, 8): # pragma: >=3.8 cover
|
||||
import importlib.metadata as importlib_metadata
|
||||
else: # pragma: <3.8 cover
|
||||
import importlib_metadata
|
||||
|
||||
CONFIG_FILE = '.pre-commit-config.yaml'
|
||||
MANIFEST_FILE = '.pre-commit-hooks.yaml'
|
||||
|
|
|
@ -12,9 +12,11 @@ from pre_commit.util import CalledProcessError
|
|||
from pre_commit.util import cmd_output
|
||||
from pre_commit.util import cmd_output_b
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# see #2046
|
||||
NO_FS_MONITOR = ('-c', 'core.useBuiltinFSMonitor=false')
|
||||
|
||||
|
||||
def zsplit(s: str) -> List[str]:
|
||||
s = s.strip('\0')
|
||||
|
@ -39,9 +41,10 @@ def no_git_env(
|
|||
return {
|
||||
k: v for k, v in _env.items()
|
||||
if not k.startswith('GIT_') or
|
||||
k.startswith(('GIT_CONFIG_KEY_', 'GIT_CONFIG_VALUE_')) or
|
||||
k in {
|
||||
'GIT_EXEC_PATH', 'GIT_SSH', 'GIT_SSH_COMMAND', 'GIT_SSL_CAINFO',
|
||||
'GIT_SSL_NO_VERIFY',
|
||||
'GIT_SSL_NO_VERIFY', 'GIT_CONFIG_COUNT',
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,10 +188,11 @@ def init_repo(path: str, remote: str) -> None:
|
|||
if os.path.isdir(remote):
|
||||
remote = os.path.abspath(remote)
|
||||
|
||||
git = ('git', *NO_FS_MONITOR)
|
||||
env = no_git_env()
|
||||
# avoid the user's template so that hooks do not recurse
|
||||
cmd_output_b('git', 'init', '--template=', path, env=env)
|
||||
cmd_output_b('git', 'remote', 'add', 'origin', remote, cwd=path, env=env)
|
||||
cmd_output_b(*git, 'init', '--template=', path, env=env)
|
||||
cmd_output_b(*git, 'remote', 'add', 'origin', remote, cwd=path, env=env)
|
||||
|
||||
|
||||
def commit(repo: str = '.') -> None:
|
||||
|
|
|
@ -27,6 +27,7 @@ class Hook(NamedTuple):
|
|||
additional_dependencies: Sequence[str]
|
||||
args: Sequence[str]
|
||||
always_run: bool
|
||||
fail_fast: bool
|
||||
pass_filenames: bool
|
||||
description: str
|
||||
language_version: str
|
||||
|
|
|
@ -124,4 +124,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -411,4 +411,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -39,4 +39,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -77,4 +77,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -13,4 +13,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -1,44 +1,20 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env bash
|
||||
# File generated by pre-commit: https://pre-commit.com
|
||||
# ID: 138fd403232d2ddd5efb44317e38bf03
|
||||
import os
|
||||
import sys
|
||||
|
||||
# we try our best, but the shebang of this script is difficult to determine:
|
||||
# - macos doesn't ship with python3
|
||||
# - windows executables are almost always `python.exe`
|
||||
# therefore we continue to support python2 for this small script
|
||||
if sys.version_info < (3, 3):
|
||||
from distutils.spawn import find_executable as which
|
||||
else:
|
||||
from shutil import which
|
||||
|
||||
# work around https://github.com/Homebrew/homebrew-core/issues/30445
|
||||
os.environ.pop('__PYVENV_LAUNCHER__', None)
|
||||
|
||||
# start templated
|
||||
INSTALL_PYTHON = ''
|
||||
ARGS = ['hook-impl']
|
||||
INSTALL_PYTHON=''
|
||||
ARGS=(hook-impl)
|
||||
# end templated
|
||||
ARGS.extend(('--hook-dir', os.path.realpath(os.path.dirname(__file__))))
|
||||
ARGS.append('--')
|
||||
ARGS.extend(sys.argv[1:])
|
||||
|
||||
DNE = '`pre-commit` not found. Did you forget to activate your virtualenv?'
|
||||
if os.access(INSTALL_PYTHON, os.X_OK):
|
||||
CMD = [INSTALL_PYTHON, '-mpre_commit']
|
||||
elif which('pre-commit'):
|
||||
CMD = ['pre-commit']
|
||||
else:
|
||||
raise SystemExit(DNE)
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
ARGS+=(--hook-dir "$HERE" -- "$@")
|
||||
|
||||
CMD.extend(ARGS)
|
||||
if sys.platform == 'win32': # https://bugs.python.org/issue19124
|
||||
import subprocess
|
||||
|
||||
if sys.version_info < (3, 7): # https://bugs.python.org/issue25942
|
||||
raise SystemExit(subprocess.Popen(CMD).wait())
|
||||
else:
|
||||
raise SystemExit(subprocess.call(CMD))
|
||||
else:
|
||||
os.execvp(CMD[0], CMD)
|
||||
if [ -x "$INSTALL_PYTHON" ]; then
|
||||
exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
|
||||
elif command -v pre-commit > /dev/null; then
|
||||
exec pre-commit "${ARGS[@]}"
|
||||
else
|
||||
echo '`pre-commit` not found. Did you forget to activate your virtualenv?' 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
@ -13,6 +13,12 @@ from pre_commit.xargs import xargs
|
|||
|
||||
logger = logging.getLogger('pre_commit')
|
||||
|
||||
# without forcing submodule.recurse=0, changes in nested submodules will be
|
||||
# discarded if `submodule.recurse=1` is configured
|
||||
# we choose this instead of `--no-recurse-submodules` because it works on
|
||||
# versions of git before that option was added to `git checkout`
|
||||
_CHECKOUT_CMD = ('git', '-c', 'submodule.recurse=0', 'checkout', '--', '.')
|
||||
|
||||
|
||||
def _git_apply(patch: str) -> None:
|
||||
args = ('apply', '--whitespace=nowarn', patch)
|
||||
|
@ -58,7 +64,7 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
|
|||
|
||||
# prevent recursive post-checkout hooks (#1418)
|
||||
no_checkout_env = dict(os.environ, _PRE_COMMIT_SKIP_POST_CHECKOUT='1')
|
||||
cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)
|
||||
cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
|
||||
|
||||
try:
|
||||
yield
|
||||
|
@ -74,7 +80,7 @@ def _unstaged_changes_cleared(patch_dir: str) -> Generator[None, None, None]:
|
|||
# We failed to apply the patch, presumably due to fixes made
|
||||
# by hooks.
|
||||
# Roll back the changes made by hooks.
|
||||
cmd_output_b('git', 'checkout', '--', '.', env=no_checkout_env)
|
||||
cmd_output_b(*_CHECKOUT_CMD, env=no_checkout_env)
|
||||
_git_apply(patch_filename)
|
||||
|
||||
logger.info(f'Restored changes from {patch_filename}.')
|
||||
|
|
|
@ -21,10 +21,10 @@ import yaml
|
|||
|
||||
from pre_commit import parse_shebang
|
||||
|
||||
if sys.version_info >= (3, 7): # pragma: no cover (PY37+)
|
||||
if sys.version_info >= (3, 7): # pragma: >=3.7 cover
|
||||
from importlib.resources import open_binary
|
||||
from importlib.resources import read_text
|
||||
else: # pragma: no cover (<PY37)
|
||||
else: # pragma: <3.7 cover
|
||||
from importlib_resources import open_binary
|
||||
from importlib_resources import read_text
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
covdefaults
|
||||
covdefaults>=2.1
|
||||
coverage
|
||||
distlib
|
||||
pytest
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[metadata]
|
||||
name = pre_commit
|
||||
version = 2.15.0
|
||||
version = 2.16.0
|
||||
description = A framework for managing and maintaining multi-language pre-commit hooks.
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
|
@ -17,6 +17,7 @@ classifiers =
|
|||
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 :: PyPy
|
||||
|
||||
|
@ -30,7 +31,7 @@ install_requires =
|
|||
toml
|
||||
virtualenv>=20.0.8
|
||||
importlib-metadata;python_version<"3.8"
|
||||
importlib-resources;python_version<"3.7"
|
||||
importlib-resources<5.3;python_version<"3.7"
|
||||
python_requires = >=3.6.1
|
||||
|
||||
[options.packages.find]
|
||||
|
|
|
@ -25,4 +25,4 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -80,4 +80,4 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -68,4 +68,4 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -106,4 +106,4 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -45,4 +45,4 @@ def main() -> int:
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main())
|
||||
raise SystemExit(main())
|
||||
|
|
|
@ -247,38 +247,64 @@ def test_warn_mutable_rev_conditional():
|
|||
cfgv.validate(config_obj, CONFIG_REPO_DICT)
|
||||
|
||||
|
||||
def test_validate_optional_sensible_regex_at_hook_level(caplog):
|
||||
config_obj = {
|
||||
'id': 'flake8',
|
||||
'files': 'dir/*.py',
|
||||
}
|
||||
cfgv.validate(config_obj, CONFIG_HOOK_DICT)
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
@pytest.mark.parametrize(
|
||||
('regex', 'warning'),
|
||||
(
|
||||
(
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
r'dir/*.py',
|
||||
"The 'files' field in hook 'flake8' is a regex, not a glob -- "
|
||||
"matching '/*' probably isn't what you want here",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def test_validate_optional_sensible_regex_at_top_level(caplog):
|
||||
(
|
||||
r'dir[\/].*\.py',
|
||||
r"pre-commit normalizes slashes in the 'files' field in hook "
|
||||
r"'flake8' to forward slashes, so you can use / instead of [\/]",
|
||||
),
|
||||
(
|
||||
r'dir[/\\].*\.py',
|
||||
r"pre-commit normalizes slashes in the 'files' field in hook "
|
||||
r"'flake8' to forward slashes, so you can use / instead of [/\\]",
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_validate_optional_sensible_regex_at_hook(caplog, regex, warning):
|
||||
config_obj = {
|
||||
'files': 'dir/*.py',
|
||||
'id': 'flake8',
|
||||
'files': regex,
|
||||
}
|
||||
cfgv.validate(config_obj, CONFIG_HOOK_DICT)
|
||||
|
||||
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
('regex', 'warning'),
|
||||
(
|
||||
(
|
||||
r'dir/*.py',
|
||||
"The top-level 'files' field is a regex, not a glob -- "
|
||||
"matching '/*' probably isn't what you want here",
|
||||
),
|
||||
(
|
||||
r'dir[\/].*\.py',
|
||||
r"pre-commit normalizes the slashes in the top-level 'files' "
|
||||
r'field to forward slashes, so you can use / instead of [\/]',
|
||||
),
|
||||
(
|
||||
r'dir[/\\].*\.py',
|
||||
r"pre-commit normalizes the slashes in the top-level 'files' "
|
||||
r'field to forward slashes, so you can use / instead of [/\\]',
|
||||
),
|
||||
),
|
||||
)
|
||||
def test_validate_optional_sensible_regex_at_top_level(caplog, regex, warning):
|
||||
config_obj = {
|
||||
'files': regex,
|
||||
'repos': [],
|
||||
}
|
||||
cfgv.validate(config_obj, CONFIG_SCHEMA)
|
||||
|
||||
assert caplog.record_tuples == [
|
||||
(
|
||||
'pre_commit',
|
||||
logging.WARNING,
|
||||
"The top-level 'files' field is a regex, not a glob -- matching "
|
||||
"'/*' probably isn't what you want here",
|
||||
),
|
||||
]
|
||||
assert caplog.record_tuples == [('pre_commit', logging.WARNING, warning)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('fn', (validate_config_main, validate_manifest_main))
|
||||
|
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
import yaml
|
||||
|
||||
import pre_commit.constants as C
|
||||
from pre_commit import envcontext
|
||||
from pre_commit import git
|
||||
from pre_commit import util
|
||||
from pre_commit.commands.autoupdate import _check_hooks_still_exist_at_rev
|
||||
|
@ -176,6 +177,14 @@ def test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store):
|
|||
assert cfg.read() == fmt.format(out_of_date.path, out_of_date.head_rev)
|
||||
|
||||
|
||||
def test_autoupdate_with_core_useBuiltinFSMonitor(out_of_date, tmpdir, store):
|
||||
# force the setting on "globally" for git
|
||||
home = tmpdir.join('fakehome').ensure_dir()
|
||||
home.join('.gitconfig').write('[core]\nuseBuiltinFSMonitor = true\n')
|
||||
with envcontext.envcontext((('HOME', str(home)),)):
|
||||
test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store)
|
||||
|
||||
|
||||
def test_autoupdate_pure_yaml(out_of_date, tmpdir, store):
|
||||
with mock.patch.object(util, 'Dumper', yaml.SafeDumper):
|
||||
test_autoupdate_out_of_date_repo(out_of_date, tmpdir, store)
|
||||
|
|
|
@ -278,11 +278,7 @@ def test_environment_not_sourced(tempdir_factory, store):
|
|||
hook = os.path.join(path, '.git/hooks/pre-commit')
|
||||
with open(hook) as f:
|
||||
src = f.read()
|
||||
src = re.sub(
|
||||
'\nINSTALL_PYTHON =.*\n',
|
||||
'\nINSTALL_PYTHON = "/dne"\n',
|
||||
src,
|
||||
)
|
||||
src = re.sub('\nINSTALL_PYTHON=.*\n', '\nINSTALL_PYTHON="/dne"\n', src)
|
||||
with open(hook, 'w') as f:
|
||||
f.write(src)
|
||||
|
||||
|
|
|
@ -985,6 +985,18 @@ def test_fail_fast(cap_out, store, repo_with_failing_hook):
|
|||
assert printed.count(b'Failing hook') == 1
|
||||
|
||||
|
||||
def test_fail_fast_per_hook(cap_out, store, repo_with_failing_hook):
|
||||
with modify_config() as config:
|
||||
# More than one hook
|
||||
config['repos'][0]['hooks'] *= 2
|
||||
config['repos'][0]['hooks'][0]['fail_fast'] = True
|
||||
stage_a_file()
|
||||
|
||||
ret, printed = _do_run(cap_out, store, repo_with_failing_hook, run_opts())
|
||||
# it should have only run one hook
|
||||
assert printed.count(b'Failing hook') == 1
|
||||
|
||||
|
||||
def test_classifier_removes_dne():
|
||||
classifier = Classifier(('this_file_does_not_exist',))
|
||||
assert classifier.filenames == []
|
||||
|
|
|
@ -227,6 +227,11 @@ def test_no_git_env():
|
|||
'GIT_SSH': '/usr/bin/ssh',
|
||||
'GIT_SSH_COMMAND': 'ssh -o',
|
||||
'GIT_DIR': '/none/shall/pass',
|
||||
'GIT_CONFIG_KEY_0': 'user.name',
|
||||
'GIT_CONFIG_VALUE_0': 'anthony',
|
||||
'GIT_CONFIG_KEY_1': 'user.email',
|
||||
'GIT_CONFIG_VALUE_1': 'asottile@example.com',
|
||||
'GIT_CONFIG_COUNT': '2',
|
||||
}
|
||||
no_git_env = git.no_git_env(env)
|
||||
assert no_git_env == {
|
||||
|
@ -234,6 +239,11 @@ def test_no_git_env():
|
|||
'GIT_EXEC_PATH': '/some/git/exec/path',
|
||||
'GIT_SSH': '/usr/bin/ssh',
|
||||
'GIT_SSH_COMMAND': 'ssh -o',
|
||||
'GIT_CONFIG_KEY_0': 'user.name',
|
||||
'GIT_CONFIG_VALUE_0': 'anthony',
|
||||
'GIT_CONFIG_KEY_1': 'user.email',
|
||||
'GIT_CONFIG_VALUE_1': 'asottile@example.com',
|
||||
'GIT_CONFIG_COUNT': '2',
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -111,8 +111,8 @@ def test_local_conda_additional_dependencies(store):
|
|||
'name': 'local-conda',
|
||||
'entry': 'python',
|
||||
'language': 'conda',
|
||||
'args': ['-c', 'import tzdata; print("OK")'],
|
||||
'additional_dependencies': ['python-tzdata'],
|
||||
'args': ['-c', 'import botocore; print("OK")'],
|
||||
'additional_dependencies': ['botocore'],
|
||||
}],
|
||||
}
|
||||
hook = _get_hook(config, store, 'local-conda')
|
||||
|
@ -164,7 +164,7 @@ def test_python_hook_weird_setup_cfg(in_git_dir, tempdir_factory, store):
|
|||
)
|
||||
|
||||
|
||||
def test_python_venv(tempdir_factory, store): # pragma: no cover (no venv)
|
||||
def test_python_venv(tempdir_factory, store):
|
||||
_test_hook_repo(
|
||||
tempdir_factory, store, 'python_venv_hooks_repo',
|
||||
'foo', [os.devnull],
|
||||
|
@ -245,7 +245,6 @@ def test_run_a_docker_image_hook(tempdir_factory, store, hook_id):
|
|||
)
|
||||
|
||||
|
||||
@xfailif_windows # pragma: win32 no cover
|
||||
def test_run_a_node_hook(tempdir_factory, store):
|
||||
_test_hook_repo(
|
||||
tempdir_factory, store, 'node_hooks_repo',
|
||||
|
@ -253,7 +252,6 @@ def test_run_a_node_hook(tempdir_factory, store):
|
|||
)
|
||||
|
||||
|
||||
@xfailif_windows # pragma: win32 no cover
|
||||
def test_run_a_node_hook_default_version(tempdir_factory, store):
|
||||
# make sure that this continues to work for platforms where node is not
|
||||
# installed at the system
|
||||
|
@ -263,7 +261,6 @@ def test_run_a_node_hook_default_version(tempdir_factory, store):
|
|||
test_run_a_node_hook(tempdir_factory, store)
|
||||
|
||||
|
||||
@xfailif_windows # pragma: win32 no cover
|
||||
def test_run_versioned_node_hook(tempdir_factory, store):
|
||||
_test_hook_repo(
|
||||
tempdir_factory, store, 'node_versioned_hooks_repo',
|
||||
|
@ -271,7 +268,6 @@ def test_run_versioned_node_hook(tempdir_factory, store):
|
|||
)
|
||||
|
||||
|
||||
@xfailif_windows # pragma: win32 no cover
|
||||
def test_node_hook_with_npm_userconfig_set(tempdir_factory, store, tmpdir):
|
||||
cfg = tmpdir.join('cfg')
|
||||
cfg.write('cache=/dne\n')
|
||||
|
@ -653,7 +649,6 @@ def test_additional_ruby_dependencies_installed(tempdir_factory, store):
|
|||
assert 'tins' in output
|
||||
|
||||
|
||||
@xfailif_windows # pragma: win32 no cover
|
||||
def test_additional_node_dependencies_installed(tempdir_factory, store):
|
||||
path = make_repo(tempdir_factory, 'node_hooks_repo')
|
||||
config = make_config_from_repo(path)
|
||||
|
@ -1007,6 +1002,7 @@ def test_manifest_hooks(tempdir_factory, store):
|
|||
types=['file'],
|
||||
types_or=[],
|
||||
verbose=False,
|
||||
fail_fast=False,
|
||||
)
|
||||
|
||||
|
||||
|
@ -1025,13 +1021,13 @@ def test_local_perl_additional_dependencies(store):
|
|||
'name': 'hello',
|
||||
'entry': 'perltidy --version',
|
||||
'language': 'perl',
|
||||
'additional_dependencies': ['SHANCOCK/Perl-Tidy-20200110.tar.gz'],
|
||||
'additional_dependencies': ['SHANCOCK/Perl-Tidy-20211029.tar.gz'],
|
||||
}],
|
||||
}
|
||||
hook = _get_hook(config, store, 'hello')
|
||||
ret, out = _hook_run(hook, (), color=False)
|
||||
assert ret == 0
|
||||
assert _norm_out(out).startswith(b'This is perltidy, v20200110')
|
||||
assert _norm_out(out).startswith(b'This is perltidy, v20211029')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
|
|
@ -181,9 +181,11 @@ def test_img_conflict(img_staged, patch_dir):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def submodule_with_commits(tempdir_factory):
|
||||
def repo_with_commits(tempdir_factory):
|
||||
path = git_dir(tempdir_factory)
|
||||
with cwd(path):
|
||||
open('foo', 'a+').close()
|
||||
cmd_output('git', 'add', 'foo')
|
||||
git_commit()
|
||||
rev1 = cmd_output('git', 'rev-parse', 'HEAD')[1].strip()
|
||||
git_commit()
|
||||
|
@ -196,18 +198,21 @@ def checkout_submodule(rev):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def sub_staged(submodule_with_commits, tempdir_factory):
|
||||
def sub_staged(repo_with_commits, tempdir_factory):
|
||||
path = git_dir(tempdir_factory)
|
||||
with cwd(path):
|
||||
open('bar', 'a+').close()
|
||||
cmd_output('git', 'add', 'bar')
|
||||
git_commit()
|
||||
cmd_output(
|
||||
'git', 'submodule', 'add', submodule_with_commits.path, 'sub',
|
||||
'git', 'submodule', 'add', repo_with_commits.path, 'sub',
|
||||
)
|
||||
checkout_submodule(submodule_with_commits.rev1)
|
||||
checkout_submodule(repo_with_commits.rev1)
|
||||
cmd_output('git', 'add', 'sub')
|
||||
yield auto_namedtuple(
|
||||
path=path,
|
||||
sub_path=os.path.join(path, 'sub'),
|
||||
submodule=submodule_with_commits,
|
||||
submodule=repo_with_commits,
|
||||
)
|
||||
|
||||
|
||||
|
@ -242,6 +247,34 @@ def test_sub_something_unstaged(sub_staged, patch_dir):
|
|||
_test_sub_state(sub_staged, 'rev2', 'AM')
|
||||
|
||||
|
||||
def test_submodule_does_not_discard_changes(sub_staged, patch_dir):
|
||||
with open('bar', 'w') as f:
|
||||
f.write('unstaged changes')
|
||||
|
||||
foo_path = os.path.join(sub_staged.sub_path, 'foo')
|
||||
with open(foo_path, 'w') as f:
|
||||
f.write('foo contents')
|
||||
|
||||
with staged_files_only(patch_dir):
|
||||
with open('bar') as f:
|
||||
assert f.read() == ''
|
||||
|
||||
with open(foo_path) as f:
|
||||
assert f.read() == 'foo contents'
|
||||
|
||||
with open('bar') as f:
|
||||
assert f.read() == 'unstaged changes'
|
||||
|
||||
with open(foo_path) as f:
|
||||
assert f.read() == 'foo contents'
|
||||
|
||||
|
||||
def test_submodule_does_not_discard_changes_recurse(sub_staged, patch_dir):
|
||||
cmd_output('git', 'config', 'submodule.recurse', '1', cwd=sub_staged.path)
|
||||
|
||||
test_submodule_does_not_discard_changes(sub_staged, patch_dir)
|
||||
|
||||
|
||||
def test_stage_utf8_changes(foo_staged, patch_dir):
|
||||
contents = '\u2603'
|
||||
with open('foo', 'w', encoding='UTF-8') as foo_file:
|
||||
|
|
Loading…
Add table
Reference in a new issue