Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/rebuild_qt5_toolchain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# This is an out-of-band prereq: docker/Dockerfile.qt5-webview-builder.j2
# fetches these tarballs from the release the viewer image references
# via tools/image_builder/utils.py (qt5_toolchain_url, currently
# WebView-v2026.07.0). If that release doesn't have trixie-* artifacts
# WebView-v2026.07.1). If that release doesn't have trixie-* artifacts
# for both pi2 and pi3, pi2/pi3 viewer image builds fail with
# sha256sum: no properly formatted checksum lines found
# (the curl on the missing .sha256 falls back to a 404 HTML page).
Expand Down Expand Up @@ -132,7 +132,7 @@ echo " (cd '${OUT_DIR}' && sha256sum -c qt5-5.15.19-trixie-*.tar.gz.sha256)"
echo
echo "Upload to a WebView-v* release. If you re-use the tag the viewer"
echo "image references in tools/image_builder/utils.py via qt5_toolchain_url"
echo "(currently WebView-v2026.07.0), no source change is needed;"
echo "(currently WebView-v2026.07.1), no source change is needed;"
echo "otherwise bump that URL to the new tag in the same commit."
echo " gh release upload <WebView-vX.Y.Z> \\"
echo " '${OUT_DIR}'/qt5-5.15.19-trixie-pi2.tar.gz{,.sha256} \\"
Expand Down
6 changes: 3 additions & 3 deletions docker/Dockerfile.qt5-webview-builder.j2
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
at via `-sysroot /sysroot`.
2. webview-builder — host x86 (`$BUILDPLATFORM`); installs the
Linaro gcc-7.4.1 cross-compiler plus the pre-built Qt 5
toolchain pinned to WebView-v2026.07.0, then compiles the
toolchain pinned to WebView-v2026.07.1, then compiles the
in-tree src/anthias_webview/ source.

Qt 5 is frozen for pi2/pi3 boards, so the toolchain artifact at
WebView-v2026.07.0 never changes. To CVE-patch the toolchain run
WebView-v2026.07.1 never changes. To CVE-patch the toolchain run
bin/rebuild_qt5_toolchain.sh and re-upload to that tag.
#}
FROM --platform=linux/arm/v7 {{ base_image }}:{{ base_image_tag }} AS qt5-sysroot
Expand Down Expand Up @@ -214,7 +214,7 @@ RUN mkdir -p /src/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin && \
"/src/gcc-linaro-7.4.1-2019.02-x86_64_arm-linux-gnueabihf/bin/$(basename "$tool")"; \
done

# Pre-built Qt 5 toolchain — pinned permanently to WebView-v2026.07.0
# Pre-built Qt 5 toolchain — pinned permanently to WebView-v2026.07.1
# (Qt 5 is frozen for pi2/pi3). The qt5pi/ tree contains both host
# qmake (x86) and armhf libs the webview app links against.
RUN mkdir -p /src/{{ artifact_board }} && \
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,9 @@ exclude = [
# the file header. Adding type annotations would put the file
# off-pin and silently break the provenance comment.
"^src/anthias_webview/sysroot-relativelinks\\.py$",
# Build-time-only stdlib shims (imp/pipes/cgi) for Chromium 87's
# Python-2-era build tooling; not app code, emulate removed stdlib.
"^src/anthias_webview/pyshim/",
]

[[tool.mypy.overrides]]
Expand Down
5 changes: 5 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ exclude = [
# the file header. Reformatting would put the file off-pin and
# silently break the provenance comment.
"src/anthias_webview/sysroot-relativelinks.py",
# Build-time-only stdlib shims (imp/pipes/cgi) copied into the Qt 5
# toolchain builder image to satisfy Chromium 87's Python-2-era build
# tooling on Debian trixie's Python 3.13. Not app code; they emulate
# removed-stdlib APIs, so app formatting/typing rules don't apply.
"src/anthias_webview/pyshim/*.py",
]
target-version = "py313"

Expand Down
4 changes: 2 additions & 2 deletions src/anthias_server/lib/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
# The releases *list*, not ``/releases/latest``: that endpoint returns
# the newest non-draft, non-prerelease release of ANY kind, and the
# repo also publishes non-app releases (e.g. the frozen Qt 5 toolchain,
# tagged ``WebView-v2026.07.0``). The day such a release goes out,
# tagged ``WebView-v2026.07.1``). The day such a release goes out,
# ``/releases/latest`` stops pointing at an Anthias CalVer tag and the
# update check degrades fleet-wide until the next app release (Sentry
# ANTHIAS-3P). Listing lets us pick the highest CalVer-parseable tag
Expand Down Expand Up @@ -81,7 +81,7 @@ def _latest_parseable_tag(payload: object) -> str | None:
``/releases`` list payload.

