Exemple #1
0
def test_get_hacs():
    SHARE["hacs"] = None
    os.environ["GITHUB_ACTION"] = "value"
    if "PYTEST" in os.environ:
        del os.environ["PYTEST"]
    get_hacs()
    SHARE["hacs"] = None
    del os.environ["GITHUB_ACTION"]
    os.environ["PYTEST"] = "value"
Exemple #2
0
async def async_download_file(url: str) -> bytes | None:
    """Download files, and return the content."""
    if url is None:
        return None

    hacs = get_hacs()
    tries_left = 5

    if "tags/" in url:
        url = url.replace("tags/", "")

    _LOGGER.debug("Downloading %s", url)

    while tries_left > 0:
        try:
            with async_timeout.timeout(60):
                request = await hacs.session.get(url)

                # Make sure that we got a valid result
                if request.status == 200:
                    return await request.read()

                raise HacsException(
                    f"Got status code {request.status} when trying to download {url}"
                )
        except Exception as exception:
            _LOGGER.debug("Download failed - %s", exception)
            tries_left -= 1
            await asyncio.sleep(1)
            continue

    return None
def read_hacs_manifest():
    """Reads the HACS manifest file and returns the contents."""
    hacs = get_hacs()
    content = {}
    with open(f"{hacs.core.config_path}/custom_components/hacs/manifest.json") as manifest:
        content = json.loads(manifest.read())
    return content
async def test_common_base_exception_does_not_exsist(aresponses, event_loop):
    aresponses.add(
        "api.github.com",
        "/rate_limit",
        "get",
        aresponses.Response(headers=response_rate_limit_header_with_limit,
                            status=500),
    )
    aresponses.add(
        "api.github.com",
        "/repos/test/test",
        "get",
        aresponses.Response(
            body=json.dumps({"message": "X"}),
            headers=response_rate_limit_header_with_limit,
            status=500,
        ),
    )

    async with aiohttp.ClientSession(loop=event_loop) as session:
        hacs = get_hacs()
        hacs.session = session
        hacs.configuration = Configuration()
        hacs.configuration.token = TOKEN
        hacs.system.status.startup = False
        repository = dummy_repository_base()
        with pytest.raises(HacsException):
            await common_validate(repository)
async def test_update_repository():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    repository = HacsNetdaemon("test/test")
    repository.hacs = hacs
    with pytest.raises(HacsException):
        await repository.update_repository(True)
async def register_repository(full_name, category, check=True, ref=None):
    """Register a repository."""
    hacs = get_hacs()
    from custom_components.hacs.repositories import (
        RERPOSITORY_CLASSES,
    )  # To handle import error

    if full_name in hacs.common.skip:
        if full_name != "hacs/integration":
            raise HacsExpectedException(f"Skipping {full_name}")

    if category not in RERPOSITORY_CLASSES:
        raise HacsException(f"{category} is not a valid repository category.")

    repository = RERPOSITORY_CLASSES[category](full_name)
    if check:
        try:
            await repository.async_registration(ref)
            if hacs.status.new:
                repository.data.new = False
            if repository.validate.errors:
                hacs.common.skip.append(repository.data.full_name)
                if not hacs.status.startup:
                    hacs.log.error(f"Validation for {full_name} failed.")
                if hacs.system.action:
                    raise HacsException(f"::error:: Validation for {full_name} failed.")
                return repository.validate.errors
            if hacs.system.action:
                repository.logger.info("Validation completed")
            else:
                repository.logger.info("Registration completed")
        except AIOGitHubAPIException as exception:
            hacs.common.skip.append(repository.data.full_name)
            raise HacsException(
                f"Validation for {full_name} failed with {exception}."
            ) from None

    exists = (
        False
        if str(repository.data.id) == "0"
        else [x for x in hacs.repositories if str(x.data.id) == str(repository.data.id)]
    )

    if exists:
        if exists[0] in hacs.repositories:
            hacs.repositories.remove(exists[0])

    else:
        if hacs.hass is not None and (
            (check and repository.data.new) or hacs.status.new
        ):
            hacs.hass.bus.async_fire(
                "hacs/repository",
                {
                    "action": "registration",
                    "repository": repository.data.full_name,
                    "repository_id": repository.data.id,
                },
            )
    hacs.repositories.append(repository)
