Files
cs249r_book/shared/release/release-pill.html
T
Vijay Janapa Reddi f7860e888b fix(release-pill): docstring no longer terminates outer HTML comment early
The note at lines 36-42 explained why you should not write literal `<!-- ... -->` inside the docstring, and in doing so, wrote literal `<!-- ... -->` inside the docstring. HTML disallows nested comments, so the inner `-->` token in the warning text closed the outer comment on line 1, leaking the example markup (span and script tags) into the rendered page on every Quarto site that included this snippet via include-after-body. The leaked script tag carries a literal U+2026 ellipsis as part of its src attribute (the docstring abbreviated the path), so every page fetched a 404 for `<subsite>/…release-pill.js`.

Rewrites the note to describe the rule without using the literal characters it warns against. The outer comment now opens on line 1 and closes on line 61 with no intermediate delimiters.

After this lands, the release-pill 404 disappears across vol1, vol2, tinytorch, kits, mlsysim, and instructors. The actual release-pill JS (inline script at the bottom of this file) was always intact — only the leak from the bad docstring was failing.
2026-05-13 21:52:11 -04:00

143 lines
5.1 KiB
HTML

<!--
MLSysBook release-pill — shared footer snippet for Quarto + static sites.
Renders a small "v0.1.0 · Apr 26, 2026" pill from a deployed
release-manifest.json. The pill is best-effort chrome: silent on any
fetch failure (manifest missing, offline, CORS). Use at any site that
emits a manifest via scripts/version/release.py emit-manifest.
── Per-project setup (one of two patterns) ──
Pattern A — Quarto, configured via _quarto.yml:
project:
resources:
- ../shared/release/release-pill.html
format:
html:
include-in-header:
- text: |
<meta name="release-manifest" content="/<project-base>/release-manifest.json">
include-after-body:
- file: ../shared/release/release-pill.html
The meta tag tells the snippet where to find the manifest. The
``include-after-body`` injects the snippet into every rendered page.
Pattern B — Next.js / hand-rolled HTML:
<meta name="release-manifest" content="/release-manifest.json" />
// in footer:
<span data-release-pill></span>
<script src="/release-pill.js"></script>
(See shared/release/release-pill.js for the standalone JS form.)
NOTE: This docstring must not contain literal HTML-comment delimiters
(open or close). HTML disallows nested comments, so an inner close
delimiter would terminate this outer comment early and leak the
example markup below it (the span and script tags) into the rendered
page — producing a 404 on every Quarto site that includes the snippet
via include-after-body. The earlier version of this note tripped that
exact bug; if you need to describe HTML-comment syntax, do it without
using the literal characters.
── Manifest contract ──
The manifest is the JSON written by scripts/version/release.py
emit-manifest. Required keys: releaseId, releaseHash. Optional but
recommended: buildDate, project. Anything else is ignored.
── Why runtime fetch instead of build-time templating ──
Build-time templating (Lua filter, Quarto partial with variable
interpolation) would be deterministic and faster but requires
per-project Lua/template machinery. Runtime fetch is project-agnostic
and works identically across Quarto, Next.js, and plain HTML. The
~50ms fetch cost happens once per page-load and is invisible because
the pill is below the fold. If a future project needs the version
visible above the fold, it should bake at build time (StaffML does
this for its citation card).
-->
<span class="release-pill" data-release-pill aria-live="polite">
<noscript><a href="#">release</a></noscript>
</span>
<style>
.release-pill {
display: inline-flex;
align-items: baseline;
gap: 0.25em;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
font-size: 0.75rem;
line-height: 1;
color: var(--bs-secondary, var(--release-pill-color, #6c757d));
}
.release-pill a {
color: inherit;
text-decoration: none;
}
.release-pill a:hover {
color: var(--bs-link-color, #0d6efd);
text-decoration: underline;
text-underline-offset: 2px;
}
.release-pill-date {
opacity: 0.7;
}
</style>
<script>
(function () {
var el = document.querySelector('[data-release-pill]');
if (!el) return;
// Manifest URL resolution order:
// 1. data-manifest attribute on the pill (per-page override)
// 2. <meta name="release-manifest" content="..."> in <head>
// 3. './release-manifest.json' relative to current page (fallback)
var meta = document.querySelector('meta[name="release-manifest"]');
var manifestPath =
el.getAttribute('data-manifest') ||
(meta && meta.getAttribute('content')) ||
'./release-manifest.json';
var linkHref = el.getAttribute('data-link') || '#release';
fetch(manifestPath, { cache: 'no-store' })
.then(function (res) { return res.ok ? res.json() : null; })
.then(function (m) {
if (!m || !m.releaseId || !m.releaseHash) return;
var date = m.buildDate
? new Date(m.buildDate).toLocaleDateString('en-US',
{ year: 'numeric', month: 'short', day: 'numeric' })
: '';
var shortHash = String(m.releaseHash).slice(0, 7);
var title =
(m.project ? m.project + ' ' : '') +
'v' + m.releaseId +
(date ? ' · built ' + date : '') +
' · hash ' + shortHash;
var a = document.createElement('a');
a.href = linkHref;
a.title = title;
a.setAttribute('aria-label',
'Release v' + m.releaseId + (date ? ', built ' + date : ''));
var v = document.createElement('span');
v.className = 'release-pill-version';
v.textContent = 'v' + m.releaseId;
a.appendChild(v);
if (date) {
var d = document.createElement('span');
d.className = 'release-pill-date';
d.textContent = ' · ' + date;
a.appendChild(d);
}
el.innerHTML = '';
el.appendChild(a);
})
.catch(function () { /* silent: best-effort chrome */ });
})();
</script>