Amend approach for no-commit-to-branch to use regex matching based on

feedback. Adds --pattern optional argument which can be used alongside
--branch to block commits to a branch which matches a supplied
regex expression
This commit is contained in:
Marc Jay 2019-04-20 13:46:49 +01:00
parent d6847c4827
commit 8d2785b9d6
3 changed files with 22 additions and 9 deletions

View file

@ -79,10 +79,12 @@ Add this to your `.pre-commit-config.yaml`
- `name-tests-test` - Assert that files in tests/ end in `_test.py`. - `name-tests-test` - Assert that files in tests/ end in `_test.py`.
- Use `args: ['--django']` to match `test*.py` instead. - Use `args: ['--django']` to match `test*.py` instead.
- `no-commit-to-branch` - Protect specific branches from direct checkins. - `no-commit-to-branch` - Protect specific branches from direct checkins.
- Use `args: [--branch, staging, --branch, master, --branch, release/*]` to set the branch. - Use `args: [--branch, staging, --branch, master]` to set the branch.
`master` is the default if no argument is set. `master` is the default if no argument is set.
- `-b` / `--branch` may be specified multiple times to protect multiple - `-b` / `--branch` may be specified multiple times to protect multiple
branches. branches.
- `-p` / `--pattern` can be used to protect branches that match a supplied regex
(e.g. `--pattern, release/.*`). May be specified multiple times.
- `pretty-format-json` - Checks that all your JSON files are pretty. "Pretty" - `pretty-format-json` - Checks that all your JSON files are pretty. "Pretty"
here means that keys are sorted and indented. You can configure this with here means that keys are sorted and indented. You can configure this with
the following commandline options: the following commandline options:

View file

@ -1,7 +1,7 @@
from __future__ import print_function from __future__ import print_function
import argparse import argparse
import fnmatch import re
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import Set from typing import Set
@ -10,14 +10,17 @@ from pre_commit_hooks.util import CalledProcessError
from pre_commit_hooks.util import cmd_output from pre_commit_hooks.util import cmd_output
def is_on_branch(protected): # type: (Set[str]) -> bool def is_on_branch(protected, patterns=set()):
# type: (Set[str], Set[str]) -> bool
try: try:
ref_name = cmd_output('git', 'symbolic-ref', 'HEAD') ref_name = cmd_output('git', 'symbolic-ref', 'HEAD')
except CalledProcessError: except CalledProcessError:
return False return False
chunks = ref_name.strip().split('/') chunks = ref_name.strip().split('/')
branch_name = '/'.join(chunks[2:]) branch_name = '/'.join(chunks[2:])
return any(fnmatch.fnmatch(branch_name, s) for s in protected) return branch_name in protected or any(
re.match(p, branch_name) for p in patterns
)
def main(argv=None): # type: (Optional[Sequence[str]]) -> int def main(argv=None): # type: (Optional[Sequence[str]]) -> int
@ -26,10 +29,18 @@ def main(argv=None): # type: (Optional[Sequence[str]]) -> int
'-b', '--branch', action='append', '-b', '--branch', action='append',
help='branch to disallow commits to, may be specified multiple times', help='branch to disallow commits to, may be specified multiple times',
) )
parser.add_argument(
'-p', '--pattern', action='append',
help=(
'regex pattern for branch name to disallow commits to, '
'May be specified multiple times'
),
)
args = parser.parse_args(argv) args = parser.parse_args(argv)
protected = set(args.branch or ('master',)) protected = set(args.branch or ('master',))
return int(is_on_branch(protected)) patterns = set(args.pattern or ())
return int(is_on_branch(protected, patterns))
if __name__ == '__main__': if __name__ == '__main__':

View file

@ -44,17 +44,17 @@ def test_forbid_multiple_branches(temp_git_dir, branch_name):
assert main(('--branch', 'b1', '--branch', 'b2')) assert main(('--branch', 'b1', '--branch', 'b2'))
def test_branch_wildcard_fail(temp_git_dir): def test_branch_pattern_fail(temp_git_dir):
with temp_git_dir.as_cwd(): with temp_git_dir.as_cwd():
cmd_output('git', 'checkout', '-b', 'another/branch') cmd_output('git', 'checkout', '-b', 'another/branch')
assert is_on_branch({'another/*'}) is True assert is_on_branch(set(), {'another/.*'}) is True
@pytest.mark.parametrize('branch_name', ('master', 'another/branch')) @pytest.mark.parametrize('branch_name', ('master', 'another/branch'))
def test_branch_wildcard_multiple_branches_fail(temp_git_dir, branch_name): def test_branch_pattern_multiple_branches_fail(temp_git_dir, branch_name):
with temp_git_dir.as_cwd(): with temp_git_dir.as_cwd():
cmd_output('git', 'checkout', '-b', branch_name) cmd_output('git', 'checkout', '-b', branch_name)
assert main(('--branch', 'master', '--branch', 'another/*')) assert main(('--branch', 'master', '--pattern', 'another/.*'))
def test_main_default_call(temp_git_dir): def test_main_default_call(temp_git_dir):