Skip to content

Add option to use frontend PR artifact to frontend integration - #161291

Merged
emontnemery merged 25 commits into
devfrom
frontend-pr-download
Feb 3, 2026
Merged

emontnemery merged 25 commits into
devfrom
frontend-pr-download

Conversation

@wendevlin

@wendevlin wendevlin commented Jan 20, 2026

Copy link
Copy Markdown
Member

Proposed change

  • adds development_pr option to the frontend integration to easily try out PRs
    • Downloads frontend PR artifact from github and uses it on startup

This should be an option for HAOS instances. For example the product team can easily test changes without the need to setup a dev env.

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New integration (thank you!)
  • New feature (which adds functionality to an existing integration)
  • Deprecation (breaking change to happen in the future)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • I understand the code I am submitting and can explain how it works.
  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • I have followed the perfect PR recommendations
  • The code has been formatted using Ruff (ruff format homeassistant tests)
  • Tests have been added to verify that the new code works.
  • Any generated code has been carefully reviewed for correctness and compliance with project standards.

If user exposed functionality or configuration variables are added/changed:

If the code communicates with devices, web services, or third-party tools:

  • The manifest file has all fields filled out correctly.
    Updated and included derived files by running: python3 -m script.hassfest.
  • New or updated dependencies have been added to requirements_all.txt.
    Updated by running python3 -m script.gen_requirements_all.
  • For the updated dependencies - a link to the changelog, or at minimum a diff between library versions is added to the PR description.

To help with the load of incoming pull requests:

wendevlin and others added 2 commits January 20, 2026 15:23
Use dictionary mapping for HTTP error messages to eliminate code duplication.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Move GitHub error messages to module-level constants to avoid duplication
across _get_pr_head_sha and _download_artifact_data functions.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@home-assistant

Copy link
Copy Markdown
Contributor

Hey there @home-assistant/frontend, mind taking a look at this pull request as it has been labeled with an integration (frontend) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of frontend can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign frontend Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

@home-assistant

Copy link
Copy Markdown
Contributor

Hey there @home-assistant/core, mind taking a look at this pull request as it has been labeled with an integration (backup) you are listed as a code owner for? Thanks!

Code owner commands

Code owners of backup can trigger bot actions by commenting:

  • @home-assistant close Closes the pull request.
  • @home-assistant rename Awesome new title Renames the pull request.
  • @home-assistant reopen Reopen the pull request.
  • @home-assistant unassign backup Removes the current integration label and assignees on the pull request, add the integration domain after the command.
  • @home-assistant add-label needs-more-information Add a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) to the pull request.
  • @home-assistant remove-label needs-more-information Remove a label (needs-more-information, problem in dependency, problem in custom component, problem in config, problem in device, feature-request) on the pull request.

@wendevlin wendevlin changed the title Frontend development PR Frontend integration: add option to use frontend PR artifact Jan 20, 2026
@wendevlin wendevlin changed the title Frontend integration: add option to use frontend PR artifact Add option to use frontend PR artifact to frontend integration Jan 20, 2026
wendevlin and others added 2 commits January 20, 2026 17:43
Replace sync PyGithub with aiogithubapi and requests with aiohttp.
- Use aiogithubapi.GitHubAPI for GitHub API calls
- Use aiohttp_client.async_get_clientsession for HTTP downloads
- Remove executor job calls for API operations (now fully async)
- Keep executor jobs only for blocking I/O (file operations, zip extraction)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Use specific aiogithubapi exception types instead of checking status attribute
- Handle GitHubAuthenticationException for 401 errors
- Handle GitHubRatelimitException and GitHubPermissionException for 403 errors
- Handle GitHubNotFoundException for 404 errors
- Update tests to use aiogithubapi and aiohttp mocks
- Replace PyGithub mocks with GitHubAPI.generic() mocks
- Replace requests mocks with aioclient_mock (AiohttpClientMocker)
- Add test for GitHub API error handling

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@MartinHjelmare

Copy link
Copy Markdown
Member

Why isn't it enough to checkout the frontend PR and use the development_repo option before starting core?

@wendevlin

Copy link
Copy Markdown
Member Author

Why isn't it enough to checkout the frontend PR and use the development_repo option before starting core?

This should be an option for HAOS instances. For example the product team can easily test changes without the need to setup a dev env.

@MartinHjelmare

Copy link
Copy Markdown
Member

Please add this context to the PR description.

@emontnemery emontnemery left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty good.

Some comments.
I've not reviewed the tests.

