예제 #1
0
    def setup_method(self, method):
        TestFlowBase.setup_method(self, method)

        # create the config file
        self.project_property_file = 'project.properties'
        config_file = os.path.join(self.git_working_copy,
                                   const.DEFAULT_CONFIG_FILE)
        config = {
            const.CONFIG_VERSIONING_SCHEME: 'semverWithSeq',
            const.CONFIG_PROJECT_PROPERTY_FILE: self.project_property_file,
            const.CONFIG_VERSION_PROPERTY: 'version',
            const.CONFIG_SEQUENCE_NUMBER_PROPERTY: 'seq',
            const.CONFIG_VERSION_TAG_PREFIX: ''
        }

        PropertyIO.write_file(config_file, config)

        self.version_tag_prefix = config.get(
            const.CONFIG_VERSION_TAG_PREFIX,
            const.DEFAULT_VERSION_TAG_PREFIX) or ''

        # create & push the initial commit
        self.add(config_file)
        self.commit('initial commit: gitflow config file')
        self.push()

        self.assert_refs({'refs/heads/master', 'refs/remotes/origin/master'})
예제 #2
0
    def setup_method(self, method):
        super().setup_method(method)

        self.git_working_copy = os.path.join(self.tempdir.name,
                                             'exported_working_copy')
        os.makedirs(self.git_working_copy, exist_ok=True)

        # create the config file
        self.project_property_file = 'project.properties'
        config_file = os.path.join(self.git_working_copy,
                                   const.DEFAULT_CONFIG_FILE)
        config = {
            const.CONFIG_VERSIONING_SCHEME: 'semverWithSeq',
            const.CONFIG_PROJECT_PROPERTY_FILE: self.project_property_file,
            const.CONFIG_VERSION_PROPERTY: 'version',
            const.CONFIG_SEQUENCE_NUMBER_PROPERTY: 'seq',
            const.CONFIG_BUILD: {
                'stages': {
                    'assemble': [['echo', 'assemble#1']],
                    'test': {
                        'steps': {
                            'app': [['echo', 'test#1'],
                                    ['echo', '\\$HOME: $HOME'],
                                    ['echo', '\\\\\\$HOME: \\$HOME'],
                                    ['echo', '\\\\\\\\\\$HOME: \\\\$HOME'],
                                    ['echo', '\\${HOME}: ${HOME}'],
                                    ['echo', '\\\\\\${HOME}: \\${HOME}'],
                                    ['echo', '\\\\\\\\\\${HOME}: \\\\${HOME}']]
                        }
                    },
                    'google_testing_lab': {
                        'type': 'integration_test',
                        'steps': {
                            'monkey_test': [['echo', 'monkey_test']],
                            'instrumentation_test':
                            [['echo', 'instrumentation_test']]
                        }
                    }
                }
            }
        }

        PropertyIO.write_file(config_file, config)

        os.chdir(self.git_working_copy)
예제 #3
0
    def setup_method(self, method):
        TestFlowBase.setup_method(self, method)

        # create the config file
        self.project_property_file = 'project.properties'
        config_file = os.path.join(self.git_working_copy,
                                   const.DEFAULT_CONFIG_FILE)
        config = {
            const.CONFIG_VERSIONING_SCHEME: 'semverWithSeq',
            const.CONFIG_PROJECT_PROPERTY_FILE: self.project_property_file,
            const.CONFIG_VERSION_PROPERTY: 'version',
            const.CONFIG_SEQUENCE_NUMBER_PROPERTY: 'seq',
            const.CONFIG_BUILD: {
                'stages': {
                    'assemble': [['echo', 'assemble#1']],
                    'test': {
                        'steps': {
                            'app': [['echo', 'test#1'],
                                    ['echo', '\\$HOME: $HOME'],
                                    ['echo', '\\\\\\$HOME: \\$HOME'],
                                    ['echo', '\\\\\\\\\\$HOME: \\\\$HOME'],
                                    ['echo', '\\${HOME}: ${HOME}'],
                                    ['echo', '\\\\\\${HOME}: \\${HOME}'],
                                    ['echo', '\\\\\\\\\\${HOME}: \\\\${HOME}']]
                        }
                    },
                    'google_testing_lab': {
                        'type': 'integration_test',
                        'steps': {
                            'monkey_test': [['echo', 'monkey_test']],
                            'instrumentation_test':
                            [['echo', 'instrumentation_test']]
                        }
                    }
                }
            }
        }
        PropertyIO.write_file(config_file, config)

        # create & push the initial commit
        self.add(config_file)
        self.commit('initial commit: gitflow config file')
        self.push()

        self.assert_refs({'refs/heads/master', 'refs/remotes/origin/master'})
