- deploy.sh dispatcher + thin shims (deploy-local.sh / -hardware.sh / -prod.sh). - scripts/deploy/local.sh: build UI bundle and serve via tmux session on :8770 for offline iteration. - scripts/deploy/hardware.sh: rsync-based push to a Pi over SSH and restart bbctrl.service. - scripts/deploy/prod.sh: bundle release tarball. - scripts/deploy/patch_font_mime.py: hot-patches Chromium 72's broken WOFF2 mime handling on the kiosk Pi.
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Patch bbctrl Web.py so font files get the correct MIME type.
|
|
|
|
Background
|
|
----------
|
|
The Onefinity controller (Pi 3B running Raspbian stretch) ships Python
|
|
3.5, whose ``mimetypes`` module does not recognize ``.woff``, ``.woff2``
|
|
or ``.ttf``. Tornado's ``StaticFileHandler`` therefore falls back to
|
|
``application/octet-stream`` for those, and Chromium 72 (the Pi's
|
|
onboard kiosk browser) refuses to use such payloads as web fonts. The
|
|
result is that every FontAwesome icon renders as an empty box on the
|
|
kiosk display.
|
|
|
|
This patch monkey-patches ``StaticFileHandler.get_content_type`` to
|
|
emit the right MIME types. It is idempotent: running it twice is a
|
|
no-op. Run with ``sudo`` so it can rewrite the egg's Web.py.
|
|
|
|
Used by:
|
|
scripts/deploy/hardware.sh
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
import os
|
|
import sys
|
|
|
|
|
|
def find_web_py():
|
|
"""Return the absolute path to the bbctrl Web.py shipped in the egg."""
|
|
base = "/usr/local/lib"
|
|
for entry in os.listdir(base):
|
|
if not entry.startswith("python"):
|
|
continue
|
|
candidate_dir = os.path.join(base, entry, "dist-packages")
|
|
if not os.path.isdir(candidate_dir):
|
|
continue
|
|
for sub in os.listdir(candidate_dir):
|
|
if sub.startswith("bbctrl-") and sub.endswith(".egg"):
|
|
p = os.path.join(candidate_dir, sub, "bbctrl", "Web.py")
|
|
if os.path.isfile(p):
|
|
return p
|
|
return None
|
|
|
|
|
|
OLD_BLOCK = (
|
|
"class StaticFileHandler(tornado.web.StaticFileHandler):\n"
|
|
" def set_extra_headers(self, path):\n"
|
|
" self.set_header('Cache-Control',\n"
|
|
" 'no-store, no-cache, must-revalidate, max-age=0')"
|
|
)
|
|
|
|
NEW_BLOCK = (
|
|
"class StaticFileHandler(tornado.web.StaticFileHandler):\n"
|
|
" # FONT_MIME_FIX: Python 3.5's mimetypes module does not know\n"
|
|
" # woff/woff2/ttf, so Tornado serves them as application/octet-\n"
|
|
" # stream which Chromium 72 (the Pi's onboard kiosk browser)\n"
|
|
" # refuses to use as web fonts. Set explicit types so the FA6\n"
|
|
" # icon set actually renders on the kiosk display.\n"
|
|
" def get_content_type(self):\n"
|
|
" path = self.absolute_path or ''\n"
|
|
" if path.endswith('.woff2'): return 'font/woff2'\n"
|
|
" if path.endswith('.woff'): return 'font/woff'\n"
|
|
" if path.endswith('.ttf'): return 'font/ttf'\n"
|
|
" if path.endswith('.otf'): return 'font/otf'\n"
|
|
" if path.endswith('.eot'): return 'application/vnd.ms-fontobject'\n"
|
|
" return super().get_content_type()\n"
|
|
"\n"
|
|
" def set_extra_headers(self, path):\n"
|
|
" self.set_header('Cache-Control',\n"
|
|
" 'no-store, no-cache, must-revalidate, max-age=0')"
|
|
)
|
|
|
|
|
|
def main():
|
|
target = find_web_py()
|
|
if target is None:
|
|
print("ERROR: could not locate bbctrl Web.py under /usr/local/lib",
|
|
file=sys.stderr)
|
|
return 1
|
|
|
|
with open(target) as f:
|
|
src = f.read()
|
|
|
|
if "FONT_MIME_FIX" in src:
|
|
print("font mime: already patched ({})".format(target))
|
|
return 0
|
|
|
|
if OLD_BLOCK not in src:
|
|
print("font mime: expected block not found in {} -- skipping".format(target),
|
|
file=sys.stderr)
|
|
# Don't fail the deploy; just log and continue.
|
|
return 0
|
|
|
|
new_src = src.replace(OLD_BLOCK, NEW_BLOCK, 1)
|
|
with open(target, "w") as f:
|
|
f.write(new_src)
|
|
print("font mime: patched {}".format(target))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|