From 0ba88f8d34ec83fec237b4a12a6cf46db5811676 Mon Sep 17 00:00:00 2001
From: sideshowbarker
Date: Tue, 3 Feb 2026 16:15:38 +0900
Subject: [PATCH] Build markdown documents to HTML in the CI workflow
Add a build step that converts .md files in spec directories
(explainers, transition documents, proposals, etc.) to HTML so
they're served from GitHub Pages. The legacy draft server that
previously handled this conversion is struggling with ~60% uptime.
Relates to https://github.com/w3c/csswg-drafts/issues/12054
---
.github/workflows/build-specs.yml | 4 ++-
bin/build-markdown.py | 55 +++++++++++++++++++++++++++++++
2 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 bin/build-markdown.py
diff --git a/.github/workflows/build-specs.yml b/.github/workflows/build-specs.yml
index ca318245146c..5de93e468539 100644
--- a/.github/workflows/build-specs.yml
+++ b/.github/workflows/build-specs.yml
@@ -33,7 +33,7 @@ jobs:
python-version: "3.14"
cache: 'pip'
- - run: pip install bikeshed
+ - run: pip install bikeshed markdown
- run: bikeshed update
# The following chunk of code all stolen from andeubotella
@@ -63,6 +63,8 @@ jobs:
echo " $file"
bikeshed issues-list "$file" || true
done
+ - name: Build markdown
+ run: python ./bin/build-markdown.py
- name: Build index & symlinks
run: python ./bin/build-index.py
- run: rm -rf ./.git{,attributes,ignore}
diff --git a/bin/build-markdown.py b/bin/build-markdown.py
new file mode 100644
index 000000000000..cd8966f126e3
--- /dev/null
+++ b/bin/build-markdown.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+"""Convert markdown files in spec directories to HTML."""
+
+import glob
+import os
+import re
+
+import markdown
+
+TEMPLATE = """\
+
+
+{title}
+
+{body}
+"""
+
+
+def extract_title(text):
+ m = re.search(r"^#\s+(.+)", text, re.MULTILINE)
+ return m.group(1).strip() if m else "Untitled"
+
+
+def main():
+ md = markdown.Markdown(extensions=["fenced_code", "tables"])
+
+ for md_file in sorted(glob.glob("*/*.md")):
+ if md_file.startswith("."):
+ continue
+
+ html_file = os.path.splitext(md_file)[0] + ".html"
+ if os.path.exists(html_file):
+ continue
+
+ with open(md_file, encoding="utf-8") as f:
+ text = f.read()
+
+ title = extract_title(text)
+ body = md.convert(text)
+ md.reset()
+
+ with open(html_file, "w", encoding="utf-8") as f:
+ f.write(TEMPLATE.format(title=title, body=body))
+
+ print(f" {html_file}")
+
+
+if __name__ == "__main__":
+ main()