Skip to content

Commit 2aa683f

Browse files
committed
Try running the index generator.
1 parent d05dc6d commit 2aa683f

File tree

4 files changed

+294
-7
lines changed

4 files changed

+294
-7
lines changed

.github/workflows/build-specs.yml

+5-7
Original file line numberDiff line numberDiff line change
@@ -33,30 +33,28 @@ jobs:
3333

3434
- run: pip install bikeshed
3535
- run: bikeshed update
36+
- run: pip install jinja2
3637

3738
# The following chunk of code all stolen from andeubotella
3839
# Thanks, dude!
3940
- name: Correct local links
4041
run: python ./bin/configure-spec-data.py
41-
- name: Compile
42+
- name: Build specs
4243
run: |
4344
set -e
4445
for file in ./**/*.bs; do
4546
bikeshed -f spec "$file"
4647
done
47-
- name: Fix directory defaults
48+
- name: Rename to index.html
4849
# The default pages for directories are assumed to be Overview.html, but
4950
# that doesn't work for Github Pages. So we copy them as index.html.
5051
run: |
5152
set -e
5253
for file in ./**/Overview.html; do
5354
cp "$file" "$(dirname "$file")/index.html"
5455
done
55-
56-
# The index code uses more dependencies,
57-
# so I'll figure it out later.
58-
# - name: Build index
59-
# run: python ./bin/build-index.py
56+
- name: Build index & redirects
57+
run: python ./bin/build-index.py
6058
- run: rm -rf ./.git{,attributes,ignore}
6159

6260

bin/build-index.py

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
"""Builds the CSSWG directory index.
2+
3+
It also sets up redirects from shortnames to the current work spec, by building
4+
an index.html with a <meta refresh>.
5+
"""
6+
7+
import json
8+
import os
9+
import os.path
10+
import re
11+
import sys
12+
from collections import defaultdict
13+
14+
from html.parser import HTMLParser
15+
16+
from bikeshed import Spec, constants
17+
18+
import jinja2
19+
20+
jinja_env = jinja2.Environment(
21+
loader=jinja2.PackageLoader("build-index", "templates"),
22+
autoescape=jinja2.select_autoescape(),
23+
trim_blocks=True,
24+
lstrip_blocks=True
25+
)
26+
27+
28+
def title_from_html(file):
29+
class HTMLTitleParser(HTMLParser):
30+
def __init__(self):
31+
super().__init__()
32+
self.in_title = False
33+
self.title = ""
34+
self.done = False
35+
36+
def handle_starttag(self, tag, attrs):
37+
if tag == "title":
38+
self.in_title = True
39+
40+
def handle_data(self, data):
41+
if self.in_title:
42+
self.title += data
43+
44+
def handle_endtag(self, tag):
45+
if tag == "title" and self.in_title:
46+
self.in_title = False
47+
self.done = True
48+
self.reset()
49+
50+
parser = HTMLTitleParser()
51+
with open(file, encoding="UTF-8") as f:
52+
for line in f:
53+
parser.feed(line)
54+
if parser.done:
55+
break
56+
if not parser.done:
57+
parser.close()
58+
59+
return parser.title if parser.done else None
60+
61+
62+
def get_bs_spec_metadata(folder_name, path):
63+
spec = Spec(path)
64+
spec.assembleDocument()
65+
66+
level = int(spec.md.level) if spec.md.level else 0
67+
68+
if spec.md.shortname == "css-animations-2":
69+
shortname = "css-animations"
70+
elif spec.md.shortname == "css-gcpm-4":
71+
shortname = "css-gcpm"
72+
elif spec.md.shortname == "css-transitions-2":
73+
shortname = "css-transitions"
74+
elif spec.md.shortname == "scroll-animations-1":
75+
shortname = "scroll-animations"
76+
else:
77+
# Fix CSS snapshots (e.g. "css-2022")
78+
snapshot_match = re.match(
79+
"^css-(20[0-9]{2})$", spec.md.shortname)
80+
if snapshot_match:
81+
shortname = "css-snapshot"
82+
level = int(snapshot_match.group(1))
83+
else:
84+
shortname = spec.md.shortname
85+
86+
return {
87+
"shortname": shortname,
88+
"level": level,
89+
"title": spec.md.title,
90+
"workStatus": spec.md.workStatus
91+
}
92+
93+
94+
def get_html_spec_metadata(folder_name, path):
95+
match = re.match("^([a-z0-9-]+)-([0-9]+)$", folder_name)
96+
if match and match.group(1) == "css":
97+
shortname = "css-snapshot"
98+
title = f"CSS Snapshot {match.group(2)}"
99+
else:
100+
shortname = match.group(1) if match else folder_name
101+
title = title_from_html(path)
102+
103+
return {
104+
"shortname": shortname,
105+
"level": int(match.group(2)) if match else 0,
106+
"title": title,
107+
"workStatus": "completed" # It's a good heuristic
108+
}
109+
110+
111+
def build_redirect(shortname, spec_folder):
112+
"""Builds redirects from the shortname to the current work for that spec.
113+
114+
Since Github Actions doesn't allow anything like mod_rewrite, we do this by
115+
creating an empty index.html in the shortname folder that redirects to the
116+
correct spec.
117+
"""
118+
119+
template = jinja_env.get_template("redirect.html.j2")
120+
contents = template.render(spec_folder=spec_folder)
121+
122+
folder = os.path.join("./csswg-drafts", shortname)
123+
try:
124+
os.mkdir(folder)
125+
except FileExistsError:
126+
pass
127+
128+
index = os.path.join(folder, "index.html")
129+
with open(index, mode='w', encoding="UTF-8") as f:
130+
f.write(contents)
131+
132+
133+
CURRENT_WORK_EXCEPTIONS = {
134+
"css-conditional": 5,
135+
"css-easing": 2,
136+
"css-grid": 2,
137+
"css-snapshot": None, # always choose the last spec
138+
"css-values": 4,
139+
"css-writing-modes": 4,
140+
"web-animations": 2
141+
}
142+
143+
# ------------------------------------------------------------------------------
144+
145+
146+
constants.setErrorLevel("nothing")
147+
148+
specgroups = defaultdict(list)
149+
150+
for entry in os.scandir("./csswg-drafts"):
151+
if entry.is_dir():
152+
# Not actual specs, just examples.
153+
if entry.name in ["css-module"]:
154+
continue
155+
156+
bs_file = os.path.join(entry.path, "Overview.bs")
157+
html_file = os.path.join(entry.path, "Overview.html")
158+
if os.path.exists(bs_file):
159+
metadata = get_bs_spec_metadata(entry.name, bs_file)
160+
elif os.path.exists(html_file):
161+
metadata = get_html_spec_metadata(entry.name, html_file)
162+
else:
163+
# Not a spec
164+
continue
165+
166+
metadata["dir"] = entry.name
167+
metadata["currentWork"] = False
168+
specgroups[metadata["shortname"]].append(metadata)
169+
170+
# Reorder the specs with common shortname based on their level (or year, for
171+
# CSS snapshots), and determine which spec is the current work.
172+
for shortname, specgroup in specgroups.items():
173+
if len(specgroup) == 1:
174+
if shortname != specgroup[0]["dir"]:
175+
build_redirect(shortname, specgroup[0]["dir"])
176+
else:
177+
specgroup.sort(key=lambda spec: spec["level"])
178+
179+
# TODO: This algorithm for determining which spec is the current work
180+
# is wrong in a number of cases. Try and come up with a better
181+
# algorithm, rather than maintaining a list of exceptions.
182+
for spec in specgroup:
183+
if shortname in CURRENT_WORK_EXCEPTIONS:
184+
if CURRENT_WORK_EXCEPTIONS[shortname] == spec["level"]:
185+
spec["currentWork"] = True
186+
currentWorkDir = spec["dir"]
187+
break
188+
elif spec["workStatus"] != "completed":
189+
spec["currentWork"] = True
190+
currentWorkDir = spec["dir"]
191+
break
192+
else:
193+
specgroup[-1]["currentWork"] = True
194+
currentWorkDir = specgroup[-1]["dir"]
195+
196+
if shortname != currentWorkDir:
197+
build_redirect(shortname, currentWorkDir)
198+
if shortname == "css-snapshot":
199+
build_redirect("css", currentWorkDir)
200+
201+
with open("./csswg-drafts/index.html", mode='w', encoding="UTF-8") as f:
202+
template = jinja_env.get_template("index.html.j2")
203+
f.write(template.render(specgroups=specgroups))