Comment thread homeassistant/components/frontend/__init__.py Outdated
Comment thread homeassistant/components/frontend/__init__.py Outdated
Comment thread homeassistant/components/frontend/__init__.py Outdated
Comment thread homeassistant/components/frontend/__init__.py Outdated
Comment thread homeassistant/components/frontend/__init__.py Outdated
Comment thread homeassistant/components/frontend/pr_download.py Outdated
- Added validation for GitHub token requirement when development PR is set.
- Refactored frontend configuration schema to include validation.
- Improved test coverage for PR download scenarios, ensuring proper handling of GitHub token absence and cache usage.
@wendevlin
wendevlin requested a review from emontnemery January 27, 2026 13:15

@emontnemery emontnemery left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Comment on lines +633 to +641
def cache_path(self, *path: str) -> str:
"""Generate path to the file within the cache directory.

The cache directory is used for temporary data that can be
regenerated and is not included in backups.

Async friendly.
"""
return self.path(".cache", *path)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add some tests of this method, it's fine to just duplicate the tests for the path method above:

async def test_config_path_with_file() -> None:
"""Test get_config_path method."""
hass = Mock()
hass.data = {}
config = Config(hass, "/test/ha-config")
assert config.path("test.conf") == "/test/ha-config/test.conf"
async def test_config_path_with_dir_and_file() -> None:
"""Test get_config_path method."""
hass = Mock()
hass.data = {}
config = Config(hass, "/test/ha-config")
assert config.path("dir", "test.conf") == "/test/ha-config/dir/test.conf"

