Martin Bauer 6fcc345a26 Only rebuild the venv when the deployed commit changes
The previous fix made the install convergent but not idempotent: it keyed
"changed" off uv printing "Installed", and uv prints that every single run.
musicmouse is a local path dependency, so uv rebuilds and reinstalls it every
time regardless. Every ansible run therefore restarted the service and made it
rescan the whole library for nothing.

Decide from the device instead: the commit the checkout landed on, recorded in
the venv, plus whether the venv can import the package at all. That keeps the
convergence the handler lacked - an empty venv reinstalls even when the commit
matches - without touching anything on a run where nothing moved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-19 21:13:00 +02:00
2024-07-28 08:45:01 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00
2026-09-19 16:28:00 +02:00
2026-09-10 21:33:19 +02:00

ansible

Personal Ansible setup for provisioning and maintaining a fleet of Raspberry Pis (audio players, sensors, music mouse, etc.) plus one home server.

Layout

  • inventory.yml — hosts and group vars (Pis under iot, plus server). group_vars/ and host_vars/ hold vars for the mediapis group (the four parallel audio-player Pis) and their host-specific overrides.
  • mediapis.yml, working.yml, server.yml, newrpi-provisioning.yml, octopisetup.yml — top-level playbooks. mediapis.yml is the canonical playbook for the mediapis group (musicdolphin, kitchenpi, bedroompi, musicmouse); the others are narrower/ad-hoc runs kept around for specific hosts or one-off tasks.
  • roles/ — one role per piece of functionality (audio backends, sensors, bluetooth monitoring, server basics, etc.). Each has a short README.md.
  • update-packages.yml — deliberate, fleet-wide package update (see "Keeping packages up to date" below). update-packages-pinned-example.yml is a template for pinning or bumping a single package outside that.
  • lookup_plugins/keepass.py — custom lookup plugin that fetches secrets (device passwords, wifi passphrase) from a running KeePassXC instance via its browser-integration protocol, instead of storing them in the repo.
  • pis/ — loose config files/scripts used when provisioning Pis by hand.
  • scripts/ — standalone helper scripts (Raspbian image creation, a network logger) that aren't Ansible roles.
  • archive/ — retired setups kept for reference (not actively maintained).

Setup

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

Secrets are pulled from KeePassXC at run time via the keepass lookup plugin — see the header of lookup_plugins/keepass.py for how to enable Browser Integration in KeePassXC. Freshly-flashed Raspberry Pis are reached first with the OS-default pi/raspberry credentials (see roles/pi_standard_setup), which the role then rotates to a KeePassXC-managed password.

Running a playbook

ansible-playbook mediapis.yml --limit <host>

ansible.cfg points Ansible at inventory.yml and roles/ by default, so no extra flags are needed for those.

Keeping packages up to date

Regular playbook runs (mediapis.yml, server.yml, etc.) use state: present for packages, so they only install what's missing — they never upgrade anything as a side effect of an unrelated config change. Two separate, deliberate mechanisms handle upgrades instead:

Security patches — automatic. The unattended_upgrades role (applied to every host in mediapis.yml/server.yml) configures unattended-upgrades to install security-origin updates automatically, with a scheduled reboot window (default 03:00, see roles/unattended_upgrades/defaults/main.yml) for patches that need one. Not scoped to full dist-upgrades.

Everything else — deliberate, ad hoc.

just update <host_or_group>          # e.g. `just update kitchenpi` or `just update`
venv/bin/ansible-playbook update-packages.yml --limit <host> --check --diff   # dry run first

Defaults to upgrade: safe (upgrades in place, never installs/removes packages to resolve dependencies — see the comment header in update-packages.yml for the tradeoff against dist). There is no cron/ schedule wired up for this yet; run it ad hoc when you want the fleet updated, or add a crontab entry yourself (a starting point is documented in update-packages.yml's header).

To pin a package to an exact version, or deliberately bump one named package to latest outside this schedule, copy the pattern in update-packages-pinned-example.yml.

Description
No description provided
Readme 85 MiB
Languages
Jupyter Notebook 79.8%
Python 13.3%
Jinja 3.2%
C++ 1.7%
Shell 1.3%
Other 0.6%