Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New actions for GitCommitBear #2927

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 22 additions & 10 deletions bears/vcs/CommitBear.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from coalib.settings.Setting import typed_list
from coalib.settings.FunctionMetadata import FunctionMetadata
from dependency_management.requirements.PipRequirement import PipRequirement
from bears.vcs.actions.EditCommitMessageAction import EditCommitMessageAction
from bears.vcs.actions.AddNewlineAction import AddNewlineAction


class _CommitBear(GlobalBear):
Expand Down Expand Up @@ -188,21 +190,24 @@ def check_shortlog(self, shortlog,
'character(s). This is {} character(s) longer than '
'the limit ({} > {}).'.format(
len(shortlog), diff,
len(shortlog), shortlog_length))
len(shortlog), shortlog_length),
actions=[EditCommitMessageAction()])

if (shortlog[-1] != '.') == shortlog_trailing_period:
yield Result(self,
'Shortlog of HEAD commit contains no period at end.'
if shortlog_trailing_period else
'Shortlog of HEAD commit contains a period at end.')
'Shortlog of HEAD commit contains a period at end.',
actions=[EditCommitMessageAction()])

if shortlog_regex:
match = re.fullmatch(shortlog_regex, shortlog)
if not match:
yield Result(
self,
'Shortlog of HEAD commit does not match given regex:'
' {regex}'.format(regex=shortlog_regex))
' {regex}'.format(regex=shortlog_regex),
actions=[EditCommitMessageAction()])