bin/templates/index.html.j2

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<meta charset="UTF-8" />
3+
<title>CSS Working Group Draft Specifications</title>
4+
<style>
5+
:root {
6+
color-scheme: light dark;
7+
--text: black;
8+
--bg: #f7f8f9;
9+
--a-normal-text: #034575;
10+
--a-normal-underline: #707070;
11+
12+
background-color: var(--bg);
13+
color: var(--text);
14+
}
15+
16+
h1 {
17+
text-align: center;
18+
}
19+
20+
a[href] {
21+
color: var(--a-normal-text);
22+
text-decoration-color: var(--a-normal-underline);
23+
text-decoration-skip-ink: none;
24+
}
25+
26+
a[href]:focus,
27+
a[href]:hover {
28+
text-decoration-thickness: 2px;
29+
text-decoration-skip-ink: none;
30+
}
31+
32+
@media (prefers-color-scheme: dark) {
33+
:root {
34+
--text: #ddd;
35+
--bg: #080808;
36+
--a-normal-text: #6af;
37+
--a-normal-underline: #555;
38+
}
39+
}
40+
41+
li {
42+
margin-block-start: 1em;
43+
margin-block-end: 1em;
44+
}
45+
46+
li p,
47+
li ul,
48+
li ul li {
49+
margin-block-start: 0;
50+
margin-block-end: 0;
51+
}
52+
</style>
53+
54+
<h1>CSS Working Group Draft Specifications</h1>
55+
56+
<ul>
57+
{% for shortname, specgroup in specgroups|dictsort %}
58+
{% if specgroup|count == 1 %}
59+
{% set spec = specgroup[0] %}
60+
<li><a href="./{{ spec.dir }}">{{ spec.title }}</a></li>
61+
{% else %}
62+
<li>
63+
<p>{{ shortname }}</p>
64+
<ul>
65+
{% for spec in specgroup %}
66+
<li><a href="./{{ spec.dir }}">{{ spec.title }}</a>
67+
{%- if spec.currentWork -%}
68+
{{ "" }} (current work)
69+
{%- endif -%}
70+
</li>
71+
{% endfor %}
72+
</ul>
73+
</li>
74+
{% endif %}
75+
{% endfor %}
76+
</ul>

bin/templates/redirect.html.j2

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<!DOCTYPE html>
2+
<meta charset="UTF-8" />
3+
<meta http-equiv="refresh" content="0; URL=../{{ spec_folder }}/" />
4+
<title>Redirecting...</title>
5+
<style>
6+
:root {
7+
color-scheme: light dark;
8+
}
9+
</style>
10+
<p>Redirecting to <a href="../{{ spec_folder }}/">{{ spec_folder }}</a>...</p>

0 commit comments

Comments
 (0)