diff --git a/.gitea/workflows/publish-pyfglair.yaml b/.gitea/workflows/publish-pyfglair.yaml new file mode 100644 index 0000000..a827446 --- /dev/null +++ b/.gitea/workflows/publish-pyfglair.yaml @@ -0,0 +1,95 @@ +# Сборка и публикация pyfglair в PyPI-реестр пакетов Gitea. +# Запуск: Actions → Publish pyfglair → Run workflow (ручной, workflow_dispatch). +# +# Секреты репозитория (Settings → Actions → Secrets): +# GITEA_PYPI_TOKEN — personal access token со scope write:package +# GITEA_PYPI_USER — (опционально) имя пользователя Gitea для upload; +# по умолчанию — актор запуска. +# +# Примечания Gitea Actions: +# * встроенный GITEA_TOKEN не может публиковать пакеты — нужен PAT; +# * actions/checkout и actions/setup-python тянутся с github.com +# (если инстанс изолирован — замените на абсолютные URL своих зеркал); +# * сборка wheel с bundled mbedtls требует доступ к github.com +# (FetchContent); при BUNDLED=0 ставится системный libmbedtls-dev. +name: Publish pyfglair + +on: + workflow_dispatch: + inputs: + bundled_mbedtls: + description: "1 — встроить mbedtls, 0 — использовать системный" + required: false + default: "1" + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Toolchain + run: | + if ! command -v cmake >/dev/null || ! command -v ninja >/dev/null \ + || ! command -v g++ >/dev/null; then + sudo apt-get update + sudo apt-get install -y --no-install-recommends cmake ninja-build g++ + fi + if [ "${{ inputs.bundled_mbedtls }}" = "0" ]; then + sudo apt-get update + sudo apt-get install -y --no-install-recommends libmbedtls-dev + fi + cmake --version | head -1 + g++ --version | head -1 + + - name: Build wheel + sdist + env: + BUNDLED: ${{ inputs.bundled_mbedtls }} + run: | + if [ "$BUNDLED" = "0" ]; then + export FGL_BUNDLED_MBEDTLS=0 + else + export FGL_BUNDLED_MBEDTLS=1 + fi + pip install --upgrade build + python -m build + ls -l dist + + - name: Smoke test wheel + run: | + python -m venv /tmp/wheeltest + /tmp/wheeltest/bin/pip install --quiet dist/*.whl + /tmp/wheeltest/bin/python -c "import pyfglair.session as s; print('pyfglair:', s.__file__)" + /tmp/wheeltest/bin/python -c "from pyfglair import Template, Prop, convert_to_display; assert convert_to_display(Template.A, Prop.DISPLAY_TEMPERATURE, 7000) == 200" + + - name: Publish to Gitea package registry (PyPI) + env: + TOKEN: ${{ secrets.GITEA_PYPI_TOKEN }} + SECRET_USER: ${{ secrets.GITEA_PYPI_USER }} + run: | + if [ -z "$TOKEN" ]; then + echo "::error::Задайте секрет GITEA_PYPI_TOKEN (PAT со scope write:package)" + exit 1 + fi + SERVER="${GITHUB_SERVER_URL:-}" + if [ -z "$SERVER" ]; then + SERVER="$(git remote get-url origin | sed -E 's#^git@([^:]+):.*#https://\1#; s#^(https?://[^/]+)/.*#\1#')" + fi + REPO="${GITHUB_REPOSITORY:-$(git remote get-url origin | sed -E 's#.*[:/]([^/]+/[^/]+?)(\.git)?$#\1#')}" + OWNER="${REPO%%/*}" + GITEA_USER="${SECRET_USER:-${GITHUB_ACTOR:-git}}" + pip install --upgrade twine + python -m twine upload \ + --repository-url "$SERVER/api/packages/$OWNER/pypi" \ + --username "$GITEA_USER" \ + --password "$TOKEN" \ + --skip-existing \ + --non-interactive \ + dist/* + echo "Опубликовано: $SERVER/api/packages/$OWNER/pypi/simple/pyfglair/" diff --git a/custom_components/fglair/README.md b/custom_components/fglair/README.md index eb6b71a..a8a3bec 100644 --- a/custom_components/fglair/README.md +++ b/custom_components/fglair/README.md @@ -11,9 +11,11 @@ ## Требования * Home Assistant ≥ 2025.1 (Linux x86_64/aarch64). -* Пакет `pyfglair` (манифест ставит его автоматически из PyPI). - До публикации на PyPI установите wheel вручную в python-окружение HA: - `pip install pyfglair-*.whl` (см. [`docs/RELEASE_HA.md`](../../docs/RELEASE_HA.md)). +* Пакет `pyfglair`. Манифест ставит его из PyPI; при внутренней установке + (Gitea, без публичного PyPI) поставьте вручную из PyPI-реестра пакетов + Gitea: + `pip install --index-url https://:@git.ratigorsk-12.ru/api/packages//pypi/simple --no-deps pyfglair` + (подробности и сборка wheel — [`docs/RELEASE_HA.md`](../../docs/RELEASE_HA.md)). * Модуль кондиционера в той же LAN. Модуль поддерживает **2 LAN-сессии**: телефон с FGLair и HA уживаются; третья (например, ESPHome) получит 503. diff --git a/docs/RELEASE_HA.md b/docs/RELEASE_HA.md index 2c738ab..a9d53c0 100644 --- a/docs/RELEASE_HA.md +++ b/docs/RELEASE_HA.md @@ -1,99 +1,120 @@ -# Чек-лист релиза HA-интеграции (H5) +# Релиз и установка HA-интеграции -## 1. Публикация pyfglair (PyPI) +## 1. Внутренняя сборка и установка (Gitea, текущий режим) -Манифест компонента требует `pyfglair>=1.0.0`; штатный installer HA -резолвит требования только через PyPI. PyPI (warehouse) принимает только -`manylinux*`/`musllinux*` платформенные теги — локальный -`py3-none-linux_x86_64` будет отклонён (`HTTP 400 unsupported platform -tag`), поэтому wheel нужно «починить» auditwheel'ом в контейнере со старой -glibc (manylinux): +Внутренний репозиторий — Gitea (`git.ratigorsk-12.ru`), поэтому: + +* HACS к Gitea **не подключается** (только публичный GitHub) — для + внутреннего использования интеграция ставится копированием + `custom_components/fglair/` в `/custom_components/`; +* зависимость `pyfglair` публикуется в **PyPI-реестр пакетов Gitea** и + ставится оттуда. + +### 1.1. Сборка и публикация wheel (Gitea Actions, ручной запуск) + +Workflow: `.gitea/workflows/publish-pyfglair.yaml`. + +1. Создайте personal access token Gitea со scope `write:package` + (Settings → Applications → Generate token). +2. Settings репозитория → Actions → Secrets: + * `GITEA_PYPI_TOKEN` — этот токен (обязательно); + * `GITEA_PYPI_USER` — имя пользователя Gitea (опционально; иначе актор + запуска). +3. Actions → **Publish pyfglair** → Run workflow (вход `bundled_mbedtls`: + `1` — встроить mbedtls, `0` — системный `libmbedtls-dev`). +4. Артефакт появится в: `https://git.ratigorsk-12.ru/api/packages//pypi/simple/pyfglair/` + (`` — владелец репозитория, напр. `esphome`). + +Workflow собирает `wheel` + `sdist`, прогоняет smoke-тест установки и +публикует через `twine upload --skip-existing`. Повторная публикация той же +версии пропускается; для новой версии поднимите `version` в +`pyproject.toml`. + +### 1.2. Установка pyfglair в Home Assistant + +Пакет обязателен (cffi-биндинги). Варианты: + +* **Вручную в окружение HA** (проще всего для теста; HA увидит + установленный дистрибутив и не будет вызывать pip для `pyfglair>=1.0.0`): + + ```sh + # в контейнере/venv Home Assistant (Advanced SSH & Web Terminal, docker exec и т.п.) + pip install --index-url https://:@git.ratigorsk-12.ru/api/packages//pypi/simple \ + --no-deps pyfglair + ``` + +* **Через индекс pip в окружении HA** (`pip.conf`/`PIP_EXTRA_INDEX_URL`): + тогда HA сам поставит `pyfglair` из манифеста. Учтите риск dependency + confusion при `--extra-index-url` — для внутреннего стенда допустимо. + +* **Публичный PyPI** — см. §2 (для распространения вне сети). + +### 1.3. Обновление интеграции + +`custom_components/fglair/` обновляется из Gitea (git pull / копирование), +затем перезапуск HA. `pyfglair` — переустановкой из пакетов Gitea. + +## 2. Публикация pyfglair на PyPI (если понадобится публично) + +PyPI (warehouse) принимает только `manylinux*`/`musllinux*` теги, поэтому +локальный `py3-none-linux_x86_64` нужно чинить auditwheel'ом в контейнере +со старой glibc: ```sh pip install build auditwheel twine -# для каждой архитектуры (linux x86_64/aarch64) в manylinux-контейнере -# (например, quay.io/pypa/manylinux2014_): -python -m build --wheel # .so без внешнего mbedcrypto -auditwheel repair dist/pyfglair-*.whl \ - --plat manylinux2014_x86_64 -w dist/manylinux/ # или manylinux_2_28 -rm -f dist/pyfglair-*-linux_*.whl # linux-тег PyPI отклонит -python -m build --sdist # sdist с C++-исходниками (MANIFEST.in) +# для каждой архитектуры (linux x86_64/aarch64) в manylinux-контейнере: +python -m build --wheel +auditwheel repair dist/pyfglair-*.whl --plat manylinux2014_x86_64 -w dist/manylinux/ +rm -f dist/pyfglair-*-linux_*.whl +python -m build --sdist twine upload dist/manylinux/* dist/*.tar.gz ``` -Проверки перед публикацией: +Проверки: `scripts/py-ci.sh`, `scripts/ci.sh`, `readelf -d` по `.so` +(нет внешней `libmbedcrypto`), установка wheel в чистый venv. -* `scripts/py-ci.sh` — pyfglair + acceptance self-test + components; -* `scripts/ci.sh` — ядро (gcc/clang, ASan/UBSan) 11/11; -* `readelf -d build-pyfglair/libfgl-aircon.so*` (или распакованный wheel: - `pyfglair/libfgl-aircon.so*`) — нет внешней `libmbedcrypto` (встроен); -* `auditwheel show` для repaired-wheel — тег manylinux и только - базовые системные библиотеки; -* установка wheel в чистый venv вне репозитория: - `python -c "import pyfglair.session"`; -* aarch64: сборка в соответствующем manylinux-контейнере (QEMU или - нативная), иначе пользователи ARM не смогут поставить `pyfglair`. - -Альтернатива — cibuildwheel (`pip install cibuildwheel`) с `auditwheel -repair` внутри, но текущий `bdist_wheel`-хук отдаёт тег -`py3-none-`, поэтому проверяйте итоговые теги вручную. - -## 2. GitHub-зеркало и Custom Repositories +## 3. GitHub-зеркало и Custom Repositories (HACS) HACS устанавливает интеграции только с публичных **GitHub**-репозиториев -(GitLab/Gitea не поддерживаются: https://hacs.xyz/docs/faq/other_git_providers/). -Публикация в default-репозиторий HACS не требуется — достаточно Custom -Repositories, но хостинг обязан быть GitHub. +(GitLab/Gitea не поддерживаются: +https://hacs.xyz/docs/faq/other_git_providers/). Публикация в +default-репозиторий HACS не требуется — достаточно Custom Repositories, но +хостинг обязан быть GitHub. ```sh -# один раз: зеркало и перенос веток/тегов git remote add github git@github.com:/fgl-aircon.git git push --mirror github ``` После зеркала: -* заполнить в настройках GitHub: Description, Topics - (`home-assistant`, `hacs`, `fujitsu`, `air-conditioner`), включить Issues - (эти пункты проверяет HACS Action; файлами они не настраиваются); -* проверить `codeowners` в `custom_components/fglair/manifest.json` — - сейчас `@petr.polezhaev`; для GitHub Action/HACS это должен быть реальный - GitHub-логин; -* проверить `documentation`/`issue_tracker` в манифесте; -* обновить `version` манифеста под номер тега (сейчас `0.1.0`, pyfglair — - `1.0.0`; компонент и пакет версионируются отдельно); -* заменить заглушки `custom_components/fglair/screenshots/step-N.png` - реальными скриншотами (описания «что должно быть видно» — в README); -* заменить заглушки `custom_components/fglair/brand/*.png` фирменными - ассетами (сейчас — сгенерированные плейсхолдеры: icon/icon@2x/logo и - тёмные варианты); -* создать git-тег (semver) в GitHub-зеркале — HACS покажет версию. +* заполнить на GitHub Description, Topics (`home-assistant`, `hacs`, + `fujitsu`, `air-conditioner`), включить Issues (проверяет HACS Action); +* проверить `codeowners` в manifest (`@petr.polezhaev`; для GitHub нужен + реальный GitHub-логин); +* обновить `version` манифеста под тег; заменить заглушки + `screenshots/step-N.png` и `brand/*.png` реальными ассетами; +* создать тег (semver) — HACS покажет версию. -Файлы, которые HACS/hassfest проверяют у интеграции: `hacs.json`, -`custom_components/fglair/manifest.json` (domain/documentation/ -issue_tracker/codeowners/name/version), `custom_components/fglair/brand/icon.png`, -README/info.md, OSI-лицензия (`LICENSE`, MIT). Всё это в репозитории есть; -workflow `.github/workflows/{validate-hacs,hassfest}.yaml` включатся на -GitHub. +Готовые файлы HACS: `hacs.json`, `info.md`, `LICENSE` (MIT), +`custom_components/fglair/brand/` (icon/icon@2x/logo + dark-варианты), +workflows `.github/workflows/{validate-hacs,hassfest}.yaml`. -## 3. Приёмка на живом стенде +## 4. Приёмка на живом стенде * `tests/acceptance/test_esphome_ha.py quick` — матрица изменений (HA; с `--esphome-host` шаги «режим/уставка» идут через ESPHome, проверка — по состоянию в HA), возврат к исходному. -* `long --hours 24 --interval 3600 --report acceptance.csv` — суточный - прогон, отчёт дописывается по ходу. -* Сценарии: 503 (два слота заняты), key_error + Repair, offline и - восстановление, два устройства (разные порты прослушивания), options flow - (копирование/смена ключа), reconfigure без потери конверсий. +* `long --hours 24 --interval 3600 --report acceptance.csv`. +* Сценарии: 503 (два слота), key_error + Repair, offline/восстановление, + два устройства, options flow (копирование/смена ключа), reconfigure. -## 4. Ограничения текущего стенда +## 5. Ограничения стенда -* Реального железа/облака в CI нет: протокол проверен mock-модулем - (`tests/ayla/mock_ac.py`), облако — мок-сервером; приёмочный скрипт имеет - self-test на моке HA REST. +* Реального железа/облака в CI нет: протокол проверен mock-модулем, + облако — мок-сервером; приёмочный скрипт имеет self-test на моке HA REST. * Суточный soak, re-key и облачный вход — только на приборе/аккаунте. -* ESPHome-канал приёмки требует установленного `aioesphomeapi` и - работающего ESPHome-компонента (PLAN_ESPHOME); без них скрипт работает - только со стороны HA. +* Gitea Actions: встроенный `GITEA_TOKEN` не публикует пакеты (нужен PAT); + `actions/checkout`/`setup-python` тянутся с github.com (для изолированного + инстанса используйте абсолютные URL зеркал). diff --git a/setup.py b/setup.py index ba4f108..29b6964 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ """Сборка wheel pyfglair: вместе с python-пакетом собирается libfgl-aircon.so из корня монорепо (cmake). Метаданные — в pyproject.toml; здесь только хуки setuptools для сборки нативного ядра.""" +import os from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path @@ -32,7 +33,8 @@ class build_py(_build_py): dest.mkdir(parents=True, exist_ok=True) for stale in dest.glob("libfgl-aircon.*"): stale.unlink() - _load_corebuild().build_core(dest=dest, bundled_mbedtls=True) + bundled = os.environ.get("FGL_BUNDLED_MBEDTLS", "1") != "0" + _load_corebuild().build_core(dest=dest, bundled_mbedtls=bundled) super().run()