diff --git a/Makefile b/Makefile index 334dcc3..c362996 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,19 @@ -.PHONY: build build-release frontend backend android run migrate reconcile reconcile-delete tidy test testdata clean +.PHONY: help build build-release frontend backend android install-android-app run migrate reconcile reconcile-delete tidy test testdata clean + +# Default goal: print the list of available targets. +.DEFAULT_GOAL := help # Android SDK location. Override when the SDK lives elsewhere. ANDROID_SDK ?= $(HOME)/Android/sdk +# adb from the Android SDK's platform-tools (matches the SDK we build against). +ADB ?= $(ANDROID_SDK)/platform-tools/adb + +# Device serial for `install-android-app` (falls back to $ANDROID_SERIAL). When +# empty, the sole connected device is used; with several connected devices the +# target lists them and stops instead of guessing. Override with SERIAL=. +SERIAL ?= $(ANDROID_SERIAL) + # Release version stamped into the binary (-X main.version). Override with VERSION=... VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev) @@ -14,6 +25,13 @@ PHOTOS ?= 10 SEED ?= 0 TIMEOUT ?= 30 +# Print the list of available targets. +help: + @echo "Usage: make " + @echo + @echo "Available targets:" + @grep -hE '^[a-zA-Z0-9_-]+:' $(MAKEFILE_LIST) | cut -d: -f1 | sed 's/^/ /' + frontend: cd frontend && npm ci && npm run build rm -rf backend/static/* @@ -32,6 +50,28 @@ build: frontend backend android: cd android && ANDROID_HOME=$(ANDROID_SDK) ./gradlew :app:assembleDebug +# Install the debug Android client on a connected device. Builds the APK via the +# `android` target first; only devices in the `device` state are considered. +install-android-app: android + @devices="$$($(ADB) devices | awk 'NR>1 && $$2 == "device" {print $$1}')"; \ + if [ -n "$(SERIAL)" ]; then \ + target="$(SERIAL)"; \ + else \ + count=$$(printf '%s\n' "$$devices" | grep -c .); \ + if [ "$$count" -eq 0 ]; then \ + echo "install-android-app: no connected device (see '$(ADB) devices')" >&2; \ + exit 1; \ + elif [ "$$count" -gt 1 ]; then \ + echo "install-android-app: several devices connected; set SERIAL=:" >&2; \ + printf '%s\n' "$$devices" | sed 's/^/ /' >&2; \ + exit 1; \ + else \ + target="$$devices"; \ + fi; \ + fi; \ + echo "Installing app-debug.apk on $$target"; \ + $(ADB) -s "$$target" install -r android/app/build/outputs/apk/debug/app-debug.apk + # Release build: full static, stripped and (if available) UPX-compressed binary # with the version embedded. Produces bin/galo and a copy at bin/galo-$(VERSION). build-release: frontend diff --git a/android/README.md b/android/README.md index 234c7e7..085970b 100644 --- a/android/README.md +++ b/android/README.md @@ -29,8 +29,21 @@ From the repository root: ```sh make android # runs :app:assembleDebug (ANDROID_SDK=... to override) +make install-android-app # build and install the debug APK on a device ``` +`install-android-app` builds the debug APK and installs it onto the sole connected +device. To choose a device explicitly, pass its serial number: + +```sh +make install-android-app SERIAL=emulator-5554 +``` + +`SERIAL` also falls back to `$ANDROID_SERIAL`. When several devices are connected and +`SERIAL` is not set, the target prints the list of devices (only those in the `device` +state) and stops without installing. `ADB=...` overrides the `adb` binary (defaults to +`$(ANDROID_SDK)/platform-tools/adb`). + Debug output: `android/app/build/outputs/apk/debug/app-debug.apk`. ## Signing (release) diff --git a/openspec/changes/add-make-install-android/.openspec.yaml b/openspec/changes/add-make-install-android/.openspec.yaml new file mode 100644 index 0000000..5fcc9ec --- /dev/null +++ b/openspec/changes/add-make-install-android/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-13 diff --git a/openspec/changes/add-make-install-android/proposal.md b/openspec/changes/add-make-install-android/proposal.md new file mode 100644 index 0000000..3b2cb9f --- /dev/null +++ b/openspec/changes/add-make-install-android/proposal.md @@ -0,0 +1,41 @@ +# Proposal + +## Why + +Разработчику нужно ставить Android-клиент на подключённое устройство, но сегодня в +`Makefile` есть только `make android`, который собирает debug-APK и никуда его не +устанавливает. При этом голый `make` по умолчанию запускает цель `frontend` +(`cd frontend && npm ci && npm run build`), что неочевидно и тянет `npm ci`. + +## What Changes + +- Новая Make-цель `install-android-app`: собирает debug-APK клиента и устанавливает его + на устройство. +- Выбор устройства: явный серийный номер (`SERIAL`/`ANDROID_SERIAL`) либо автоматически, + если подключено ровно одно устройство. +- Если устройств несколько и серийный номер не задан — цель печатает список устройств и + **не** выполняет установку, завершаясь ненулевым кодом. +- **BREAKING** (изменение поведения по умолчанию): голый `make` теперь печатает список + доступных целей и больше не собирает фронтенд; сборка фронтенда остаётся за целями + `frontend` и `build`. +- Обновление `android/README.md`: документировать установку на устройство. + +## Capabilities + +### New Capabilities + +- `dev-make-workflow`: Make-интерфейс разработчика — установка Android-клиента на + выбранное устройство и обзор доступных Make-целей. + +### Modified Capabilities + + + +## Impact + +- `Makefile`: новая цель `install-android-app`, новая цель `help`, смена `.DEFAULT_GOAL` + с `frontend` на `help`. +- `android/README.md`: раздел «Build» дополняется установкой на устройство. +- Код сервера и Android-приложения не затрагивается. +- Новых внешних зависимостей нет: используется уже требуемый Android SDK и `adb` из + `platform-tools`. diff --git a/openspec/changes/add-make-install-android/specs/dev-make-workflow/spec.md b/openspec/changes/add-make-install-android/specs/dev-make-workflow/spec.md new file mode 100644 index 0000000..90a635c --- /dev/null +++ b/openspec/changes/add-make-install-android/specs/dev-make-workflow/spec.md @@ -0,0 +1,55 @@ +# Spec Delta + +## Purpose + +Make-интерфейс разработчика: установка Android-клиента на выбранное устройство и обзор +доступных целей сборки. + +## ADDED Requirements + +### Requirement: Установка Android-приложения через Make-цель +Проект SHALL предоставлять Make-цель `install-android-app`, которая собирает debug-APK +Android-клиента и устанавливает его на выбранное устройство. Повторный запуск SHALL +переустанавливать приложение без ручного удаления. + +#### Scenario: Установка на устройство +- **WHEN** цель запущена и устройство выбрано +- **THEN** собирается debug-APK и устанавливается на это устройство + +#### Scenario: Повторная установка +- **WHEN** приложение уже установлено и цель запущена снова +- **THEN** установка завершается успешно без ручного удаления приложения + +### Requirement: Выбор устройства для установки +Цель установки SHALL брать устройство из явно заданного серийного номера, а при его +отсутствии — из единственного подключённого устройства. Если подключено несколько +устройств и серийный номер не задан, цель SHALL напечатать список устройств и SHALL не +выполнять установку. + +#### Scenario: Серийный номер задан +- **WHEN** задан серийный номер устройства +- **THEN** приложение устанавливается именно на это устройство + +#### Scenario: Единственное устройство +- **WHEN** серийный номер не задан и подключено ровно одно устройство +- **THEN** приложение устанавливается на него + +#### Scenario: Несколько устройств без выбора +- **WHEN** серийный номер не задан и подключено несколько устройств +- **THEN** цель печатает список устройств, не выполняет установку и завершается ненулевым кодом + +#### Scenario: Нет устройств +- **WHEN** не подключено ни одного устройства +- **THEN** цель сообщает об ошибке и завершается ненулевым кодом + +### Requirement: Обзор доступных Make-целей +Голый `make` (без цели) SHALL печатать список доступных целей и SHALL не запускать +сборку. Любая цель, включая сборку фронтенда, SHALL оставаться доступной по явному имени. + +#### Scenario: Голый make +- **WHEN** `make` запущен без цели +- **THEN** печатается список доступных целей, сборка не запускается + +#### Scenario: Явная цель +- **WHEN** `make` запущен с именем цели, например `make frontend` +- **THEN** выполняется именно эта цель diff --git a/openspec/changes/add-make-install-android/tasks.md b/openspec/changes/add-make-install-android/tasks.md new file mode 100644 index 0000000..8f09197 --- /dev/null +++ b/openspec/changes/add-make-install-android/tasks.md @@ -0,0 +1,20 @@ +# Tasks + +## 1. Список Make-целей по умолчанию + +- [x] 1.1 Добавить цель `help`, печатающую список доступных целей, и включить `help` в строку `.PHONY`; проверка — `make help` выводит все цели, перечисленные в `.PHONY`. +- [x] 1.2 Переключить `.DEFAULT_GOAL` с `frontend` на `help`; проверка — голый `make` печатает список целей и не выполняет ни одной рецептуры (в частности, не запускает `npm ci`). +- [x] 1.3 Подтвердить, что явные цели не изменились; проверка — `make -n frontend`, `make -n backend`, `make -n testdata` печатают те же команды, что и до правки. + +## 2. Установка Android-приложения + +- [x] 2.1 Задать переменную выбора устройства `SERIAL` и источник `adb` из `$(ANDROID_SDK)/platform-tools/adb`; проверка — `make -n install-android-app` подставляет путь к SDK-`adb`. +- [x] 2.2 Добавить цель `install-android-app`, которая собирает debug-APK (переиспользуя цель `android`, без дублирования gradle-вызова) и ставит его через `adb install -r` на `android/app/build/outputs/apk/debug/app-debug.apk`; проверка — при заданном `SERIAL` приложение устанавливается на указанное устройство. +- [x] 2.3 Реализовать разбор устройства до установки: задан `SERIAL` — ставим на него; ровно одно подключённое — ставим на него; несколько без `SERIAL` — печатаем список устройств, установку не выполняем и выходим с ненулевым кодом; ноль устройств — сообщаем об ошибке и выходим с ненулевым кодом; проверка — каждый сценарий воспроизводится и его исход совпадает со спекой. +- [x] 2.4 Учитывать только устройства в состоянии `device`; проверка — устройство `offline`/`unauthorized` не попадает в список выбора и не выбирается автоматически. +- [x] 2.5 Обновить `android/README.md`: документировать `make install-android-app`, переменную `SERIAL` и поведение при нескольких устройствах; проверка — документированные команды дословно совпадают с `Makefile`. + +## Workflow follow-up + +- Прогнать `openspec validate add-make-install-android --strict` после реализации. +- Архивация change после проверки.