diff --git a/cfbs/args.py b/cfbs/args.py new file mode 100644 index 00000000..2faf6ba1 --- /dev/null +++ b/cfbs/args.py @@ -0,0 +1,82 @@ +import argparse + +from cfbs import commands +from cfbs.utils import cache + + +def get_args(): + parser = _get_arg_parser() + args = parser.parse_args() + return args + + +def print_help(): + parser = _get_arg_parser() + parser.print_help() + + +@cache +def _get_arg_parser(): + command_list = [ + cmd.split("_")[0] for cmd in dir(commands) if cmd.endswith("_command") + ] + parser = argparse.ArgumentParser(description="CFEngine Build System.") + parser.add_argument( + "command", + metavar="cmd", + type=str, + nargs="?", + help="The command to perform ({})".format(", ".join(command_list)), + ) + parser.add_argument("args", nargs="*", help="Command arguments") + parser.add_argument( + "--loglevel", + "-l", + help="Set log level for more/less detailed output", + type=str, + default="warning", + ) + parser.add_argument( + "--version", "-V", help="Print version number", action="store_true" + ) + parser.add_argument( + "--force", help="Force rebuild / redownload", action="store_true" + ) + parser.add_argument( + "--non-interactive", + help="Don't prompt, use defaults (only for testing)", + action="store_true", + ) + parser.add_argument("--index", help="Specify alternate index", type=str) + parser.add_argument( + "--check", help="Check if file(s) would be reformatted", action="store_true" + ) + parser.add_argument( + "--checksum", + type=str, + default=None, + help="Expected checksum of the downloaded file", + ) + parser.add_argument( + "--keep-order", + help="Keep order of items in the JSON in 'cfbs pretty'", + action="store_true", + ) + parser.add_argument( + "--git", + choices=("yes", "no"), + help="Override git option in cfbs.json", + ) + parser.add_argument( + "--git-user-name", + help="Specify git user name", + ) + parser.add_argument( + "--git-user-email", + help="Specify git user email", + ) + parser.add_argument( + "--git-commit-message", + help="Specify git commit message", + ) + return parser diff --git a/cfbs/commands.py b/cfbs/commands.py index aada9986..35cbd494 100644 --- a/cfbs/commands.py +++ b/cfbs/commands.py @@ -6,6 +6,7 @@ import re import logging as log import json +from cfbs.args import get_args from cfbs.utils import ( cfbs_dir, @@ -23,10 +24,10 @@ is_a_commit_hash, ) +from cfbs.args import get_args from cfbs.pretty import pretty_check_file, pretty_file from cfbs.build import init_out_folder, perform_build_steps from cfbs.cfbs_config import CFBSConfig, CFBSReturnWithoutCommit -from cfbs.cfbs_json import CFBSJson from cfbs.validate import CFBSIndexException, validate_index from cfbs.internal_file_management import ( fetch_archive, @@ -49,8 +50,8 @@ _MODULES_URL = "https://archive.build.cfengine.com/modules" -PLURAL_S = lambda args, kwargs: "s" if len(args[0]) > 1 else "" -FIRST_ARG_SLIST = lambda args, kwargs: ", ".join("'%s'" % module for module in args[0]) +PLURAL_S = lambda args, _: "s" if len(args[0]) > 1 else "" +FIRST_ARG_SLIST = lambda args, _: ", ".join("'%s'" % module for module in args[0]) def pretty_command(filenames: list, check: bool, keep_order: bool) -> int: @@ -134,33 +135,43 @@ def init_command(index=None, non_interactive=False) -> int: if index: config["index"] = index + do_git = get_args().git is_git = is_git_repo() - if is_git: - git_ans = prompt_user( - "This is a git repository. Do you want cfbs to make commits to it?", - choices=YES_NO_CHOICES, - default="yes", - ) + if do_git is None: + if is_git: + git_ans = prompt_user( + "This is a git repository. Do you want cfbs to make commits to it?", + choices=YES_NO_CHOICES, + default="yes", + ) + else: + git_ans = prompt_user( + "Do you want cfbs to initialize a git repository and make commits to it?", + choices=YES_NO_CHOICES, + default="yes", + ) + do_git = git_ans.lower() in ("yes", "y") else: - git_ans = prompt_user( - "Do you want cfbs to initialize a git repository and make commits to it?", - choices=YES_NO_CHOICES, - default="yes", - ) - do_git = git_ans.lower() in ("yes", "y") - - if do_git: - user_name = git_get_config("user.name") - user_email = git_get_config("user.email") - user_name = prompt_user( - "Please enter user name to use for git commits", default=user_name or "cfbs" - ) + assert do_git in ("yes", "no") + do_git = True if do_git == "yes" else False + + if do_git is True: + user_name = get_args().git_user_name + if not user_name: + user_name = git_get_config("user.name") + user_name = prompt_user( + "Please enter user name to use for git commits", + default=user_name or "cfbs", + ) - node_name = os.uname().nodename - user_email = prompt_user( - "Please enter user email to use for git commits", - default=user_email or ("cfbs@%s" % node_name), - ) + user_email = get_args().git_user_email + if not user_email: + user_email = git_get_config("user.email") + node_name = os.uname().nodename + user_email = prompt_user( + "Please enter user email to use for git commits", + default=user_email or ("cfbs@%s" % node_name), + ) if not is_git: try: diff --git a/cfbs/git.py b/cfbs/git.py index 487230e7..4546f439 100644 --- a/cfbs/git.py +++ b/cfbs/git.py @@ -85,7 +85,9 @@ def git_init(user_name=None, user_email=None, description=None): f.write(description + "\n") -def git_commit(commit_msg, edit_commit_msg=False, scope="all"): +def git_commit( + commit_msg, edit_commit_msg=False, user_name=None, user_email=None, scope="all" +): """Create a commit in the CWD Git repository :param commit_msg: commit message to use for the commit @@ -93,6 +95,8 @@ def git_commit(commit_msg, edit_commit_msg=False, scope="all"): :type scope: str or an iterable of str :param edit_commit_message=False: whether the user should be prompted to edit and save the commit message or not + :param user_name: override git config user name + :param user_email: override git config user email """ @@ -113,6 +117,10 @@ def git_commit(commit_msg, edit_commit_msg=False, scope="all"): except CalledProcessError as cpe: raise CFBSGitError("Failed to add %s to commit" % item) from cpe + # Override git config user name and email if specified + u_name = ["-c", "user.name=%s" % user_name] if user_name else [] + u_email = ["-c", "user.email=%s" % user_email] if user_email else [] + if edit_commit_msg: fd, name = tempfile.mkstemp(dir=".git", prefix="commit-msg") with os.fdopen(fd, "w") as f: @@ -120,7 +128,11 @@ def git_commit(commit_msg, edit_commit_msg=False, scope="all"): # If the user doesn't edit the message, the commit fails. In such case # we need to make the commit the same way as in non-interactive mode. - result = run(["git", "commit", "--template", name], check=False, stderr=PIPE) + result = run( + ["git"] + u_name + u_email + ["commit", "--template", name], + check=False, + stderr=PIPE, + ) os.unlink(name) if result.returncode == 0: print("") @@ -130,7 +142,11 @@ def git_commit(commit_msg, edit_commit_msg=False, scope="all"): # else try: - run(["git", "commit", "-F-"], input=commit_msg.encode("utf-8"), check=True) + run( + ["git"] + u_name + u_email + ["commit", "-F-"], + input=commit_msg.encode("utf-8"), + check=True, + ) print("") except CalledProcessError as cpe: raise CFBSGitError("Failed to commit changes") from cpe diff --git a/cfbs/git_magic.py b/cfbs/git_magic.py index c7291d06..cb9f5376 100644 --- a/cfbs/git_magic.py +++ b/cfbs/git_magic.py @@ -7,15 +7,32 @@ from collections import namedtuple from cfbs.prompts import YES_NO_CHOICES, prompt_user from cfbs.cfbs_config import CFBSConfig, CFBSReturnWithoutCommit -from cfbs.git import git_commit, git_discard_changes_in_file, CFBSGitError +from cfbs.git import git_commit, git_discard_changes_in_file, CFBSGitError, is_git_repo +from cfbs.args import get_args +import logging as log from functools import partial Result = namedtuple("Result", ["rc", "commit", "msg"]) +first_commit = True + def git_commit_maybe_prompt(commit_msg, non_interactive, scope="all"): edit_commit_msg = False + args = get_args() + + # Override message if --git-commit-message option is used + if args.git_commit_message: + global first_commit + if first_commit: + commit_msg = args.git_commit_message + non_interactive = True + first_commit = False + else: + log.warning( + "Commit message specified, but command produced multiple commits, using default commit message" + ) if not non_interactive: prompt = "The default commit message is '{}' - edit it?".format(commit_msg) @@ -31,7 +48,14 @@ def git_commit_maybe_prompt(commit_msg, non_interactive, scope="all"): default="no", ) edit_commit_msg = ans.lower() in ("yes", "y") - git_commit(commit_msg, edit_commit_msg, scope) + + git_commit( + commit_msg, + edit_commit_msg, + args.git_user_name, + args.git_user_email, + scope, + ) def with_git_commit( @@ -66,8 +90,19 @@ def decorated_fn(*args, **kwargs): return ret config = CFBSConfig.get_instance() - if not config.get("git", False): + do_git = get_args().git + if do_git == "yes": + if not is_git_repo(): + log.error( + "Used '--git=yes' option on what appears to not be a git repository" + ) + return ret + elif do_git == "no": return ret + else: + assert do_git is None + if not config.get("git", False): + return ret if ret not in successful_returns: return ret diff --git a/cfbs/main.py b/cfbs/main.py index 748873ac..8d804c18 100644 --- a/cfbs/main.py +++ b/cfbs/main.py @@ -4,69 +4,13 @@ __authors__ = ["Ole Herman Schumacher Elgesem"] __copyright__ = ["Northern.tech AS"] -import argparse import logging as log from cfbs.version import string as version -from cfbs.utils import user_error, is_cfbs_repo, cache +from cfbs.utils import user_error, is_cfbs_repo from cfbs.cfbs_config import CFBSConfig from cfbs import commands - - -@cache -def _get_arg_parser(): - command_list = [ - cmd.split("_")[0] for cmd in dir(commands) if cmd.endswith("_command") - ] - parser = argparse.ArgumentParser(description="CFEngine Build System.") - parser.add_argument( - "command", - metavar="cmd", - type=str, - nargs="?", - help="The command to perform ({})".format(", ".join(command_list)), - ) - parser.add_argument("args", nargs="*", help="Command arguments") - parser.add_argument( - "--loglevel", - "-l", - help="Set log level for more/less detailed output", - type=str, - default="warning", - ) - parser.add_argument( - "--version", "-V", help="Print version number", action="store_true" - ) - parser.add_argument( - "--force", help="Force rebuild / redownload", action="store_true" - ) - parser.add_argument( - "--non-interactive", - help="Don't prompt, use defaults (only for testing)", - action="store_true", - ) - parser.add_argument("--index", help="Specify alternate index", type=str) - parser.add_argument( - "--check", help="Check if file(s) would be reformatted", action="store_true" - ) - parser.add_argument( - "--checksum", - type=str, - default=None, - help="Expected checksum of the downloaded file", - ) - parser.add_argument( - "--keep-order", - help="Keep order of items in the JSON in 'cfbs pretty'", - action="store_true", - ) - return parser - - -def get_args(): - parser = _get_arg_parser() - args = parser.parse_args() - return args +from cfbs.args import get_args, print_help def init_logging(level): @@ -98,7 +42,7 @@ def main() -> int: return 0 if not args.command: - _get_arg_parser().print_help() + print_help() print("") user_error("No command given") @@ -124,10 +68,10 @@ def main() -> int: # Commands you can run outside a cfbs repo: if args.command == "help": - _get_arg_parser().print_help() + print_help() return 0 - config = CFBSConfig.get_instance(args.index, args.non_interactive) + CFBSConfig.get_instance(args.index, args.non_interactive) if args.command == "init": return commands.init_command( @@ -166,5 +110,5 @@ def main() -> int: if args.command == "update": return commands.update_command(args.args) - _get_arg_parser().print_help() + print_help() user_error("Command '%s' not found" % args.command)