Comment thread pyproject.toml
requires-python = ">=3.13.2"
dependencies = [
"aiodns==4.0.0",
"aiogithubapi==24.6.0",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"aiogithubapi==24.6.0",
# aiogithubapi is needed by frontend
"aiogithubapi==24.6.0",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain why it's needed by frontend and can't be set as a manifest requirement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I understand it: we import pr_download top level in frontend and pr_download imports aiogithubapi. This happens before manifest requirements are installed and so aiogithubapi wouldn't be available in pr_download. I initially loaded pr_download lazy in the setup but @emontnemery asked me to import it globally.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's special about the frontend integration in this regard? Other integrations can import 3rd party libraries top level if they specify them in the integration manifest requirements.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frontend is a stage 0 integration in bootstrap.py, so it's loaded before setup.py installs req from manifest. A regular integration is loaded after setup.py did this installations.

If it's the wrong way of doing it please tell me how I should implement it.

@wendevlin wendevlin Jan 30, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I remove aiogithubapi from pyproject, it removes it from requirements.txt. When I now start it with just requirements.txt installed it crashes. I think this is how HA works: base req are installed from requirements.txt and all in requirements_all.txt is installed on demand. But this on demand happens after frontend setups.

The crash:

Traceback (most recent call last):
  File "/home/wendelin/ohf/home-assistant/core/.venv/bin/hass", line 10, in <module>
    sys.exit(main())
             ~~~~^^
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/__main__.py", line 188, in main
    from . import config, runner  # noqa: PLC0415
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/runner.py", line 27, in <module>
    from . import bootstrap
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/bootstrap.py", line 41, in <module>
    from .components import (
    ...<22 lines>...
    )
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/components/config/__init__.py", line 5, in <module>
    from homeassistant.components import frontend
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/components/frontend/__init__.py", line 40, in <module>
    from .pr_download import download_pr_artifact
  File "/home/wendelin/ohf/home-assistant/core/homeassistant/components/frontend/pr_download.py", line 11, in <module>
    from aiogithubapi import (
    ...<6 lines>...
    )
ModuleNotFoundError: No module named 'aiogithubapi'

I had the issue only in the CI, because on my system I have all req from tests. Erik told me then to add it to pyproject.toml to automatically add it to requirements.txt.

I don't think there is another way of adding it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a problem with setup or on demand installation requirements. It's a problem with imports and the weird way we have set dependencies in frontend at the moment. Bootstrap pre-imports integrations that frontend has set as dependencies as a performance optimization, but then those integrations import from frontend. Normally that's totally incorrect.

I'll look at this separately, later.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay so you'll come back when you have a solution? Or can we merge this and fix this import problem in a separate PR?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can address that separately. I'm not sure how easy it is. Let's keep it like this here for now.

@emontnemery emontnemery Feb 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First of all, the reason why frontend has integrations in its dependencies which it doesn't depend on is to ensure non-optional HTTP/WS endpoints are available when the webserver is started. Relevant PR is #139199 which removed the backup integration from frontend's dependencies and also has a not yet implemented proposal for how we can remove some/most of the other dependencies:

Going forward, we should add a way for frontend to check which integrations will be setup and a way for it to wait for an integration to set up.

In this PR it's a different problem, we're now adding a new library requirement to frontend itself. Specifying the github API in frontend's manifest.json doesn't break core startup, that still works fine without any issue (provided that the aiogithubapi is installed).

What does NOT work fine however is calculating and validating the dependencies.

Without aiogithubapi installed, this is the output from the "Prepare dependencies" CI-job:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/runner/work/core/core/script/gen_requirements_all.py", line 649, in <module>
    sys.exit(main(_VAL, _CI))
             ~~~~^^^^^^^^^^^
  File "/home/runner/work/core/core/script/gen_requirements_all.py", line 581, in main
    data = gather_modules()
  File "/home/runner/work/core/core/script/gen_requirements_all.py", line 346, in gather_modules
    gather_requirements_from_modules(errors, reqs)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/runner/work/core/core/script/gen_requirements_all.py", line 399, in gather_requirements_from_modules
    explore_module("homeassistant.scripts", True)
    ~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/core/core/script/gen_requirements_all.py", line 260, in explore_module
    module = importlib.import_module(package)
  File "/opt/hostedtoolcache/Python/3.13.11/x64/lib/python3.13/importlib/__init__.py", line 88, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 1023, in exec_module
  File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
  File "/home/runner/work/core/core/homeassistant/scripts/__init__.py", line 13, in <module>
    from homeassistant import runner
  File "/home/runner/work/core/core/homeassistant/runner.py", line 27, in <module>
    from . import bootstrap
  File "/home/runner/work/core/core/homeassistant/bootstrap.py", line 41, in <module>
    from .components import (
    ...<22 lines>...
    )
  File "/home/runner/work/core/core/homeassistant/components/config/__init__.py", line 5, in <module>
    from homeassistant.components import frontend
  File "/home/runner/work/core/core/homeassistant/components/frontend/__init__.py", line 40, in <module>
    from .pr_download import download_pr_artifact
  File "/home/runner/work/core/core/homeassistant/components/frontend/pr_download.py", line 11, in <module>
    from aiogithubapi import (
    ...<6 lines>...
    )
ModuleNotFoundError: No module named 'aiogithubapi'

If running the generate requirements script with aiogithubapi available, aiogithubapi is however not promoted from requirements_all.txt to requirements.txt, and there's not a warning from hassfest.

@emontnemery emontnemery left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks @wendevlin 👍

@emontnemery emontnemery added the noteworthy Marks a PR as noteworthy and should be in the release notes (in case it normally would not appear) label Jan 28, 2026
@MartinHjelmare
MartinHjelmare marked this pull request as draft January 28, 2026 08:02
@MartinHjelmare

Copy link
Copy Markdown
Member

Shouldn't this be documented in the dev docs since it's aimed for development?

@MartinHjelmare

Copy link
Copy Markdown
Member

There are unresolved comments above.

@wendevlin
wendevlin marked this pull request as ready for review January 28, 2026 08:54
@wendevlin

Copy link
Copy Markdown
Member Author

Shouldn't this be documented in the dev docs since it's aimed for development?

I am not sure where to put it, because it's not to develop features, it is to easier test frontend PRs.
To develop the PR you still need a dev environment.

I think it would make more sense to get into more detail in the integration docs. @MindFreeze what do you think?

@wendevlin
wendevlin marked this pull request as draft January 28, 2026 09:23
@MindFreeze

Copy link
Copy Markdown
Member

I would say it's still mostly a development feature as users would have to be on Github to find a PR to test. Suggest to explain it in the dev docs and add a brief mention that it's possible in https://www.home-assistant.io/integrations/frontend/ with a link to the dev docs.

@wendevlin

Copy link
Copy Markdown
Member Author

I added dev docs and updated the user docs to reference to the dev docs.

@MartinHjelmare
MartinHjelmare marked this pull request as draft January 28, 2026 23:09
@MartinHjelmare
MartinHjelmare marked this pull request as ready for review February 2, 2026 10:50

@emontnemery emontnemery left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's clarify the comment in pyproject.toml

Comment thread pyproject.toml Outdated
Co-authored-by: Erik Montnemery <erik@montnemery.com>
@emontnemery

Copy link
Copy Markdown
Contributor

I discussed this a bit with @MartinHjelmare on Discord, he has no further objections which blocks this PR so I'll go ahead and merge it.

@emontnemery
emontnemery merged commit e6a60df into dev Feb 3, 2026
46 checks passed
@emontnemery
emontnemery deleted the frontend-pr-download branch February 3, 2026 09:23
@github-actions github-actions Bot locked and limited conversation to collaborators Feb 4, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants