summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.pre-commit-config.yaml8
-rw-r--r--CHANGELOG.md20
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--pre_commit/commands/hook_impl.py18
-rw-r--r--pre_commit/commands/run.py10
-rw-r--r--pre_commit/languages/helpers.py5
-rw-r--r--pre_commit/languages/ruby.py1
-rw-r--r--pre_commit/main.py97
-rw-r--r--pre_commit/parse_shebang.py5
-rw-r--r--pre_commit/util.py4
-rw-r--r--setup.cfg2
-rw-r--r--testing/fixtures.py2
-rw-r--r--testing/util.py4
-rw-r--r--tests/commands/hook_impl_test.py36
-rw-r--r--tests/commands/run_test.py7
-rw-r--r--tests/conftest.py18
-rw-r--r--tests/error_handler_test.py6
-rw-r--r--tests/repository_test.py7
18 files changed, 165 insertions, 87 deletions
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 7791f76..94a35a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.2.0
+ rev: v4.3.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -14,7 +14,7 @@ repos:
hooks:
- id: setup-cfg-fmt
- repo: https://github.com/asottile/reorder_python_imports
- rev: v3.1.0
+ rev: v3.3.0
hooks:
- id: reorder-python-imports
exclude: ^(pre_commit/resources/|testing/resources/python3_hooks_repo/)
@@ -25,7 +25,7 @@ repos:
- id: add-trailing-comma
args: [--py36-plus]
- repo: https://github.com/asottile/pyupgrade
- rev: v2.32.0
+ rev: v2.34.0
hooks:
- id: pyupgrade
args: [--py37-plus]
@@ -38,7 +38,7 @@ repos:
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.950
+ rev: v0.961
hooks:
- id: mypy
additional_dependencies: [types-all]
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b6d8b6..03a7c80 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,23 @@
+2.20.0 - 2022-07-10
+===================
+
+### Features
+- Expose `source` and `object-name` (positional args) of `prepare-commit-msg`
+ hook as `PRE_COMMIT_COMIT_MSG_SOURCE` and `PRE_COMMIT_COMMIT_OBJECT_NAME`.
+ - #2407 PR by @M-Whitaker.
+ - #2406 issue by @M-Whitaker.
+
+### Fixes
+- Fix `language: ruby` installs when `--user-install` is set in gemrc.
+ - #2394 PR by @narpfel.
+ - #2393 issue by @narpfel.
+- Adjust pty setup for solaris.
+ - #2390 PR by @gaige.
+ - #2389 issue by @gaige.
+- Remove unused `--config` option from `gc`, `sample-config`,
+ `validate-config`, `validate-manifest` sub-commands.
+ - #2429 PR by @asottile.
+
2.19.0 - 2022-05-05
===================
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fa1678c..310c17e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -72,7 +72,7 @@ to implement. The current implemented languages are at varying levels:
- 3rd class - pre-commit requires the user to install both the tool and the
language globally (current examples: script, system)
-"third class" is usually the easiest to implement first and is perfectly
+"second class" is usually the easiest to implement first and is perfectly
acceptable.
Ideally the language works on the supported platforms for pre-commit (linux,
diff --git a/pre_commit/commands/hook_impl.py b/pre_commit/commands/hook_impl.py
index f315c04..f5995e9 100644
--- a/pre_commit/commands/hook_impl.py
+++ b/pre_commit/commands/hook_impl.py
@@ -76,6 +76,8 @@ def _ns(
remote_name: str | None = None,
remote_url: str | None = None,
commit_msg_filename: str | None = None,
+ prepare_commit_message_source: str | None = None,
+ commit_object_name: str | None = None,
checkout_type: str | None = None,
is_squash_merge: str | None = None,
rewrite_command: str | None = None,
@@ -90,6 +92,8 @@ def _ns(
remote_name=remote_name,
remote_url=remote_url,
commit_msg_filename=commit_msg_filename,
+ prepare_commit_message_source=prepare_commit_message_source,
+ commit_object_name=commit_object_name,
all_files=all_files,
checkout_type=checkout_type,
is_squash_merge=is_squash_merge,
@@ -202,8 +206,20 @@ def _run_ns(
_check_args_length(hook_type, args)
if hook_type == 'pre-push':
return _pre_push_ns(color, args, stdin)
- elif hook_type in {'commit-msg', 'prepare-commit-msg'}:
+ elif hook_type in 'commit-msg':
return _ns(hook_type, color, commit_msg_filename=args[0])
+ elif hook_type == 'prepare-commit-msg' and len(args) == 1:
+ return _ns(hook_type, color, commit_msg_filename=args[0])
+ elif hook_type == 'prepare-commit-msg' and len(args) == 2:
+ return _ns(
+ hook_type, color, commit_msg_filename=args[0],
+ prepare_commit_message_source=args[1],
+ )
+ elif hook_type == 'prepare-commit-msg' and len(args) == 3:
+ return _ns(
+ hook_type, color, commit_msg_filename=args[0],
+ prepare_commit_message_source=args[1], commit_object_name=args[2],
+ )
elif hook_type in {'post-commit', 'pre-merge-commit', 'pre-commit'}:
return _ns(hook_type, color)
elif hook_type == 'post-checkout':
diff --git a/pre_commit/commands/run.py b/pre_commit/commands/run.py
index 37f989b..ad3d766 100644
--- a/pre_commit/commands/run.py
+++ b/pre_commit/commands/run.py
@@ -361,6 +361,16 @@ def run(
):
return 0
+ # Expose prepare_commit_message_source / commit_object_name
+ # as environment variables for the hooks
+ if args.prepare_commit_message_source:
+ environ['PRE_COMMIT_COMMIT_MSG_SOURCE'] = (
+ args.prepare_commit_message_source
+ )
+
+ if args.commit_object_name:
+ environ['PRE_COMMIT_COMMIT_OBJECT_NAME'] = args.commit_object_name
+
# Expose from-ref / to-ref as environment variables for hooks to consume
if args.from_ref and args.to_ref:
# legacy names
diff --git a/pre_commit/languages/helpers.py b/pre_commit/languages/helpers.py
index 05a7165..0be08b5 100644
--- a/pre_commit/languages/helpers.py
+++ b/pre_commit/languages/helpers.py
@@ -5,9 +5,9 @@ import os
import random
import re
from typing import Any
+from typing import NoReturn
from typing import overload
from typing import Sequence
-from typing import TYPE_CHECKING
import pre_commit.constants as C
from pre_commit import parse_shebang
@@ -16,9 +16,6 @@ from pre_commit.prefix import Prefix
from pre_commit.util import cmd_output_b
from pre_commit.xargs import xargs
-if TYPE_CHECKING:
- from typing import NoReturn
-
FIXED_RANDOM_SEED = 1542676187
SHIMS_RE = re.compile(r'[/\\]shims[/\\]')
diff --git a/pre_commit/languages/ruby.py b/pre_commit/languages/ruby.py
index 6c5cff2..8955dd0 100644
--- a/pre_commit/languages/ruby.py
+++ b/pre_commit/languages/ruby.py
@@ -138,6 +138,7 @@ def install_environment(
(
'gem', 'install',
'--no-document', '--no-format-executable',
+ '--no-user-install',
*prefix.star('.gem'), *additional_dependencies,
),
)
diff --git a/pre_commit/main.py b/pre_commit/main.py
index 6d2814b..b4fa966 100644
--- a/pre_commit/main.py
+++ b/pre_commit/main.py
@@ -108,6 +108,20 @@ def _add_run_options(parser: argparse.ArgumentParser) -> None:
help='Filename to check when running during `commit-msg`',
)
parser.add_argument(
+ '--prepare-commit-message-source',
+ help=(
+ 'Source of the commit message '
+ '(typically the second argument to .git/hooks/prepare-commit-msg)'
+ ),
+ )
+ parser.add_argument(
+ '--commit-object-name',
+ help=(
+ 'Commit object name '
+ '(typically the third argument to .git/hooks/prepare-commit-msg)'
+ ),
+ )
+ parser.add_argument(
'--remote-name', help='Remote name used by `git push`.',
)
parser.add_argument('--remote-url', help='Remote url used by `git push`.')
@@ -167,11 +181,15 @@ def main(argv: Sequence[str] | None = None) -> int:
subparsers = parser.add_subparsers(dest='command')
- autoupdate_parser = subparsers.add_parser(
+ def _add_cmd(name: str, *, help: str) -> argparse.ArgumentParser:
+ parser = subparsers.add_parser(name, help=help)
+ add_color_option(parser)
+ return parser
+
+ autoupdate_parser = _add_cmd(
'autoupdate',
help="Auto-update pre-commit config to the latest repos' versions.",
)
- add_color_option(autoupdate_parser)
_add_config_option(autoupdate_parser)
autoupdate_parser.add_argument(
'--bleeding-edge', action='store_true',
@@ -189,34 +207,17 @@ def main(argv: Sequence[str] | None = None) -> int:
help='Only update this repository -- may be specified multiple times.',
)
- clean_parser = subparsers.add_parser(
- 'clean', help='Clean out pre-commit files.',
- )
- add_color_option(clean_parser)
- _add_config_option(clean_parser)
-
- hook_impl_parser = subparsers.add_parser('hook-impl')
- add_color_option(hook_impl_parser)
- _add_config_option(hook_impl_parser)
- hook_impl_parser.add_argument('--hook-type')
- hook_impl_parser.add_argument('--hook-dir')
- hook_impl_parser.add_argument(
- '--skip-on-missing-config', action='store_true',
- )
- hook_impl_parser.add_argument(dest='rest', nargs=argparse.REMAINDER)
+ _add_cmd('clean', help='Clean out pre-commit files.')
- gc_parser = subparsers.add_parser('gc', help='Clean unused cached repos.')
- add_color_option(gc_parser)
- _add_config_option(gc_parser)
+ _add_cmd('gc', help='Clean unused cached repos.')
- init_templatedir_parser = subparsers.add_parser(
+ init_templatedir_parser = _add_cmd(
'init-templatedir',
help=(
'Install hook script in a directory intended for use with '
'`git config init.templateDir`.'
),
)
- add_color_option(init_templatedir_parser)
_add_config_option(init_templatedir_parser)
init_templatedir_parser.add_argument(
'directory', help='The directory in which to write the hook script.',
@@ -229,10 +230,7 @@ def main(argv: Sequence[str] | None = None) -> int:
)
_add_hook_type_option(init_templatedir_parser)
- install_parser = subparsers.add_parser(
- 'install', help='Install the pre-commit script.',
- )
- add_color_option(install_parser)
+ install_parser = _add_cmd('install', help='Install the pre-commit script.')
_add_config_option(install_parser)
install_parser.add_argument(
'-f', '--overwrite', action='store_true',
@@ -254,7 +252,7 @@ def main(argv: Sequence[str] | None = None) -> int:
),
)
- install_hooks_parser = subparsers.add_parser(
+ install_hooks_parser = _add_cmd(
'install-hooks',
help=(
'Install hook environments for all environments in the config '
@@ -262,32 +260,24 @@ def main(argv: Sequence[str] | None = None) -> int:
'useful.'
),
)
- add_color_option(install_hooks_parser)
_add_config_option(install_hooks_parser)
- migrate_config_parser = subparsers.add_parser(
+ migrate_config_parser = _add_cmd(
'migrate-config',
help='Migrate list configuration to new map configuration.',
)
- add_color_option(migrate_config_parser)
_add_config_option(migrate_config_parser)
- run_parser = subparsers.add_parser('run', help='Run hooks.')
- add_color_option(run_parser)
+ run_parser = _add_cmd('run', help='Run hooks.')
_add_config_option(run_parser)
_add_run_options(run_parser)
- sample_config_parser = subparsers.add_parser(
- 'sample-config', help=f'Produce a sample {C.CONFIG_FILE} file',
- )
- add_color_option(sample_config_parser)
- _add_config_option(sample_config_parser)
+ _add_cmd('sample-config', help=f'Produce a sample {C.CONFIG_FILE} file')
- try_repo_parser = subparsers.add_parser(
+ try_repo_parser = _add_cmd(
'try-repo',
help='Try the hooks in a repository, useful for developing new hooks.',
)
- add_color_option(try_repo_parser)
_add_config_option(try_repo_parser)
try_repo_parser.add_argument(
'repo', help='Repository to source hooks from.',
@@ -301,32 +291,39 @@ def main(argv: Sequence[str] | None = None) -> int:
)
_add_run_options(try_repo_parser)
- uninstall_parser = subparsers.add_parser(
+ uninstall_parser = _add_cmd(
'uninstall', help='Uninstall the pre-commit script.',
)
- add_color_option(uninstall_parser)
_add_config_option(uninstall_parser)
_add_hook_type_option(uninstall_parser)
- validate_config_parser = subparsers.add_parser(
+ validate_config_parser = _add_cmd(
'validate-config', help='Validate .pre-commit-config.yaml files',
)
- add_color_option(validate_config_parser)
- _add_config_option(validate_config_parser)
validate_config_parser.add_argument('filenames', nargs='*')
- validate_manifest_parser = subparsers.add_parser(
+ validate_manifest_parser = _add_cmd(
'validate-manifest', help='Validate .pre-commit-hooks.yaml files',
)
- add_color_option(validate_manifest_parser)
- _add_config_option(validate_manifest_parser)
validate_manifest_parser.add_argument('filenames', nargs='*')
+ # does not use `_add_cmd` because it doesn't use `--color`
help = subparsers.add_parser(
'help', help='Show help for a specific command.',
)
help.add_argument('help_cmd', nargs='?', help='Command to show help for.')
+ # not intended for users to call this directly
+ hook_impl_parser = subparsers.add_parser('hook-impl')
+ add_color_option(hook_impl_parser)
+ _add_config_option(hook_impl_parser)
+ hook_impl_parser.add_argument('--hook-type')
+ hook_impl_parser.add_argument('--hook-dir')
+ hook_impl_parser.add_argument(
+ '--skip-on-missing-config', action='store_true',
+ )
+ hook_impl_parser.add_argument(dest='rest', nargs=argparse.REMAINDER)
+
# argparse doesn't really provide a way to use a `default` subparser
if len(argv) == 0:
argv = ['run']
@@ -340,11 +337,11 @@ def main(argv: Sequence[str] | None = None) -> int:
with error_handler(), logging_handler(args.color):
git.check_for_cygwin_mismatch()
+ store = Store()
+
if args.command not in COMMANDS_NO_GIT:
_adjust_args_and_chdir(args)
-
- store = Store()
- store.mark_config_used(args.config)
+ store.mark_config_used(args.config)
if args.command == 'autoupdate':
return autoupdate(
diff --git a/pre_commit/parse_shebang.py b/pre_commit/parse_shebang.py
index 3fd3129..3ac933c 100644
--- a/pre_commit/parse_shebang.py
+++ b/pre_commit/parse_shebang.py
@@ -2,13 +2,10 @@ from __future__ import annotations
import os.path
from typing import Mapping
-from typing import TYPE_CHECKING
+from typing import NoReturn
from identify.identify import parse_shebang_from_file
-if TYPE_CHECKING:
- from typing import NoReturn
-
class ExecutableNotFoundError(OSError):
def to_output(self) -> tuple[int, bytes, None]:
diff --git a/pre_commit/util.py b/pre_commit/util.py
index 40c53e5..8c296f4 100644
--- a/pre_commit/util.py
+++ b/pre_commit/util.py
@@ -168,10 +168,10 @@ if os.name != 'nt': # pragma: win32 no cover
self.r, self.w = openpty()
# tty flags normally change \n to \r\n
- attrs = termios.tcgetattr(self.r)
+ attrs = termios.tcgetattr(self.w)
assert isinstance(attrs[1], int)
attrs[1] &= ~(termios.ONLCR | termios.OPOST)
- termios.tcsetattr(self.r, termios.TCSANOW, attrs)
+ termios.tcsetattr(self.w, termios.TCSANOW, attrs)
return self
diff --git a/setup.cfg b/setup.cfg
index 93a485c..ae214f6 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = pre_commit
-version = 2.19.0
+version = 2.20.0
description = A framework for managing and maintaining multi-language pre-commit hooks.
long_description = file: README.md
long_description_content_type = text/markdown
diff --git a/testing/fixtures.py b/testing/fixtures.py
index ef5a041..5182a08 100644
--- a/testing/fixtures.py
+++ b/testing/fixtures.py
@@ -38,7 +38,7 @@ def copy_tree_to_path(src_dir, dest_dir):
def git_dir(tempdir_factory):
path = tempdir_factory.get()
- cmd_output('git', 'init', path)
+ cmd_output('git', '-c', 'init.defaultBranch=master', 'init', path)
return path
diff --git a/testing/util.py b/testing/util.py
index 0dd1784..e807f04 100644
--- a/testing/util.py
+++ b/testing/util.py
@@ -76,6 +76,8 @@ def run_opts(
hook_stage='commit',
show_diff_on_failure=False,
commit_msg_filename='',
+ prepare_commit_message_source='',
+ commit_object_name='',
checkout_type='',
is_squash_merge='',
rewrite_command='',
@@ -97,6 +99,8 @@ def run_opts(
hook_stage=hook_stage,
show_diff_on_failure=show_diff_on_failure,
commit_msg_filename=commit_msg_filename,
+ prepare_commit_message_source=prepare_commit_message_source,
+ commit_object_name=commit_object_name,
checkout_type=checkout_type,
is_squash_merge=is_squash_merge,
rewrite_command=rewrite_command,
diff --git a/tests/commands/hook_impl_test.py b/tests/commands/hook_impl_test.py
index 3e20874..aa321da 100644
--- a/tests/commands/hook_impl_test.py
+++ b/tests/commands/hook_impl_test.py
@@ -154,6 +154,42 @@ def test_run_ns_commit_msg():
assert ns.commit_msg_filename == '.git/COMMIT_MSG'
+def test_run_ns_prepare_commit_msg_one_arg():
+ ns = hook_impl._run_ns(
+ 'prepare-commit-msg', False,
+ ('.git/COMMIT_MSG',), b'',
+ )
+ assert ns is not None
+ assert ns.hook_stage == 'prepare-commit-msg'
+ assert ns.color is False
+ assert ns.commit_msg_filename == '.git/COMMIT_MSG'
+
+
+def test_run_ns_prepare_commit_msg_two_arg():
+ ns = hook_impl._run_ns(
+ 'prepare-commit-msg', False,
+ ('.git/COMMIT_MSG', 'message'), b'',
+ )
+ assert ns is not None
+ assert ns.hook_stage == 'prepare-commit-msg'
+ assert ns.color is False
+ assert ns.commit_msg_filename == '.git/COMMIT_MSG'
+ assert ns.prepare_commit_message_source == 'message'
+
+
+def test_run_ns_prepare_commit_msg_three_arg():
+ ns = hook_impl._run_ns(
+ 'prepare-commit-msg', False,
+ ('.git/COMMIT_MSG', 'message', 'HEAD'), b'',
+ )
+ assert ns is not None
+ assert ns.hook_stage == 'prepare-commit-msg'
+ assert ns.color is False
+ assert ns.commit_msg_filename == '.git/COMMIT_MSG'
+ assert ns.prepare_commit_message_source == 'message'
+ assert ns.commit_object_name == 'HEAD'
+
+
def test_run_ns_post_commit():
ns = hook_impl._run_ns('post-commit', True, (), b'')
assert ns is not None
diff --git a/tests/commands/run_test.py b/tests/commands/run_test.py
index 085b063..2634c0c 100644
--- a/tests/commands/run_test.py
+++ b/tests/commands/run_test.py
@@ -810,7 +810,12 @@ def test_prepare_commit_msg_hook(cap_out, store, prepare_commit_msg_repo):
cap_out,
store,
prepare_commit_msg_repo,
- {'hook_stage': 'prepare-commit-msg', 'commit_msg_filename': filename},
+ {
+ 'hook_stage': 'prepare-commit-msg',
+ 'commit_msg_filename': filename,
+ 'prepare_commit_message_source': 'commit',
+ 'commit_object_name': 'HEAD',
+ },
expected_outputs=[b'Add "Signed off by:"', b'Passed'],
expected_ret=0,
stage=False,
diff --git a/tests/conftest.py b/tests/conftest.py
index b68a1d0..40c0c05 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -21,24 +21,6 @@ from testing.util import cwd
from testing.util import git_commit
-@pytest.fixture(autouse=True)
-def no_warnings(recwarn):
- yield
- warnings = []
- for warning in recwarn: # pragma: no cover
- message = str(warning.message)
- # ImportWarning: Not importing directory '...' missing __init__(.py)
- if not (
- isinstance(warning.message, ImportWarning) and
- message.startswith('Not importing directory ') and
- ' missing __init__' in message
- ):
- warnings.append(
- f'{warning.filename}:{warning.lineno} {message}',
- )
- assert not warnings
-
-
@pytest.fixture
def tempdir_factory(tmpdir):
class TmpdirFactory:
diff --git a/tests/error_handler_test.py b/tests/error_handler_test.py
index 31c71d2..47e2afa 100644
--- a/tests/error_handler_test.py
+++ b/tests/error_handler_test.py
@@ -45,9 +45,11 @@ def test_error_handler_fatal_error(mocked_log_and_exit):
r'Traceback \(most recent call last\):\n'
r' File ".+pre_commit.error_handler.py", line \d+, in error_handler\n'
r' yield\n'
+ r'( \^\^\^\^\^\n)?'
r' File ".+tests.error_handler_test.py", line \d+, '
r'in test_error_handler_fatal_error\n'
r' raise exc\n'
+ r'( \^\^\^\^\^\^\^\^\^\n)?'
r'(pre_commit\.errors\.)?FatalError: just a test\n',
)
pattern.assert_matches(mocked_log_and_exit.call_args[0][3])
@@ -69,9 +71,11 @@ def test_error_handler_uncaught_error(mocked_log_and_exit):
r'Traceback \(most recent call last\):\n'
r' File ".+pre_commit.error_handler.py", line \d+, in error_handler\n'
r' yield\n'
+ r'( \^\^\^\^\^\n)?'
r' File ".+tests.error_handler_test.py", line \d+, '
r'in test_error_handler_uncaught_error\n'
r' raise exc\n'
+ r'( \^\^\^\^\^\^\^\^\^\n)?'
r'ValueError: another test\n',
)
pattern.assert_matches(mocked_log_and_exit.call_args[0][3])
@@ -93,9 +97,11 @@ def test_error_handler_keyboardinterrupt(mocked_log_and_exit):
r'Traceback \(most recent call last\):\n'
r' File ".+pre_commit.error_handler.py", line \d+, in error_handler\n'
r' yield\n'
+ r'( \^\^\^\^\^\n)?'
r' File ".+tests.error_handler_test.py", line \d+, '
r'in test_error_handler_keyboardinterrupt\n'
r' raise exc\n'
+ r'( \^\^\^\^\^\^\^\^\^\n)?'
r'KeyboardInterrupt\n',
)
pattern.assert_matches(mocked_log_and_exit.call_args[0][3])
diff --git a/tests/repository_test.py b/tests/repository_test.py
index 3729ab1..11d452c 100644
--- a/tests/repository_test.py
+++ b/tests/repository_test.py
@@ -332,6 +332,13 @@ def test_run_a_ruby_hook(tempdir_factory, store):
)
+def test_run_a_ruby_hook_with_user_install_set(tempdir_factory, store, tmpdir):
+ gemrc = tmpdir.join('gemrc')
+ gemrc.write('gem: --user-install\n')
+ with envcontext((('GEMRC', str(gemrc)),)):
+ test_run_a_ruby_hook(tempdir_factory, store)
+
+
@xfailif_windows # pragma: win32 no cover
def test_run_versioned_ruby_hook(tempdir_factory, store):
_test_hook_repo(