예제 #4
0
    def assert_project_properties_contain(self, expected: dict):
        property_reader = PropertyIO.get_instance_by_filename(
            self.project_property_file)
        try:
            actual = property_reader.from_file(self.project_property_file)
        except FileNotFoundError:
            actual = dict()

        self.assert_same_pairs(expected, actual)
예제 #5
0
def cmd_convert_config(context):
    result = Result()

    with open(context.args['<input-file>'], mode='r', encoding='utf-8') as in_file:
        if in_file is None:
            result.fail(os.EX_USAGE,
                        _("Failed to open input file"),
                        None)
            return result
        input = PropertyIO.get_instance_by_filename(in_file.name)

        with open(context.args['<output-file>'], mode='w', encoding='utf-8') as out_file:
            if out_file is None:
                result.fail(os.EX_USAGE,
                            _("Failed to open output file"),
                            None)
                return result

            output = PropertyIO.get_instance_by_filename(out_file.name)
            config = input.from_stream(in_file)
            output.to_stream(out_file, config)
    return result
예제 #6
0
def update_project_property_file(context: Context, prev_properties: dict,
                                 new_version: str, new_sequential_version: int,
                                 commit_out: CommitInfo):
    result = Result()
    result.value = False

    if context.config.property_file is not None:
        property_reader = PropertyIO.get_instance_by_filename(
            context.config.property_file)
        if property_reader is None:
            result.fail(
                os.EX_DATAERR,
                _("Property file not supported: {path}\n"
                  "Currently supported:\n"
                  "{listing}").format(path=repr(context.config.property_file),
                                      listing='\n'.join(
                                          ' - ' + type
                                          for type in ['*.properties'])), None)

        properties = update_project_properties(context, prev_properties,
                                               new_version,
                                               new_sequential_version)

        property_reader.write_file(context.config.property_file, properties)
        commit_out.add_file(context.config.property_file)
        result.value = True
    else:
        properties = None

    var_separator = ' : '

    if properties is not None:

        def log_property(properties: dict, key: str):
            if key is not None:
                commit_out.add_message(
                    '#properties[' + utils.quote(key, '"') + ']' +
                    var_separator + cli.if_none(properties.get(key), "null"))

        for property_key in [
                context.config.version_property,
                context.config.sequence_number_property
        ]:
            log_property(properties, property_key)

    if context.verbose and result.value != 0:
        print("properties have changed")
        print("commit message:")
        print(commit_out.message)

    return result
예제 #7
0
    def __test_load_store(self, file_name: str):
        property_file: PropertyIO = PropertyIO.get_instance_by_filename(
            file_name)
        properties = dict()

        properties['bla'] = 'blub'
        property_file.to_file(file_name, properties)

        print('===================================')
        print(file_name)
        print('---------- FILE CONTENTS ----------')
        with open(file_name, 'r') as file:
            print(file.read())
        print('-----------------------------------')

        stored_properties = property_file.from_file(file_name)

        assert properties == stored_properties
예제 #8
0
    def __test_load_store_bytes(self, file_name: str):
        property_file: PropertyIO = PropertyIO.get_instance_by_filename(
            file_name)
        properties = property_file.from_bytes(bytes(), 'UTF-8')

        assert len(properties) == 0

        properties['bla'] = 'blub'
        written_bytes = property_file.to_bytes(properties, 'UTF-8')

        print('===================================')
        print(file_name)
        print('---------- FILE CONTENTS ----------')
        print(str(written_bytes, 'UTF-8'))
        print('-----------------------------------')

        stored_properties = property_file.from_bytes(written_bytes, 'UTF-8')

        assert properties == stored_properties
