diff --git a/justfile b/justfile index 64b632e..ef4dbff 100644 --- a/justfile +++ b/justfile @@ -57,3 +57,35 @@ plugin-dev-run path: # Vehicle Simulator (плейсхолдер; домен E, v2) sim: @echo "Vehicle Simulator — v2 (домен E): vcan + ELM327-emu; каркас в sim/." + +# --- Lima-VM (часть 2 Плана 5: нужен limactl — brew install lima) --- + +# поднять dev-VM (создание + провижининг) +vm-up: + limactl start --name=shturman lima/shturman.yaml + +# остановить VM +vm-down: + limactl stop shturman + +# шелл в VM +vm-shell: + limactl shell shturman + +# пересоздать VM «с нуля» (сброс — ничего на хосте не ломаем) +vm-reset: + -limactl stop shturman + -limactl delete shturman + limactl start --name=shturman lima/shturman.yaml + +# собрать + развернуть + поднять target в VM (boot → сервисы → кадр) +run: + limactl shell shturman -- bash -lc 'cd /shturman && bash tests/e2e/run.sh' + +# сквозной E2E в VM (приёмка v0.1/v0.6 + шагающий скелет) +e2e: + limactl shell shturman -- bash -lc 'cd /shturman && bash tests/e2e/run.sh' + +# ручная проверка кадра: на хосте — окно Slint (headless PNG-screenshot — часть 2/Lima) +shell-frame: + cargo run -p shturman-shell diff --git a/lima/shturman.yaml b/lima/shturman.yaml new file mode 100644 index 0000000..3ab9f83 --- /dev/null +++ b/lima/shturman.yaml @@ -0,0 +1,70 @@ +# Lima-шаблон Штурмана: ARM64 Ubuntu, нативно к таргету RK3588 (dev-environment). +# Подъём: just vm-up. Сборка/деплой/E2E — just run / just e2e (внутри VM). Валидируется в части 2 Плана 5. +vmType: vz +cpus: 4 +memory: "6GiB" +disk: "20GiB" + +images: + - location: "https://cloud-images.ubuntu.com/releases/24.04/release/ubuntu-24.04-server-cloudimg-arm64.img" + arch: "aarch64" + +mounts: + # репозиторий (writable). location — под свою раскладку; в гостье — /shturman. + - location: "~/GitHub/shturman" + mountPoint: "/shturman" + writable: true + +provision: + # OS-база (root). Бинари/юниты-старт — в just run/e2e (бинарей ещё нет на этом шаге). + - mode: system + script: | + #!/bin/bash + set -eux + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y \ + dbus pipewire wireplumber weston \ + can-utils python3 python3-venv \ + systemd-zram-generator fake-hwclock \ + fonts-dejavu-core fonts-noto-core \ + build-essential pkg-config curl + + # vcan (для Vehicle Simulator, v2 — поднимаем заранее для воспроизводимости) + modprobe vcan || true + ip link add dev vcan0 type vcan 2>/dev/null || true + ip link set up vcan0 || true + + # loopback /data (ext4, power-safe) + data.mount + mkdir -p /var/lib/shturman + if [ ! -f /var/lib/shturman/data.img ]; then + truncate -s 1G /var/lib/shturman/data.img + mkfs.ext4 -q -L shturman-data /var/lib/shturman/data.img + fi + + # systemd-юниты + конфиги из репозитория + install -m644 /shturman/systemd/shturman.target /etc/systemd/system/ + install -m644 /shturman/systemd/data.mount /etc/systemd/system/ + install -m644 /shturman/systemd/shturman-*.service /etc/systemd/system/ + install -d /etc/dbus-1/system.d + install -m644 /shturman/systemd/dbus/ru.shturman.conf /etc/dbus-1/system.d/ + install -d /etc/systemd/journald.conf.d + install -m644 /shturman/systemd/journald-shturman.conf /etc/systemd/journald.conf.d/shturman.conf + install -m644 /shturman/systemd/zram-generator.conf /etc/systemd/zram-generator.conf + install -d /etc/systemd/oomd.conf.d + install -m644 /shturman/systemd/oomd-shturman.conf /etc/systemd/oomd.conf.d/shturman.conf + + # fake-hwclock → /data (не на rootfs; A07/A11) + echo 'FILE=/data/state/fake-hwclock.data' > /etc/default/fake-hwclock || true + + systemctl daemon-reload + systemctl enable systemd-oomd.service || true + # shturman.target включаем, но НЕ стартуем здесь — бинарей ещё нет (just run/e2e). + systemctl enable shturman.target || true + + # Rust-тулчейн (user) + - mode: user + script: | + #!/bin/bash + set -eux + command -v cargo >/dev/null || curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/systemd/data.mount b/systemd/data.mount new file mode 100644 index 0000000..dbfb6ce --- /dev/null +++ b/systemd/data.mount @@ -0,0 +1,15 @@ +# Журналируемый /data (power-safe). Loopback-образ создаёт provisioning (lima/shturman.yaml). +# Имя юнита (data.mount) обязано соответствовать Where=/data. +[Unit] +Description=Штурман /data (журналируемый, power-safe) +Before=shturman-firstboot.service + +[Mount] +What=/var/lib/shturman/data.img +Where=/data +Type=ext4 +# барьеры — дефолт ядра; errors=remount-ro — реально отображаемая non-default опция (ассертит E2E). +Options=rw,noatime,errors=remount-ro,loop + +[Install] +WantedBy=shturman.target diff --git a/systemd/dbus/ru.shturman.conf b/systemd/dbus/ru.shturman.conf new file mode 100644 index 0000000..644d837 --- /dev/null +++ b/systemd/dbus/ru.shturman.conf @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/systemd/journald-shturman.conf b/systemd/journald-shturman.conf new file mode 100644 index 0000000..2e0aacf --- /dev/null +++ b/systemd/journald-shturman.conf @@ -0,0 +1,7 @@ +# journald drop-in (A10): журнал в RAM (не на flash), rate-limit. Критичное → fsync в /data (каркас common). +# Установка: /etc/systemd/journald.conf.d/shturman.conf +[Journal] +Storage=volatile +RuntimeMaxUse=64M +RateLimitIntervalSec=30s +RateLimitBurst=1000 diff --git a/systemd/oomd-shturman.conf b/systemd/oomd-shturman.conf new file mode 100644 index 0000000..95fbe16 --- /dev/null +++ b/systemd/oomd-shturman.conf @@ -0,0 +1,5 @@ +# systemd-oomd drop-in (A09): защищаем critical set (OOMScoreAdjust в юнитах); первые жертвы — фон/плагины. +# Установка: /etc/systemd/oomd.conf.d/shturman.conf (+ systemctl enable --now systemd-oomd). +[OOM] +SwapUsedLimit=90% +DefaultMemoryPressureLimit=60% diff --git a/systemd/shturman-firstboot.service b/systemd/shturman-firstboot.service new file mode 100644 index 0000000..59fef0c --- /dev/null +++ b/systemd/shturman-firstboot.service @@ -0,0 +1,14 @@ +[Unit] +Description=Штурман first-boot provisioning (/data init + machine-id, A06) +ConditionPathExists=!/data/.shturman-provisioned +Requires=data.mount +After=data.mount +Before=shturman-machineid.service shturman-settings.service shturman-power.service shturman-shell.service + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/local/bin/shturman-firstboot + +[Install] +WantedBy=shturman.target diff --git a/systemd/shturman-machineid.service b/systemd/shturman-machineid.service new file mode 100644 index 0000000..6a0c826 --- /dev/null +++ b/systemd/shturman-machineid.service @@ -0,0 +1,16 @@ +# Every-boot привязка persistent machine-id из /data → /etc/machine-id (a-base §11): +# генерация — one-shot в firstboot; bind волатилен и нужен на КАЖДЫЙ boot, до dbus. +[Unit] +Description=Штурман bind persistent machine-id (/data → /etc/machine-id) +Requires=data.mount +After=data.mount shturman-firstboot.service +Before=dbus.service shturman-settings.service shturman-power.service shturman-shell.service +ConditionPathExists=/data/state/machine-id + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/sh -c 'mount --bind /data/state/machine-id /etc/machine-id' + +[Install] +WantedBy=shturman.target diff --git a/systemd/shturman-power.service b/systemd/shturman-power.service new file mode 100644 index 0000000..b6aab5f --- /dev/null +++ b/systemd/shturman-power.service @@ -0,0 +1,14 @@ +[Unit] +Description=Штурман Power (ru.shturman.Power1) +Requires=data.mount shturman-firstboot.service +After=data.mount shturman-firstboot.service shturman-machineid.service dbus.service +PartOf=shturman.target + +[Service] +ExecStart=/usr/local/bin/shturman-power +Restart=on-failure +RestartSec=2 +OOMScoreAdjust=-600 + +[Install] +WantedBy=shturman.target diff --git a/systemd/shturman-settings.service b/systemd/shturman-settings.service new file mode 100644 index 0000000..088c437 --- /dev/null +++ b/systemd/shturman-settings.service @@ -0,0 +1,16 @@ +[Unit] +Description=Штурман Settings (ru.shturman.Settings1) +# Requires+After firstboot: не стартуем против полу-провиженного /data (Wants недостаточно). +Requires=data.mount shturman-firstboot.service +After=data.mount shturman-firstboot.service shturman-machineid.service dbus.service +PartOf=shturman.target + +[Service] +ExecStart=/usr/local/bin/shturman-settings +Restart=on-failure +RestartSec=2 +# защищаем critical set от OOM (a-base §8) +OOMScoreAdjust=-600 + +[Install] +WantedBy=shturman.target diff --git a/systemd/shturman-shell.service b/systemd/shturman-shell.service new file mode 100644 index 0000000..4c42765 --- /dev/null +++ b/systemd/shturman-shell.service @@ -0,0 +1,17 @@ +[Unit] +Description=Штурман Shell (первый Slint-кадр) +Requires=data.mount shturman-firstboot.service +After=shturman-power.service shturman-settings.service shturman-machineid.service +PartOf=shturman.target + +[Service] +ExecStart=/usr/local/bin/shturman-shell +Restart=on-failure +RestartSec=2 +OOMScoreAdjust=-600 +# Wayland-дисплей: provisioning/E2E поднимает weston headless (финализируется в части 2). +Environment=WAYLAND_DISPLAY=wayland-1 +Environment=XDG_RUNTIME_DIR=/run/user/0 + +[Install] +WantedBy=shturman.target diff --git a/systemd/shturman.target b/systemd/shturman.target new file mode 100644 index 0000000..b7a456e --- /dev/null +++ b/systemd/shturman.target @@ -0,0 +1,7 @@ +[Unit] +Description=Штурман — v0 critical set (Stage 1: ядро + первый кадр) +Requires=data.mount +After=data.mount + +[Install] +WantedBy=multi-user.target diff --git a/systemd/zram-generator.conf b/systemd/zram-generator.conf new file mode 100644 index 0000000..788368c --- /dev/null +++ b/systemd/zram-generator.conf @@ -0,0 +1,4 @@ +# zram (A09): сжатый swap в RAM; swap-на-flash запрещён. Установка: /etc/systemd/zram-generator.conf +[zram0] +zram-size = min(ram / 2, 4096) +compression-algorithm = zstd diff --git a/tests/e2e/run.sh b/tests/e2e/run.sh new file mode 100644 index 0000000..b31a333 --- /dev/null +++ b/tests/e2e/run.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# Сквозной E2E Штурмана в Lima-VM (приёмка v0.1/v0.6 + шагающий скелет, спека §9.3/§9.4). +# Запуск: just e2e (внутри VM через limactl shell). Системная шина устройства. +# Часть 2 Плана 5 — здесь финализируются weston-screenshot и калибровка eMMC-порога. +set -euo pipefail + +REPO=/shturman +cd "$REPO" + +echo "== сборка ==" +cargo build --release --workspace +sudo install -m755 target/release/shturman-firstboot /usr/local/bin/ +sudo install -m755 target/release/shturman-settings /usr/local/bin/ +sudo install -m755 target/release/shturman-power /usr/local/bin/ +sudo install -m755 target/release/shturman-shell /usr/local/bin/ + +echo "== старт target ==" +sudo systemctl daemon-reload +sudo systemctl start shturman.target +sleep 3 + +fail() { echo "E2E FAIL: $*" >&2; exit 1; } + +echo "== 1. /data смонтирован до сервисов, реальные опции ==" +findmnt /data || fail "/data не смонтирован" +findmnt -no OPTIONS /data | grep -q errors=remount-ro || fail "нет errors=remount-ro" + +echo "== 2. first-boot идемпотентен ==" +test -f /data/.shturman-provisioned || fail "нет маркера provisioned" +test -f /data/state/machine-id || fail "нет machine-id" + +echo "== 3. per-unit critical set active (не довольствуемся degraded) ==" +for u in shturman-power shturman-settings shturman-shell; do + systemctl is-active --quiet "$u" || fail "$u не active" +done + +echo "== 4. имена на системной шине ==" +busctl --system list | grep -q ru.shturman.Power || fail "нет ru.shturman.Power" +busctl --system list | grep -q ru.shturman.Settings || fail "нет ru.shturman.Settings" + +echo "== 5. fake-ACC: SetAcc -> AccChanged ==" +# (подписка+вызов dev.PowerMock1; реализация ассерта — busctl monitor/call, финал в части 2) + +echo "== 6. персист настроек через reboot + machine-id стабилен ==" +# (Settings.Set -> sudo reboot -> повторный прогон сверяет; оформляется в части 2) + +echo "== 7. первый кадр (software-render PNG не пустой) ==" +# (weston headless + shturman-shell + screenshot; финал — часть 2) + +echo "== 8. база: journald volatile / zram / eMMC-прокси ==" +journalctl --header 2>/dev/null | grep -qi volatile || echo "WARN: journald не volatile?" +zramctl | grep -q zram0 || echo "WARN: zram0 не активен?" + +echo "E2E OK (каркас; пункты 5–7 финализируются в части 2)"