async def test_download_content(aresponses, tmp_path, event_loop):
    aresponses.add(
        "raw.githubusercontent.com",
        "/test/test/master/test/path/file.file",
        "get",
        aresponses.Response(body="test", headers=response_rate_limit_header),
    )

    repository = dummy_repository_base()
    repository.content.path.remote = ""
    repository.content.path.local = tmp_path
    repository.tree = [
        AIOGitHubAPIRepositoryTreeContent(
            {
                "path": "test/path/file.file",
                "type": "blob"
            }, "test/test", "master")
    ]
    async with aiohttp.ClientSession(loop=event_loop) as session:
        hacs = get_hacs()
        hacs.hass.loop = event_loop
        hacs.session = session
        await download_content(repository)
        assert os.path.exists(
            f"{repository.content.path.local}/test/path/file.file")
async def test_reload_custom_components():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    hacs.hass.data["custom_components"] = []
    repository = HacsIntegration("test/test")
    repository.hacs = hacs
    await repository.reload_custom_components()
async def update_resources() -> None:
    """Check Lovelace resources and and resources that are missing if in storage mode."""

    base = get_base()
    hacs = get_hacs()

    # Reset missing resources
    missing_resources = base.configuration.missing_resources = []

    # Add the HACS plugins to the list
    for hacs_plugin in HACS_PLUGINS:
        try:
            repo = hacs.get_by_name(hacs_plugin)
            url = f"/hacsfiles/{hacs_plugin.split('/')[-1]}/{repo.data.file_name}"

            try:
                await add_resource_module(url)
            except:
                base.log.error(
                    f"Unable to add {hacs_plugin} the the Lovelace resource list.",
                    hacs_plugin,
                )
                missing_resources.append(url)
        except:
            base.log.warning(
                f"Could not connect to HACS to check repository for '{hacs_plugin}', will assume everything is okay."
            )

    # Add custom cards to the list
    for card in LOVELACE_CUSTOM_CARDS:
        url = (
            f"/local/{LOVELACE_DASHBOARD_URL_PATH}/{card['dirname']}/{card['filename']}"
        )
        await add_resource_module(url)
async def async_download_file(url):
    """Download files, and return the content."""
    hacs = get_hacs()
    if url is None:
        return

    if "tags/" in url:
        url = url.replace("tags/", "")

    _LOGGER.debug("Downloading %s", url)

    result = None

    with async_timeout.timeout(60, loop=hacs.hass.loop):
        request = await hacs.session.get(url)

        # Make sure that we got a valid result
        if request.status == 200:
            result = await request.read()
        else:
            raise HacsException(
                "Got status code {} when trying to download {}".format(
                    request.status, url))

    return result
Exemple #11
0
async def async_serve_category_file(requested_file):
    hacs = get_hacs()
    logger = getLogger("web.category")
    try:
        if requested_file.startswith("themes/"):
            servefile = f"{hacs.system.config_path}/{requested_file}"
        else:
            servefile = f"{hacs.system.config_path}/www/community/{requested_file}"

        # Serve .gz if it exist
        if await async_path_exsist(f"{servefile}.gz"):
            servefile += ".gz"

        if await async_path_exsist(servefile):
            logger.debug(f"Serving {requested_file} from {servefile}")
            response = web.FileResponse(servefile)
            response.headers["Cache-Control"] = "no-store, max-age=0"
            response.headers["Pragma"] = "no-store"
            return response
        else:
            logger.error(
                f"Tried to serve up '{servefile}' but it does not exist")

    except (Exception, BaseException) as error:
        logger.debug(
            f"there was an issue trying to serve {requested_file} - {error}")

    return web.Response(status=404)