예제 #9
0
    def __test_load_store_string(self, file_name: str):
        property_file: PropertyIO = PropertyIO.get_instance_by_filename(
            file_name)
        properties = property_file.from_str("")

        assert len(properties) == 0

        properties['bla'] = 'blub'
        written_string = property_file.to_str(properties)

        print('===================================')
        print(file_name)
        print('---------- FILE CONTENTS ----------')
        print(written_string)
        print('-----------------------------------')

        stored_properties = property_file.from_str(written_string)

        assert properties == stored_properties
예제 #10
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
예제 #11
0
def read_config_in_commit(
        repo: RepoContext,
        commit: str,
        config_file_path: str = const.DEFAULT_CONFIG_FILE) -> dict:
    if config_file_path is None:
        config_str = None
        for config_filename in const.DEFAULT_CONFIGURATION_FILE_NAMES:
            config_str = repotools.get_file_contents(repo, commit,
                                                     config_filename)
            if config_filename is not None:
                break
    else:
        config_str = repotools.get_file_contents(repo, commit,
                                                 config_file_path)

    if config_str is not None:
        config = PropertyIO.get_instance_by_filename(
            config_file_path).from_bytes(config_str,
                                         const.DEFAULT_PROPERTY_ENCODING)
    else:
        config = None
    return config
