70 lines
2.3 KiB
Python
70 lines
2.3 KiB
Python
"""Stamping for the service worker.
|
|
|
|
The dev server stamps sw.js on every request; the deploy stamps it once into
|
|
the uploaded copy. Both import this, because a digest computed two different
|
|
ways is a worker that thinks its caches are current when they are not.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import os
|
|
|
|
WORKER = "sw.js"
|
|
|
|
VERSION_LINE = "const VERSION = 'dev';"
|
|
LIBRARY_VERSION_LINE = "const LIBRARY_VERSION = 'dev';"
|
|
LIBRARY_LINE = "const LIBRARY = [];"
|
|
|
|
CHUNK = 1 << 20
|
|
|
|
def walk(roots):
|
|
found = {}
|
|
for root in roots:
|
|
for dirpath, _dirs, names in os.walk(root):
|
|
for name in sorted(names):
|
|
path = os.path.join(dirpath, name)
|
|
rel = os.path.relpath(path, root).replace(os.sep, "/")
|
|
found.setdefault(rel, path)
|
|
return found
|
|
|
|
def digest(files):
|
|
# Contents, not size and mtime: a checkout, a rebuild and a rsync all move
|
|
# mtimes without changing a byte, and each one of those would otherwise
|
|
# throw away every cache on every device for nothing.
|
|
out = hashlib.sha256()
|
|
for rel in sorted(files):
|
|
out.update(rel.encode("utf-8") + b"\0")
|
|
with open(files[rel], "rb") as fh:
|
|
for chunk in iter(lambda: fh.read(CHUNK), b""):
|
|
out.update(chunk)
|
|
out.update(b"\0")
|
|
return out.hexdigest()[:12]
|
|
|
|
def page_url(rel):
|
|
if rel == "index.html":
|
|
return "./"
|
|
return "./" + (rel[:-len(".html")] if rel.endswith(".html") else rel)
|
|
|
|
def stamp_worker(roots):
|
|
chrome = walk(roots[:1])
|
|
library = {rel: path for rel, path in walk(roots).items() if rel not in chrome}
|
|
|
|
src = chrome.pop(WORKER, None)
|
|
if src is None:
|
|
return None
|
|
with open(src, encoding="utf-8") as fh:
|
|
body = fh.read()
|
|
|
|
for line, value in (
|
|
(VERSION_LINE, "const VERSION = '%s';" % digest(chrome)),
|
|
(LIBRARY_VERSION_LINE, "const LIBRARY_VERSION = '%s';" % digest(library)),
|
|
(LIBRARY_LINE, "const LIBRARY = %s;"
|
|
% json.dumps(sorted(page_url(rel) for rel in library), indent=2)),
|
|
):
|
|
if line not in body:
|
|
raise RuntimeError("%s: expected to find %r to stamp" % (WORKER, line))
|
|
body = body.replace(line, value, 1)
|
|
return body.encode("utf-8")
|