Compare commits

...

6 Commits

Author SHA1 Message Date
Robert Resch d6edd6b5ee use lock file in production 2026-06-11 14:35:59 +00:00
Robert Resch d37ef21cf8 Let renovate update the lock file 2026-06-10 13:31:37 +00:00
Robert Resch 686daaed85 Combine hash 2026-06-08 11:45:35 +00:00
Robert Resch bce05bd2c7 Generate always lockfiles 2026-06-08 11:43:02 +00:00
Robert Resch 33363ef07b Fix autogenerated comment 2026-06-08 11:37:49 +00:00
Robert Resch 32a9e115da Generate and use requirements lock files 2026-06-08 10:37:19 +00:00
9 changed files with 28692 additions and 29 deletions
+2 -3
View File
@@ -18,8 +18,7 @@ homeassistant/generated/*.py linguist-generated=true
pylint/plugins/pylint_home_assistant/generated/*.py linguist-generated=true
machine/* linguist-generated=true
mypy.ini linguist-generated=true
requirements.txt linguist-generated=true
requirements_all.txt linguist-generated=true
requirements_test_pre_commit.txt linguist-generated=true
requirements*.txt linguist-generated=true
requirements_*.lock linguist-generated=true
script/hassfest/docker/Dockerfile linguist-generated=true
.github/workflows/*.lock.yml linguist-generated=true
+5
View File
@@ -5,6 +5,7 @@
"enabledManagers": [
"pep621",
"pip_requirements",
"pip-compile",
"pre-commit",
"dockerfile",
"custom.regex",
@@ -22,6 +23,10 @@
]
},
"pip-compile": {
"managerFilePatterns": ["/(^|/)requirements[\\w_-]*\\.lock$/"]
},
"dockerfile": {
"managerFilePatterns": ["/^Dockerfile$/"]
},
+4
View File
@@ -157,6 +157,8 @@ jobs:
homeassistant/package_constraints.txt
sed -i "s|home-assistant-frontend==.*||" requirements_all.txt
# Drop the released pin from the lock file; the nightly wheel is installed separately
sed -i "/^home-assistant-frontend==/,/^[^[:space:]#]/ { /^home-assistant-frontend==/d; /^[[:space:]#]/d }" requirements_all.lock
fi
if [[ "$(ls home_assistant_intents*.whl)" =~ ^home_assistant_intents-(.*)-py3-none-any.whl$ ]]; then
@@ -175,6 +177,8 @@ jobs:
homeassistant/package_constraints.txt
sed -i "s|home-assistant-intents==.*||" requirements_all.txt requirements.txt
# Drop the released pin from the lock file; the nightly wheel is installed separately
sed -i "/^home-assistant-intents==/,/^[^[:space:]#]/ { /^home-assistant-intents==/d; /^[[:space:]#]/d }" requirements_all.lock
fi
- name: Download translations
+4 -9
View File
@@ -104,15 +104,11 @@ jobs:
- name: Generate partial Python venv restore key
id: generate_python_cache_key
env:
HASH_REQUIREMENTS_TEST: ${{ hashFiles('requirements_test.txt', 'requirements_test_pre_commit.txt') }}
HASH_REQUIREMENTS: ${{ hashFiles('requirements.txt') }}
HASH_REQUIREMENTS_ALL: ${{ hashFiles('requirements_all.txt') }}
HASH_PACKAGE_CONSTRAINTS: ${{ hashFiles('homeassistant/package_constraints.txt') }}
HASH_GEN_REQUIREMENTS: ${{ hashFiles('script/gen_requirements_all.py') }}
HASH_FILES: ${{ hashFiles('requirements_ci.lock', 'script/gen_requirements_all.py') }}
run: |
# Include HA_SHORT_VERSION to force the immediate creation
# of a new uv cache entry after a version bump.
echo "key=venv-${CACHE_VERSION}-${HA_SHORT_VERSION}-${HASH_REQUIREMENTS_TEST}-${HASH_REQUIREMENTS}-${HASH_REQUIREMENTS_ALL}-${HASH_PACKAGE_CONSTRAINTS}-${HASH_GEN_REQUIREMENTS}" >> $GITHUB_OUTPUT
echo "key=venv-${CACHE_VERSION}-${HA_SHORT_VERSION}-${HASH_FILES}" >> $GITHUB_OUTPUT
- name: Filter for core changes
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: core
@@ -365,7 +361,7 @@ jobs:
RUNNER_OS: ${{ runner.os }}
RUNNER_ARCH: ${{ runner.arch }}
PYTHON_VERSION: ${{ steps.python.outputs.python-version }}
HASH_FILES: ${{ hashFiles('requirements.txt', 'requirements_all.txt', 'requirements_test.txt', 'homeassistant/package_constraints.txt') }}
HASH_FILES: ${{ hashFiles('requirements_ci.lock') }}
run: |
partial_key="${RUNNER_OS}-${RUNNER_ARCH}-${PYTHON_VERSION}-uv-"
echo "partial_key=${partial_key}" >> $GITHUB_OUTPUT
@@ -414,8 +410,7 @@ jobs:
python -m venv venv
. venv/bin/activate
python --version
uv pip install -r requirements.txt
uv pip install -r requirements_all.txt -r requirements_test.txt
uv pip install -r requirements_ci.lock --require-hashes
uv pip install -e . --config-settings editable_mode=compat
- name: Dump pip freeze
run: |
+6 -11
View File
@@ -29,24 +29,19 @@ COPY rootfs /
COPY --from=ghcr.io/alexxit/go2rtc:1.9.14@sha256:675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae /usr/local/bin/go2rtc /bin/go2rtc
## Setup Home Assistant Core dependencies
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
COPY --parents requirements_all.lock home_assistant_frontend-* home_assistant_intents-* homeassistant/
RUN \
# Verify go2rtc can be executed
go2rtc --version \
# Install uv at the version pinned in the requirements file
&& pip3 install --no-cache-dir "uv==$(awk -F'==' '/^uv==/{print $2}' homeassistant/requirements.txt)" \
&& uv pip install \
--no-build \
-r homeassistant/requirements.txt
COPY requirements_all.txt home_assistant_frontend-* home_assistant_intents-* homeassistant/
RUN \
if ls homeassistant/home_assistant_*.whl 1> /dev/null 2>&1; then \
# Install uv at the version pinned in the lock file
&& pip3 install --no-cache-dir "uv==$(awk -F'[= ;]+' '/^uv==/{print $2}' homeassistant/requirements_all.lock)" \
&& if ls homeassistant/home_assistant_*.whl 1> /dev/null 2>&1; then \
uv pip install homeassistant/home_assistant_*.whl; \
fi \
&& uv pip install \
--no-build \
-r homeassistant/requirements_all.txt
--require-hashes \
-r homeassistant/requirements_all.lock
## Setup Home Assistant Core
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
+8
View File
@@ -94,6 +94,14 @@ dependencies = [
[project.scripts]
hass = "homeassistant.__main__:main"
[tool.uv]
# Constrain the universal `uv pip compile` resolution (used for the .lock files)
# to the platforms Home Assistant actually targets: Linux and macOS.
environments = [
"sys_platform == 'linux'",
# "sys_platform == 'darwin'",
]
[tool.setuptools]
include-package-data = true
+14065
View File
File diff suppressed because it is too large Load Diff
+14519
View File
File diff suppressed because it is too large Load Diff
+79 -6
View File
@@ -1,13 +1,16 @@
#!/usr/bin/env python3
"""Generate updated constraint and requirements files."""
from collections.abc import Sequence
import difflib
import importlib
from operator import itemgetter
from pathlib import Path
import pkgutil
import re
import subprocess
import sys
import tempfile
import tomllib
from typing import Any
@@ -261,6 +264,11 @@ IGNORE_PRE_COMMIT_HOOK_ID = (
PACKAGE_REGEX = re.compile(r"^(?:--.+\s)?([-_\.\w\d]+).*==.+$")
PIP_COMPILE_LOCK_FILES = {
"requirements_all.lock": ("requirements_all.txt",),
"requirements_ci.lock": ("requirements_all.txt", "requirements_test.txt"),
}
def explore_module(package: str, explore_children: bool) -> list[str]:
"""Explore the modules."""
@@ -542,18 +550,57 @@ def gather_constraints() -> str:
)
def diff_file(filename: str, content: str) -> list[str]:
"""Diff a file."""
def diff_content(
content_a: Sequence[str], filename_a: str, content_b: Sequence[str], filename_b: str
) -> list[str]:
"""Diff two strings as if they were files."""
return list(
difflib.context_diff(
[f"{line}\n" for line in Path(filename).read_text().split("\n")],
[f"{line}\n" for line in content.split("\n")],
filename,
"generated",
content_a,
content_b,
filename_a,
filename_b,
)
)
def diff_file(filename: str, content: str) -> list[str]:
"""Diff a file."""
return diff_content(
[f"{line}\n" for line in Path(filename).read_text().split("\n")],
filename,
[f"{line}\n" for line in content.split("\n")],
"generated",
)
def generate_lock_file(output_path: str, source_files: tuple[str, ...]) -> None:
"""Resolve requirements_all.txt into a hash-pinned lock file.
This shells out to `uv pip compile --generate-hashes`, which performs a
full networked dependency resolution, so callers should only invoke it
when requirements/dependencies have changed.
uv records its invocation in the lock file header, which lets Renovate's
pip-compile manager parse and re-run it to refresh the lock file when it
bumps a dependency.
"""
subprocess.run(
[
"uv",
"pip",
"compile",
"--generate-hashes",
"--quiet",
"--universal",
"--output-file",
output_path,
*source_files,
],
check=True,
)
def main(validate: bool, ci: bool) -> int:
"""Run the script."""
if not Path("requirements_all.txt").is_file():
@@ -596,6 +643,29 @@ def main(validate: bool, ci: bool) -> int:
if diff:
errors.append("".join(diff))
with tempfile.TemporaryDirectory() as tmp_dir:
for lock_file, source_files in PIP_COMPILE_LOCK_FILES.items():
tmp_lock = str(Path(tmp_dir) / lock_file)
# The header contains the generated command, which we need to ignore when comparing the existing lock file with the generated one due the tempoary folder used for generation.
lock_content = [
f"{line}\n"
for line in Path(lock_file).read_text(encoding="utf-8").split("\n")
][1:]
generate_lock_file(tmp_lock, source_files)
generated_lock_content = [
f"{line}\n"
for line in Path(tmp_lock).read_text(encoding="utf-8").split("\n")
][1:]
lock_diff = diff_content(
lock_content,
lock_file,
generated_lock_content,
"generated",
)
if lock_diff:
errors.append("".join(lock_diff))
if errors:
print("ERROR - FOUND THE FOLLOWING DIFFERENCES")
print()
@@ -610,6 +680,9 @@ def main(validate: bool, ci: bool) -> int:
for filename, content in files:
Path(filename).write_text(content)
for lock_file, source_files in PIP_COMPILE_LOCK_FILES.items():
generate_lock_file(lock_file, source_files)
return 0