if shortlog_imperative_check:
colon_pos = shortlog.find(':')
Expand All @@ -214,7 +219,8 @@ def check_shortlog(self, shortlog,
bad_word = has_flaws[0]
yield Result(self,
"Shortlog of HEAD commit isn't in imperative "
"mood! Bad words are '{}'".format(bad_word))
"mood! Bad words are '{}'".format(bad_word),
actions=[EditCommitMessageAction()])
if shortlog_wip_check:
if 'wip' in shortlog.lower()[:4]:
yield Result(
Expand Down Expand Up @@ -267,12 +273,16 @@ def check_body(self, body,
"""
if len(body) == 0:
if force_body:
yield Result(self, 'No commit message body at HEAD.')
yield Result(self, 'No commit message body at HEAD.',
actions=[EditCommitMessageAction()])
return

if body[0] != '\n':
yield Result(self, 'No newline found between shortlog and body at '
'HEAD commit. Please add one.')
yield Result(self,
'No newline found between shortlog and body at '
'HEAD commit. Please add one.',
actions=[EditCommitMessageAction(),
AddNewlineAction()])
return

if body_regex and not re.fullmatch(body_regex, body.strip()):
Expand All @@ -284,9 +294,11 @@ def check_body(self, body,
if any((len(line) > body_line_length and
not any(regex.search(line) for regex in ignore_regexes))
for line in body[1:]):
yield Result(self, 'Body of HEAD commit contains too long lines. '
'Commit body lines should not exceed {} '
'characters.'.format(body_line_length))
yield Result(self,
'Body of HEAD commit contains too long lines. '
'Commit body lines should not exceed {} '
'characters.'.format(body_line_length),
actions=[EditCommitMessageAction()])

def check_issue_reference(self, body,
body_close_issue: bool = False,
Expand Down
35 changes: 35 additions & 0 deletions bears/vcs/actions/AddNewlineAction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from coalib.misc.Shell import run_shell_command
from coalib.results.result_actions.ResultAction import ResultAction


class AddNewlineAction(ResultAction):
"""
Adds a newline between shortlog and body of the commit message
of the HEAD commit.
"""

SUCCESS_MESSAGE = 'New Line added successfully.'

def is_applicable(self,
result,
original_file_dict,
file_diff_dict,
applied_actions=()):
new_message, _ = run_shell_command('git log -1 --pretty=%B')
Copy link
Member Author

@akshatkarani akshatkarani Jul 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

User can first apply EditCommitMessageAction and change the commit message, so to check if AddNewlineAction is applicable or not, commit message is extracted first.

new_message = new_message.rstrip('\n')
pos = new_message.find('\n')
self.shortlog = new_message[:pos] if pos != -1 else new_message
self.body = new_message[pos+1:] if pos != -1 else ''
if self.body[0] != '\n':
return True
else:
return False

def apply(self, result, original_file_dict, file_diff_dict):
"""
Add New(L)ine [Note: This may rewrite your commit history]
"""
new_commit_message = '{}\n\n{}'.format(self.shortlog, self.body)
command = 'git commit -o --amend -m "{}"'.format(new_commit_message)
stdout, err = run_shell_command(command)
return file_diff_dict
17 changes: 17 additions & 0 deletions bears/vcs/actions/EditCommitMessageAction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import subprocess
from coalib.results.result_actions.ResultAction import ResultAction


class EditCommitMessageAction(ResultAction):
"""
Opens an editor to edit the commit message of the HEAD commit.
"""

SUCCESS_MESSAGE = 'Commit message edited successfully.'

def apply(self, result, original_file_dict, file_diff_dict):
"""
Edit (C)ommit Message [Note: This may rewrite your commit history]
"""
subprocess.check_call(['git', 'commit', '-o', '--amend'])
return file_diff_dict
Empty file added bears/vcs/actions/__init__.py
Empty file.
96 changes: 96 additions & 0 deletions tests/vcs/actions/AddNewlineActionTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import unittest
import os
import platform
import shutil
from tempfile import mkdtemp, mkstemp
from unittest.mock import Mock

from coalib.results.Result import Result
from bears.vcs.actions.AddNewlineAction import AddNewlineAction
from coala_utils.ContextManagers import retrieve_stdout
from coalib.misc.Shell import run_shell_command


class AddNewlineActionTest(unittest.TestCase):

@staticmethod
def run_git_command(*args, stdin=None):
return run_shell_command(' '.join(('git',) + args), stdin)

def setUp(self):
self.shortlog = 'file.py: Add something'
self.body = ('Added something, wrote some things\n'
'Wrote tests\n'
'\n'
'Fixes #issue')
self.uut = AddNewlineAction()
self.result = Result('origin', 'message')

# Creating a temporary git repository and
# adding a commit to test
self._old_cwd = os.getcwd()
self.gitdir = mkdtemp()
os.chdir(self.gitdir)
self.gitfile = mkstemp(dir=self.gitdir)
self.run_git_command('init')
self.run_git_command('config', 'user.email coala@coala.io')
self.run_git_command('config', 'user.name coala')
self.msg = self.shortlog + '\n' + self.body
self.run_git_command('add .')
self.run_git_command('commit',
'--file=-',
stdin=self.msg)

def tearDown(self):
# Deleting the temporary repository
os.chdir(self._old_cwd)
if platform.system() == 'Windows':
onerror = self._windows_rmtree_remove_readonly
else:
onerror = None
shutil.rmtree(self.gitdir, onerror=onerror)

def test_is_applicable_apply(self):
# Applicable because there is no newline between shortlog and body
self.assertTrue(self.uut.is_applicable(self.result, {}, {}))

with retrieve_stdout() as stdout:
self.uut.apply(self.result, {}, {})
new_message, _ = run_shell_command('git log -1 --pretty=%B')
new_message = new_message.rstrip('\n')
self.assertEqual(new_message,
self.shortlog + '\n\n' + self.body)
self.assertEqual(stdout.getvalue(), '')

# Not applicable after action is applied
self.assertFalse(self.uut.is_applicable(self.result, {}, {}))

# Undoing the amend done by applying the action
self.run_git_command('commit',
'--amend',
'--file=-',
stdin=self.msg)

def test_is_applicable_edited_message(self):
# Applicable because there is no newline between shortlog and body
self.assertTrue(self.uut.is_applicable(self.result, {}, {}))

# Mocking EditCommitMessageAction to test cases where user first
# changes commit message by appying EditCommitMessageAction, then
# checking the applicability of AddNewlineAction
EditCommitMessageAction = Mock()
edited_msg1 = ('This is new commit message\n'
'Still no new line')
edited_msg2 = ('This is lastest commit message\n'
'\n'
'Finally a new line!!')

EditCommitMessageAction.apply.side_effect = self.run_git_command(
'commit', '--amend', '--file=-', stdin=edited_msg1)
EditCommitMessageAction.apply()
self.assertTrue(self.uut.is_applicable(self.result, {}, {}))

EditCommitMessageAction.apply.side_effect = self.run_git_command(
'commit', '--amend', '--file=-', stdin=edited_msg2)
EditCommitMessageAction.apply()
self.assertFalse(self.uut.is_applicable(self.result, {}, {}))
24 changes: 24 additions & 0 deletions tests/vcs/actions/EditCommitMessageActionTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import unittest
from unittest.mock import patch
from coala_utils.ContextManagers import retrieve_stdout
from coalib.results.Result import Result
from bears.vcs.actions.EditCommitMessageAction import EditCommitMessageAction


class EditCommitMessageActionTest(unittest.TestCase):

def setUp(self):
self.uut = EditCommitMessageAction()
self.result = Result('origin', 'message')

def test_is_applicable(self):
self.assertTrue(self.uut.is_applicable(self.result, {}, {}))

def test_apply(self):
with retrieve_stdout() as stdout:
patcher = ('bears.vcs.actions.EditCommitMessageAction.'
'subprocess.check_call')
with patch(patcher):
ret = self.uut.apply(self.result, {}, {'file': 'diff'})
self.assertEqual(ret, {'file': 'diff'})
self.assertEqual(stdout.getvalue(), '')
Empty file added tests/vcs/actions/__init__.py
Empty file.