* 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 (9535745), and `flatted` is an npm dep in
`package-lock.json`, not a Python dep.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): bump wheel from 0.38.1 to 0.46.2
Closes Dependabot PR #2651.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 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 (9535745), and `flatted` is an npm dep in
`package-lock.json`, not a Python dep.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(deps): bump wheel from 0.38.1 to 0.46.2
Closes Dependabot PR #2651.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: adapt sh 2.x API changes in wait.py and viewer
Two real breakages uncovered by auditing every `sh.*` call site
against the sh 1.x → 2.x API:
- bin/wait.py: `sh.grep(sh.route(), 'default')` no longer pipes
in sh 2.x — the inner command stringifies to its stdout and
becomes a literal argument to grep, producing
`grep '<route_output>' default` and an ErrorReturnCode_2. Use
the idiomatic `sh.grep('default', _in=sh.route())` instead.
- viewer/__init__.py: `browser.process.alive` is gone in sh 2.x
(`OProc` no longer exposes it). Use `browser.process.is_alive()[0]`,
which returns the `(alive_bool, exit_code)` tuple.
Plus two review nits:
- Add trailing newline to docs/migrating-assets-to-screenly.md
- Use `diff -u` in the requirements.host.txt CI drift check so
failures print a readable unified diff.
Verified against sh==2.2.2 inside the rebuilt server image:
- `sh.grep('default', _in=sh.echo('…'))` pipes correctly
- `cmd.process.is_alive()` → `(True, None)` while running,
`(False, 0)` after wait()
- `cmd.process.stdout.decode('utf-8')` still works on `_bg=True`
processes
83/83 unit tests + 12/12 integration tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(docker): serialize apt cache access with sharing=locked
The multi-stage uv-builder + runtime layout means two RUN steps can
race on BuildKit's shared `/var/cache/apt` cache mount. apt requires
an exclusive lock on /var/cache/apt/archives, so a concurrent
apt-get in the sibling stage causes the build to fail with
`E: Could not get lock /var/cache/apt/archives/lock`.
BuildKit's default cache mount sharing mode is `shared` (unrestricted
concurrent access). Switching to `sharing=locked` makes BuildKit
serialize access across stages, matching apt's locking model.
Discovered while cross-compiling `pi4-64` under QEMU, where the
slower emulated apt-get in stage 1 overlapped with the host-speed
apt-get in stage 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: fix ansible-lint and sbom workflows
**ansible-lint** (broken since 2026-04-08, #2732):
- `ansible-community/ansible-lint-action@main` repo is gone (404),
so every run failed with "Unable to resolve action".
- Rewrite the workflow to use setup-uv + `uv run ansible-lint` from
a new `ansible-lint==26.4.0` entry in the `dev-host` dependency
group — matches the uv-based pattern already used by
`python-lint.yaml`.
- Add `.ansible-lint` config with a skip list covering 19
pre-existing violations in `ansible/` roles
(`var-naming[no-role-prefix]`, `risky-shell-pipe`, `no-free-form`)
so the workflow can go green today; follow-up PRs should drive
the skip list down.
- Extend the path triggers to fire on config, workflow, and lock
changes — not just `ansible/**`.
**sbom** (broken since 2026-04-02):
- The `sbomify/github-action` renamed `SBOM_FILE` to `LOCK_FILE` for
lockfile inputs. Every run has been failing with "`uv.lock` is a
lock file, not an SBOM. Please use LOCK_FILE instead of SBOM_FILE."
- Rename both `SBOM_FILE` envs (`package-lock.json` and `uv.lock`)
to `LOCK_FILE`.
Verified locally: `uv run ansible-lint ansible/` passes (0
failures, 0 warnings).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(build): replace webpack, npm, and jest with bun
Collapses the JS toolchain to a single tool. Bun handles installs
(replacing npm), bundling via `bun build` + `sass` CLI (replacing
webpack + ts-loader + babel + mini-css-extract-plugin), and testing
via `bun test` (replacing jest + ts-jest + jest-fixed-jsdom). Dev/test
Dockerfiles pull the bun binary from the official `oven/bun` image via
`COPY --from=`; production uses `oven/bun` as a builder stage.
Removes 18 devDependencies and 5 config files; adds only `bunfig.toml`
and `@happy-dom/global-registrator`.
Drive-by fix: `FormData` was imported as a value from `@/types` in
two files but is a type-only interface shadowing the browser global.
Webpack+ts-loader silently erased it; Bun's bundler surfaced the bug.
Converted to `import type`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(docker): symlink bunx to bun in dev and test images
`bunx` is a symlink to `bun` in the official `oven/bun` image, so the
single-file `COPY --from=oven/bun:...-slim /usr/local/bin/bun` missed it.
Result: `bun run dev:css` / `bun run build:css` failed with
`bunx: command not found` inside dev and test containers.
Recreate the symlink after the copy. Production is unaffected because
its builder stage uses `FROM oven/bun` (bunx already present).
Caught by full end-to-end build verification.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci: SHA-pin all external GitHub Actions
Addresses SonarCloud rule githubactions:S7637 ("Use full commit SHA
hash for this dependency") and brings the repo in line with the
hardened CI guidance from OpenSSF, CISA, and GitHub itself: tag refs
like @v7 or @master are mutable and can be retargeted by the action
owner or via compromise. Pinning to a full commit SHA removes that
supply-chain risk.
Every `uses:` reference to an external action across all 13 workflow
files is now pinned by SHA, with the original tag preserved as an
inline comment so the intent remains readable:
uses: actions/checkout@de0fac2 # v6
Dependabot's github-actions ecosystem (already configured in
.github/dependabot.yml) recognises this `<SHA> # <tag>` format and
will update both the SHA and the comment together on future version
bumps, so we don't lose automated update coverage.
Scope: 21 distinct external actions × 73 total use sites across
ansible-lint, build-balena-disk-image, build-webview, codeql-analysis,
deploy-website, docker-build, generate-openapi-schema, javascript-lint,
lint-workflows, python-lint, sbom, and test-runner. Local workflow
references (./.github/workflows/...) left untouched.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs,chore: address review feedback on bun migration
- Update CLAUDE.md and docs/developer-documentation.md to replace
npm/webpack/jest references with bun equivalents. The old webpack
ProvidePlugin bullet was superseded by tsconfig's react-jsx runtime;
restate that.
- Add comments in setupTests.ts explaining (1) why Bun's native fetch
is stashed and restored around happy-dom's GlobalRegistrator (so MSW
can intercept) and (2) why testing-library is imported dynamically
after registration (so `screen` binds to a live document.body).
- Narrow the production builder SCSS COPY back to `*.scss` and drop
the unused `bunfig.toml` copy (it's only consumed by `bun test`).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(dev): fail-fast when a watcher crashes in `bun run dev`
`wait` without arguments returns the last-exiting job's status, so a
crashing JS or CSS watcher could leave the script reporting success.
Track each watcher's PID, use `wait -n` to exit on the first failure,
and kill the survivor via a trap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps wheel from 0.38.1 to 0.46.2.
Release notes
Sourced from wheel's releases.
Changelog
Sourced from wheel's changelog.
... (truncated)
Commits
eba4036Updated the version number for v0.46.2557fb54Created a new release7a7d2deFixed security issue around wheel unpack (#675)41418faFixed test failures due to metadata normalization changesc1d442b[pre-commit.ci] pre-commit autoupdate (#674)0bac882Update github actions environments (#673)be9f45b[pre-commit.ci] pre-commit autoupdate (#667)6244f08Update pre-commit ruff legacy alias (#668)15b7577PEP 639 compliance (#670)fc8cb41Revert "Removed redundant Python version from the publish workflow (#666)"You can trigger a rebase of this PR by commenting
@dependabot rebase.Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR:
@dependabot rebasewill rebase this PR@dependabot recreatewill recreate this PR, overwriting any edits that have been made to it@dependabot mergewill merge this PR after your CI passes on it@dependabot squash and mergewill squash and merge this PR after your CI passes on it@dependabot cancel mergewill cancel a previously requested merge and block automerging@dependabot reopenwill reopen this PR if it is closed@dependabot closewill close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually@dependabot show <dependency name> ignore conditionswill show all of the ignore conditions of the specified dependency@dependabot ignore this major versionwill close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this minor versionwill close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)@dependabot ignore this dependencywill close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)You can disable automated security fix PRs for this repo from the Security Alerts page.