Esempio n. 1
0
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))
Esempio n. 2
0
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
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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
Esempio n. 7
0
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
Esempio n. 8
0
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