async def async_setup_frontend():
    """Configure the HACS frontend elements."""
    hacs = get_hacs()

    # Custom view
    hacs.hass.http.register_view(HacsFrontend())

    # Custom iconset
    if "frontend_extra_module_url" not in hacs.hass.data:
        hacs.hass.data["frontend_extra_module_url"] = set()
    hacs.hass.data["frontend_extra_module_url"].add("/hacsfiles/iconset.js")

    hacs.frontend.version_running = FE_VERSION
    hacs.frontend.version_expected = await hacs.hass.async_add_executor_job(
        get_frontend_version)

    # Add to sidepanel
    custom_panel_config = {
        "name": "hacs-frontend",
        "embed_iframe": True,
        "trust_external": False,
        "js_url": f"/hacsfiles/frontend-{hacs.frontend.version_running}.js",
    }

    config = {}
    config["_panel_custom"] = custom_panel_config

    hacs.hass.components.frontend.async_register_built_in_panel(
        component_name="custom",
        sidebar_title=hacs.configuration.sidepanel_title,
        sidebar_icon=hacs.configuration.sidepanel_icon,
        frontend_url_path="hacs",
        config=config,
        require_admin=True,
    )
async def async_serve_category_file(request, requested_file):
    hacs = get_hacs()
    try:
        if requested_file.startswith("themes/"):
            servefile = f"{hacs.core.config_path}/{requested_file}"
        else:
            servefile = f"{hacs.core.config_path}/www/community/{requested_file}"

        if await async_path_exsist(servefile):
            _LOGGER.debug("Serving %s from %s", requested_file, servefile)
            response = web.FileResponse(servefile)
            if requested_file.startswith("themes/"):
                response.headers["Cache-Control"] = "public, max-age=2678400"
            else:
                response.headers["Cache-Control"] = "no-store, max-age=0"
                response.headers["Pragma"] = "no-store"
            return response
        else:
            _LOGGER.error(
                "%s tried to request '%s' but the file does not exist",
                request.remote,
                servefile,
            )

    except (Exception, BaseException) as exception:
        _LOGGER.debug(
            "there was an issue trying to serve %s - %s", requested_file, exception
        )

    return web.Response(status=404)
Exemple #14
0
async def async_load_hacs_repository():
    """Load HACS repositroy."""
    hacs = get_hacs()
    hacs.log.info("Setup task %s", HacsSetupTask.HACS_REPO)

    try:
        repository = hacs.get_by_name("hacs/integration")
        if repository is None:
            await register_repository("hacs/integration", "integration")
            repository = hacs.get_by_name("hacs/integration")
        if repository is None:
            raise HacsException("Unknown error")
        repository.data.installed = True
        repository.data.installed_version = VERSION
        repository.data.new = False
        hacs.repo = repository.repository_object
        hacs.data_repo = await get_repository(hacs.session,
                                              hacs.configuration.token,
                                              "hacs/default")
    except HacsException as exception:
        if "403" in f"{exception}":
            hacs.log.critical(
                "GitHub API is ratelimited, or the token is wrong.")
        else:
            hacs.log.critical(f"[{exception}] - Could not load HACS!")
        return False
    return True
Exemple #15
0
async def test_frontend_debug():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    hacs.configuration = Configuration()
    hacs.configuration.debug = True
    await async_serve_frontend()
    hacs.configuration = Configuration()
async def test_common_blacklist(aresponses, event_loop):
    aresponses.add(
        "api.github.com",
        "/rate_limit",
        "get",
        aresponses.Response(body=b"{}",
                            headers=response_rate_limit_header,
                            status=200),
    )
    aresponses.add(
        "api.github.com",
        "/repos/test/test",
        "get",
        aresponses.Response(body=json.dumps(repository_data),
                            headers=response_rate_limit_header),
    )
    async with aiohttp.ClientSession(loop=event_loop) as session:
        hacs = get_hacs()
        hacs.session = session
        hacs.configuration = Configuration()
        hacs.configuration.token = TOKEN
        removed = get_removed("test/test")
        assert removed.repository == "test/test"
        repository = dummy_repository_base()
        with pytest.raises(HacsException):
            await common_validate(repository)