Skips drafts, prereleases, and any release whose tag doesn't parse
(non-app releases like ``WebView-v2026.07.0``, hand-edited tags
(non-app releases like ``WebView-v2026.07.1``, hand-edited tags
like ``nightly``). Highest-by-version rather than first-in-list:
the list is ordered by creation date, and a non-app release
created after the newest app release would otherwise mask it.
Expand Down
4 changes: 2 additions & 2 deletions src/anthias_webview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ viewer image build:
Qt 5 toolchain, all in
`docker/Dockerfile.qt5-webview-builder.j2`. Qt 5 is frozen for
these boards, so the toolchain itself is a permanent artifact at
the `WebView-v2026.07.0` GitHub release.
the `WebView-v2026.07.1` GitHub release.

To rebuild a viewer image (which rebuilds the binary):

Expand All @@ -39,7 +39,7 @@ uv run python -m tools.image_builder \
If the Qt 5 toolchain itself needs to change (CVE patch, base image
bump), `bin/rebuild_qt5_toolchain.sh` produces fresh
`qt5-5.15.19-trixie-{pi2,pi3}.tar.gz` tarballs. Re-uploading them to
the same `WebView-v2026.07.0` release tag means no source change is
the same `WebView-v2026.07.1` release tag means no source change is
needed elsewhere; uploading to a new tag means bumping
`qt5_toolchain_url` in `tools/image_builder/utils.py`. Runtime is
~2-4 hours per board on a beefy x86 host (Qt 5 + QtWebEngine under
Expand Down
108 changes: 107 additions & 1 deletion src/anthias_webview/build_qt5.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,74 @@
QT_BUG_FIX="19"
QT_VERSION="$QT_MAJOR.$QT_MINOR.$QT_BUG_FIX"
DEBIAN_VERSION=$(lsb_release -cs)

# Debian trixie ships Python 3.13, which removed stdlib modules Chromium
# 87's build tooling still imports: `imp` (gone 3.12), `pipes`/`cgi`
# (gone 3.13). Install importlib-backed shims (src/anthias_webview/pyshim/)
# into python3's site-packages ONLY. Do NOT put them on a shared
# PYTHONPATH: the build runs some codegen actions under python2.7 (which
# has real pipes/imp/cgi), and a shared PYTHONPATH would shadow those with
# the py3-only shim and break it ("cannot import name quote"). python3's
# site-packages is only consulted after the stdlib, so it fills the gaps
# for removed modules without shadowing anything that still exists.
# These shims are required on Python 3.13, so let a failed copy abort the
# build (set -e) rather than surface later as a confusing ImportError deep
# inside a Chromium gn/ninja action.
# `ls` guard so an empty (or absent) pyshim dir doesn't feed cp a literal
# unexpanded glob and abort the build under set -e.
if ls /webview/pyshim/*.py >/dev/null 2>&1; then
PY3_SITE="$(python3 -c 'import sysconfig; print(sysconfig.get_path("purelib"))')"
cp -f /webview/pyshim/*.py "$PY3_SITE/"
fi

# Chromium 87's grit/build tooling `import six` (and six.moves); trixie's
# python3.13 ships no six by default, so the gn/ninja codegen actions
# abort with "No module named 'six.moves'". Install the distro package
# (six 1.17 supports 3.13). No pip fallback — the builder image has apt
# but not necessarily python3-pip, and a failed apt install should abort
# the build (set -e) rather than pretend a missing pip3 could recover it.
if ! python3 -c 'import six.moves' 2>/dev/null; then
apt-get update -qq
apt-get install -y -qq python3-six
fi
# Chromium 87's licenses/gyp tooling imports distutils (spawn, version,
# dir_util, archive_util), removed from the stdlib in Python 3.12.
# setuptools re-provides it via its distutils-precedence .pth. apt-only for
# the same reason as six above.
if ! python3 -c 'import distutils.spawn' 2>/dev/null; then
apt-get update -qq
apt-get install -y -qq python3-setuptools
fi

# Force -mno-unaligned-access on EVERY armhf compile. Modern Debian gcc
# lowers struct zero-init/copy of an 8-byte-aligned type into a NEON
# block-move store with a :64 alignment assertion (arm_block_set_aligned_vect);
# on the Cortex-A7 (Pi 2) that SIGBUSes whenever the object is only
# 4-byte-aligned (Chromium/Qt misaligned-pointer UB), blanking the viewer.
# GCC won't change this (PR 93031 WONTFIX). -mno-unaligned-access makes
# arm_block_set_vect bail (`... && !unaligned_access) return false`), so gcc
# emits 4-byte-safe `vstr` instead. arm_use_neon=false (common.pri, below)
# covers only Chromium's gn C++; Qt's own libs (libQt5Core/Gui/Qml/Quick,
# built by qmake with -mfpu=neon-vfpv4) and the WebEngine integration layer
# still emit it, so wrap the compiler itself — both qmake and gn resolve
# arm-linux-gnueabihf-{gcc,g++} through /usr/bin. Move the original aside
# to <tool>.real first so the wrapper never execs itself (safe whether the
# original is an alternatives symlink or a real binary). Guarded on the
# tool existing and not already being wrapped, so it's idempotent and
# doesn't fail before fetch_cross_compile_tool's own presence check.
for _t in gcc g++; do
_bin="/usr/bin/arm-linux-gnueabihf-$_t"
if [ -e "$_bin" ] && [ ! -e "$_bin.real" ]; then

Check failure on line 78 in src/anthias_webview/build_qt5.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=Screenly_screenly-ose&issues=AZ9NrwxSCQbpmkJVIrmd&open=AZ9NrwxSCQbpmkJVIrmd&pullRequest=3184

Check failure on line 78 in src/anthias_webview/build_qt5.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=Screenly_screenly-ose&issues=AZ9NrwxSCQbpmkJVIrmc&open=AZ9NrwxSCQbpmkJVIrmc&pullRequest=3184
mv "$_bin" "$_bin.real"
cat > "$_bin" <<EOF
#!/bin/sh
exec "$_bin.real" -mno-unaligned-access "\$@"
EOF
chmod +x "$_bin"
echo "Wrapped $_bin with -mno-unaligned-access"
fi
done

# MAKE_CORES caps parallelism. Overridable via env so the wrapper can
# tune for available memory: each cc1plus under qemu-arm peaks at
# ~3-4 GB during the chromium compile, so the default `nproc + 2`
Expand Down Expand Up @@ -154,6 +222,44 @@
# Make sure we have a clean QT 5 tree
fetch_qt

# Chromium 87 vendors six 1.14, whose ``six.moves`` meta-path
# importer is broken under Python 3.12+ (grit then dies with
# "No module named 'six.moves'"). Overwrite every vendored
# six.py with the distro's 3.13-compatible six so the gn/grit
# codegen actions — which import the vendored copy ahead of any
# site-packages — get a working ``six.moves``. Runs after
# fetch_qt because that rsync --delete restores the stale copy.
# grit inserts tools/grit/third_party at sys.path[0] and vendors
# six 1.10 as a *package* (six/__init__.py) there, so overwrite
# both the six.py files and every six/ package __init__.py with
# the distro copy.
# No error swallowing: six was installed above, so the import must
# succeed, and a failed overwrite would only resurface later as the
# "No module named 'six.moves'" grit failure this is meant to fix.
SYS_SIX="$(python3 -c 'import six, sys; sys.stdout.write(six.__file__)')"
CHROMIUM_DIR="/src/qt$QT_MAJOR/qtwebengine/src/3rdparty/chromium"
find "$CHROMIUM_DIR" \
\( -path '*/six/six.py' -o -path '*/six/__init__.py' \) \
-print -exec cp "$SYS_SIX" {} \;

# Force arm_use_neon=false for the Chromium build (Yocto's fix:
# OSSystems meta-chromium / lgsvl meta-lgsvl-browser). QtWebEngine
# leaves arm_use_neon unset, so Chromium's arm.gni default resolves
# it to true on Linux and applies -mfpu=neon to EVERY C++ TU. Modern
# Debian gcc (12-16) then compiles struct zero-init/copy of 8-byte-
# aligned types into a NEON block-move store with a :64 alignment
# assertion (arm_block_set_aligned_vect); on the Cortex-A7 (Pi 2)
# that faults (SIGBUS) whenever the object lands 4-byte-aligned,
# blanking the viewer. With arm_use_neon=false the general C++ path
# compiles -mfpu=vfpv3-d16 (no NEON block store), while codec/
# graphics (libvpx, Skia) re-add -mfpu=neon per-file with runtime
# detection, so hardware SIMD decode is preserved. Appended after
# fetch_qt because rsync --delete restores the pristine .pri;
# grep-guarded so a re-run in the same workdir doesn't duplicate it.
_common_pri="/src/qt$QT_MAJOR/qtwebengine/src/core/config/common.pri"
grep -qF 'arm_use_neon=false' "$_common_pri" \
|| echo 'gn_args += arm_use_neon=false' >> "$_common_pri"

if [ "${CLEAN_BUILD-x}" == "1" ]; then
rm -rf "$SRC_DIR"
fi
Expand Down Expand Up @@ -268,7 +374,7 @@
# (docker/Dockerfile.qt5-webview-builder.j2 includes this Qt 5
# toolchain at build time). This script only emits the toolchain
# tarball — bin/rebuild_qt5_toolchain.sh uploads it to the frozen
# WebView-v2026.07.0 release.
# WebView-v2026.07.1 release.
}

# Modify paths for build process
Expand Down
16 changes: 16 additions & 0 deletions src/anthias_webview/pyshim/cgi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Shim for the stdlib ``cgi`` module, removed in Python 3.13 (Debian
trixie). Chromium 87 build tooling uses only ``cgi.escape`` (dropped from
the stdlib back in 3.8 in favour of ``html.escape``).
"""


def escape(s, quote=False):
# Deliberately faithful to the ORIGINAL stdlib ``cgi.escape`` — with
# quote=True it escapes only the double quote, NOT the single quote.
# (That is what distinguished it from ``html.escape``, which escapes
# both.) Matching cgi.escape exactly keeps the generated output
# byte-identical to what Chromium 87's tooling produced on Python 2.
s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
if quote:
s = s.replace('"', '&quot;')
return s
Comment thread
vpetersson marked this conversation as resolved.
Loading