mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-04-13 23:47:47 +00:00
Merge pull request #220413 from NixOS/home-assistant
home-assistant: 2023.3.2 -> 2023.3.3
This commit is contained in:
commit
3a9aec4691
@ -11,7 +11,7 @@
|
||||
|
||||
buildPythonPackage rec {
|
||||
pname = "roombapy";
|
||||
version = "1.6.5";
|
||||
version = "1.6.6";
|
||||
format = "pyproject";
|
||||
|
||||
disabled = pythonOlder "3.7";
|
||||
@ -20,7 +20,7 @@ buildPythonPackage rec {
|
||||
owner = "pschmitt";
|
||||
repo = "roombapy";
|
||||
rev = version;
|
||||
sha256 = "sha256-Xjeh29U+FCzI5n/i5s6wC0B88Ktmb8pnNDdOzCiKWi4=";
|
||||
hash = "sha256-dfeMd/THlj2HQYcLPmeC3AWP3vR/6+8BFU1QtSu5xg4=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = [
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Do not edit!
|
||||
|
||||
{
|
||||
version = "2023.3.2";
|
||||
version = "2023.3.3";
|
||||
components = {
|
||||
"3_day_blinds" = ps: with ps; [
|
||||
];
|
||||
|
@ -263,7 +263,7 @@ let
|
||||
extraBuildInputs = extraPackages python.pkgs;
|
||||
|
||||
# Don't forget to run parse-requirements.py after updating
|
||||
hassVersion = "2023.3.2";
|
||||
hassVersion = "2023.3.3";
|
||||
|
||||
in python.pkgs.buildPythonApplication rec {
|
||||
pname = "homeassistant";
|
||||
@ -279,7 +279,7 @@ in python.pkgs.buildPythonApplication rec {
|
||||
# Primary source is the pypi sdist, because it contains translations
|
||||
src = fetchPypi {
|
||||
inherit pname version;
|
||||
hash = "sha256-I6NSVoMS3xbUqh/7BxJj/Evkk7+g3N0dZVJjEbr2pCs=";
|
||||
hash = "sha256-AJJ0w66a8D3kiLHhnoFmnGRWyDJ4OCebwwKTGdprGa0=";
|
||||
};
|
||||
|
||||
# Secondary source is git for tests
|
||||
@ -287,7 +287,7 @@ in python.pkgs.buildPythonApplication rec {
|
||||
owner = "home-assistant";
|
||||
repo = "core";
|
||||
rev = "refs/tags/${version}";
|
||||
hash = "sha256-Qd++/73c9VDNe4AMdiDIVJXxh4qFx2x4HDkY1An2VjE=";
|
||||
hash = "sha256-KTmMA8P0MhYAiwp073Q3s60budFKHrsBnAJSqYC7zis=";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with python3.pkgs; [
|
||||
@ -442,6 +442,7 @@ in python.pkgs.buildPythonApplication rec {
|
||||
python
|
||||
supportedComponentsWithTests;
|
||||
pythonPath = python3.pkgs.makePythonPath (componentBuildInputs ++ extraBuildInputs);
|
||||
frontend = python.pkgs.home-assistant-frontend;
|
||||
intents = python.pkgs.home-assistant-intents;
|
||||
tests = {
|
||||
nixos = nixosTests.home-assistant;
|
||||
|
@ -4,7 +4,7 @@ buildPythonPackage rec {
|
||||
# the frontend version corresponding to a specific home-assistant version can be found here
|
||||
# https://github.com/home-assistant/home-assistant/blob/master/homeassistant/components/frontend/manifest.json
|
||||
pname = "home-assistant-frontend";
|
||||
version = "20230306.0";
|
||||
version = "20230309.0";
|
||||
format = "wheel";
|
||||
|
||||
src = fetchPypi {
|
||||
@ -12,7 +12,7 @@ buildPythonPackage rec {
|
||||
pname = "home_assistant_frontend";
|
||||
dist = "py3";
|
||||
python = "py3";
|
||||
hash = "sha256-E/e1XyhwFiNMLz7+o99eG9sW2ZCCfPFnkBcu3BpCbxQ=";
|
||||
hash = "sha256-gHc93xKIm0LDQrkTtlMdLv/N2smfYz5lQ6uLV+Cqj+s=";
|
||||
};
|
||||
|
||||
# there is nothing to strip in this package
|
||||
|
@ -1,5 +1,5 @@
|
||||
#! /usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ mypy attrs packaging rich ])
|
||||
#! nix-shell -i python3 -p "python3.withPackages (ps: with ps; [ attrs packaging rich ])" -p nodePackages.pyright ruff isort"
|
||||
#
|
||||
# This script downloads Home Assistant's source tarball.
|
||||
# Inside the homeassistant/components directory, each integration has an associated manifest.json,
|
||||
@ -25,8 +25,9 @@ import tarfile
|
||||
import tempfile
|
||||
from functools import reduce
|
||||
from io import BytesIO
|
||||
from typing import Dict, Optional, Set, Any
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from urllib.request import urlopen
|
||||
|
||||
from packaging import version as Version
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
@ -45,17 +46,21 @@ PKG_PREFERENCES = {
|
||||
}
|
||||
|
||||
|
||||
def run_mypy() -> None:
|
||||
cmd = ["mypy", "--ignore-missing-imports", __file__]
|
||||
|
||||
def run_sync(cmd: List[str]) -> None:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
subprocess.run(cmd, check=True)
|
||||
process = subprocess.run(cmd)
|
||||
|
||||
if process.returncode != 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_version():
|
||||
def get_version() -> str:
|
||||
with open(os.path.dirname(sys.argv[0]) + "/default.nix") as f:
|
||||
# A version consists of digits, dots, and possibly a "b" (for beta)
|
||||
m = re.search('hassVersion = "([\\d\\.b]+)";', f.read())
|
||||
return m.group(1)
|
||||
if match := re.search('hassVersion = "([\\d\\.b]+)";', f.read()):
|
||||
return match.group(1)
|
||||
raise RuntimeError("hassVersion not in default.nix")
|
||||
|
||||
|
||||
def parse_components(version: str = "master"):
|
||||
@ -74,7 +79,7 @@ def parse_components(version: str = "master"):
|
||||
components_with_tests.append(entry.name)
|
||||
|
||||
sys.path.append(core_path)
|
||||
from script.hassfest.model import Integration
|
||||
from script.hassfest.model import Integration # type: ignore
|
||||
integrations = Integration.load_dir(
|
||||
pathlib.Path(
|
||||
os.path.join(core_path, "homeassistant/components")
|
||||
@ -270,5 +275,7 @@ def main() -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_mypy()
|
||||
run_sync(["pyright", __file__])
|
||||
run_sync(["ruff", "--ignore=E501", __file__])
|
||||
run_sync(["isort", __file__])
|
||||
main()
|
||||
|
263
pkgs/servers/home-assistant/update.py
Executable file
263
pkgs/servers/home-assistant/update.py
Executable file
@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -I nixpkgs=channel:nixpkgs-unstable -i python3 -p "python3.withPackages (ps: with ps; [ aiohttp packaging ])" -p git nurl nodePackages.pyright ruff isort
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from subprocess import check_output, run
|
||||
from typing import Dict, Final, List, Optional, Union
|
||||
|
||||
import aiohttp
|
||||
from aiohttp import ClientSession
|
||||
from packaging.version import Version
|
||||
|
||||
ROOT: Final = check_output([
|
||||
"git",
|
||||
"rev-parse",
|
||||
"--show-toplevel",
|
||||
]).decode().strip()
|
||||
|
||||
|
||||
def run_sync(cmd: List[str]) -> None:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
process = run(cmd)
|
||||
|
||||
if process.returncode != 0:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
async def check_async(cmd: List[str]) -> str:
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode != 0:
|
||||
error = stderr.decode()
|
||||
raise RuntimeError(f"{cmd[0]} failed: {error}")
|
||||
|
||||
return stdout.decode().strip()
|
||||
|
||||
|
||||
async def run_async(cmd: List[str]):
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
print(stdout.decode())
|
||||
|
||||
if process.returncode != 0:
|
||||
error = stderr.decode()
|
||||
raise RuntimeError(f"{cmd[0]} failed: {error}")
|
||||
|
||||
|
||||
class File:
|
||||
def __init__(self, path: str):
|
||||
self.path = os.path.join(ROOT, path)
|
||||
|
||||
def __enter__(self):
|
||||
with open(self.path, "r") as handle:
|
||||
self.text = handle.read()
|
||||
return self
|
||||
|
||||
def get_exact_match(self, attr: str, value: str):
|
||||
matches = re.findall(
|
||||
rf'{re.escape(attr)}\s+=\s+\"?{re.escape(value)}\"?',
|
||||
self.text
|
||||
)
|
||||
|
||||
n = len(matches)
|
||||
if n > 1:
|
||||
raise ValueError(f"multiple occurrences found for {attr}={value}")
|
||||
elif n == 1:
|
||||
return matches.pop()
|
||||
else:
|
||||
raise ValueError(f"no occurrence found for {attr}={value}")
|
||||
|
||||
def substitute(self, attr: str, old_value: str, new_value: str) -> None:
|
||||
old_line = self.get_exact_match(attr, old_value)
|
||||
new_line = old_line.replace(old_value, new_value)
|
||||
self.text = self.text.replace(old_line, new_line)
|
||||
print(f"Substitute `{attr}` value `{old_value}` with `{new_value}`")
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
with open(self.path, "w") as handle:
|
||||
handle.write(self.text)
|
||||
|
||||
class Nurl:
|
||||
@classmethod
|
||||
async def prefetch(cls, url: str, version: str, *extra_args: str) -> str:
|
||||
cmd = [
|
||||
"nurl",
|
||||
"--hash",
|
||||
url,
|
||||
version,
|
||||
]
|
||||
cmd.extend(extra_args)
|
||||
return await check_async(cmd)
|
||||
|
||||
|
||||
class Nix:
|
||||
base_cmd: Final = [
|
||||
"nix",
|
||||
"--show-trace",
|
||||
"--extra-experimental-features", "nix-command"
|
||||
]
|
||||
|
||||
@classmethod
|
||||
async def _run(cls, args: List[str]) -> Optional[str]:
|
||||
return await check_async(cls.base_cmd + args)
|
||||
|
||||
@classmethod
|
||||
async def eval(cls, expr: str) -> Union[List, Dict, int, float, str, bool]:
|
||||
response = await cls._run([
|
||||
"eval",
|
||||
"-f", f"{ROOT}/default.nix",
|
||||
"--json",
|
||||
expr
|
||||
])
|
||||
if response is None:
|
||||
raise RuntimeError("Nix eval expression returned no response")
|
||||
try:
|
||||
return json.loads(response)
|
||||
except (TypeError, ValueError):
|
||||
raise RuntimeError("Nix eval response could not be parsed from JSON")
|
||||
|
||||
@classmethod
|
||||
async def hash_to_sri(cls, algorithm: str, value: str) -> Optional[str]:
|
||||
return await cls._run([
|
||||
"hash",
|
||||
"to-sri",
|
||||
"--type", algorithm,
|
||||
value
|
||||
])
|
||||
|
||||
|
||||
class HomeAssistant:
|
||||
def __init__(self, session: ClientSession):
|
||||
self._session = session
|
||||
|
||||
async def get_latest_core_version(
|
||||
self,
|
||||
owner: str = "home-assistant",
|
||||
repo: str = "core"
|
||||
) -> str:
|
||||
async with self._session.get(
|
||||
f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
|
||||
) as response:
|
||||
document = await response.json()
|
||||
try:
|
||||
return str(document.get("name"))
|
||||
except KeyError:
|
||||
raise RuntimeError("No tag name in response document")
|
||||
|
||||
|
||||
async def get_latest_frontend_version(
|
||||
self,
|
||||
core_version: str
|
||||
) -> str:
|
||||
async with self._session.get(
|
||||
f"https://raw.githubusercontent.com/home-assistant/core/{core_version}/homeassistant/components/frontend/manifest.json"
|
||||
) as response:
|
||||
document = await response.json(content_type="text/plain")
|
||||
|
||||
requirements = [
|
||||
requirement
|
||||
for requirement in document.get("requirements", [])
|
||||
if requirement.startswith("home-assistant-frontend==")
|
||||
]
|
||||
|
||||
if len(requirements) > 1:
|
||||
raise RuntimeError(
|
||||
"Found more than one version specifier for the frontend package"
|
||||
)
|
||||
elif len(requirements) == 1:
|
||||
requirement = requirements.pop()
|
||||
_, version = requirement.split("==", maxsplit=1)
|
||||
return str(version)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"Found no version specifier for frontend package"
|
||||
)
|
||||
|
||||
|
||||
async def update_core(self, old_version: str, new_version: str) -> None:
|
||||
old_sdist_hash = str(await Nix.eval("home-assistant.src.outputHash"))
|
||||
new_sdist_hash = await Nurl.prefetch("https://pypi.org/project/homeassistant/", new_version)
|
||||
print(f"sdist: {old_sdist_hash} -> {new_sdist_hash}")
|
||||
|
||||
old_git_hash = str(await Nix.eval("home-assistant.gitSrc.outputHash"))
|
||||
new_git_hash = await Nurl.prefetch("https://github.com/home-assistant/core/", new_version)
|
||||
print(f"git: {old_git_hash} -> {new_git_hash}")
|
||||
|
||||
with File("pkgs/servers/home-assistant/default.nix") as file:
|
||||
file.substitute("hassVersion", old_version, new_version)
|
||||
file.substitute("hash", old_sdist_hash, new_sdist_hash)
|
||||
file.substitute("hash", old_git_hash, new_git_hash)
|
||||
|
||||
async def update_frontend(self, old_version: str, new_version: str) -> None:
|
||||
old_hash = str(await Nix.eval("home-assistant.frontend.src.outputHash"))
|
||||
new_hash = await Nurl.prefetch(
|
||||
"https://pypi.org/project/home_assistant_frontend/",
|
||||
new_version,
|
||||
"-A", "format", "wheel",
|
||||
"-A", "dist", "py3",
|
||||
"-A", "python", "py3"
|
||||
)
|
||||
print(f"frontend: {old_hash} -> {new_hash}")
|
||||
|
||||
with File("pkgs/servers/home-assistant/frontend.nix") as file:
|
||||
file.substitute("version", old_version, new_version)
|
||||
file.substitute("hash", old_hash, new_hash)
|
||||
|
||||
async def update_components(self):
|
||||
await run_async([
|
||||
f"{ROOT}/pkgs/servers/home-assistant/parse-requirements.py"
|
||||
])
|
||||
|
||||
|
||||
async def main():
|
||||
headers = {}
|
||||
if token := os.environ.get("GITHUB_TOKEN", None):
|
||||
headers.update({"GITHUB_TOKEN": token})
|
||||
|
||||
async with aiohttp.ClientSession(headers=headers) as client:
|
||||
hass = HomeAssistant(client)
|
||||
|
||||
core_current = str(await Nix.eval("home-assistant.version"))
|
||||
core_latest = await hass.get_latest_core_version()
|
||||
|
||||
if Version(core_latest) > Version(core_current):
|
||||
print(f"New Home Assistant version {core_latest} is available")
|
||||
await hass.update_core(str(core_current), str(core_latest))
|
||||
|
||||
frontend_current = str(await Nix.eval("home-assistant.frontend.version"))
|
||||
frontend_latest = await hass.get_latest_frontend_version(str(core_latest))
|
||||
|
||||
if Version(frontend_latest) > Version(frontend_current):
|
||||
await hass.update_frontend(str(frontend_current), str(frontend_latest))
|
||||
|
||||
await hass.update_components()
|
||||
|
||||
else:
|
||||
print(f"Home Assistant {core_current} is still the latest version.")
|
||||
|
||||
# wait for async client sessions to close
|
||||
# https://docs.aiohttp.org/en/stable/client_advanced.html#graceful-shutdown
|
||||
await asyncio.sleep(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_sync(["pyright", __file__])
|
||||
run_sync(["ruff", "--ignore=E501", __file__])
|
||||
run_sync(["isort", __file__])
|
||||
asyncio.run(main())
|
@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env nix-shell
|
||||
#!nix-shell -p nix -p jq -p curl -p bash -p git -p nix-update -i bash
|
||||
|
||||
set -eux
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
cd "$DIR"
|
||||
|
||||
CURRENT_VERSION=$(nix-instantiate ../../.. --eval --strict -A home-assistant.version | tr -d '"')
|
||||
TARGET_VERSION=$(curl https://api.github.com/repos/home-assistant/core/releases/latest | jq -r '.name')
|
||||
MANIFEST=$(curl https://raw.githubusercontent.com/home-assistant/core/${TARGET_VERSION}/homeassistant/components/frontend/manifest.json)
|
||||
FRONTEND_VERSION=$(echo $MANIFEST | jq -r '.requirements[] | select(startswith("home-assistant-frontend")) | sub(".*==(?<vers>.*)"; .vers)')
|
||||
|
||||
if [[ "$CURRENT_VERSION" == "$TARGET_VERSION" ]]; then
|
||||
echo "home-assistant is up-to-date: ${CURRENT_VERSION}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
sed -i -e "s/version =.*/version = \"${TARGET_VERSION}\";/" \
|
||||
component-packages.nix
|
||||
|
||||
sed -i -e "s/hassVersion =.*/hassVersion = \"${TARGET_VERSION}\";/" \
|
||||
default.nix
|
||||
|
||||
(
|
||||
# update the frontend before running parse-requirements, so it doesn't get shown as outdated
|
||||
cd ../../..
|
||||
nix-update --version "$FRONTEND_VERSION" home-assistant.python.pkgs.home-assistant-frontend
|
||||
)
|
||||
|
||||
./parse-requirements.py
|
||||
|
||||
read
|
||||
|
||||
(
|
||||
cd ../../..
|
||||
nix-update --version "$TARGET_VERSION" --build home-assistant
|
||||
)
|
||||
|
||||
#git add ./component-packages.nix ./default.nix ./frontend.nix
|
||||
#git commit -m "home-assistant: ${CURRENT_VERSION} -> ${TARGET_VERSION}"
|
Loading…
Reference in New Issue
Block a user