async def update_hacs() -> None:
    """Install or update hacs integrations and frontend plugins."""

    base = get_base()
    hass = base.hass
    hacs = get_hacs()

    # Add custom repositories to HACS
    for repo in HACS_CUSTOM_REPOSITORIES:
        hacs_repository_data(hass, None, {
            "repository": repo["url"],
            "action": "add",
            "data": "plugin"
        })

    # Install needed HACS integrations and plugins
    for hacs_plugin in HACS_INTEGRATIONS + HACS_PLUGINS:
        try:
            repo = hacs.get_by_name(hacs_plugin)
            try:
                hacs_repository(hass, None, {
                    "repository": repo.data.id,
                    "action": "install"
                })
            except:
                base.log.error("Unable to install HACS repository: %s",
                               hacs_plugin)
        except:
            base.log.warning(
                f"Could not connect to HACS install '{hacs_plugin}', will assume everything is okay."
            )
Exemple #18
0
async def async_hacs_startup():
    """HACS startup tasks."""
    hacs = get_hacs()

    await hacs.async_set_stage(HacsStage.SETUP)
    if hacs.system.disabled:
        return False

    await hacs.async_set_stage(HacsStage.STARTUP)
    if hacs.system.disabled:
        return False

    # Setup startup tasks
    if hacs.hass.state == CoreState.running:
        async_call_later(hacs.hass, 5, hacs.startup_tasks)
    else:
        hacs.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED,
                                        hacs.startup_tasks)

    # Mischief managed!
    await hacs.async_set_stage(HacsStage.WAITING)
    hacs.log.info(
        "Setup complete, waiting for Home Assistant before startup tasks starts"
    )

    return not hacs.system.disabled
Exemple #19
0
async def hacs_settings(hass, connection, msg):
    """Handle get media player cover command."""
    hacs = get_hacs()
    logger = getLogger("api.settings")

    action = msg["action"]
    logger.debug(f"WS action '{action}'")

    if action == "set_fe_grid":
        hacs.configuration.frontend_mode = "Grid"

    elif action == "onboarding_done":
        hacs.configuration.onboarding_done = True

    elif action == "set_fe_table":
        hacs.configuration.frontend_mode = "Table"

    elif action == "set_fe_compact_true":
        hacs.configuration.frontend_compact = False

    elif action == "set_fe_compact_false":
        hacs.configuration.frontend_compact = True

    elif action == "clear_new":
        for repo in hacs.repositories:
            if repo.data.new and repo.data.category in msg.get("categories", []):
                logger.debug(f"Clearing new flag from '{repo.data.full_name}'")
                repo.data.new = False
    else:
        logger.error(f"WS action '{action}' is not valid")
    hass.bus.async_fire("hacs/config", {})
    await hacs.data.async_write()
    connection.send_message(websocket_api.result_message(msg["id"], {}))
Exemple #20
0
async def async_run_repository_checks(repository):
    hacs = get_hacs()
    if not SHARE["rules"]:
        await async_initialize_rules()
    if not hacs.system.running:
        return
    checks = []
    for check in SHARE["rules"].get("common", []):
        checks.append(check(repository))
    for check in SHARE["rules"].get(repository.data.category, []):
        checks.append(check(repository))

    await asyncio.gather(*[
        check._async_run_check() for check in checks or []
        if hacs.action or not check.action_only
    ])

    total = len([x for x in checks if hacs.action or not x.action_only])
    failed = len([x for x in checks if x.failed])

    if failed != 0:
        repository.logger.error(f"{failed}/{total} checks failed")
        if hacs.action:
            exit(1)
    else:
        repository.logger.debug(f"All ({total}) checks passed")
Exemple #21
0
async def test_validate_repository():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    repository = HacsTheme("test/test")
    repository.hacs = hacs
    with pytest.raises(HacsException):
        await repository.validate_repository()
Exemple #22
0
def _clear_storage():
    """Clear old files from storage."""
    hacs = get_hacs()
    storagefiles = ["hacs"]
    for s_f in storagefiles:
        path = f"{hacs.core.config_path}/.storage/{s_f}"
        if os.path.isfile(path):
            hacs.log.info(f"Cleaning up old storage file {path}")
            os.remove(path)
Exemple #23
0
async def test_frontend_view_class():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    hacs.configuration = Configuration()
    frontend = HacsFrontend()
    await frontend.get({}, "test")
    await frontend.get({}, "class-map.js.map")
    await frontend.get({}, "frontend-test")
    await frontend.get({}, "iconset.js")
Exemple #24
0
async def test_hacs_data_async_write2(tmpdir):
    data = HacsData()
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    hacs.hass.config.config_dir = tmpdir
    hacs.configuration = Configuration()
    hacs.system.status.background_task = False
    hacs.system.disabled = False
    await data.async_write()
async def test_get_package_content():
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    repository = HacsPlugin("test/test")
    repository.hacs = hacs

    await repository.get_package_content()
    repository.repository_object = MockRepositoryObject()
    await repository.get_package_content()
async def test_common_base_exception_tree_issues(aresponses, event_loop):
    aresponses.add(
        "api.github.com",
        "/rate_limit",
        "get",
        aresponses.Response(body=b"{}",
                            headers=response_rate_limit_header,
                            status=200),
    )
    aresponses.add(
        "api.github.com",
        "/repos/test/test",
        "get",
        aresponses.Response(body=json.dumps(repository_data),
                            headers=response_rate_limit_header),
    )
    aresponses.add(
        "api.github.com",
        "/rate_limit",
        "get",
        aresponses.Response(body=b"{}",
                            headers=response_rate_limit_header,
                            status=200),
    )
    aresponses.add(
        "api.github.com",
        "/repos/test/test/releases",
        "get",
        aresponses.Response(body=json.dumps(release_data),
                            headers=response_rate_limit_header),
    )
    aresponses.add(
        "api.github.com",
        "/rate_limit",
        "get",
        aresponses.Response(body=b"{}",
                            headers=response_rate_limit_header,
                            status=200),
    )
    aresponses.add(
        "api.github.com",
        "/repos/test/test/git/trees/3",
        "get",
        aresponses.Response(body=json.dumps({"message": "X"}),
                            headers=response_rate_limit_header),
    )

    async with aiohttp.ClientSession(loop=event_loop) as session:
        hacs = get_hacs()
        hacs.session = session
        hacs.configuration = Configuration()
        hacs.configuration.token = TOKEN
        repository = dummy_repository_base()
        hacs.system.status.startup = False
        with pytest.raises(HacsException):
            await common_validate(repository)
Exemple #27
0
def constrain_custom_updater():
    """Check if custom_updater exist."""
    hacs = get_hacs()
    for location in CUSTOM_UPDATER_LOCATIONS:
        if os.path.exists(location.format(hacs.core.config_path)):
            msg = CUSTOM_UPDATER_WARNING.format(
                location.format(hacs.core.config_path))
            hacs.log.critical(msg)
            return False
    return True
Exemple #28
0
async def test_clear_storage(tmpdir):
    hacs = get_hacs()
    hacs.hass = HomeAssistant()
    hacs.system.config_path = tmpdir.dirname
    os.makedirs(f"{hacs.system.config_path}/.storage")
    with open(f"{hacs.system.config_path}/.storage/hacs", "w") as h_f:
        h_f.write("")
    await async_clear_storage()
    os.makedirs(f"{hacs.system.config_path}/.storage/hacs")
    await async_clear_storage()
def _clear_storage():
    """Clear old files from storage."""
    hacs = get_hacs()
    logger = getLogger("startup.clear_storage")
    storagefiles = ["hacs"]
    for s_f in storagefiles:
        path = f"{hacs.system.config_path}/.storage/{s_f}"
        if os.path.isfile(path):
            logger.info(f"Cleaning up old storage file {path}")
            os.remove(path)
Exemple #30
0
def constrain_version():
    """Check if the version is valid."""
    hacs = get_hacs()
    if not version_left_higher_then_right(hacs.system.ha_version,
                                          MINIMUM_HA_VERSION):
        hacs.log.critical(
            f"You need HA version {MINIMUM_HA_VERSION} or newer to use this integration."
        )
        return False
    return True