Exemple #1
0
 def __init__(self):
     """Set up HacsRepository."""
     self.hacs = get_hacs()
     self.data = RepositoryData()
     self.content = RepositoryContent()
     self.content.path = RepositoryPath()
     self.information = RepositoryInformation()
     self.repository_object = None
     self.status = RepositoryStatus()
     self.state = None
     self.manifest = {}
     self.repository_manifest = HacsManifest.from_dict({})
     self.validate = Validate()
     self.releases = RepositoryReleases()
     self.versions = RepositoryVersions()
     self.pending_restart = False
     self.tree = []
     self.treefiles = []
     self.ref = None
Exemple #2
0
def dummy_repository_base(repository=None):
    if repository is None:
        repository = HacsRepository()
    repository.hacs.hass = HomeAssistant()
    repository.hacs.hass.data = {"custom_components": []}
    repository.logger = Logger("hacs.test.test")
    repository.information.name = "test"
    repository.information.full_name = "test/test"
    repository.versions.available = "3"
    repository.status.selected_tag = "3"
    repository.ref = version_to_install(repository)
    repository.manifest = {"config_flow": False, "domain": "test"}
    repository.releases.published_tags = ["1", "2", "3"]
    repository.data = RepositoryData().create_from_dict(repository_data)
    return repository
Exemple #3
0
class HacsRepository:
    """HacsRepository."""
    def __init__(self):
        """Set up HacsRepository."""
        self.hacs = get_hacs()
        self.data = RepositoryData()
        self.content = RepositoryContent()
        self.content.path = RepositoryPath()
        self.information = RepositoryInformation()
        self.repository_object = None
        self.status = RepositoryStatus()
        self.state = None
        self.manifest = {}
        self.repository_manifest = HacsManifest.from_dict({})
        self.validate = Validate()
        self.releases = RepositoryReleases()
        self.versions = RepositoryVersions()
        self.pending_restart = False
        self.tree = []
        self.treefiles = []
        self.ref = None

    @property
    def pending_upgrade(self):
        """Return pending upgrade."""
        if self.status.installed:
            if self.status.selected_tag is not None:
                if self.status.selected_tag == self.data.default_branch:
                    if self.versions.installed_commit != self.versions.available_commit:
                        return True
                    return False
            if self.display_installed_version != self.display_available_version:
                return True
        return False

    @property
    def config_flow(self):
        """Return bool if integration has config_flow."""
        if self.manifest:
            if self.information.full_name == "hacs/integration":
                return False
            return self.manifest.get("config_flow", False)
        return False

    @property
    def custom(self):
        """Return flag if the repository is custom."""
        if self.information.full_name.split("/")[0] in [
                "custom-components",
                "custom-cards",
        ]:
            return False
        if self.information.full_name in self.hacs.common.default:
            return False
        if self.information.full_name == "hacs/integration":
            return False
        return True

    @property
    def can_install(self):
        """Return bool if repository can be installed."""
        target = None
        if self.information.homeassistant_version is not None:
            target = self.information.homeassistant_version
        if self.repository_manifest is not None:
            if self.data.homeassistant is not None:
                target = self.data.homeassistant

        if target is not None:
            if self.releases.releases:
                if not version_left_higher_then_right(
                        self.hacs.system.ha_version, target):
                    return False
        return True

    @property
    def display_name(self):
        """Return display name."""
        return get_repository_name(
            self.repository_manifest,
            self.information.name,
            self.information.category,
            self.manifest,
        )

    @property
    def display_status(self):
        """Return display_status."""
        if self.status.new:
            status = "new"
        elif self.pending_restart:
            status = "pending-restart"
        elif self.pending_upgrade:
            status = "pending-upgrade"
        elif self.status.installed:
            status = "installed"
        else:
            status = "default"
        return status

    @property
    def display_status_description(self):
        """Return display_status_description."""
        description = {
            "default": "Not installed.",
            "pending-restart": "Restart pending.",
            "pending-upgrade": "Upgrade pending.",
            "installed": "No action required.",
            "new": "This is a newly added repository.",
        }
        return description[self.display_status]

    @property
    def display_installed_version(self):
        """Return display_authors"""
        if self.versions.installed is not None:
            installed = self.versions.installed
        else:
            if self.versions.installed_commit is not None:
                installed = self.versions.installed_commit
            else:
                installed = ""
        return installed

    @property
    def display_available_version(self):
        """Return display_authors"""
        if self.versions.available is not None:
            available = self.versions.available
        else:
            if self.versions.available_commit is not None:
                available = self.versions.available_commit
            else:
                available = ""
        return available

    @property
    def display_version_or_commit(self):
        """Does the repositoriy use releases or commits?"""
        if self.releases.releases:
            version_or_commit = "version"
        else:
            version_or_commit = "commit"
        return version_or_commit

    @property
    def main_action(self):
        """Return the main action."""
        actions = {
            "new": "INSTALL",
            "default": "INSTALL",
            "installed": "REINSTALL",
            "pending-restart": "REINSTALL",
            "pending-upgrade": "UPGRADE",
        }
        return actions[self.display_status]

    async def common_validate(self):
        """Common validation steps of the repository."""
        await common_validate(self)

    async def common_registration(self):
        """Common registration steps of the repository."""
        # Attach repository
        if self.repository_object is None:
            self.repository_object = await get_repository(
                self.hacs.session,
                self.hacs.configuration.token,
                self.information.full_name,
            )
            self.data = self.data.create_from_dict(
                self.repository_object.attributes)

        # Set id
        self.information.uid = str(self.data.id)

        # Set topics
        self.information.topics = self.data.topics

        # Set stargazers_count
        self.information.stars = self.data.stargazers_count

        # Set description
        self.information.description = self.data.description

    async def common_update(self):
        """Common information update steps of the repository."""
        self.logger.debug("Getting repository information")

        # Attach repository
        self.repository_object = await get_repository(
            self.hacs.session, self.hacs.configuration.token,
            self.information.full_name)
        self.data = self.data.create_from_dict(
            self.repository_object.attributes)

        # Set ref
        self.ref = version_to_install(self)

        # Update tree
        self.tree = await get_tree(self.repository_object, self.ref)
        self.treefiles = []
        for treefile in self.tree:
            self.treefiles.append(treefile.full_path)

        # Update description
        self.information.description = self.data.description

        # Set stargazers_count
        self.information.stars = self.data.stargazers_count

        # Update last updaeted
        self.information.last_updated = self.repository_object.attributes.get(
            "pushed_at", 0)

        # Update topics
        self.information.topics = self.data.topics

        # Update last available commit
        await self.repository_object.set_last_commit()
        self.versions.available_commit = self.repository_object.last_commit

        # Get the content of hacs.json
        await self.get_repository_manifest_content()

        # Update "info.md"
        self.information.additional_info = await get_info_md_content(self)

    async def install(self):
        """Common installation steps of the repository."""
        await install_repository(self)

    async def download_zip(self, validate):
        """Download ZIP archive from repository release."""
        try:
            contents = False

            for release in self.releases.objects:
                self.logger.info(
                    f"ref: {self.ref}  ---  tag: {release.tag_name}")
                if release.tag_name == self.ref.split("/")[1]:
                    contents = release.assets

            if not contents:
                return validate

            for content in contents or []:
                filecontent = await async_download_file(content.download_url)

                if filecontent is None:
                    validate.errors.append(
                        f"[{content.name}] was not downloaded.")
                    continue

                result = await async_save_file(
                    f"{tempfile.gettempdir()}/{self.data.filename}",
                    filecontent)
                with zipfile.ZipFile(
                        f"{tempfile.gettempdir()}/{self.data.filename}",
                        "r") as zip_file:
                    zip_file.extractall(self.content.path.local)

                if result:
                    self.logger.info(f"download of {content.name} complete")
                    continue
                validate.errors.append(f"[{content.name}] was not downloaded.")
        except Exception:
            validate.errors.append(f"Download was not complete.")

        return validate

    async def download_content(self, validate, directory_path, local_directory,
                               ref):
        """Download the content of a directory."""
        from custom_components.hacs.helpers.download import download_content

        validate = await download_content(self)
        return validate

    async def get_repository_manifest_content(self):
        """Get the content of the hacs.json file."""
        if not "hacs.json" in [x.filename for x in self.tree]:
            return
        if self.ref is None:
            self.ref = version_to_install(self)
        try:
            manifest = await self.repository_object.get_contents(
                "hacs.json", self.ref)
            self.repository_manifest = HacsManifest.from_dict(
                json.loads(manifest.content))
            self.data.update_data(json.loads(manifest.content))
        except (AIOGitHubException, Exception):  # Gotta Catch 'Em All
            pass

    def remove(self):
        """Run remove tasks."""
        self.logger.info("Starting removal")

        if self.information.uid in self.hacs.common.installed:
            self.hacs.common.installed.remove(self.information.uid)
        for repository in self.hacs.repositories:
            if repository.information.uid == self.information.uid:
                self.hacs.repositories.remove(repository)

    async def uninstall(self):
        """Run uninstall tasks."""
        self.logger.info("Uninstalling")
        await self.remove_local_directory()
        self.status.installed = False
        if self.information.category == "integration":
            if self.config_flow:
                await self.reload_custom_components()
            else:
                self.pending_restart = True
        elif self.information.category == "theme":
            try:
                await self.hacs.hass.services.async_call(
                    "frontend", "reload_themes", {})
            except Exception:  # pylint: disable=broad-except
                pass
        if self.information.full_name in self.hacs.common.installed:
            self.hacs.common.installed.remove(self.information.full_name)
        self.versions.installed = None
        self.versions.installed_commit = None
        self.hacs.hass.bus.async_fire(
            "hacs/repository",
            {
                "id": 1337,
                "action": "uninstall",
                "repository": self.information.full_name,
            },
        )

    async def remove_local_directory(self):
        """Check the local directory."""
        import shutil
        from asyncio import sleep

        try:
            if self.information.category == "python_script":
                local_path = "{}/{}.py".format(self.content.path.local,
                                               self.information.name)
            elif self.information.category == "theme":
                local_path = "{}/{}.yaml".format(self.content.path.local,
                                                 self.information.name)
            else:
                local_path = self.content.path.local

            if os.path.exists(local_path):
                self.logger.debug(f"Removing {local_path}")

                if self.information.category in ["python_script", "theme"]:
                    os.remove(local_path)
                else:
                    shutil.rmtree(local_path)

                while os.path.exists(local_path):
                    await sleep(1)

        except Exception as exception:
            self.logger.debug(f"Removing {local_path} failed with {exception}")
            return
Exemple #4
0
def test_data_structure():
    data = RepositoryData.create_from_dict(repository_data)
    assert isinstance(data.to_json(), dict)
Exemple #5
0
def test_data_update():
    data = RepositoryData.create_from_dict({})
    assert not data.fork
    data.update_data({"fork": True})
    assert data.fork