def test_git_no_commits_error(self, sh): # No commits: returned by 'git log' err = b"fatal: your current branch 'master' does not have any commits yet" sh.git.side_effect = ErrorReturnCode("git log -1 --pretty=%H", b"", err) expected_msg = "Current branch has no commits. Gitlint requires at least one commit to function." with self.assertRaisesMessage(GitContextError, expected_msg): GitContext.from_local_repository("fåke/path") # assert that commit message was read using git command sh.git.assert_called_once_with("log", "-1", "--pretty=%H", **self.expected_sh_special_args) sh.git.reset_mock() # Unknown reference 'HEAD' commits: returned by 'git rev-parse' err = (b"HEAD" b"fatal: ambiguous argument 'HEAD': unknown revision or path not in the working tree." b"Use '--' to separate paths from revisions, like this:" b"'git <command> [<revision>...] -- [<file>...]'") sh.git.side_effect = [ "#\n", # git config --get core.commentchar ErrorReturnCode("rev-parse --abbrev-ref HEAD", b"", err) ] with self.assertRaisesMessage(GitContextError, expected_msg): context = GitContext.from_commit_msg("test") context.current_branch # assert that commit message was read using git command sh.git.assert_called_with("rev-parse", "--abbrev-ref", "HEAD", _tty_out=False, _cwd=None)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] try: if sys.stdin.isatty(): # If target has not been set explicitly before, fallback to the current directory gitcontext = GitContext.from_local_repository(lint_config.target) else: gitcontext = GitContext.from_commit_msg(sys.stdin.read()) except GitContextError as e: click.echo(str(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message config_builder.set_config_from_commit(last_commit) lint_config = config_builder.build(lint_config) # Let's get linting! linter = GitLinter(lint_config) violations = linter.lint(last_commit) linter.print_violations(violations) exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def test_from_commit_msg_fixup_squash_commit(self): commit_types = ["fixup", "squash"] for commit_type in commit_types: commit_msg = f"{commit_type}! Test message" gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, commit_msg) self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertEqual(len(gitcontext.commits), 1) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_revert_commit) # Asserting that squash and fixup are correct for type in commit_types: attr = "is_" + type + "_commit" self.assertEqual(getattr(commit, attr), commit_type == type)
def test_from_commit_msg_full(self): gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample1")) expected_title = u"Commit title contåining 'WIP', as well as trailing punctuation." expected_body = [ "This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", u"This line has a tråiling space. ", "This line has a trailing tab.\t" ] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + u"\n# This is a cömmented line\n" commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, expected_title) self.assertEqual(commit.message.body, expected_body) self.assertEqual(commit.message.full, expected_full) self.assertEqual(commit.message.original, expected_original) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def gitcontext(commit_msg_str, changed_files=None): """ Utility method to easily create gitcontext objects based on a given commit msg string and an optional set of changed files""" gitcontext = GitContext.from_commit_msg(commit_msg_str) commit = gitcontext.commits[-1] if changed_files: commit.changed_files = changed_files return gitcontext
def gitcontext(commit_msg_str, changed_files=None): """ Utility method to easily create gitcontext objects based on a given commit msg string and set of changed files""" gitcontext = GitContext.from_commit_msg(commit_msg_str) commit = gitcontext.commits[-1] if changed_files: commit.changed_files = changed_files return gitcontext
def test_set_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample2")) self.assertEqual(gitcontext.commits[-1].message.title, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].message.original, "Just a title containing WIP") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_empty(self): gitcontext = GitContext.from_commit_msg("") self.assertEqual(gitcontext.commits[-1].message.title, "") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "") self.assertEqual(gitcontext.commits[-1].message.original, "") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def gitcontext(commit_msg_str, changed_files=None, ): """ Utility method to easily create gitcontext objects based on a given commit msg string and an optional set of changed files""" with patch("gitlint.git.git_commentchar") as comment_char: comment_char.return_value = u"#" gitcontext = GitContext.from_commit_msg(commit_msg_str) commit = gitcontext.commits[-1] if changed_files: commit.changed_files = changed_files return gitcontext
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg("Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].message.title, "Title") self.assertEqual(gitcontext.commits[-1].message.body, ["", "Body 1", "Body 2"]) self.assertEqual(gitcontext.commits[-1].message.full, "Title\n\nBody 1\nBody 2") self.assertEqual(gitcontext.commits[-1].message.original, "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] try: if sys.stdin.isatty(): # If target has not been set explicitly before, fallback to the current directory gitcontext = GitContext.from_local_repository(lint_config.target, ctx.obj[2]) else: stdin_str = ustr(sys.stdin.read()) gitcontext = GitContext.from_commit_msg(stdin_str) except GitContextError as e: click.echo(ustr(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) number_of_commits = len(gitcontext.commits) # Exit if we don't have commits in the specified range. Use a 0 exit code, since a popular use-case is one # where users are using --commits in a check job to check the commit messages inside a CI job. By returning 0, we # ensure that these jobs don't fail if for whatever reason the specified commit range is empty. if number_of_commits == 0: click.echo(u'No commits in range "{0}".'.format(ctx.obj[2])) ctx.exit(0) general_config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Let's get linting! first_violation = True exit_code = 0 for commit in gitcontext.commits: # Build a config_builder and linter taking into account the commit specific config (if any) config_builder = general_config_builder.clone() config_builder.set_config_from_commit(commit) lint_config = config_builder.build(lint_config) linter = GitLinter(lint_config) # Actually do the linting violations = linter.lint(commit) # exit code equals the total number of violations in all commits exit_code += len(violations) if violations: # Display the commit hash & new lines intelligently if number_of_commits > 1 and commit.sha: linter.display.e(u"{0}Commit {1}:".format( "\n" if not first_violation or commit is last_commit else "", commit.sha[:10] )) linter.print_violations(violations) first_violation = False # cap actual max exit code because bash doesn't like exit codes larger than 255: # http://tldp.org/LDP/abs/html/exitcodes.html exit_code = min(MAX_VIOLATION_ERROR_CODE, exit_code) LOG.debug("Exit Code = %s", exit_code) ctx.exit(exit_code)
def test_from_commit_msg_empty(self): gitcontext = GitContext.from_commit_msg("") self.assertEqual(gitcontext.commits[-1].message.title, "") self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, "") self.assertEqual(gitcontext.commits[-1].message.original, "") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertFalse(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def build_git_context(lint_config, msg_filename, refspec): """ Builds a git context based on passed parameters and order of precedence """ # Order of precedence: # 1. Any data specified via --msg-filename if msg_filename: LOG.debug("Attempting to read from --msg-filename.") return GitContext.from_commit_msg(ustr(msg_filename.read())) # 2. Any data sent to stdin (unless stdin is being ignored) if not lint_config.ignore_stdin: stdin_input = get_stdin_data() if stdin_input: LOG.debug("Stdin data: %r", stdin_input) LOG.debug("Stdin detected and not ignored. Will be used as input.") return GitContext.from_commit_msg(stdin_input) # 3. Fallback to reading from local repository LOG.debug( "No --msg-filename flag, no or empty data passed to stdin. Attempting to read from the local repo." ) return GitContext.from_local_repository(lint_config.target, refspec)
def test_from_commit_msg_merge_commit(self): commit_msg = "Merge f919b8f34898d9b48048bcd703bc47139f4ff621 into 8b0409a26da6ba8a47c1fd2e746872a8dab15401" gitcontext = GitContext.from_commit_msg(commit_msg) self.assertEqual(gitcontext.commits[-1].message.title, commit_msg) self.assertEqual(gitcontext.commits[-1].message.body, []) self.assertEqual(gitcontext.commits[-1].message.full, commit_msg) self.assertEqual(gitcontext.commits[-1].message.original, commit_msg) self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertTrue(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample2")) commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, u"Just a title contåining WIP") self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, u"Just a title contåining WIP") self.assertEqual(commit.message.original, u"Just a title contåining WIP") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] try: if sys.stdin.isatty(): # If target has not been set explicitly before, fallback to the current directory gitcontext = GitContext.from_local_repository( lint_config.target, ctx.obj[2]) else: stdin_str = ustr(sys.stdin.read()) gitcontext = GitContext.from_commit_msg(stdin_str) except GitContextError as e: click.echo(ustr(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) number_of_commits = len(gitcontext.commits) # Exit if we don't have commits in the specified range. Use a 0 exit code, since a popular use-case is one # where users are using --commits in a check job to check the commit messages inside a CI job. By returning 0, we # ensure that these jobs don't fail if for whatever reason the specified commit range is empty. if number_of_commits == 0: click.echo(u'No commits in range "{0}".'.format(ctx.obj[2])) ctx.exit(0) config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message config_builder.set_config_from_commit(last_commit) lint_config = config_builder.build(lint_config) # Let's get linting! linter = GitLinter(lint_config) first_violation = True for commit in gitcontext.commits: violations = linter.lint(commit) if violations: # Display the commit hash & new lines intelligently if number_of_commits > 1 and commit.sha: click.echo(u"{0}Commit {1}:".format( "\n" if not first_violation or commit is last_commit else "", commit.sha[:10])) linter.print_violations(violations) first_violation = False exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg( "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].message.title, "Title") self.assertEqual(gitcontext.commits[-1].message.body, ["", "Body 1", "Body 2"]) self.assertEqual(gitcontext.commits[-1].message.full, "Title\n\nBody 1\nBody 2") self.assertEqual(gitcontext.commits[-1].message.original, "Title\n\nBody 1\n#Comment\nBody 2") self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertListEqual(gitcontext.commits[-1].parents, []) self.assertFalse(gitcontext.commits[-1].is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_comment(self): gitcontext = GitContext.from_commit_msg( u"Tïtle\n\nBödy 1\n#Cömment\nBody 2") commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, u"Tïtle") self.assertEqual(commit.message.body, ["", u"Bödy 1", "Body 2"]) self.assertEqual(commit.message.full, u"Tïtle\n\nBödy 1\nBody 2") self.assertEqual(commit.message.original, u"Tïtle\n\nBödy 1\n#Cömment\nBody 2") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_empty(self): gitcontext = GitContext.from_commit_msg("") commit = gitcontext.commits[-1] self.assertEqual(commit.message.title, "") self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, "") self.assertEqual(commit.message.original, "") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_full(self, commentchar): commentchar.return_value = u"#" gitcontext = GitContext.from_commit_msg( self.get_sample("commit_message/sample1")) expected_title = u"Commit title contåining 'WIP', as well as trailing punctuation." expected_body = [ "This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", u"This line has a tråiling space. ", "This line has a trailing tab.\t" ] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + ( u"\n# This is a cömmented line\n" u"# ------------------------ >8 ------------------------\n" u"# Anything after this line should be cleaned up\n" u"# this line appears on `git commit -v` command\n" u"diff --git a/gitlint/tests/samples/commit_message/sample1 " u"b/gitlint/tests/samples/commit_message/sample1\n" u"index 82dbe7f..ae71a14 100644\n" u"--- a/gitlint/tests/samples/commit_message/sample1\n" u"+++ b/gitlint/tests/samples/commit_message/sample1\n" u"@@ -1 +1 @@\n") commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, expected_title) self.assertEqual(commit.message.body, expected_body) self.assertEqual(commit.message.full, expected_full) self.assertEqual(commit.message.original, expected_original) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_full(self): gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample1")) expected_title = "Commit title containing 'WIP', as well as trailing punctuation." expected_body = ["This line should be empty", "This is the first line of the commit message body and it is meant to test a " + "line that exceeds the maximum line length of 80 characters.", "This line has a trailing space. ", "This line has a trailing tab.\t", ""] expected_full = expected_title + "\n" + "\n".join(expected_body) expected_original = expected_full + "# This is a commented line\n" self.assertEqual(gitcontext.commits[-1].message.title, expected_title) self.assertEqual(gitcontext.commits[-1].message.body, expected_body) self.assertEqual(gitcontext.commits[-1].message.full, expected_full) self.assertEqual(gitcontext.commits[-1].message.original, expected_original) self.assertEqual(gitcontext.commits[-1].author_name, None) self.assertEqual(gitcontext.commits[-1].author_email, None) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_just_title(self): gitcontext = GitContext.from_commit_msg(self.get_sample("commit_message/sample2")) commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, "Just a title contåining WIP") self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, "Just a title contåining WIP") self.assertEqual(commit.message.original, "Just a title contåining WIP") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_revert_commit(self): commit_msg = "Revert \"Prev commit message\"\n\nThis reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c." gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, "Revert \"Prev commit message\"") self.assertEqual(commit.message.body, ["", "This reverts commit a8ad67e04164a537198dea94a4fde81c5592ae9c."]) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertTrue(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_merge_commit(self): commit_msg = "Merge f919b8f34898d9b48048bcd703bc47139f4ff621 into 8b0409a26da6ba8a47c1fd2e746872a8dab15401" gitcontext = GitContext.from_commit_msg(commit_msg) commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, commit_msg) self.assertEqual(commit.message.body, []) self.assertEqual(commit.message.full, commit_msg) self.assertEqual(commit.message.original, commit_msg) self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertTrue(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def test_from_commit_msg_comment(self, commentchar): commentchar.return_value = "#" gitcontext = GitContext.from_commit_msg("Tïtle\n\nBödy 1\n#Cömment\nBody 2") commit = gitcontext.commits[-1] self.assertIsInstance(commit, GitCommit) self.assertFalse(isinstance(commit, LocalGitCommit)) self.assertEqual(commit.message.title, "Tïtle") self.assertEqual(commit.message.body, ["", "Bödy 1", "Body 2"]) self.assertEqual(commit.message.full, "Tïtle\n\nBödy 1\nBody 2") self.assertEqual(commit.message.original, "Tïtle\n\nBödy 1\n#Cömment\nBody 2") self.assertEqual(commit.author_name, None) self.assertEqual(commit.author_email, None) self.assertEqual(commit.date, None) self.assertListEqual(commit.parents, []) self.assertListEqual(commit.branches, []) self.assertFalse(commit.is_merge_commit) self.assertFalse(commit.is_fixup_commit) self.assertFalse(commit.is_squash_commit) self.assertFalse(commit.is_revert_commit) self.assertEqual(len(gitcontext.commits), 1)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj try: if sys.stdin.isatty(): gitcontext = GitContext.from_local_repository(lint_config.target) else: gitcontext = GitContext.from_commit_msg(sys.stdin.read()) except GitContextError as e: click.echo(str(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message lint_config.apply_config_from_commit(last_commit) # Let's get linting! linter = GitLinter(lint_config) violations = linter.lint(last_commit) linter.print_violations(violations) exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj try: if sys.stdin.isatty(): gitcontext = GitContext.from_local_repository(lint_config.target) else: gitcontext = GitContext.from_commit_msg(sys.stdin.read()) except GitContextError as e: click.echo(str(e)) ctx.exit(GIT_CONTEXT_ERROR_CODE) last_commit = gitcontext.commits[-1] # Apply an additional config that is specified in the last commit message lint_config.apply_config_from_commit(last_commit) # Let's get linting! linter = GitLinter(lint_config) violations = linter.lint(last_commit, gitcontext) linter.print_violations(violations) exit_code = min(MAX_VIOLATION_ERROR_CODE, len(violations)) ctx.exit(exit_code)
def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj[0] msg_filename = ctx.obj[3] # Let's determine where our input data is coming from: # Order of precedence: # 1. Any data specified via --msg-filename # 2. Any data sent to stdin # 3. Fallback to reading from local repository stdin_input = get_stdin_data() if msg_filename: LOG.debug("Attempting to read from --msg-filename.") gitcontext = GitContext.from_commit_msg(ustr(msg_filename.read())) elif stdin_input: LOG.debug("No --msg-filename flag. Attempting to read from stdin.") gitcontext = GitContext.from_commit_msg(stdin_input) else: LOG.debug( "No --msg-filename flag, no or empty data passed to stdin. Attempting to read from the local repo." ) gitcontext = GitContext.from_local_repository(lint_config.target, ctx.obj[2]) number_of_commits = len(gitcontext.commits) # Exit if we don't have commits in the specified range. Use a 0 exit code, since a popular use-case is one # where users are using --commits in a check job to check the commit messages inside a CI job. By returning 0, we # ensure that these jobs don't fail if for whatever reason the specified commit range is empty. if number_of_commits == 0: LOG.debug(u'No commits in range "%s"', ctx.obj[2]) ctx.exit(0) general_config_builder = ctx.obj[1] last_commit = gitcontext.commits[-1] # Let's get linting! first_violation = True exit_code = 0 for commit in gitcontext.commits: # Build a config_builder taking into account the commit specific config (if any) config_builder = general_config_builder.clone() config_builder.set_config_from_commit(commit) # Create a deepcopy from the original config, so we have a unique config object per commit # This is important for configuration rules to be able to modifying the config on a per commit basis commit_config = config_builder.build(copy.deepcopy(lint_config)) # Actually do the linting linter = GitLinter(commit_config) violations = linter.lint(commit) # exit code equals the total number of violations in all commits exit_code += len(violations) if violations: # Display the commit hash & new lines intelligently if number_of_commits > 1 and commit.sha: linter.display.e(u"{0}Commit {1}:".format( "\n" if not first_violation or commit is last_commit else "", commit.sha[:10])) linter.print_violations(violations) first_violation = False # cap actual max exit code because bash doesn't like exit codes larger than 255: # http://tldp.org/LDP/abs/html/exitcodes.html exit_code = min(MAX_VIOLATION_ERROR_CODE, exit_code) LOG.debug("Exit Code = %s", exit_code) ctx.exit(exit_code)