예제 #12
0
    def create(args: dict, result_out: Result) -> 'Context':
        context = Context()
        context.config: Config = Config()

        if args is not None:
            context.args = args

            context.batch = context.args['--batch']
            context.assume_yes = context.args.get('--assume-yes')
            context.dry_run = context.args.get('--dry-run')
            # TODO remove this workaround
            context.verbose = (context.args['--verbose'] + 1) // 2
            context.pretty = context.args['--pretty']
        else:
            context.args = dict()

        # configure CLI
        cli.set_allow_color(not context.batch)

        # initialize repo context and attempt to load the config file
        if '--root' in context.args and context.args['--root'] is not None:
            context.root = context.args['--root']

            context.repo = RepoContext()
            context.repo.dir = context.root
            context.repo.verbose = context.verbose

            context.git_version = repotools.git_version(context.repo)
            # context.repo.use_root_dir_arg = semver.compare(context.git_version, "2.9.0") >= 0
            context.repo.use_root_dir_arg = False

            repo_root = repotools.git_rev_parse(context.repo, '--show-toplevel')

            # None when invalid or bare
            if repo_root is not None:
                context.repo.dir = repo_root

                if context.verbose >= const.TRACE_VERBOSITY:
                    cli.print("--------------------------------------------------------------------------------")
                    cli.print("refs in {repo}:".format(repo=context.repo.dir))
                    cli.print("--------------------------------------------------------------------------------")
                    for ref in repotools.git_list_refs(context.repo):
                        cli.print(repr(ref))
                    cli.print("--------------------------------------------------------------------------------")
                config_dir = context.repo.dir
            else:
                context.repo = None
                config_dir = context.root

            gitflow_config_file: Optional[str] = None
            if context.args['--config'] is not None:
                gitflow_config_file = os.path.join(config_dir, context.args['--config'])
                if gitflow_config_file is None:
                    result_out.fail(os.EX_DATAERR,
                                    _("the specified config file does not exist or is not a regular file: {path}.")
                                    .format(path=repr(gitflow_config_file)),
                                    None
                                    )
            else:
                for config_filename in const.DEFAULT_CONFIGURATION_FILE_NAMES:
                    path = os.path.join(config_dir, config_filename)
                    if os.path.exists(path):
                        gitflow_config_file = path
                        break
                if gitflow_config_file is None:
                    result_out.fail(os.EX_DATAERR,
                                    _("config file not found.")
                                    .format(path=repr(gitflow_config_file)),
                                    _("Default config files are\n:{list}")
                                    .format(list=const.DEFAULT_CONFIGURATION_FILE_NAMES)
                                    )

            if context.verbose >= const.TRACE_VERBOSITY:
                cli.print("gitflow_config_file: " + gitflow_config_file)

            with open(gitflow_config_file) as json_file:
                config = PropertyIO.get_instance_by_filename(gitflow_config_file).from_stream(json_file)
        else:
            config = object()

        build_config_json = config.get(const.CONFIG_BUILD)

        context.config.version_change_actions = config.get(const.CONFIG_ON_VERSION_CHANGE, [])

        context.config.build_stages = list()

        if build_config_json is not None:
            stages_json = build_config_json.get('stages')
            if stages_json is not None:
                for stage_key, stage_json in stages_json.items():

                    stage = BuildStage()

                    if isinstance(stage_json, dict):
                        stage.type = stage_json.get('type') or stage_key
                        if stage.type not in const.BUILD_STAGE_TYPES:
                            result_out.fail(
                                os.EX_DATAERR,
                                _("Configuration failed."),
                                _("Invalid build stage type {key}."
                                  .format(key=repr(stage.type)))
                            )

                        stage.name = stage_json.get('name') or stage_key

                        stage_labels = stage_json.get('labels')
                        if isinstance(stage_labels, list):
                            stage.labels.extend(stage_labels)
                        else:
                            stage.labels.append(stage_labels)

                        stage_steps_json = stage_json.get('steps')
                        if stage_steps_json is not None:
                            for step_key, step_json in stage_steps_json.items():
                                step = BuildStep()

                                if isinstance(step_json, dict):
                                    step.name = step_json.get('name') or step_key
                                    step.commands = step_json.get('commands')

                                    stage_labels = stage_json.get('labels')
                                    if isinstance(stage_labels, list):
                                        stage.labels.extend(stage_labels)
                                    else:
                                        stage.labels.append(stage_labels)
                                elif isinstance(step_json, list):
                                    step.name = step_key
                                    step.type = step_key
                                    step.commands = step_json
                                else:
                                    result_out.fail(
                                        os.EX_DATAERR,
                                        _("Configuration failed."),
                                        _("Invalid build step definition {type} {key}."
                                          .format(type=repr(type(step_json)), key=repr(step_key)))
                                    )

                                stage.steps.append(step)
                    elif isinstance(stage_json, list):
                        stage.type = stage_key
                        stage.name = stage_key

                        if len(stage_json):
                            step = BuildStep()
                            step.name = '#'
                            step.commands = stage_json
                            stage.steps.append(step)
                    else:
                        result_out.fail(
                            os.EX_DATAERR,
                            _("Configuration failed."),
                            _("Invalid build stage definition {key}."
                              .format(key=repr(stage_key)))
                        )
                    context.config.build_stages.append(stage)

        context.config.build_stages.sort(key=utils.cmp_to_key(lambda stage_a, stage_b:
                                                              const.BUILD_STAGE_TYPES.index(stage_a.type)
                                                              - const.BUILD_STAGE_TYPES.index(stage_b.type)
                                                              ),
                                         reverse=False
                                         )

        # project properties config

        context.config.property_file = config.get(const.CONFIG_PROJECT_PROPERTY_FILE)
        if context.config.property_file is not None:
            context.config.property_file = os.path.join(context.root, context.config.property_file)

        context.config.version_property = config.get(const.CONFIG_VERSION_PROPERTY)
        context.config.sequence_number_property = config.get(
            const.CONFIG_SEQUENCE_NUMBER_PROPERTY)
        context.config.version_property = config.get(
            const.CONFIG_VERSION_PROPERTY)

        property_names = [property for property in
                          [context.config.sequence_number_property, context.config.version_property] if
                          property is not None]
        duplicate_property_names = [item for item, count in collections.Counter(property_names).items() if count > 1]

        if len(duplicate_property_names):
            result_out.fail(os.EX_DATAERR, _("Configuration failed."),
                            _("Duplicate property names: {duplicate_property_names}").format(
                                duplicate_property_names=', '.join(duplicate_property_names))
                            )

        # version config

        context.config.version_config = VersionConfig()

        versioning_scheme = config.get(const.CONFIG_VERSIONING_SCHEME, const.DEFAULT_VERSIONING_SCHEME)

        if versioning_scheme not in const.VERSIONING_SCHEMES:
            result_out.fail(os.EX_DATAERR, _("Configuration failed."),
                            _("The versioning scheme {versioning_scheme} is invalid.").format(
                                versioning_scheme=utils.quote(versioning_scheme, '\'')))

        context.config.version_config.versioning_scheme = const.VERSIONING_SCHEMES[versioning_scheme]

        if context.config.version_config.versioning_scheme == VersioningScheme.SEMVER:
            qualifiers = config.get(const.CONFIG_VERSION_TYPES, const.DEFAULT_PRE_RELEASE_QUALIFIERS)
            if isinstance(qualifiers, str):
                qualifiers = [qualifier.strip() for qualifier in qualifiers.split(",")]
            if qualifiers != sorted(qualifiers):
                result_out.fail(
                    os.EX_DATAERR,
                    _("Configuration failed."),
                    _("Pre-release qualifiers are not specified in ascending order.")
                )
            context.config.version_config.qualifiers = qualifiers
            context.config.version_config.initial_version = const.DEFAULT_INITIAL_VERSION
        elif context.config.version_config.versioning_scheme == VersioningScheme.SEMVER_WITH_SEQ:
            context.config.version_config.qualifiers = None
            context.config.version_config.initial_version = const.DEFAULT_INITIAL_SEQ_VERSION
        else:
            context.fail(os.EX_CONFIG, "configuration error", "invalid versioning scheme")

        # branch config

        context.config.remote_name = "origin"
        context.config.release_branch_base = config.get(const.CONFIG_RELEASE_BRANCH_BASE,
                                                        const.DEFAULT_RELEASE_BRANCH_BASE)

        remote_prefix = repotools.create_ref_name(const.REMOTES_PREFIX, context.config.remote_name)

        context.release_base_branch_matcher = VersionMatcher(
            [const.LOCAL_BRANCH_PREFIX, remote_prefix],
            None,
            re.escape(context.config.release_branch_base),
        )

        context.release_branch_matcher = VersionMatcher(
            [const.LOCAL_BRANCH_PREFIX, remote_prefix],
            config.get(
                const.CONFIG_RELEASE_BRANCH_PREFIX,
                const.DEFAULT_RELEASE_BRANCH_PREFIX),
            config.get(
                const.CONFIG_RELEASE_BRANCH_PATTERN,
                const.DEFAULT_RELEASE_BRANCH_PATTERN),
        )

        context.work_branch_matcher = VersionMatcher(
            [const.LOCAL_BRANCH_PREFIX, remote_prefix],
            [const.BRANCH_PREFIX_DEV, const.BRANCH_PREFIX_PROD],
            config.get(
                const.CONFIG_WORK_BRANCH_PATTERN,
                const.DEFAULT_WORK_BRANCH_PATTERN),
        )

        context.version_tag_matcher = VersionMatcher(
            [const.LOCAL_TAG_PREFIX],
            config.get(
                const.CONFIG_VERSION_TAG_PREFIX,
                const.DEFAULT_VERSION_TAG_PREFIX),
            config.get(
                const.CONFIG_VERSION_TAG_PATTERN,
                const.DEFAULT_SEMVER_VERSION_TAG_PATTERN
                if context.config.version_config.versioning_scheme == VersioningScheme.SEMVER
                else const.DEFAULT_SEMVER_WITH_SEQ_VERSION_TAG_PATTERN)
        )
        context.version_tag_matcher.group_unique_code = None \
            if context.config.version_config.versioning_scheme == VersioningScheme.SEMVER \
            else 'prerelease_type'

        context.discontinuation_tag_matcher = VersionMatcher(
            [const.LOCAL_TAG_PREFIX],
            config.get(
                const.CONFIG_DISCONTINUATION_TAG_PREFIX,
                const.DEFAULT_DISCONTINUATION_TAG_PREFIX),
            config.get(
                const.CONFIG_DISCONTINUATION_TAG_PATTERN,
                const.DEFAULT_DISCONTINUATION_TAG_PATTERN),
            None
        )

        return context