#!/usr/bin/env python3 """Bundle the deck into one self-contained HTML file for uploading. python3 slides/build.py # writes slides/dist/hands-on-bgp.html Everything the page fetches at runtime is inlined: reveal.js and its plugins (from the vendored copy, so run vendor-reveal.sh first), the stylesheets, the favicon, our scripts and the Markdown sections. The result opens from any web server and from disk. Two things still need a network: the IBM Plex fonts (Google Fonts) and the live DNS and RIPE lookups. """ import base64 import os import re import sys HERE = os.path.dirname(os.path.abspath(__file__)) REVEAL = os.path.join(HERE, "reveal.js", "dist") OUT = os.path.join(HERE, "dist", "hands-on-bgp.html") def read(*parts): with open(os.path.join(HERE, *parts), encoding="utf-8") as f: return f.read() def main(): if not os.path.isdir(REVEAL): sys.exit("reveal.js is not vendored; run slides/vendor-reveal.sh first") html = read("index.html") # Stylesheets: reveal's first, then ours, as the loader orders them. css = "\n".join(read("reveal.js", "dist", p) for p in ["reset.css", "reveal.css", "plugin/highlight/monokai.css"]) css += "\n" + read("theme.css") + "\n" + read("style.css") html = re.sub(r'\s*', "", html) html = re.sub(r'\s*', f"\n ", html, count=1) icon = "data:image/svg+xml;base64," + base64.b64encode(read("images", "favicon.svg").encode()).decode() html = html.replace('href="images/favicon.svg"', f'href="{icon}"') # Sections: the Markdown goes inline in a text/template script. def inline_section(m): attrs, path = m.group(1), m.group(2) text = read(path) if "\n\n' html, n = re.subn(r'', lambda m: inline_section(type("M", (), {"group": lambda self, i: (None, m.group(2), m.group(1))[i]})()), html) if n == 0: sys.exit("no data-markdown sections found in index.html") # Our scripts. for name in ["dns-lookup.js", "prefix-explorer.js"]: html, k = re.subn(rf'', lambda m: f"", html) if k != 1: sys.exit(f"expected one ', "", html, count=1, flags=re.S) # The loader: the module script that picks a reveal.js location and # imports it becomes reveal.js itself, its plugins, and the deck setup. m = re.search(r'', html, flags=re.S) if not m: sys.exit("loader script not found") loader = m.group(1) setup_start = loader.index("// BIRD configuration") setup = loader[setup_start:] setup = setup.replace("await deck.initialize().catch(fail(\"reveal.js failed to start\"));", "deck.initialize().then(() => {") setup = setup.replace("plugins: [Markdown, Highlight, Notes],", "plugins: [RevealMarkdown, RevealHighlight, RevealNotes],") setup = setup.rstrip() + "\n });\n" libs = "\n".join(read("reveal.js", "dist", p) for p in ["reveal.js", "plugin/markdown.js", "plugin/highlight.js", "plugin/notes.js"]) html = html[: m.start()] + f"\n " + html[m.end():] if "await " in setup or "import(" in setup: sys.exit("loader still contains module-only code") html = html.replace("reveal.js is loaded from ./reveal.js/dist if present", "Built by build.py from index.html: everything is inlined. reveal.js was loaded from ./reveal.js/dist if present") os.makedirs(os.path.dirname(OUT), exist_ok=True) with open(OUT, "w", encoding="utf-8") as f: f.write(html) print(f"wrote {os.path.relpath(OUT)} ({os.path.getsize(OUT) // 1024} KB, {n} sections)") if __name__ == "__main__": main()