fix: bug scan — API hardening, safer disk picker, updater rollback, friendlier installer #19

Merged
daniel merged 7 commits from fix/bug-scan-hw-compat into main 2026-09-28 14:06:09 +02:00
Showing only changes of commit 87dcb369db - Show all commits

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# ruff: noqa: E501 — inline SVG markup reads better unwrapped
"""Generate the logo-poll showcase assets for /logo/ from one geometry source. """Generate the logo-poll showcase assets for /logo/ from one geometry source.
python3 website/tools/logo-assets.py python3 website/tools/logo-assets.py
@ -10,6 +11,7 @@ the output — the site build itself needs nothing beyond Hugo.
Mark M: lowercase f knocked out of a gate, 64-unit grid (even-odd fill). Mark M: lowercase f knocked out of a gate, 64-unit grid (even-odd fill).
Mark R: window, wheel and F, 104x114 grid, stroke 8 (from Robert's sketch). Mark R: window, wheel and F, 104x114 grid, stroke 8 (from Robert's sketch).
""" """
import base64 import base64
import io import io
import json import json
@ -119,7 +121,9 @@ def raster(svgtxt, w, h):
def mark_png(k, tier, height, color=None): def mark_png(k, tier, height, color=None):
_, _, w, h = MARKS[k]["vb"] _, _, w, h = MARKS[k]["vb"]
return raster(svg(k, tier, color or MARKS[k]["color"], standalone=True), round(height * w / h), height) return raster(
svg(k, tier, color or MARKS[k]["color"], standalone=True), round(height * w / h), height
)
# ---------------------------------------------------------------- wordmark # ---------------------------------------------------------------- wordmark
@ -141,7 +145,9 @@ def wordmark(text, size, x0=0.0, baseline=0.0, tracking=-0.028):
g.draw(pen) g.draw(pen)
d = pen.getCommands() d = pen.getCommands()
if d: if d:
parts.append(f'<path transform="translate({x:.2f} {baseline:.2f}) scale({s:.5f} {-s:.5f})" d="{d}"/>') parts.append(
f'<path transform="translate({x:.2f} {baseline:.2f}) scale({s:.5f} {-s:.5f})" d="{d}"/>'
)
x += g.width * s + tracking * size x += g.width * s + tracking * size
return "".join(parts), x - x0 - tracking * size return "".join(parts), x - x0 - tracking * size
@ -241,21 +247,24 @@ def vote_card(path):
m = mark_png(k, "flat", 330) m = mark_png(k, "flat", 330)
im.alpha_composite(m, (cx - m.width // 2, 110 - (m.height - 330) // 2)) im.alpha_composite(m, (cx - m.width // 2, 110 - (m.height - 330) // 2))
size = 46 size = 46
d = ImageDraw.Draw(im)
for text, cx, col in (("M", W // 4, "#6b6b6f"), ("R", 3 * W // 4, "#8a8a90")): for text, cx, col in (("M", W // 4, "#6b6b6f"), ("R", 3 * W // 4, "#8a8a90")):
paths, tw = wordmark(text, size) paths, tw = wordmark(text, size)
# letters via the same outlines: rasterise a tiny SVG for crisp text # letters via the same outlines: rasterise a tiny SVG for crisp text
svgtxt = (f'<svg {XMLNS} viewBox="0 -{size} {tw:.0f} {size * 1.3:.0f}">' svgtxt = (
f'<g fill="{col}">{paths}</g></svg>') f'<svg {XMLNS} viewBox="0 -{size} {tw:.0f} {size * 1.3:.0f}">'
f'<g fill="{col}">{paths}</g></svg>'
)
t = raster(svgtxt, round(tw), round(size * 1.3)) t = raster(svgtxt, round(tw), round(size * 1.3))
im.alpha_composite(t, (cx - t.width // 2, 500)) im.alpha_composite(t, (cx - t.width // 2, 500))
# url line, centred on the seam # url line, centred on the seam
paths, tw = wordmark("furtka.org/logo", 40) paths, tw = wordmark("furtka.org/logo", 40)
svgtxt = (f'<svg {XMLNS} viewBox="0 -40 {tw:.0f} 52"><g fill="#c03a28">{paths}</g></svg>') svgtxt = f'<svg {XMLNS} viewBox="0 -40 {tw:.0f} 52"><g fill="#c03a28">{paths}</g></svg>'
t = raster(svgtxt, round(tw), 52) t = raster(svgtxt, round(tw), 52)
pad = 18 pad = 18
box = Image.new("RGBA", (t.width + 2 * pad, t.height + 2 * pad), "#f7f6f3") box = Image.new("RGBA", (t.width + 2 * pad, t.height + 2 * pad), "#f7f6f3")
ImageDraw.Draw(box).rounded_rectangle([0, 0, box.width - 1, box.height - 1], 14, outline="#e4e3dc") ImageDraw.Draw(box).rounded_rectangle(
[0, 0, box.width - 1, box.height - 1], 14, outline="#e4e3dc"
)
box.alpha_composite(t, (pad, pad)) box.alpha_composite(t, (pad, pad))
im.alpha_composite(box, (W // 2 - box.width // 2, 40)) im.alpha_composite(box, (W // 2 - box.width // 2, 40))
im.convert("RGB").save(path, optimize=True) im.convert("RGB").save(path, optimize=True)