diff --git a/.github/workflows/build-specs.yml b/.github/workflows/build-specs.yml index de44a693cc01..ca318245146c 100644 --- a/.github/workflows/build-specs.yml +++ b/.github/workflows/build-specs.yml @@ -57,6 +57,12 @@ jobs: SHORT_DATE="$(date --date=@"$TIMESTAMP" --utc +%F)" bikeshed -f spec "$file" "${file%Overview.bs}index.html" --md-date="$SHORT_DATE" --md-Text-Macro="BUILTBYGITHUBCI foo" done + - name: Build issues lists + run: | + for file in ./**/*.bsi ./**/issues-*.txt; do + echo " $file" + bikeshed issues-list "$file" || true + done - name: Build index & symlinks run: python ./bin/build-index.py - run: rm -rf ./.git{,attributes,ignore} diff --git a/bin/build-index.py b/bin/build-index.py index be3a52ca200b..9b81e4fcd21d 100644 --- a/bin/build-index.py +++ b/bin/build-index.py @@ -2,18 +2,19 @@ All the drafts are built by the build-specs workflow itself. This handles the rest of the work: -* creates a root page that just redirects to the draft server root listing. +* creates an index page listing all specs * creates symlinks for unlevelled urls, linking to the appropriate levelled folder -* builds timestamps.json, which provides a bunch of metadata about the specs which is consumed by some W3C tooling. +* builds timestamps.json, which provides metadata about the specs """ +import glob import json import os import os.path import re -import sys import subprocess from collections import defaultdict +from datetime import datetime, timezone import bikeshed from html.parser import HTMLParser @@ -122,6 +123,21 @@ def create_symlink(shortname, spec_folder): pass +def format_timestamp(ts): + """Format a Unix timestamp as a human-readable date string.""" + dt = datetime.fromtimestamp(ts, tz=timezone.utc) + return dt.strftime("%Y-%m-%d") + + +def escape_html(text): + """Escape HTML special characters.""" + return (text + .replace("&", "&") + .replace("<", "<") + .replace(">", ">") + .replace('"', """)) + + CURRENT_WORK_EXCEPTIONS = { "css-conditional": 5, "css-easing": 2, @@ -159,6 +175,9 @@ def create_symlink(shortname, spec_folder): metadata["dir"] = entry.name metadata["currentWork"] = False + issues_files = sorted(f for f in glob.glob(os.path.join(entry.path, "issues-*.html")) + if not f.endswith(".bsi.html")) + metadata["issues"] = [os.path.basename(f) for f in issues_files] specgroups[metadata["shortname"]].append(metadata) # Reorder the specs with common shortname based on their level (or year, for @@ -195,13 +214,359 @@ def create_symlink(shortname, spec_folder): with open('./timestamps.json', 'w') as f: json.dump(timestamps, f, indent = 2, sort_keys=True) -with open("./index.html", mode='w', encoding="UTF-8") as f: - f.write(""" +# Build the index page + +# Flatten all specs into a single list with full metadata +all_specs = [] +for shortname, specgroup in specgroups.items(): + group_size = len(specgroup) + for spec in specgroup: + dir_name = spec["dir"] + ts = timestamps.get(dir_name, 0) + title = spec["title"] or dir_name + + doc_links = [] + for fname in spec.get("issues", []): + label = fname.replace("issues-", "").replace(".html", "") + doc_links.append((f"./{dir_name}/{fname}", label)) + + all_specs.append({ + "shortname": shortname, + "dir": dir_name, + "title": title, + "ts": ts if isinstance(ts, int) else 0, + "currentWork": spec["currentWork"], + "level": spec["level"], + "group_size": group_size, + "doc_links": doc_links, + }) + +# Sort by timestamp descending (most recent first) for default view +all_specs.sort(key=lambda s: s["ts"], reverse=True) + +# Generate HTML for each spec +REPO = "https://github.com/w3c/csswg-drafts" +spec_items = [] +for spec in all_specs: + t = escape_html(spec["title"]) + d = spec["dir"] + sn = spec["shortname"] + ts = spec["ts"] + lv = spec["level"] + gs = spec["group_size"] + date_str = format_timestamp(ts) if ts else "" + iso_date = datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") if ts else "" + + cw = ' Current Work' if spec["currentWork"] else "" + + links = [ + f'Issues', + f'PRs', + f'History', + ] + for url, label in spec["doc_links"]: + links.append(f'DoC: {escape_html(label)}') + links_html = ' \u00b7 '.join(links) + + spec_items.append( + f'
\n' + f'
\n' + f' \n' + f' {t}{cw}\n' + f' {escape_html(d)}\n' + f' \n' + f'
\n' + f' \n' + f'
' + ) + +specs_html = "\n".join(spec_items) + +HTML_START = """\ - -Redirecting to the Drafts listing... - - -""") + + + + CSS Working Group Editor Drafts + + + +

CSS Working Group Editor Drafts

+ +
+ + + +
+
+""" + +HTML_END = """\ +
+
No matching specifications.
+ + + +""" + +with open("./index.html", mode='w', encoding="UTF-8") as f: + f.write(HTML_START) + f.write(specs_html) + f.write(HTML_END)