diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-10-13 05:34:54 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-10-13 05:34:54 +0000 |
commit | b8d423e7d13686d6627571d6c4adf12661d82147 (patch) | |
tree | 11d64ff26fb53c3c01ee35d062ca0c51fb883550 /gitlint/cli.py | |
parent | Adding upstream version 0.15.1. (diff) | |
download | gitlint-upstream/0.16.0.tar.xz gitlint-upstream/0.16.0.zip |
Adding upstream version 0.16.0.upstream/0.16.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'gitlint/cli.py')
-rw-r--r-- | gitlint/cli.py | 62 |
1 files changed, 39 insertions, 23 deletions
diff --git a/gitlint/cli.py b/gitlint/cli.py index 9b16d47..19676b3 100644 --- a/gitlint/cli.py +++ b/gitlint/cli.py @@ -18,6 +18,7 @@ from gitlint.utils import LOG_FORMAT from gitlint.exception import GitlintError # Error codes +GITLINT_SUCCESS = 0 MAX_VIOLATION_ERROR_CODE = 252 USAGE_ERROR_CODE = 253 GIT_CONTEXT_ERROR_CODE = 254 @@ -61,7 +62,8 @@ def log_system_info(): def build_config( # pylint: disable=too-many-arguments - target, config_path, c, extra_path, ignore, contrib, ignore_stdin, staged, verbose, silent, debug + target, config_path, c, extra_path, ignore, contrib, ignore_stdin, staged, fail_without_commits, verbose, + silent, debug ): """ Creates a LintConfig object based on a set of commandline parameters. """ config_builder = LintConfigBuilder() @@ -102,6 +104,9 @@ def build_config( # pylint: disable=too-many-arguments if staged: config_builder.set_option('general', 'staged', staged) + if fail_without_commits: + config_builder.set_option('general', 'fail-without-commits', fail_without_commits) + config = config_builder.build() return config, config_builder @@ -139,7 +144,7 @@ def get_stdin_data(): return False -def build_git_context(lint_config, msg_filename, refspec): +def build_git_context(lint_config, msg_filename, commit_hash, refspec): """ Builds a git context based on passed parameters and order of precedence """ # Determine which GitContext method to use if a custom message is passed @@ -168,7 +173,11 @@ def build_git_context(lint_config, msg_filename, refspec): # 3. Fallback to reading from local repository LOG.debug("No --msg-filename flag, no or empty data passed to stdin. Using the local repo.") - return GitContext.from_local_repository(lint_config.target, refspec) + + if commit_hash and refspec: + raise GitLintUsageError("--commit and --commits are mutually exclusive, use one or the other.") + + return GitContext.from_local_repository(lint_config.target, refspec=refspec, commit_hash=commit_hash) def handle_gitlint_error(ctx, exc): @@ -187,9 +196,10 @@ def handle_gitlint_error(ctx, exc): class ContextObj: """ Simple class to hold data that is passed between Click commands via the Click context. """ - def __init__(self, config, config_builder, refspec, msg_filename, gitcontext=None): + def __init__(self, config, config_builder, commit_hash, refspec, msg_filename, gitcontext=None): self.config = config self.config_builder = config_builder + self.commit_hash = commit_hash self.refspec = refspec self.msg_filename = msg_filename self.gitcontext = gitcontext @@ -205,6 +215,7 @@ class ContextObj: @click.option('-c', multiple=True, help="Config flags in format <rule>.<option>=<value> (e.g.: -c T1.line-length=80). " + "Flag can be used multiple times to set multiple config values.") # pylint: disable=bad-continuation +@click.option('--commit', envvar='GITLINT_COMMIT', default=None, help="Hash (SHA) of specific commit to lint.") @click.option('--commits', envvar='GITLINT_COMMITS', default=None, help="The range of commits to lint. [default: HEAD]") @click.option('-e', '--extra-path', envvar='GITLINT_EXTRA_PATH', help="Path to a directory or python module with extra user-defined rules", @@ -217,6 +228,8 @@ class ContextObj: help="Ignore any stdin data. Useful for running in CI server.") @click.option('--staged', envvar='GITLINT_STAGED', is_flag=True, help="Read staged commit meta-info from the local repository.") +@click.option('--fail-without-commits', envvar='GITLINT_FAIL_WITHOUT_COMMITS', is_flag=True, + help="Hard fail when the target commit range is empty.") @click.option('-v', '--verbose', envvar='GITLINT_VERBOSITY', count=True, default=0, help="Verbosity, more v's for more verbose output (e.g.: -v, -vv, -vvv). [default: -vvv]", ) @click.option('-s', '--silent', envvar='GITLINT_SILENT', is_flag=True, @@ -225,8 +238,9 @@ class ContextObj: @click.version_option(version=gitlint.__version__) @click.pass_context def cli( # pylint: disable=too-many-arguments - ctx, target, config, c, commits, extra_path, ignore, contrib, - msg_filename, ignore_stdin, staged, verbose, silent, debug, + ctx, target, config, c, commit, commits, extra_path, ignore, contrib, + msg_filename, ignore_stdin, staged, fail_without_commits, verbose, + silent, debug, ): """ Git lint tool, checks your git commit messages for styling issues @@ -242,11 +256,11 @@ def cli( # pylint: disable=too-many-arguments # Get the lint config from the commandline parameters and # store it in the context (click allows storing an arbitrary object in ctx.obj). - config, config_builder = build_config(target, config, c, extra_path, ignore, contrib, - ignore_stdin, staged, verbose, silent, debug) + config, config_builder = build_config(target, config, c, extra_path, ignore, contrib, ignore_stdin, staged, + fail_without_commits, verbose, silent, debug) LOG.debug("Configuration\n%s", config) - ctx.obj = ContextObj(config, config_builder, commits, msg_filename) + ctx.obj = ContextObj(config, config_builder, commit, commits, msg_filename) # If no subcommand is specified, then just lint if ctx.invoked_subcommand is None: @@ -262,9 +276,10 @@ def lint(ctx): """ Lints a git repository [default command] """ lint_config = ctx.obj.config refspec = ctx.obj.refspec + commit_hash = ctx.obj.commit_hash msg_filename = ctx.obj.msg_filename - gitcontext = build_git_context(lint_config, msg_filename, refspec) + gitcontext = build_git_context(lint_config, msg_filename, commit_hash, refspec) # Set gitcontext in the click context, so we can use it in command that are ran after this # in particular, this is used by run-hook ctx.obj.gitcontext = gitcontext @@ -273,17 +288,20 @@ def lint(ctx): # 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. + # This behavior can be overridden by using the --fail-without-commits flag. if number_of_commits == 0: - LOG.debug(u'No commits in range "%s"', refspec) - ctx.exit(0) + LOG.debug('No commits in range "%s"', refspec) + if lint_config.fail_without_commits: + raise GitLintUsageError(f'No commits in range "{refspec}"') + ctx.exit(GITLINT_SUCCESS) - LOG.debug(u'Linting %d commit(s)', number_of_commits) + LOG.debug('Linting %d commit(s)', number_of_commits) general_config_builder = ctx.obj.config_builder last_commit = gitcontext.commits[-1] # Let's get linting! first_violation = True - exit_code = 0 + exit_code = GITLINT_SUCCESS for commit in gitcontext.commits: # Build a config_builder taking into account the commit specific config (if any) config_builder = general_config_builder.clone() @@ -301,10 +319,8 @@ def lint(ctx): if violations: # Display the commit hash & new lines intelligently if number_of_commits > 1 and commit.sha: - linter.display.e("{0}Commit {1}:".format( - "\n" if not first_violation or commit is last_commit else "", - commit.sha[:10] - )) + commit_separator = "\n" if not first_violation or commit is last_commit else "" + linter.display.e(f"{commit_separator}Commit {commit.sha[:10]}:") linter.print_violations(violations) first_violation = False @@ -323,7 +339,7 @@ def install_hook(ctx): hooks.GitHookInstaller.install_commit_msg_hook(ctx.obj.config) hook_path = hooks.GitHookInstaller.commit_msg_hook_path(ctx.obj.config) click.echo(f"Successfully installed gitlint commit-msg hook in {hook_path}") - ctx.exit(0) + ctx.exit(GITLINT_SUCCESS) except hooks.GitHookInstallerError as e: click.echo(e, err=True) ctx.exit(GIT_CONTEXT_ERROR_CODE) @@ -337,7 +353,7 @@ def uninstall_hook(ctx): hooks.GitHookInstaller.uninstall_commit_msg_hook(ctx.obj.config) hook_path = hooks.GitHookInstaller.commit_msg_hook_path(ctx.obj.config) click.echo(f"Successfully uninstalled gitlint commit-msg hook from {hook_path}") - ctx.exit(0) + ctx.exit(GITLINT_SUCCESS) except hooks.GitHookInstallerError as e: click.echo(e, err=True) ctx.exit(GIT_CONTEXT_ERROR_CODE) @@ -361,7 +377,7 @@ def run_hook(ctx): sys.stdout.flush() exit_code = e.exit_code - if exit_code == 0: + if exit_code == GITLINT_SUCCESS: click.echo("gitlint: " + click.style("OK", fg='green') + " (no violations in commit message)") continue @@ -387,7 +403,7 @@ def run_hook(ctx): if value == "y": LOG.debug("run-hook: commit message accepted") - exit_code = 0 + exit_code = GITLINT_SUCCESS elif value == "e": LOG.debug("run-hook: editing commit message") msg_filename = ctx.obj.msg_filename @@ -428,7 +444,7 @@ def generate_config(ctx): LintConfigGenerator.generate_config(path) click.echo(f"Successfully generated {path}") - ctx.exit(0) + ctx.exit(GITLINT_SUCCESS) # Let's Party! |