128 строки
4.0 KiB
Python
128 строки
4.0 KiB
Python
import glob
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from backend.shell_runner import run_command
|
|
|
|
DATA_DIR = Path(os.getenv('DEVCONSOLE_DATA_DIR', '/var/lib/devconsole'))
|
|
PROJECTS_DIR = DATA_DIR / 'projects'
|
|
|
|
|
|
def list_devices() -> dict:
|
|
result = run_command('adb devices -l', PROJECTS_DIR.as_posix(), timeout=60)
|
|
result['devices'] = parse_devices(result.get('stdout', ''))
|
|
return result
|
|
|
|
|
|
def parse_devices(stdout: str) -> list[dict]:
|
|
devices: list[dict] = []
|
|
|
|
for line in stdout.splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith('List of devices'):
|
|
continue
|
|
|
|
parts = line.split()
|
|
if len(parts) < 2:
|
|
continue
|
|
|
|
serial = parts[0]
|
|
state = parts[1]
|
|
|
|
if state != 'device':
|
|
devices.append({
|
|
'serial': serial,
|
|
'state': state,
|
|
'title': f'{serial} ({state})',
|
|
'subtitle': 'Устройство не готово',
|
|
})
|
|
continue
|
|
|
|
props = get_device_props(serial)
|
|
brand = props.get('brand') or ''
|
|
model = props.get('model') or ''
|
|
device = props.get('device') or ''
|
|
android = props.get('android') or ''
|
|
sdk = props.get('sdk') or ''
|
|
|
|
title = ' '.join(part for part in [brand, model] if part).strip() or serial
|
|
subtitle_parts = []
|
|
if android:
|
|
subtitle_parts.append(f'Android {android}')
|
|
if sdk:
|
|
subtitle_parts.append(f'SDK {sdk}')
|
|
if device:
|
|
subtitle_parts.append(device)
|
|
subtitle_parts.append(serial)
|
|
|
|
devices.append({
|
|
'serial': serial,
|
|
'state': state,
|
|
'brand': brand,
|
|
'model': model,
|
|
'device': device,
|
|
'android': android,
|
|
'sdk': sdk,
|
|
'title': title,
|
|
'subtitle': ' · '.join(subtitle_parts),
|
|
})
|
|
|
|
return devices
|
|
|
|
|
|
def get_device_props(serial: str) -> dict:
|
|
commands = {
|
|
'brand': f'adb -s {serial} shell getprop ro.product.brand',
|
|
'model': f'adb -s {serial} shell getprop ro.product.model',
|
|
'device': f'adb -s {serial} shell getprop ro.product.device',
|
|
'android': f'adb -s {serial} shell getprop ro.build.version.release',
|
|
'sdk': f'adb -s {serial} shell getprop ro.build.version.sdk',
|
|
}
|
|
|
|
props: dict[str, str] = {}
|
|
|
|
for key, command in commands.items():
|
|
try:
|
|
result = run_command(command, PROJECTS_DIR.as_posix(), timeout=20)
|
|
props[key] = (result.get('stdout') or '').strip()
|
|
except Exception:
|
|
props[key] = ''
|
|
|
|
return props
|
|
|
|
|
|
def find_apks(workspace: str) -> list[str]:
|
|
root = Path(workspace).resolve()
|
|
patterns = [
|
|
str(root / '**' / 'build' / 'app' / 'outputs' / 'flutter-apk' / '*.apk'),
|
|
str(root / '**' / 'build' / 'outputs' / 'apk' / '**' / '*.apk'),
|
|
str(root / '**' / '*.apk'),
|
|
]
|
|
found: list[str] = []
|
|
for pattern in patterns:
|
|
found.extend(glob.glob(pattern, recursive=True))
|
|
return sorted(set(found), key=lambda p: os.path.getmtime(p), reverse=True)
|
|
|
|
|
|
def build_android(workspace: str) -> dict:
|
|
root = Path(workspace).resolve()
|
|
|
|
if (root / 'pubspec.yaml').exists():
|
|
return run_command('flutter build apk --debug', root.as_posix(), timeout=1800)
|
|
|
|
gradlew = root / 'gradlew'
|
|
if gradlew.exists():
|
|
return run_command('chmod +x ./gradlew && ./gradlew assembleDebug', root.as_posix(), timeout=1800)
|
|
|
|
if (root / 'android' / 'gradlew').exists():
|
|
return run_command('cd android && chmod +x ./gradlew && ./gradlew assembleDebug', root.as_posix(), timeout=1800)
|
|
|
|
return run_command('gradle assembleDebug', root.as_posix(), timeout=1800)
|
|
|
|
|
|
def install_latest_apk(workspace: str) -> dict:
|
|
apks = find_apks(workspace)
|
|
if not apks:
|
|
raise ValueError('APK not found. Build project first.')
|
|
return run_command(f'adb install -r "{apks[0]}"', Path(workspace).resolve().as_posix(), timeout=600)
|