def execute_version_change_actions(context: Context, old_version: str, new_version: str): variables = dict(os.environ) variables['OLD_VERSION'] = old_version or '' variables['NEW_VERSION'] = new_version for command in context.config.version_change_actions: command_string = ' '.join(shlex.quote(token) for token in command) if context.verbose >= const.TRACE_VERBOSITY: print(command_string) command = [expand_vars(token, variables) for token in command] proc = subprocess.Popen( args=command, # stdin=subprocess.PIPE, # stdout=subprocess.PIPE, cwd=context.repo.dir, env=None) proc.wait() if proc.returncode != os.EX_OK: context.fail( os.EX_DATAERR, _("version change action failed."), _("{command}\n" "returned with an error.").format(command=command_string))
def create_temp_context(context: Context, result: Result, directory: str) -> Context: clone_context = Context.create( { '--root': directory, '--config': context.args['--config'], # no override here '--batch': context.batch, '--dry-run': context.dry_run, '--verbose': context.verbose, '--pretty': context.pretty, }, result) if clone_context.temp_dirs is None: clone_context.temp_dirs = list() clone_context.temp_dirs.append(directory) if context.clones is None: context.clones = list() context.clones.append(clone_context) return clone_context
def call(context: Context): command_context = get_command_context(context=context, object_arg=context.args['<object>']) if context.repo is not None: if context.args['--inplace']: build_context = context build_command_context = command_context else: temp_dir = TemporaryDirectory() exported_repo = repotools.git_export( context=context.repo, target_dir=temp_dir.name, object=command_context.selected_commit) build_context = Context.create( { **context.args, **{ '--root': exported_repo.dir, '--config': context.args['--config'], # no override here '--batch': context.batch, '--dry-run': context.dry_run, '--verbose': context.verbose, '--pretty': context.pretty, } }, context.result) build_command_context = get_command_context( context=build_context, object_arg=build_context.args['<object>']) check_requirements( command_context=build_command_context, ref=build_command_context.selected_ref, branch_classes=None, modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Build failed."), allow_unversioned_changes=False) else: build_context = context build_command_context = command_context selected_stages = list() for stage_type in const.BUILD_STAGE_TYPES: if build_context.args[stage_type.replace('_', '-')]: selected_stages.append(stage_type) execute_build_steps(build_command_context, selected_stages) return context.result
def read_properties_in_commit(context: Context, repo: RepoContext, config: dict, commit: str): if config is not None: property_file = config.get(const.CONFIG_PROJECT_PROPERTY_FILE) if property_file is None: return None properties_bytes = repotools.get_file_contents(repo, commit, property_file) if properties_bytes is None: return property_reader = PropertyIO.get_instance_by_filename(property_file) properties = property_reader.from_bytes( properties_bytes, const.DEFAULT_PROPERTY_ENCODING) if properties is None: context.fail(os.EX_DATAERR, _("Failed to parse properties."), None) return properties
def clone_repository(context: Context, branch: str) -> Result: """ :rtype: Result """ result = Result() remote = repotools.git_get_remote(context.repo, context.config.remote_name) if remote is None: result.fail( os.EX_DATAERR, _("Failed to clone repo."), _("The remote {remote} does not exist.").format( remote=repr(context.config.remote_name))) tempdir_path = tempfile.mkdtemp(prefix=os.path.basename(context.repo.dir) + ".gitflow-clone.") try: if os.path.exists(tempdir_path): os.chmod(path=tempdir_path, mode=0o700) if os.path.isdir(tempdir_path): if os.listdir(tempdir_path): result.fail( os.EX_DATAERR, _("Failed to clone repo."), _("Directory is not empty: {path}").format( path=tempdir_path)) else: result.fail( os.EX_DATAERR, _("Failed to clone repo."), _("File is not a directory: {path}").format( path=tempdir_path)) else: result.fail( os.EX_DATAERR, _("Failed to clone repo."), _("File does not exist: {path}").format(path=tempdir_path)) if context.config.push_to_local: returncode, out, err = repotools.git_raw(git=context.repo.git, args=[ 'clone', '--branch', branch, '--shared', context.repo.dir, tempdir_path ], verbose=context.verbose) else: returncode, out, err = repotools.git_raw(git=context.repo.git, args=[ 'clone', '--branch', branch, '--reference', context.repo.dir, remote.url, tempdir_path ], verbose=context.verbose) if returncode != os.EX_OK: result.error(os.EX_DATAERR, _("Failed to clone the repository."), _("An unexpected error occurred.")) except: result.error(os.EX_DATAERR, _("Failed to clone the repository."), _("An unexpected error occurred.")) finally: context.add_subresult(result) if not result.has_errors(): repo = RepoContext() repo.git = context.repo.git repo.dir = tempdir_path repo.verbose = context.repo.verbose result.value = repo else: shutil.rmtree(path=tempdir_path) return result
def call(context: Context) -> Result: command_context = get_command_context( context=context, object_arg=context.args['<base-object>']) check_in_repo(command_context) check_requirements( command_context=command_context, ref=command_context.selected_ref, branch_classes=None, modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Version creation failed.")) selected_work_branch = context.args.get('<work-branch>') if selected_work_branch is not None: selected_work_branch = repotools.create_ref_name(selected_work_branch) if not selected_work_branch.startswith(const.LOCAL_BRANCH_PREFIX): selected_work_branch = const.LOCAL_BRANCH_PREFIX + selected_work_branch branch_match = context.work_branch_matcher.fullmatch( selected_work_branch) if branch_match is None: context.fail( os.EX_USAGE, _("Invalid work branch: {branch}.").format( branch=repr(selected_work_branch)), None) groups = branch_match.groupdict() branch_supertype = groups['prefix'] branch_type = groups['type'] branch_short_name = groups['name'] else: branch_supertype = context.args['<supertype>'] branch_type = context.args['<type>'] branch_short_name = context.args['<name>'] if branch_supertype not in [ const.BRANCH_PREFIX_DEV, const.BRANCH_PREFIX_PROD ]: context.fail( os.EX_USAGE, _("Invalid branch super type: {supertype}.").format( supertype=repr(branch_supertype)), None) work_branch_name = repotools.create_ref_name(branch_supertype, branch_type, branch_short_name) work_branch_ref_name = repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, work_branch_name) work_branch_class = get_branch_class(context, work_branch_ref_name) if True: work_branch_info = get_branch_info(command_context, work_branch_ref_name) if work_branch_info is not None: context.fail( os.EX_USAGE, _("The branch {branch} already exists locally or remotely."). format(branch=repr(work_branch_name)), None) allowed_base_branch_class = const.BRANCHING[work_branch_class] base_branch, base_branch_class = select_ref( command_context.result, command_context.selected_branch, BranchSelection.BRANCH_PREFER_LOCAL) if not command_context.selected_explicitly and branch_supertype == const.BRANCH_PREFIX_DEV: base_branch_info = get_branch_info( command_context, repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, context.config.release_branch_base)) base_branch, base_branch_class = select_ref( command_context.result, base_branch_info, BranchSelection.BRANCH_PREFER_LOCAL) if allowed_base_branch_class != base_branch_class: context.fail( os.EX_USAGE, _("The branch {branch} is not a valid base for {supertype} branches." ).format(branch=repr(base_branch.name), supertype=repr(branch_supertype)), None) if base_branch is None: context.fail(os.EX_USAGE, _("Base branch undetermined."), None) if context.verbose: cli.print("branch_name: " + command_context.selected_ref.name) cli.print("work_branch_name: " + work_branch_name) cli.print("base_branch_name: " + base_branch.name) if not context.dry_run and not command_context.has_errors(): index_status = git(context.repo, ['diff-index', 'HEAD', '--']) if index_status == 1: context.fail( os.EX_USAGE, _("Branch creation aborted."), _("You have staged changes in your workspace.\n" "Unstage, commit or stash them and try again.")) elif index_status != 0: context.fail(os.EX_DATAERR, _("Failed to determine index status."), None) git_or_fail( context.repo, command_context.result, [ 'update-ref', work_branch_ref_name, command_context.selected_commit, '' ], _("Failed to create branch {branch_name}.").format( branch_name=work_branch_name)) git_or_fail( context.repo, command_context.result, ['checkout', work_branch_name], _("Failed to checkout branch {branch_name}.").format( branch_name=work_branch_name)) return context.result
def main(argv: list = sys.argv) -> int: if ENABLE_PROFILER: import cProfile profiler = cProfile.Profile() profiler.enable() else: profiler = None result = Result() args = docopt.docopt(argv=argv[1:], doc=__doc__, version=const.VERSION, help=True, options_first=False) try: context = Context.create(args, result) except GitFlowException as e: context = None pass # errors are in result if context is not None: try: if context.verbose >= const.DEBUG_VERBOSITY: cli.print("GitFlow version: " + const.VERSION) cli.print("Python version:" + sys.version.replace('\n', ' ')) cli.print("cwd: " + os.getcwd()) if args['--hook'] is not None: if context.verbose >= const.TRACE_VERBOSITY: cli.print('hook=' + args['--hook']) hook_func = cli.get_cmd([ hook_pre_commit, hook_pre_push, ], args['--hook'], 'hook_') try: hook_result = hook_func(context) except GitFlowException as e: hook_result = e.result result.errors.extend(hook_result.errors) else: commands = { 'status': cmd_status, 'bump-major': cmd_bump_major, 'bump-minor': cmd_bump_minor, 'bump-patch': cmd_bump_patch, 'bump-prerelease-type': cmd_bump_prerelease_type, 'bump-prerelease': cmd_bump_prerelease, 'bump-to-release': cmd_bump_to_release, 'bump-to': cmd_bump_to, 'discontinue': cmd_discontinue, 'start': cmd_start, 'finish': cmd_finish, 'log': cmd_log, 'assemble': cmd_build, 'test': cmd_build, 'integration-test': cmd_build, 'drop-cache': cmd_drop_cache, 'convert-config': cmd_convert_config, } command_funcs = list() for command_name, command_func in commands.items(): if args[command_name] is True: command_funcs.append(command_func) if not len(command_funcs): cli.fail(os.EX_SOFTWARE, "unimplemented command") if context.verbose >= const.TRACE_VERBOSITY: cli.print("commands: " + repr(command_funcs)) start_branch = repotools.git_get_current_branch(context.repo) if context.repo is not None else None for command_func in command_funcs: try: command_result = command_func(context) except GitFlowException as e: command_result = e.result result.errors.extend(command_result.errors) if result.has_errors(): break current_branch = repotools.git_get_current_branch(context.repo) if context.repo is not None else None if current_branch is not None and current_branch != start_branch: cli.print(_("You are now on {branch}.") .format(branch=repr(current_branch.short_name) if current_branch is not None else '-')) finally: context.cleanup() exit_code = os.EX_OK if len(result.errors): sys.stderr.flush() sys.stdout.flush() for error in result.errors: if error.exit_code != os.EX_OK and exit_code != os.EX_SOFTWARE: exit_code = error.exit_code cli.eprint('\n'.join(filter(None, [error.message, error.reason]))) # print dry run status, if possible if context is not None: if exit_code == os.EX_OK: if context.dry_run: cli.print('') cli.print("dry run succeeded") else: pass else: if context.dry_run: cli.print('') cli.eprint("dry run failed") else: pass if profiler is not None: profiler.disable() # pr.dump_stats('profile.pstat') profiler.print_stats(sort="calls") return exit_code
def call(context: Context) -> Result: arg_work_branch = context.args.get('<work-branch>') if arg_work_branch is None: branch_prefix = context.args['<supertype>'] branch_type = context.args['<type>'] branch_name = context.args['<name>'] if branch_prefix is not None or branch_type is not None or branch_name is not None: arg_work_branch = repotools.create_ref_name(branch_prefix, branch_type, branch_name) command_context = get_command_context( context=context, object_arg=arg_work_branch ) check_in_repo(command_context) base_command_context = get_command_context( context=context, object_arg=context.args['<base-object>'] ) check_requirements(command_context=command_context, ref=command_context.selected_ref, branch_classes=[BranchClass.WORK_DEV, BranchClass.WORK_PROD], modifiable=True, with_upstream=True, # not context.config.push_to_local in_sync_with_upstream=True, fail_message=_("Version creation failed.") ) work_branch = None selected_ref_match = context.work_branch_matcher.fullmatch(command_context.selected_ref.name) if selected_ref_match is not None: work_branch = WorkBranch() work_branch.prefix = selected_ref_match.group('prefix') work_branch.type = selected_ref_match.group('type') work_branch.name = selected_ref_match.group('name') else: if command_context.selected_explicitly: context.fail(os.EX_USAGE, _("The ref {branch} does not refer to a work branch.") .format(branch=repr(command_context.selected_ref.name)), None) work_branch_info = get_branch_info(command_context, work_branch.local_ref_name()) if work_branch_info is None: context.fail(os.EX_USAGE, _("The branch {branch} does neither exist locally nor remotely.") .format(branch=repr(work_branch.branch_name())), None) work_branch_ref, work_branch_class = select_ref(command_context.result, work_branch_info, BranchSelection.BRANCH_PREFER_LOCAL) allowed_base_branch_class = const.BRANCHING[work_branch_class] base_branch_info = get_branch_info(base_command_context, base_command_context.selected_ref) base_branch_ref, base_branch_class = select_ref(command_context.result, base_branch_info, BranchSelection.BRANCH_PREFER_LOCAL) if not base_command_context.selected_explicitly: if work_branch.prefix == const.BRANCH_PREFIX_DEV: base_branch_info = get_branch_info(base_command_context, repotools.create_ref_name(const.LOCAL_BRANCH_PREFIX, context.config.release_branch_base)) base_branch_ref, base_branch_class = select_ref(command_context.result, base_branch_info, BranchSelection.BRANCH_PREFER_LOCAL) elif work_branch.prefix == const.BRANCH_PREFIX_PROD: # discover closest merge base in release branches release_branches = repotools.git_list_refs(context.repo, repotools.create_ref_name(const.REMOTES_PREFIX, context.config.remote_name, 'release')) release_branches = list(release_branches) release_branches.sort(reverse=True, key=utils.cmp_to_key(lambda ref_a, ref_b: semver.compare( context.release_branch_matcher.format(ref_a.name), context.release_branch_matcher.format(ref_b.name) ))) for release_branch_ref in release_branches: merge_base = repotools.git_merge_base(context.repo, base_branch_ref, work_branch_ref.name) if merge_base is not None: base_branch_info = get_branch_info(base_command_context, release_branch_ref) base_branch_ref, base_branch_class = select_ref(command_context.result, base_branch_info, BranchSelection.BRANCH_PREFER_LOCAL) break if allowed_base_branch_class != base_branch_class: context.fail(os.EX_USAGE, _("The branch {branch} is not a valid base for {supertype} branches.") .format(branch=repr(base_branch_ref.name), supertype=repr(work_branch.prefix)), None) if base_branch_ref is None: context.fail(os.EX_USAGE, _("Base branch undetermined."), None) if context.verbose: cli.print("branch_name: " + command_context.selected_ref.name) cli.print("work_branch_name: " + work_branch_ref.name) cli.print("base_branch_name: " + base_branch_ref.name) # check, if already merged merge_base = repotools.git_merge_base(context.repo, base_branch_ref, work_branch_ref.name) if work_branch_ref.obj_name == merge_base: cli.print(_("Branch {branch} is already merged.") .format(branch=repr(work_branch_ref.name))) return context.result # check for staged changes index_status = git(context.repo, ['diff-index', 'HEAD', '--']) if index_status == 1: context.fail(os.EX_USAGE, _("Branch creation aborted."), _("You have staged changes in your workspace.\n" "Unstage, commit or stash them and try again.")) elif index_status != 0: context.fail(os.EX_DATAERR, _("Failed to determine index status."), None) if not context.dry_run and not command_context.has_errors(): # perform merge local_branch_ref_name = repotools.create_local_branch_ref_name(base_branch_ref.name) local_branch_name = repotools.create_local_branch_name(base_branch_ref.name) if local_branch_ref_name == base_branch_ref.name: git_or_fail(context.repo, command_context.result, ['checkout', local_branch_name], _("Failed to checkout branch {branch_name}.") .format(branch_name=repr(base_branch_ref.short_name)) ) else: git_or_fail(context.repo, command_context.result, ['checkout', '-b', local_branch_name, base_branch_ref.name], _("Failed to checkout branch {branch_name}.") .format(branch_name=repr(base_branch_ref.short_name)) ) git_or_fail(context.repo, command_context.result, ['merge', '--no-ff', work_branch_ref], _("Failed to merge work branch.\n" "Rebase {work_branch} on {base_branch} and try again") .format(work_branch=repr(work_branch_ref.short_name), base_branch=repr(base_branch_ref.short_name)) ) git_or_fail(context.repo, command_context.result, ['push', context.config.remote_name, local_branch_name], _("Failed to push branch {branch_name}.") .format(branch_name=repr(base_branch_ref.short_name)) ) return context.result