mirror of
https://github.com/home-assistant/core.git
synced 2026-04-01 13:10:53 +00:00
Compare commits
2 Commits
python-3.1
...
edenhaus-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aae63cb397 | ||
|
|
78aa5b9913 |
94
.github/workflows/builder.yml
vendored
94
.github/workflows/builder.yml
vendored
@@ -35,6 +35,7 @@ jobs:
|
||||
channel: ${{ steps.version.outputs.channel }}
|
||||
publish: ${{ steps.version.outputs.publish }}
|
||||
architectures: ${{ env.ARCHITECTURES }}
|
||||
translation_download_process: ${{ steps.translations.outputs.translation_download_process }}
|
||||
base_image_version: ${{ env.BASE_IMAGE_VERSION }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
@@ -47,6 +48,26 @@ jobs:
|
||||
with:
|
||||
python-version-file: ".python-version"
|
||||
|
||||
- name: Fail if translations files are checked in
|
||||
run: |
|
||||
if [ -n "$(find homeassistant/components/*/translations -type f)" ]; then
|
||||
echo "Translations files are checked in, please remove the following files:"
|
||||
find homeassistant/components/*/translations -type f
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Start translation download
|
||||
id: translations
|
||||
shell: bash
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
||||
run: |
|
||||
pip install -r script/translations/requirements.txt
|
||||
PROCESS_OUTPUT=$(python3 -m script.translations download --async-start)
|
||||
echo "$PROCESS_OUTPUT"
|
||||
TRANSLATION_DOWNLOAD_PROCESS=$(echo "$PROCESS_OUTPUT" | sed -n 's/.*Process ID: //p')
|
||||
echo "translation_download_process=$TRANSLATION_DOWNLOAD_PROCESS" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Get information
|
||||
id: info
|
||||
uses: home-assistant/actions/helpers/info@master # zizmor: ignore[unpinned-uses]
|
||||
@@ -62,30 +83,6 @@ jobs:
|
||||
with:
|
||||
ignore-dev: true
|
||||
|
||||
- name: Fail if translations files are checked in
|
||||
run: |
|
||||
if [ -n "$(find homeassistant/components/*/translations -type f)" ]; then
|
||||
echo "Translations files are checked in, please remove the following files:"
|
||||
find homeassistant/components/*/translations -type f
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Download Translations
|
||||
run: python3 -m script.translations download
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
||||
|
||||
- name: Archive translations
|
||||
shell: bash
|
||||
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
if-no-files-found: error
|
||||
|
||||
build_base:
|
||||
name: Build ${{ matrix.arch }} base core image
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
@@ -133,7 +130,6 @@ jobs:
|
||||
name: package
|
||||
|
||||
- name: Set up Python
|
||||
if: needs.init.outputs.channel == 'dev'
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version-file: ".python-version"
|
||||
@@ -181,22 +177,35 @@ jobs:
|
||||
sed -i "s|home-assistant-intents==.*||" requirements_all.txt requirements.txt
|
||||
fi
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: translations
|
||||
|
||||
- name: Extract translations
|
||||
run: |
|
||||
tar xvf translations.tar.gz
|
||||
rm translations.tar.gz
|
||||
|
||||
- name: Write meta info file
|
||||
shell: bash
|
||||
run: |
|
||||
echo "${GITHUB_SHA};${GITHUB_REF};${GITHUB_EVENT_NAME};${GITHUB_ACTOR}" > rootfs/OFFICIAL_IMAGE
|
||||
|
||||
- name: Build base image
|
||||
- name: Build base image (deps stage only)
|
||||
uses: edenhaus/builder/actions/build-image@b3ea9d4b1d98f979d671aa8442ad8373e38aea11
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
build-args: |
|
||||
BUILD_FROM=ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant-base:${{ needs.init.outputs.base_image_version }}
|
||||
cache-gha: false
|
||||
container-registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
cosign-base-identity: "https://github.com/home-assistant/docker/.*"
|
||||
cosign-base-verify: ghcr.io/home-assistant/${{ matrix.arch }}-homeassistant-base:${{ needs.init.outputs.base_image_version }}
|
||||
push: false
|
||||
load: true
|
||||
target: deps
|
||||
|
||||
- name: Download translations
|
||||
shell: bash
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
||||
TRANSLATION_PROCESS_ID: ${{ needs.init.outputs.translation_download_process }}
|
||||
run: |
|
||||
pip install -r script/translations/requirements.txt
|
||||
python3 -m script.translations download --process-id "$TRANSLATION_PROCESS_ID"
|
||||
|
||||
- name: Build base image (fully)
|
||||
uses: home-assistant/builder/actions/build-image@62a1597b84b3461abad9816d9cd92862a2b542c3 # 2026.03.2
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
@@ -485,14 +494,13 @@ jobs:
|
||||
python-version-file: ".python-version"
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: translations
|
||||
|
||||
- name: Extract translations
|
||||
shell: bash
|
||||
env:
|
||||
LOKALISE_TOKEN: ${{ secrets.LOKALISE_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
||||
TRANSLATION_PROCESS_ID: ${{ needs.init.outputs.translation_download_process }}
|
||||
run: |
|
||||
tar xvf translations.tar.gz
|
||||
rm translations.tar.gz
|
||||
pip install -r script/translations/requirements.txt
|
||||
python3 -m script.translations download --process-id "$TRANSLATION_PROCESS_ID"
|
||||
|
||||
- name: Build package
|
||||
shell: bash
|
||||
|
||||
4
Dockerfile
generated
4
Dockerfile
generated
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# To update, run python3 -m script.hassfest -p docker
|
||||
ARG BUILD_FROM
|
||||
FROM ${BUILD_FROM}
|
||||
FROM ${BUILD_FROM} as deps
|
||||
|
||||
LABEL \
|
||||
io.hass.type="core" \
|
||||
@@ -50,6 +50,8 @@ RUN \
|
||||
--no-build \
|
||||
-r homeassistant/requirements_all.txt
|
||||
|
||||
FROM deps
|
||||
|
||||
## Setup Home Assistant Core
|
||||
COPY . homeassistant/
|
||||
RUN \
|
||||
|
||||
@@ -17,7 +17,7 @@ DOCKERFILE_TEMPLATE = r"""# Automatically generated by hassfest.
|
||||
#
|
||||
# To update, run python3 -m script.hassfest -p docker
|
||||
ARG BUILD_FROM
|
||||
FROM ${{BUILD_FROM}}
|
||||
FROM ${{BUILD_FROM}} as deps
|
||||
|
||||
LABEL \
|
||||
io.hass.type="core" \
|
||||
@@ -65,6 +65,8 @@ RUN \
|
||||
--no-build \
|
||||
-r homeassistant/requirements_all.txt
|
||||
|
||||
FROM deps
|
||||
|
||||
## Setup Home Assistant Core
|
||||
COPY . homeassistant/
|
||||
RUN \
|
||||
|
||||
@@ -3,15 +3,22 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import io
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import time
|
||||
from typing import Any
|
||||
from zipfile import ZipFile
|
||||
|
||||
from .const import CLI_2_DOCKER_IMAGE, CORE_PROJECT_ID, INTEGRATIONS_DIR
|
||||
import lokalise
|
||||
import requests
|
||||
|
||||
from .const import CORE_PROJECT_ID, INTEGRATIONS_DIR
|
||||
from .error import ExitApp
|
||||
from .util import (
|
||||
flatten_translations,
|
||||
get_base_arg_parser,
|
||||
get_lokalise_token,
|
||||
load_json_from_path,
|
||||
substitute_references,
|
||||
@@ -20,44 +27,95 @@ from .util import (
|
||||
DOWNLOAD_DIR = Path("build/translations-download").absolute()
|
||||
|
||||
|
||||
def run_download_docker() -> None:
|
||||
"""Run the Docker image to download the translations."""
|
||||
print("Running Docker to download latest translations.")
|
||||
result = subprocess.run(
|
||||
[
|
||||
"docker",
|
||||
"run",
|
||||
"-v",
|
||||
f"{DOWNLOAD_DIR}:/opt/dest/locale",
|
||||
"--rm",
|
||||
f"lokalise/lokalise-cli-2:{CLI_2_DOCKER_IMAGE}",
|
||||
# Lokalise command
|
||||
"lokalise2",
|
||||
"--token",
|
||||
get_lokalise_token(),
|
||||
"--project-id",
|
||||
CORE_PROJECT_ID,
|
||||
"file",
|
||||
"download",
|
||||
CORE_PROJECT_ID,
|
||||
"--original-filenames=false",
|
||||
"--replace-breaks=false",
|
||||
"--filter-data",
|
||||
"nonfuzzy",
|
||||
"--disable-references",
|
||||
"--export-empty-as",
|
||||
"skip",
|
||||
"--format",
|
||||
"json",
|
||||
"--unzip-to",
|
||||
"/opt/dest",
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
print()
|
||||
POLL_INTERVAL = 5
|
||||
|
||||
if result.returncode != 0:
|
||||
raise ExitApp("Failed to download translations")
|
||||
|
||||
def get_arguments() -> argparse.Namespace:
|
||||
"""Get parsed passed in arguments."""
|
||||
parser = get_base_arg_parser()
|
||||
parser.add_argument(
|
||||
"--async-start",
|
||||
action="store_true",
|
||||
help="Start an async download and return the process ID.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--process-id",
|
||||
type=str,
|
||||
help="Process ID to wait for, then download and unzip the result.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def get_client() -> lokalise.Client:
|
||||
"""Get an authenticated Lokalise client."""
|
||||
return lokalise.Client(get_lokalise_token())
|
||||
|
||||
|
||||
def start_async_download(client: lokalise.Client) -> str:
|
||||
"""Start an async download and return the process ID."""
|
||||
process = client.download_files_async(
|
||||
CORE_PROJECT_ID,
|
||||
{
|
||||
"format": "json",
|
||||
"original_filenames": False,
|
||||
"replace_breaks": False,
|
||||
"filter_data": "nonfuzzy",
|
||||
"disable_references": True,
|
||||
"export_empty_as": "skip",
|
||||
},
|
||||
)
|
||||
return process.process_id
|
||||
|
||||
|
||||
def wait_for_process(client: lokalise.Client, process_id: str) -> str:
|
||||
"""Wait for a queued process to complete and return the process download URL."""
|
||||
while True:
|
||||
process_info = client.queued_process(CORE_PROJECT_ID, process_id)
|
||||
# Current status of the process. Can be queued, pre_processing, running,
|
||||
# post_processing, cancelled, finished or failed.
|
||||
status = process_info.status
|
||||
additional_info = ""
|
||||
if process_info.details is not None and (details := dict(process_info.details)):
|
||||
if (
|
||||
status == "running"
|
||||
and (done := details.get("items_processed")) is not None
|
||||
and (total := details.get("items_to_process")) is not None
|
||||
):
|
||||
additional_info = f" ({done}/{total})"
|
||||
elif status == "finished":
|
||||
additional_info = f" total_keys={details.get('total_number_of_keys')}"
|
||||
else:
|
||||
additional_info = f" details={details}"
|
||||
print(f"Process {process_id}: status={status}{additional_info}")
|
||||
|
||||
if status == "finished":
|
||||
return process_info.details["download_url"]
|
||||
if status in ("cancelled", "failed"):
|
||||
raise ExitApp(
|
||||
f"Process {process_id} ended with status: {status}{additional_info}"
|
||||
)
|
||||
|
||||
time.sleep(POLL_INTERVAL)
|
||||
|
||||
|
||||
def download_and_unzip(bundle_url: str) -> None:
|
||||
"""Download a zip bundle and extract it to the download directory."""
|
||||
print("Downloading translations from lokalise...")
|
||||
response = requests.get(bundle_url, timeout=120)
|
||||
response.raise_for_status()
|
||||
|
||||
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with ZipFile(io.BytesIO(response.content)) as zf:
|
||||
zf.extractall(DOWNLOAD_DIR)
|
||||
|
||||
print(f"Extracted translations to {DOWNLOAD_DIR}")
|
||||
|
||||
|
||||
def fetch_translations(client: lokalise.Client, process_id: str) -> None:
|
||||
"""Wait for a process to finish, then download and unzip the bundle."""
|
||||
download_url = wait_for_process(client, process_id)
|
||||
download_and_unzip(download_url)
|
||||
|
||||
|
||||
def save_json(filename: Path, data: list | dict) -> None:
|
||||
@@ -136,14 +194,23 @@ def delete_old_translations() -> None:
|
||||
fil.unlink()
|
||||
|
||||
|
||||
def run() -> None:
|
||||
def run() -> int:
|
||||
"""Run the script."""
|
||||
DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
||||
args = get_arguments()
|
||||
client = get_client()
|
||||
|
||||
run_download_docker()
|
||||
process_id = args.process_id
|
||||
|
||||
if not process_id:
|
||||
# if no process ID provided, start a new async download and print the process ID
|
||||
process_id = start_async_download(client)
|
||||
print(f"Async download started. Process ID: {process_id}")
|
||||
|
||||
if args.async_start:
|
||||
# If --async-start is provided, exit after starting the download
|
||||
return 0
|
||||
|
||||
fetch_translations(client, process_id)
|
||||
delete_old_translations()
|
||||
|
||||
save_integrations_translations()
|
||||
|
||||
return 0
|
||||
|
||||
2
script/translations/requirements.txt
generated
Normal file
2
script/translations/requirements.txt
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
python-lokalise-api==4.0.4
|
||||
requests
|
||||
Reference in New Issue
Block a user