Example #1
0
    def test_simple_load(self, caplog):
        yaml = """xyz:
            y: abc"""
        cciyml = cci_safe_load(StringIO(yaml))
        assert not caplog.text

        assert isinstance(cciyml, dict)  # should parse despite funny character
        assert cciyml["xyz"]["y"] == "abc", cciyml
Example #2
0
    def test_convert_nbsp(self, caplog):
        yaml = """xyz:
           \u00A0 y: abc"""
        cciyml = cci_safe_load(StringIO(yaml))
        assert "space character" in caplog.text

        assert isinstance(cciyml, dict)  # should parse despite funny character
        assert cciyml["xyz"]["y"] == "abc", cciyml
Example #3
0
    def _get_repo_dependencies(self,
                               dependencies=None,
                               include_beta=None,
                               visited_repos=None):
        """Return a list of Package objects representing all of the GitHub repositories
        in this project's dependency tree. Ignore all non-GitHub dependencies."""
        deps = []
        visited = visited_repos or set()

        for dependency in dependencies:
            if "github" in dependency:
                repo = self.project_config.get_repo_from_url(
                    dependency["github"])
                if repo is None:
                    raise DependencyResolutionError(
                        f"Github repository {dependency['github']} not found or not authorized."
                    )
                _, ref = self.project_config.get_ref_for_dependency(
                    repo, dependency, include_beta)

                contents = repo.file_contents("cumulusci.yml", ref=ref)
                cumulusci_yml = cci_safe_load(
                    io.StringIO(contents.decoded.decode("utf-8")))
                project = cumulusci_yml.get("project", {})
                namespace = project.get("package", {}).get("namespace", "")
                if namespace:
                    namespace = f"{namespace}__"
                else:
                    namespace = ""

                if f"{repo.owner}/{repo.name}" in visited:
                    continue

                deps.append(
                    Package(
                        repo,
                        project.get("package",
                                    {}).get("name",
                                            f"{repo.owner}/{repo.name}"),
                        namespace,
                        project.get("git", {}).get("prefix_release",
                                                   "release/"),
                    ))
                visited.add(f"{repo.owner}/{repo.name}")

                deps.extend(
                    self._get_repo_dependencies(
                        cumulusci_yml.get("project",
                                          {}).get("dependencies", []),
                        visited_repos=visited,
                    ))

        return deps
Example #4
0
    def process_github_dependency(  # noqa: C901
            self, dependency, indent=None, include_beta=None):
        if not indent:
            indent = ""

        self.logger.info(
            f"{indent}Processing dependencies from Github repo {dependency['github']}"
        )

        skip = dependency.get("skip")
        if not isinstance(skip, list):
            skip = [skip]

        # Initialize github3.py API against repo
        repo_owner, repo_name = dependency["github"].split("/")[3:5]
        if repo_name.endswith(".git"):
            repo_name = repo_name[:-4]
        gh = self.get_github_api(repo_owner, repo_name)
        repo = gh.repository(repo_owner, repo_name)
        if repo is None:
            raise DependencyResolutionError(
                f"{indent}Github repository {dependency['github']} not found or not authorized."
            )

        # Determine the commit
        release = None
        if "ref" in dependency:
            ref = dependency["ref"]
        else:
            if "tag" in dependency:
                try:
                    # Find the github release corresponding to this tag.
                    release = repo.release_from_tag(dependency["tag"])
                except NotFoundError:
                    raise DependencyResolutionError(
                        f"{indent}No release found for tag {dependency['tag']}"
                    )
            else:
                release = find_latest_release(repo, include_beta)
            if release:
                ref = repo.tag(
                    repo.ref("tags/" + release.tag_name).object.sha).object.sha
            else:
                self.logger.info(
                    f"{indent}No release found; using the latest commit from the {repo.default_branch} branch."
                )
                ref = repo.branch(repo.default_branch).commit.sha

        # Get the cumulusci.yml file
        contents = repo.file_contents("cumulusci.yml", ref=ref)
        cumulusci_yml = cci_safe_load(
            io.StringIO(contents.decoded.decode("utf-8")))

        # Get the namespace from the cumulusci.yml if set
        package_config = cumulusci_yml.get("project", {}).get("package", {})
        namespace = package_config.get("namespace")
        package_name = (package_config.get("name_managed")
                        or package_config.get("name") or namespace)

        # Check for unmanaged flag on a namespaced package
        unmanaged = namespace and dependency.get("unmanaged") is True

        # Look for subfolders under unpackaged/pre
        unpackaged_pre = []
        try:
            contents = repo.directory_contents("unpackaged/pre",
                                               return_as=dict,
                                               ref=ref)
        except NotFoundError:
            contents = None
        if contents:
            for dirname in list(contents.keys()):
                subfolder = f"unpackaged/pre/{dirname}"
                if subfolder in skip:
                    continue
                name = f"Deploy {subfolder}"

                unpackaged_pre.append({
                    "name":
                    name,
                    "repo_owner":
                    repo_owner,
                    "repo_name":
                    repo_name,
                    "ref":
                    ref,
                    "subfolder":
                    subfolder,
                    "unmanaged":
                    dependency.get("unmanaged"),
                    "namespace_tokenize":
                    dependency.get("namespace_tokenize"),
                    "namespace_inject":
                    dependency.get("namespace_inject"),
                    "namespace_strip":
                    dependency.get("namespace_strip"),
                })

        # Look for metadata under src (deployed if no namespace)
        unmanaged_src = None
        if unmanaged or not namespace:
            contents = repo.directory_contents("src", ref=ref)
            if contents:
                subfolder = "src"

                unmanaged_src = {
                    "name": f"Deploy {package_name or repo_name}",
                    "repo_owner": repo_owner,
                    "repo_name": repo_name,
                    "ref": ref,
                    "subfolder": subfolder,
                    "unmanaged": dependency.get("unmanaged"),
                    "namespace_tokenize": dependency.get("namespace_tokenize"),
                    "namespace_inject": dependency.get("namespace_inject"),
                    "namespace_strip": dependency.get("namespace_strip"),
                }

        # Look for subfolders under unpackaged/post
        unpackaged_post = []
        try:
            contents = repo.directory_contents("unpackaged/post",
                                               return_as=dict,
                                               ref=ref)
        except NotFoundError:
            contents = None
        if contents:
            for dirname in list(contents.keys()):
                subfolder = f"unpackaged/post/{dirname}"
                if subfolder in skip:
                    continue
                name = f"Deploy {subfolder}"

                dependency = {
                    "name": name,
                    "repo_owner": repo_owner,
                    "repo_name": repo_name,
                    "ref": ref,
                    "subfolder": subfolder,
                    "unmanaged": dependency.get("unmanaged"),
                    "namespace_tokenize": dependency.get("namespace_tokenize"),
                    "namespace_inject": dependency.get("namespace_inject"),
                    "namespace_strip": dependency.get("namespace_strip"),
                }
                # By default, we always inject the project's namespace into
                # unpackaged/post metadata
                if namespace and not dependency.get("namespace_inject"):
                    dependency["namespace_inject"] = namespace
                    dependency["unmanaged"] = unmanaged
                unpackaged_post.append(dependency)

        # Parse values from the repo's cumulusci.yml
        project = cumulusci_yml.get("project", {})
        dependencies = project.get("dependencies")
        if dependencies:
            dependencies = self.get_static_dependencies(
                dependencies, include_beta=include_beta)

        # Create the final ordered list of all parsed dependencies
        repo_dependencies = []

        # unpackaged/pre/*
        if unpackaged_pre:
            repo_dependencies.extend(unpackaged_pre)

        if namespace and not unmanaged:
            if release is None:
                raise DependencyResolutionError(
                    f"{indent}Could not find latest release for {namespace}")
            version = release.name
            # If a latest prod version was found, make the dependencies a
            # child of that install
            dependency = {
                "name": f"Install {package_name or namespace} {version}",
                "namespace": namespace,
                "version": version,
            }
            if dependencies:
                dependency["dependencies"] = dependencies
            repo_dependencies.append(dependency)

        # Unmanaged metadata from src (if referenced repo doesn't have a
        # namespace)
        else:
            if dependencies:
                repo_dependencies.extend(dependencies)
            if unmanaged_src:
                repo_dependencies.append(unmanaged_src)

        # unpackaged/post/*
        if unpackaged_post:
            repo_dependencies.extend(unpackaged_post)

        return repo_dependencies