mirror of
https://github.com/Screenly/Anthias.git
synced 2026-06-10 09:08:09 -04:00
fix/codec-rejection-not-sentry-error
23 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
68f1bc0981 |
chore(deps): bump the github-actions group with 3 updates (#2994)
Bumps the github-actions group with 3 updates: [actions/checkout](https://github.com/actions/checkout), [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) and [github/codeql-action](https://github.com/github/codeql-action). Updates `actions/checkout` from 6.0.2 to 6.0.3 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits]( |
||
|
|
861a29d38d |
refactor(ci): release flow per #2769 (master = testing, releases = stable) (#2854)
* refactor(ci): release flow per #2769 (master = testing, releases = stable) Master push now publishes container images only. Balena cloud deploy and disk-image build move to a release-triggered workflow so existing fleet devices update on cut releases instead of every merge to master. rpi-imager.json is generated once per release and shipped as a release asset; the website fetches it at build time instead of regenerating from the GitHub API on every deploy. - docker-build.yaml: drop the balena: job - build-balena-disk-image.yaml: trigger on release.published, add balena-cloud-deploy job (replaces deprecated deploy-to-balena-action), bump balena-cli 22.4.15 -> 25.1.3, install via bun, two-phase release upload so build_pi_imager_json sees per-board snippets - deploy-website.yaml: drop rpi-imager.json regeneration + test job; fetch it from the latest release instead - build_pi_imager_json.py: honour RELEASE_TAG env to bypass /releases/latest (which excludes prereleases by design) Also strips third-party action dependencies from new code (manual docker login, bun install, balena-cli install). Refs #2769 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot review on PR #2854 - deploy-website: download rpi-imager.json by tag on release-triggered runs (previously: always default-latest, which can skip prereleases and may not match the just-published release) - deploy-website: drop the now-stale prerelease comment - build-balena-disk-image: pin Bun via BUN_VERSION env so disk-image builds and balena deploys are reproducible - generate-openapi-schema: accept an optional `ref` input via workflow_call and check that out, so the schema attached to a release matches the release commit (not the default branch) - python-lint: run rpi-imager generator tests so the package keeps a PR-time CI gate after the deploy-website test job was removed - build_pi_imager_json: reword RELEASE_TAG-override comment Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot round-2 review on PR #2854 - build-balena-disk-image: capture BUILD_DATE once at the top of the packaging step so a midnight-spanning run can't reference different filenames produced earlier - build-balena-disk-image: workflow_dispatch now fails loudly when the input tag has no existing GitHub release, matching the input contract; release event always satisfies it on its own trigger - bun install: extract to .github/workflows/scripts/install-bun.sh, which downloads the pinned release archive + SHASUMS256.txt and verifies SHA-256 instead of piping a remote shell script to bash - deploy-website: re-introduce the strong jq -e validations on rpi-imager.json (os_list array, required fields, numeric sizes, https URLs, no pi1) so a malformed release asset fails fast - resolve-context: drop the unused `commit` output Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot round-3 review on PR #2854 - install-bun.sh: append \$HOME/.bun/bin to GITHUB_PATH so globally- installed CLIs (e.g. balena-cli via \`bun install -g\`) resolve in subsequent steps. Without this, the disk-image workflow's balena invocations would fail with command-not-found. - deploy-website: distinguish "release exists but lacks rpi-imager.json" (transition fallback) from transient errors (auth/rate-limit/network). Probe via gh release view --json assets before download; only fall back when the asset is genuinely missing. Other gh failures now propagate instead of silently shipping an empty os_list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot round-4 review + tighten path triggers - build-balena-disk-image: pin git rev-parse to --short=7 so the resolved short hash always matches the 7-char tag format that docker-build.yaml writes (a longer abbreviation would silently reference image tags that never exist) - deploy-website: drop the `release: published` trigger. The disk- image workflow now ends with `gh workflow run deploy-website.yaml` after rpi-imager.json has been uploaded to the release, so the deploy is guaranteed to see the asset and won't ship an empty os_list during the upload-step window - deploy-website: add `.github/workflows/scripts/install-bun.sh` to the path triggers so changes to the bun installer also redeploy the site (it's a runtime dep) - docker-build / generate-openapi-schema: exclude `tools/raspberry_pi_imager/**` and the bun installer script from triggers — neither workflow uses those files Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): name release artefacts \`anthias-<board>\` so the imager regex matches build_pi_imager_json.get_board_from_url's regex \`-(pi\d(?:-\d+)?)\.img\.zst\$\` only matches a hyphen before \`piN\`. The disk-image workflow had been writing artefacts as \`raspberrypi3.img.zst\` / \`raspberrypi4-64.img.zst\` (no hyphen between \`raspberry\` and \`pi\`), so all boards except pi2 silently failed to be picked up by the consolidation step — likely the root of the broken rpi-imager.json the user flagged. Renames the per-board release artefacts to \`<date>-anthias-<board>.img.zst\` (and matching \`.sha256\` / \`.json\`) so the existing regex picks them up. Tests already covered the \`anthias-piN\` shape, so they pass without changes. Updates the upload-artifact + attestation glob patterns accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot round-6 review on PR #2854 - Move expression substitutions in resolve-context to env vars and switch the dispatch-tag read from `inputs.tag` to `github.event.inputs.tag`, so the `inputs` context is only consulted on workflow_dispatch where it's actually populated. - Add `actions: write` permission to build-rpi-imager-json so its `gh workflow run deploy-website.yaml` fan-out has the Actions API scope it needs to dispatch the website deploy. - Split the openapi-schema checkout ref resolution into a dedicated step that uses env vars + `if -n` rather than the inline `${{ inputs.ref || github.ref }}` expression, so the inputs lookup is co-located with its fallback in one readable shell block. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ci): fix stale install-bun.sh header comment The header described the runners as linux/amd64-only and asked maintainers to extend the platform detection if that changed, but the arch case below already covers both x86_64 and aarch64 Linux. Reword the comment so it matches the script's actual behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): drop hard-coded --repo from deploy-website gh calls `gh release view/download` default to the runtime repository when `--repo` is omitted, so explicitly pinning Screenly/Anthias was making the workflow needlessly less portable to forks (or a future repo rename) without buying anything. Match the rest of the workflow, which already relies on the runtime repo context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(ci): address Copilot round-9 review on PR #2854 - Gate build-balena-disk-image.yaml's release trigger to Anthias-core tags (`v<version>`). build-webview.yaml publishes its own `WebView-v<version>` GitHub releases on tag pushes; without this guard, every webview release would have spuriously fanned out to balena OTA deploys + disk-image builds. Filter is on resolve-context so the entire downstream pipeline cascades-skips via `needs:`. - Cache sha256 + size of each multi-GB image once and reuse for both the .sha256 sidecar and the per-board JSON snippet, instead of re-hashing the same files inside jq's --arg expansions. Roughly halves the wall-clock of the package step. - Add `tools/raspberry_pi_imager` to .dockerignore. The directory is build-time-only (CI generator for rpi-imager.json) but Dockerfile.{server,viewer}.j2 do `COPY . /usr/src/app/`, so without this entry it baked into runtime images. With docker-build.yaml's matching path-trigger exclusion in place, this keeps the two filters semantically honest: a tools-only commit truly cannot change image content, so skipping the container rebuild is correct rather than a footgun. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): write the .sha256 sidecar against user-facing filenames The uncompressed-image line previously referenced \`\$BALENA_IMAGE.img\` (e.g. \`raspberrypi5.img\`), the CI-local intermediate name. That file never ships in the release asset, so \`sha256sum -c\` against the downloaded sidecar fails to find it. Switch to \`\$ARTIFACT.img\` — the filename a user gets after \`zstd -d <ARTIFACT>.img.zst\` — so both lines match files they actually have on disk. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): call .venv/bin/pytest directly in python-lint job \`uv run --group website pytest …\` implicitly syncs the project venv with the default group set, which pulls in the \`dev\` group (pytest-django==4.12.0). pytest-django then auto-activates as a plugin, reads \`DJANGO_SETTINGS_MODULE\` from pyproject.toml, and fails to bootstrap Django because the curated dev-host + website install doesn't ship pytz / channels / the other transitive bits the settings module imports. Invoke the venv binary directly so the minimal hand-curated env above is what the rpi-imager unit tests actually run against. The tests don't need Django at all — this keeps the gate fast and the dependency surface honest. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ci): pass -p no:django to the rpi-imager pytest invocation The previous attempt — calling \`.venv/bin/pytest\` directly instead of \`uv run\` — assumed the dependency-installation step bounded the venv contents. It doesn't: the earlier \`uv run ruff check\` step implicitly syncs the project venv with the default \`dev\` group, which ships pytest-django==4.12.0 + playwright + etc. By the time the rpi-imager step runs, pytest-django is sitting in .venv as an auto-loading pytest plugin, reads \`DJANGO_SETTINGS_MODULE\` from pyproject.toml, and crashes trying to bootstrap Django (pytz, channels, etc. are missing in this minimal env). The rpi-imager unit tests don't need Django at all, so disable the plugin with \`-p no:django\`. Verified locally: 22/22 pass with pytest-django installed in the venv as long as the plugin is disabled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(x86): support balenaOS x86 fleets via Wayland (#2857) * feat(x86): support balenaOS x86 fleets via Wayland (#2075) Brings x86 to feature parity with Pi for balenaOS deployments. balenaOS x86 doesn't expose /dev/fb0, so Qt's linuxfb plugin (used on Pi) has nothing to draw to and there's no host display server. Run Qt under Wayland via `cage`, a kiosk wlroots compositor that talks directly to KMS — no X server, no DISPLAY juggling, single-app by design. - bin/deploy_to_balena.sh accepts -b x86 and strips /dev/vchiq from the rendered compose (same conditional that already covers pi5). - docker/Dockerfile.viewer.j2 sets QT_QPA_PLATFORM=wayland on x86; every other board keeps linuxfb. - tools/image_builder/utils.py adds cage + qt6-wayland to the x86 viewer apt list. - bin/start_viewer.sh wraps the viewer launch in `cage --` on x86; WAYLAND_DISPLAY is added to sudo's --preserve-env so it survives the env scrub when dropping to the viewer user. - .github/workflows/build-balena-disk-image.yaml extends the release-driven preflight, balena-cloud-deploy, and balena-build-images jobs to include x86 (fleet anthias-x86, balena device type genericx86-64-ext). build-rpi-imager-json is unchanged: the .img.zst regex is Pi-only, so x86 ships on the release without polluting the Raspberry Pi Imager JSON. Supersedes the stale draft PR #2409. The orphaned changes there (home.tsx deviceModel fetch with no consumer, viewer/media_player.py x86 audio table, silent removal of sha256sum -c on the webview tarball) are intentionally not carried forward. Closes #2075 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(x86): note x86 wayland exception in viewer apt comment Address Copilot review on PR #2857. The earlier comment in get_viewer_context claimed "nothing wayland-related here" — that's no longer true once x86 pulls in cage + qt6-wayland a few lines down. Rewrite to call out x86 as the one board that breaks the rule so future cleanup doesn't try to drop the wayland deps thinking they were a mistake. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
d9ebc8051c |
chore(build): upgrade to Debian Trixie + Python 3.13, drop Balena base images (#2779)
* chore(build): upgrade to Debian Trixie + Python 3.13, drop Balena base images
Move every container off `balenalib/raspberrypi*-debian:bookworm` (Balena
hasn't published a `trixie` tag on any of those repos and last refreshed
in May 2025) onto vanilla `debian:trixie`. Pi 1 and 32-bit Pi 4 are
retired at the same time — Pi 1 has no `linux/arm/v6` variant in upstream
Debian, and Pi 4 always has a 64-bit path that avoids the messy
`libssl1.1` / `libgst-dev` / `libsqlite0-dev` Qt 5 deps. Surviving build
matrix: pi2, pi3, pi4-64, pi5, x86.
For the surviving 32-bit boards (pi2, pi3) the legacy Broadcom userland
(libraspberrypi0 → /opt/vc/lib/{libbcm_host,libmmal,libvchiq_arm}) is
still required at runtime by the Qt 5 webview. Trixie's
archive.raspberrypi.org/debian/main no longer ships those packages
(replaced by raspi-utils + libdtovl0, which actively break
libraspberrypi0), so Dockerfile.base.j2 conditionally writes Deb822
.sources entries pointing at archive.raspberrypi.org/debian trixie main
and archive.raspbian.org/raspbian trixie firmware (where the legacy
Raspbian builds of libraspberrypi0 still live, armhf only). The
.deb-form raspberrypi-archive-keyring + raspbian-archive-keyring packages
are extracted with `dpkg-deb -x` (their bundled keys carry trixie-policy-
compliant binding signatures, unlike the standalone .public.key files
which fail Sequoia/sqv's post-2026-02-01 SHA-1 ban). Architectures: armhf
on each .sources file keeps apt from querying the Pi mirrors for the
arm64 / x86 builds.
Trixie package renames also fixed: libgles2-mesa → libgles2,
ttf-wqy-zenhei → fonts-wqy-zenhei, libpng16-16 → libpng16-16t64 (time64
transition; armhf has no `Provides:` fallback like amd64 does), and the
Qt 5-only libgst-dev / libsqlite0-dev / libsrtp0-dev / libssl1.1 are
dropped (libgstreamer1.0-dev, libsqlite3-dev, libsrtp2-dev, libssl3 take
their place — first added explicitly, the rest already in the main
list). The transitional `git-core` is gone in trixie; `git` covers it.
Python 3.13 (Trixie's default) replaces the 3.11 pin everywhere:
pyproject.toml requires-python and mypy python_version, ruff.toml
target-version, .python-version, uv.lock (regenerated; only diff is
async-timeout dropped — its marker was python<3.11), uv-builder.j2's
UV_PYTHON, Dockerfile.dev's FROM, bin/install.sh's host check, and every
CI workflow's setup-python pin.
Cleanup that falls out: drop the cache_scope / device_type / version_suffix
`pi4 + arm64 → pi4-64` re-mapping (board is now self-identifying), drop
the `c_rehash` workaround in Dockerfile.base.j2 (specific to a Balena
curl bug, not vanilla Debian), drop the dead arm/v6 + arm/v8 branches in
uv-builder.j2 (only arm/v7 remains as the 32-bit ARM target), retire the
old build_qt5.sh `pi1`/`pi4` branches, and delete docker/Dockerfile.celery
(left behind from the celery-image removal in
|
||
|
|
470a37caf4 |
chore(host): unify host Python install on uv, clean up ansible roles (#2750)
* chore(ansible): fix all ansible-lint violations and remove skip list Drives the 19 deferred violations from the previous skip list to zero and deletes .ansible-lint. The roles now pass the ansible-lint 'production' profile (previously 'min'). - var-naming[no-role-prefix] (17): rename register/set_fact vars in network/screenly/splashscreen/system roles to use the role's prefix (e.g. config_path -> system_config_path, x_service -> screenly_x_service). - risky-shell-pipe (1): add 'set -o pipefail' + bash executable to the /etc/timezone shell task in system role. - no-free-form (1): switch swapoff to keyword form (cmd:/removes:). Also resyncs uv.lock with pyproject.toml's ansible-core==2.19.9 pin (drift left over from #2749). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ansible): fix bugs, simplify, drop Debian <= 7 support Bug fixes --------- - Fix /etc/sudoers.d/screenly_overrides: was copied verbatim, so the literal "${USER}" was written into sudoers (sudo doesn't expand it) and the rule pointed at the renamed upgrade_screenly.sh. Convert to a Jinja template using {{ anthias_user }}, point at upgrade_anthias.sh, and add `validate: visudo -cf %s` so a syntax error blocks install. - Fix double-prefix var name screenly_screenly_x_service_exists left over from the lint-cleanup replace_all cascade. Simplification -------------- - site.yml: lift env('USER') into anthias_user play var; assert USER + DEVICE_TYPE are set/valid in pre_tasks. Replace ~30 inline lookups across roles with {{ anthias_user }}. - system role: drop `lsb_release -cs` and `getconf LONG_BIT` shellouts; use ansible_distribution_release and ansible_userspace_bits facts. Collapse two near-duplicate "add user to docker group" tasks into one task using `append: true` plus a conditional gpio entry for ARM. - system role: replace the /etc/timezone shell pipe with a non-shell `command: readlink` + `copy: content:` pair (no pipefail dance needed). - system role: drop the unconditional `rpi-update` install on ARM; that package ships experimental kernels and shouldn't be run unattended. - screenly role: move anthias-host-agent.service template from the non-standard tasks/templates/ to the conventional roles/<role>/templates/. Debian 11 path -------------- - Bump pinned versions in the Debian 11 pip branch to current release where Python 3.9 still supports them (ansible-core 2.15.13, redis 7.4.0, requests 2.33.1, tenacity 9.1.2, getmac 0.9.5). Drop the unused docker==6.0.0 pin. Comment why ansible-core stays on 2.15.x. Drop Debian <= 7 ---------------- - splashscreen role: remove four Jessie/Wheezy tasks and the `ansible_distribution_major_version|int > 7` guards on every remaining task. Delete unused files/asplashscreen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ansible): normalize *_exist to *_exists for consistency system_cdefs_exist was the lone singular outlier among network_manager_exists / screenly_x_service_exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(systemd): harden anthias-host-agent and wifi-connect units Both units now follow modern systemd best practice: - explicit Type= (simple / oneshot) - explicit dependency on docker.service via Requires= (was just After=) - Documentation= URL + SyslogIdentifier= for journalctl filtering - structured sandboxing: PrivateTmp, PrivateDevices, ProtectSystem=full, ProtectHome=read-only, ProtectKernel{Tunables,Modules,Logs}, ProtectControlGroups, ProtectClock, ProtectHostname, ProtectProc=invisible, RestrictRealtime, RestrictSUIDSGID, RestrictNamespaces, RestrictAddressFamilies (per-service: AF_UNIX for wifi-connect, AF_UNIX/AF_INET/AF_INET6 for host-agent), LockPersonality, UMask=0027. wifi-connect (no privilege escalation, just talks to docker.sock) goes further with NoNewPrivileges, CapabilityBoundingSet=, SystemCallFilter=@system-service minus @privileged/@resources, and Type=oneshot + RemainAfterExit so systemd records "active (exited)" after `docker compose up -d` returns. host-agent is left more permissive because host_agent.py shells out to `sudo systemctl reboot|poweroff` which needs setuid + CAP_SYS_BOOT + the reboot() syscall — those would all be blocked under the tighter profile. The unit calls this out in a comment. Also makes start_wifi_connect_service.sh executable so the unit can invoke it directly instead of via a `bash` wrapper. systemd-analyze security score: anthias-host-agent: 7.5 EXPOSED -> 4.9 OK wifi-connect: 7.3 MEDIUM -> 1.1 OK Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(host): bootstrap installer_venv with uv, drop requirements.host.txt uv manages its own Python independent of the system Python, which lets both Debian 11 and Debian 12+ use the same dependency set from pyproject.toml's `host` group. The install flow no longer needs: - the requirements/requirements.host.txt frozen snapshot - the Debian 11 ansible-core==2.15.x special case in install.sh - the parallel Debian 11 / Debian 12+ pip tasks in the screenly role - the python3-pip / python3-venv / python3-full apt packages - the cryptography==38.0.1 wheel-build workaround - the python-lint.yaml drift check on requirements.host.txt - the long-stale `supervisor` pip-removal migration tasks bin/install.sh changes: - new `clone_repo` step (runs before install_ansible) so we have pyproject.toml in place - install_ansible now: curl|sh the official uv installer, then `uv sync --no-default-groups --group host --no-install-project` with UV_PROJECT_ENVIRONMENT=/home/$USER/installer_venv - run_ansible_playbook uses the venv's ansible-playbook directly instead of relying on PATH activation ansible/roles/screenly/tasks/main.yml: replace both pip tasks with a single `uv sync` task (run as the anthias user, with UV_PROJECT_ENVIRONMENT pointed at installer_venv) so the venv stays in sync if ansible-playbook is rerun standalone. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): replace pre-uv installer_venv on upgrade Existing Anthias hosts have an installer_venv created by `python3 -m venv`, which uv won't recognize on upgrade. Detect by the absence of the `uv = <version>` line in pyvenv.cfg and rm -rf it so `uv sync` rebuilds cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(systemd): allow AF_NETLINK in anthias-host-agent host_agent.py uses the netifaces C extension which opens AF_NETLINK (NETLINK_ROUTE) sockets to enumerate interfaces during the set_ip_addresses pubsub command. The previous RestrictAddressFamilies list (AF_UNIX/AF_INET/AF_INET6) blocked this with EAFNOSUPPORT. Verified by tracing netifaces under strace and by running a test service unit with the same restrictions. Audit: only three pubsub commands ever target the hostcmd channel — reboot, shutdown, set_ip_addresses. CEC lookups happen inside the anthias-celery Docker container, not on the host. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(install): apply audit fixes from review - Bug: upgrade_containers.sh was always pulled from master regardless of the user's selected ref, so a tagged install got master's upgrade script. Now uses ${BRANCH}. - Pin uv to UV_PIN_VERSION (=0.9.17, matching docker/uv-builder.j2) and reinstall if the local uv is a different version. Avoids drift between the host bootstrap and the docker image build. - Apply --ask-become-pass on every arch when the NOPASSWD sudoers file is missing (was x86_64 only — could hang on Pi if the sudo timestamp expired mid-playbook). Also tell the user a prompt is coming. - Drop the stale "Please reboot and run upgrade" branch in post_installation; both branches told the user to reboot anyway. Add an SSH-detection notice so the user knows their session will drop on reboot. - Use git -C in write_anthias_version so it doesn't rely on cwd. - Drop redundant `-e` from shebang (set -euo pipefail two lines down is stricter). - Drop `apt update -y` (-y is meaningless for update); standardize on apt-get. - Quote ${USER} interpolations everywhere; replace `A && B || C` pseudo-if-else with proper if-then-else (shellcheck SC2015); fix array expansion `${VERSION_PROMPT}` -> `${VERSION_PROMPT[*]}` (SC2128); split `local FOO=$(...)` into separate decl/assign (SC2155). shellcheck now clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ansible): drop dead migration helpers and split system role Migration helpers ----------------- Dropped tasks that only existed to clean up cruft from pre-Anthias (Screenly OSE, mid-2024-) installs. They have been idempotent no-ops on every modern install for many releases. screenly role: - Remove screenly_utils.sh (renamed long ago) - Remove cron entry "Cleanup screenly_assets" (state: absent of a cron job no Anthias release has created) - Remove old upgrade_screenly.sh (renamed to upgrade_anthias.sh) - Remove screenly_usb_assets.sh and the autoplay udev rule - Remove plymouth-quit-wait.service / plymouth-quit.service deletion (Plymouth handling is now done by the splashscreen role) - Drop the entire screenly_deprecated_systemd_units list and the three tasks that used it (X.service, screenly-celery, screenly-web, screenly-websocket_server_layer, screenly-viewer, matchbox, screenly-host-agent, udev-restart, wifi-connect — all deprecated unit names from the Screenly era) - Remove the ngrok binary cleanup network role: - Drop the screenly_net_mgr.py / screenly_net_watchdog.py removal pipeline (the whole stat -> set_fact -> stop -> rm chain). Modern installs use NetworkManager directly. upgrade-script integrity ------------------------ Documented why /usr/local/sbin/upgrade_anthias.sh is fetched from GitHub without a checksum: the URL is meant to track upstream master so users can pull in fixes without reinstalling, and integrity is bounded by HTTPS to githubusercontent. If we ever ship signed release assets, we should switch to fetching the signed asset. system role split ----------------- ~370-line system/tasks/main.yml split into focused includes: - boot.yml — /boot/{config,cmdline}.txt edits (raspberry-pi + touches-boot-partition tags applied at the include level) - packages.yml — libc6-dev cdefs.h fix, Anthias apt deps, and removal of pre-Anthias / distro-Docker packages - docker.yml — Docker repo, install, and user-to-docker-group - timezone.yml — /etc/timezone backfill from /etc/localtime - dist_upgrade.yml — apt upgrade dist + autoremove (system-upgrade tag at the include level) - misc.yml — rc.local, dpkg 01_nodoc, swap removal main.yml is now the orchestrator. Tags moved from per-task to per-include. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): pin upgrade_anthias.sh URL to the installed ref The Ansible task that wrote /usr/local/sbin/upgrade_anthias.sh hardcoded raw.githubusercontent.com/.../master/bin/install.sh. So a tag-pinned install (e.g. v0.20.4) silently received master's install.sh as its upgrade entry point, defeating the version pin. The mirror bug in install.sh::upgrade_docker_containers (which fetched upgrade_containers.sh from master regardless of selected ref) was fixed earlier in this PR; this commit closes the same gap on the upgrade-script side. - site.yml: add `anthias_branch` play var (env ANTHIAS_BRANCH, defaulting to 'master' if unset for safety on standalone ansible-playbook runs); include it in the pre_tasks assertion. - screenly role: render the URL with `{{ anthias_branch }}`. Reword the comment + task name so they actually match the behaviour. - install.sh::run_ansible_playbook: export ANTHIAS_BRANCH=${BRANCH} so the playbook can pick up the user's selected ref. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ansible): suppress sonarcloud S2612 on intentional system modes SonarCloud's ansible:S2612 ("granting access to others") fires on seven file/directory modes in the new system role task files. Each matches a Debian/Raspbian convention and the file *needs* to be world-readable to function: - /etc/timezone (0644) — libc/locale paths read it - /etc/dpkg/dpkg.cfg.d/01_nodoc (0644) — dpkg honours per any caller - /etc/apt/keyrings (0755) and /etc/apt/keyrings/docker.asc (0644) — per Debian's own apt-secure docs; the _apt user reads them - /etc/rc.local (0755) — systemd-rc-local-generator runs it - cmdline.txt and its .orig backup (0755) — Pi imager / NOOBS / pi-config ship 0755; deviating breaks raspi-config tooling Suppressing per-line with `# NOSONAR` and a one-liner reason rather than weakening the modes (which would actually break the system). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): point ansible at the venv python; pre-create netdev group Two issues found by running bin/install.sh end-to-end against a privileged Debian 12 container with systemd + DinD. 1. Ansible needs Python on the target. Since this PR drops the system python3 install in favour of uv-managed Python under installer_venv, gather_facts (and every subsequent module) failed with: "/usr/bin/python3 not found" Fix: export ANSIBLE_PYTHON_INTERPRETER=$installer_venv/bin/python in install.sh::run_ansible_playbook so Ansible uses the venv we just provisioned with uv sync. 2. The `netdev` group is created by network-manager, but Anthias can be installed with MANAGE_NETWORK=No, in which case the package isn't pulled in and the group doesn't exist. The user-membership task then failed with "Group netdev does not exist". Pre-create it with `ansible.builtin.group` (system:true) so the membership task is safe regardless of MANAGE_NETWORK. Both bugs were latent on Raspberry Pi OS (which always pre-installs NetworkManager) but bite on minimal Debian 12 / x86 images. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ansible): pre-create all required groups, not just netdev Previous fix only handled netdev. Minimal Debian/x86 images can also be missing input, plugdev, video, dialout (anything that udev or desktop-package postinsts would have created). Pre-create the whole list with ansible.builtin.group + state: present. Also dedupe the base/pi group lists between the create task and the membership task using YAML anchors so they can't drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(install): use curl instead of wget; add gettext-base End-to-end test in a privileged Debian 12 container surfaced two post-Ansible-playbook bash issues: - upgrade_docker_containers used wget, but the install_packages step only installs curl. Switch to `curl -fsSL ... -o`. - bin/upgrade_containers.sh uses `envsubst` (gettext-base). Add gettext-base to APT_INSTALL_ARGS so it's present. Both surfaced because we slimmed the apt list when migrating the host bootstrap to uv. They were latent on full RPi OS images. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(systemd): keep host-agent sudo path working under systemd <= 252 Copilot's review caught a real regression: nearly all the Protect*/ Restrict* directives we added implicitly enable NoNewPrivileges on systemd <= 252 (Debian 11/12), which blocks sudo's setuid escalation. host_agent.py shells out to `sudo systemctl reboot|poweroff`, so the hardened unit silently broke reboot/shutdown via the API on those distros. Reproduced inside a Debian 12 + systemd 252 container: every direct- ive flagged "implies NNP=yes" in systemd.exec(5) actually does so on that version. Setting `NoNewPrivileges=false` does *not* override the implication — the only fix is to drop the offending directives. Strip anthias-host-agent.service down to the directives empirically verified to keep NNP=0 on systemd 252: PrivateTmp, ProtectSystem=full, ProtectHome=read-only, ProtectControlGroups, ProtectProc=invisible, UMask=0027, CapabilityBoundingSet (narrowed), AmbientCapabilities= This still gives meaningful sandboxing (capability bounding set limits even sudo's child processes) without breaking reboot. wifi-connect.service is unaffected — it doesn't escalate. systemd-analyze security on a real Debian 12 systemd 252: anthias-host-agent: 6.5 MEDIUM (was claimed 4.9 OK on Ubuntu 24.04 host, where the implication doesn't fire) Also addressing other Copilot review comments --------------------------------------------- - screenly_overrides: tighten the systemctl rule to the specific verbs host_agent.py uses (`reboot`, `poweroff`). Granting blanket `/bin/systemctl` was overly broad — could start/stop/mask any unit on the system. - packages.yml: gate the cdefs.h / libc6-dev workaround on ansible_architecture == 'armv7l'. The header path is armhf- specific; on aarch64/x86 the task always hit "missing" and reinstalled libc6-dev unnecessarily on every run. - screenly/tasks/main.yml: assert that uv exists before `uv sync` with a clear error message pointing at install.sh, instead of letting the task fail deep inside command-not-found. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ansible): skip dhcpcd disable when service is not installed On stock Debian/Ubuntu x86 (and any host without dhcpcd installed), the unconditional systemd stop/disable failed the playbook with "Could not find the requested service dhcpcd". Gather service facts first and only run the disable when dhcpcd.service is actually present. Pi OS Buster/Bullseye still ship dhcpcd by default, so the existing behavior there is unchanged; Bookworm (12+) is already excluded by the version gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(install): mention x86 alongside Raspberry Pi in intro banner The intro banner only warned about losing the Pi's desktop environment; reword so it reflects that Anthias also runs on x86 and that the host is repurposed regardless of platform. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(host-agent): retry redis connect quietly on first-boot startup On a fresh install the host_agent.service unit starts before the redis docker container is accepting connections, so it crashed on every boot with a 50-line ConnectionRefusedError traceback, was restarted by systemd 10s later, and repeated until redis came up — typically ~5 minutes of journal noise per cold boot. Wrap the redis connect+subscribe in a tenacity Retrying matching the pattern already used by set_ip_addresses: retry only on redis.exceptions.ConnectionError, 5s fixed wait, up to 60 attempts, and use before_sleep_log so each retry logs a single WARN line instead of a traceback. After the bounded retry budget, the exception is re-raised and systemd's normal restart policy applies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(ansible): title-case Anthias in splashscreen task name The "Set plymouth default theme to anthias" task name was the only user-facing message in the playbook that didn't title-case Anthias. Audit covered task names, msg/fail_msg/debug fields, and systemd unit Description= templates; everything else was already correct. The command argument and changed_when comparison stay lowercase because they reference the literal on-disk theme identifier. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(host-agent): collapse before_sleep_log call to satisfy ruff format Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sudoers): match host_agent.py's actual systemctl path host_agent.py invokes /usr/bin/systemctl, but the rule listed /bin/systemctl. On usrmerged Debian sudo matches by inode so this worked in practice, but the rule should match what the agent actually calls so it doesn't depend on /bin -> /usr/bin staying a symlink. Also drops /sbin/shutdown — the agent never invokes it. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(host-agent): surface retry exceptions and support ens interfaces Two related fixes turned up while validating reboot/shutdown end-to-end on a multipass-launched Debian 13 VM: 1. set_ip_addresses() swallowed every retry's exception under the bare `except RetryError`, leaving only a generic "Unable to connect" warning. Add before_sleep_log(..., exc_info=True) so the actual requests.* exception is logged on each attempt, and put a 5s timeout on requests.get() so a hung connect can't stretch one attempt out. 2. SUPPORTED_INTERFACES missed `ens` (systemd "slot" naming used by QEMU/multipass and many cloud images), so get_ip_addresses() returned an empty list on those hosts even when the NIC was up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(host-agent): hoist internet probe URL to a named constant Pulls the 1.1.1.1 anycast literal out of set_ip_addresses() into INTERNET_PROBE_URL and silences the SonarCloud python:S1313 hotspot on the constant with a comment explaining the IP is Cloudflare's public anycast probe (not a private address). The previous commit's edit to the same line tripped the quality gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dde9916983 |
chore(deps): bump astral-sh/setup-uv in the github-actions group (#2748)
Bumps the github-actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv).
Updates `astral-sh/setup-uv` from 7.6.0 to 8.1.0
- [Release notes](https://github.com/astral-sh/setup-uv/releases)
- [Commits](
|
||
|
|
ee12387b06 |
chore(deps): manage Python deps via uv dependency-groups (#2744)
* chore(deps): manage Python deps via uv dependency-groups
Replaces the six service-scoped requirements*.txt files with
PEP 735 dependency-groups in pyproject.toml and rebuilds every
Docker image as a two-stage build: a uv-builder stage (using the
official ghcr.io/astral-sh/uv image, with a pip fallback for
armv6) produces /venv via `uv sync --group <svc>`, which the
runtime stage copies in. uv.lock becomes authoritative for all
services. requirements/requirements.host.txt is kept as a
committed, auto-generated artifact (`uv export --group host`) so
bin/install.sh and the Ansible role keep working; a python-lint
CI step enforces it stays in sync.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): bump Django, cryptography, pyOpenSSL, and 5 others
- Django 4.2.29 → 4.2.30 (latest 4.2 LTS)
- cryptography 3.3.2 → 46.0.7 (capped by pyOpenSSL 26's `cryptography<47`;
cryptography 47 is incompatible with the latest pyOpenSSL)
- pyOpenSSL 19.1.0 → 26.0.0 (required by newer cryptography ABI —
pyOpenSSL 19 crashed at import against cryptography ≥ ~3.4)
- requests 2.32.5 → 2.33.1 (aligned across every group, including
docker-image-builder and local)
- pyasn1 0.6.2 → 0.6.3
- redis 7.1.0 → 7.4.0
- Cython 3.2.3 → 3.2.4
- sh 1.8 → 2.2.2 (major bump; usages in celery_tasks.py, bin/wait.py,
lib/utils.py stick to the stable `sh.<cmd>` + `sh.ErrorReturnCode_N`
API — verified still works)
- python-vlc 3.0.20123 → 3.0.21203
`mako` and `flatted` were requested but skipped: `mako` was already
removed from the project (
|
||
|
|
8ee205d055 |
chore(deps): bump actions/checkout from v5 to v6 (#2703)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
b22f610a2c |
chore(deps-dev): bump ruff from 0.14.3 to 0.14.10 (#2629)
* chore(deps-dev): bump ruff from 0.14.3 to 0.14.10 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.14.3 to 0.14.10. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.14.3...0.14.10) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.14.10 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * chore: update `uv.lock` * chore: trigger Python linting workflow when `pyproject.toml` or `uv.lock` changes --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Nico Miguelino <nicomiguelino2014@gmail.com> |
||
|
|
29ae072514 |
chore: replace Poetry with uv for managing host dependencies (#2611)
|
||
|
|
6705477e5b | chore: update Ruff linting rules formatting (#2570) | ||
|
|
9fa0da6528 |
chore(deps): bump the github-actions group with 2 updates (#2490)
Bumps the github-actions group with 2 updates: [actions/setup-python](https://github.com/actions/setup-python) and [actions/setup-node](https://github.com/actions/setup-node). Updates `actions/setup-python` from 5 to 6 - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v5...v6) Updates `actions/setup-node` from 4 to 5 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-node dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
|
|
2e97c7de86 |
chore(deps): bump the github-actions group with 4 updates (#2444)
* chore(deps): bump the github-actions group with 4 updates Bumps the github-actions group with 4 updates: [actions/checkout](https://github.com/actions/checkout), [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance), [actions/download-artifact](https://github.com/actions/download-artifact) and [ncipollo/release-action](https://github.com/ncipollo/release-action). Updates `actions/checkout` from 4 to 5 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) Updates `actions/attest-build-provenance` from 1 to 2 - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/v1...v2) Updates `actions/download-artifact` from 4 to 5 - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v4...v5) Updates `ncipollo/release-action` from 1.11.2 to 1.18.0 - [Release notes](https://github.com/ncipollo/release-action/releases) - [Commits](https://github.com/ncipollo/release-action/compare/v1.11.2...v1.18.0) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/attest-build-provenance dependency-version: '2' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/download-artifact dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: ncipollo/release-action dependency-version: 1.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> * temporary: disable Docker image pushes for testing purposes * chore(ci): revert temporary changes to the build workflow --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: nicomiguelino <nicomiguelino2014@gmail.com> |
||
|
|
40100b86f7 | chore(ci): bump runner OS to Ubuntu 24.04 for the remaining workflows (#2205) | ||
|
|
490051585f |
Replace flake8 with ruff (#2092)
|
||
|
|
e67c996b8f |
chore(ci): bump action/setup-python to v5 (#2138)
|
||
|
|
ba653abfeb |
chore(ci): bump actions/checkout to v4 (#2137)
|
||
|
|
70afe9ba49 |
chore(cleanup): remove workflow related to the experimental branch (#2101)
|
||
|
|
29d4c24fb2 |
chore(ci): replace --with with --only when installing specific deps (#2063)
|
||
|
|
d5f5c63e1e | Use Poetry for the Python linter (#2042) | ||
|
|
390c40d9e8 | fix: trigger test and linter check on push and/or PR to experimental branch (#2012) | ||
|
|
591357d1bc | chore: bumps Python version used by linter from 3.7 to 3.11 (#1988) | ||
|
|
6e87511237 | chore/fix: increase scope of Python linter | ||
|
|
87e2d493ce | Adds Python linting in CI (#1939) |