Pin each MusicMouse host to a deploy branch, and freeze musicmouse

musicmouse_version pointed both hosts at rearchitect-backend, a branch that only
ever existed on a laptop - so the previous commit's rework could never actually
have run. Point each host at a deploy/<host> branch on the remote instead. Those
branches are the deployment record: what is installed on that box right now,
moved with a force-push when you mean to deploy.

musicmouse (Kinderzimmer) is the reason this matters. It is in daily use and
still runs the pre-rearchitecture backend out of /opt/musicmouse_venv and
espmusicmouse/host_driver/main.py. That commit has no python-backend/
pyproject.toml, no python-backend/musicmouse.service and no web/ at all, so this
role cannot install it - and a run would have force-checked-out incompatible
code over a working device first and failed afterwards. Set
mediapi_install_kidsmusic: false there and leave it alone;
deploy/musicmouse records the commit that is installed, for rollback. Add a
stat + fail right after the checkout so the same mistake stops with an
explanation instead of failing three tasks deeper.

Also in the role:

- Install the typing game's curriculum next to config.yml. A config with a
  tippen: section refuses to start without it - curriculum_file is validated as
  must-exist - so musicdolphin's new config needs it present.

- Stop clobbering config.yml. The app writes to it at runtime: parent mode
  patches the volume keys and the remote-control page rewrites the remote:
  block. A plain copy: undid that on every run. Install-once by default, with
  -e pi_musicmouse_force_config=true to push a change deliberately.

- Fill out config-musicdolphin.yml against the current schema: library root and
  cache, an absolute static_dir (../web/dist would resolve to /media/web/dist
  from /media/musicmouse and silently serve nothing), tippen, and the volume
  range for a screen-driven instance. ha: is left commented out until the
  Wohnzimmer-oben entity ids are picked - the ones in the dev config are
  Arbeitszimmer. No mqtt: (it would publish a device of dead entities on a
  simulate-only host) and no lirc:/remote: (this host runs irserver, a serial
  daemon, not the lircd this speaks TCP to).

The curriculum is a verbatim copy of app content, so exclude it from
ansible-lint rather than reformat it away from its source.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 19:04:57 +02:00
parent 9b1d01115b
commit 5763df90f8
9 changed files with 726 additions and 20 deletions

View File

@@ -2,8 +2,9 @@
Deploys the MusicMouse backend (`python-backend/`) and its built web frontend.
- Checks out `musicmouse_version` (a tag or branch - set per host in `host_vars`, no
default) from `musicmouse_repo` to `/opt/musicmouse`.
- Checks out `musicmouse_version` from `pi_musicmouse_repo` to `/opt/musicmouse`, then
**fails early** if that checkout has no `python-backend/pyproject.toml` - see
"Which version" below.
- Installs a pinned `uv` release and uses it to create `/opt/musicmouse/.venv` with
Python `pi_musicmouse_python_version` (the backend needs 3.12+ for PEP 695 syntax;
Raspbian Bookworm's apt python3 is only 3.11) and `pip install` the backend into it.
@@ -11,10 +12,55 @@ Deploys the MusicMouse backend (`python-backend/`) and its built web frontend.
the Pi - no Node/npm is installed on the Pi) and syncs `web/dist` over.
- Installs `/media/musicmouse/config.yml` from `files/config-<inventory_hostname>.yml`
(one file per host - the schema differs between a host with a real mouse attached and
a display-only one; see those files' own comments) and the systemd unit copied
straight from the checked-out repo's `python-backend/musicmouse.service`, so it can
never drift from what's actually in the app repo.
a display-only one; see those files' own comments), the typing game's curriculum
beside it, and the systemd unit copied straight from the checked-out repo's
`python-backend/musicmouse.service`, so it can never drift from what's actually in the
app repo.
- Sets up a Samba share of `/media/musicmouse` for copying music onto the device.
Manual steps after a first deploy: set the Samba password with `smbpasswd`, and copy
music into `/media/musicmouse` (or via the Samba share).
## Which version
`musicmouse_version` is set per host in `host_vars`, with no default, and points at a
`deploy/<host>` branch on the remote. Those branches are the deployment record: what is
installed on that box right now. Moving one is a force-push, and the next run follows it
(the `git:` task uses `force: true`):
git push -f origin rearchitect-backend:deploy/musicdolphin
just mediapis musicdolphin
This role only understands the post-rearchitecture layout - `python-backend/` as an
installable package plus a `web/` frontend. On older commits the host driver lives at
`espmusicmouse/host_driver/main.py`, there is no `pyproject.toml`, the unit file is
under `esp-firmware/`, there is no frontend at all, and the config is a different format
that this role's config files would fail to load. There is deliberately no legacy path:
the role stops at the checkout with an explanation instead.
`musicmouse` (Kinderzimmer) is on such an older commit and still working, so it has
`mediapi_install_kidsmusic: false` and this role skips it entirely; `deploy/musicmouse`
exists purely as the rollback marker for what is installed there. Migrating it means
moving `/media/musicmouse/<figure>/` into `Figuren/` and switching to the new config
schema - `files/config-musicmouse.yml` is already written for that.
## config.yml is install-once
The app writes to its own config at runtime: parent mode (`?parentMode=1`) patches the
volume keys, and the remote-control page rewrites the whole `remote:` block. A plain
`copy:` would throw those away on every ansible run, so the config task is
`force: "{{ pi_musicmouse_force_config }}"`, default `false`. Push a changed config when
you actually mean to:
just run mediapis.yml musicdolphin -e pi_musicmouse_force_config=true
The curriculum file is not written by the app, so it is installed unconditionally. A
config with a `tippen:` section will not start without it - `curriculum_file` is
validated as must-exist - so a host whose config has no `tippen:` section should set
`pi_musicmouse_curriculum_file: ""`.
## Manual steps after a first deploy
- Set the Samba password with `smbpasswd`.
- Get the music onto the device. The app repo's `scripts/sync-library.sh` is the way to
do it: as well as the library it carries over the cover and track-analysis cache,
which is the part that costs minutes of DSP per track to recompute, and it knows which
parts of that cache survive the move and which must not be copied. The Samba share
works too, and then the device does all that work itself.