Commit 3de17dec49

3de17dec498be2ba9b8875cba443243a7d759d0c

parent: 2848cb0b99

Verified · cmc

cmc <hello@cleberg.net> · 2026-03-11 05:07 UTC

Update build.py post-processing pipeline

Layout: unified · split

build.py +55 −8
@@ -183,13 +183,16 @@ def get_recent_posts_html(content_dir="./content/blog", num_posts=3):
183 183
184 # Build HTML lines 184 # Build HTML lines
185 lines = [] 185 lines = []
186 current_year = None
186 for post in recent: 187 for post in recent:
187 lines.append('\t<div class="post">') 188 year = post["date_obj"].year
188 lines.append( 189 if year != current_year:
189 f'\t\t<time datetime="{post["date_str"]}">{post["date_full"]}</time>' 190 current_year = year
190 ) 191 lines.append(f'\t<li class="post-list-year">{year}</li>')
191 lines.append(f'\t\t\t<a href="/blog/{post["slug"]}.html">{post["title"]}</a>') 192 lines.append('\t<li class="post-list-item">')
192 lines.append("\t</div>") 193 lines.append(f'\t\t<a href="/blog/{post["slug"]}.html">{post["title"]}</a>')
194 lines.append(f'\t\t<time datetime="{post["date_str"]}">{post["date_full"]}</time>')
195 lines.append("\t</li>")
193 196
194 return "\n".join(lines) 197 return "\n".join(lines)
195 198
@@ -259,12 +262,12 @@ def run_emacs_publish(dev_mode=True):
259 print(result.stderr, file=sys.stderr) 262 print(result.stderr, file=sys.stderr)
260 sys.exit(1) 263 sys.exit(1)
261 264
262 annoying_file = Path(".build/cleberg-net.html") 265 annoying_file = Path(".build/seijaku.html")
263 if annoying_file.exists(): 266 if annoying_file.exists():
264 os.remove(annoying_file) 267 os.remove(annoying_file)
265 else: 268 else:
266 print( 269 print(
267 "Warning: .build/cleberg-net.html not found, but Emacs exited successfully." 270 "Warning: .build/seijaku.html not found, but Emacs exited successfully."
268 ) 271 )
269 272
270 273
@@ -336,6 +339,48 @@ def generate_sitemap(build_dir=".build", base_url="https://cleberg.net"):
336 print(f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries.") 339 print(f"Sitemap generated at {sitemap_path} with {len(sitemap_entries)} entries.")
337 340
338 341
342def inject_blog_year_separators(blog_index_path="./.build/blog/index.html"):
343 """
344 Post-processes the rendered blog index to inject year separator <li> elements
345 between groups of posts. Weblorg/templatel doesn't support mutable loop state,
346 so this runs after the HTML is generated.
347
348 Finds each <li class="post-list-item"> that contains a <time datetime="YYYY-MM-DD">,
349 and inserts <li class="post-list-year">YYYY</li> before the first post of each year.
350 """
351 path = Path(blog_index_path)
352 if not path.exists():
353 print(f"Warning: {blog_index_path} not found, skipping year separators.")
354 return
355
356 content = path.read_text(encoding="utf-8")
357
358 # Match each post list item, capturing the date and the full element
359 item_pattern = re.compile(
360 r'(<li class="post-list-item">.*?</li>)',
361 re.DOTALL,
362 )
363 date_pattern = re.compile(r"datetime=['\"]?(\d{4})-\d{2}-\d{2}['\"]?")
364
365 current_year = None
366 def replace_item(m):
367 nonlocal current_year
368 item_html = m.group(1)
369 date_match = date_pattern.search(item_html)
370 if not date_match:
371 return item_html
372 year = date_match.group(1)
373 if year != current_year:
374 current_year = year
375 separator = f'<li class="post-list-year">{year}</li>'
376 return f"{separator}\n{item_html}"
377 return item_html
378
379 new_content = item_pattern.sub(replace_item, content)
380 path.write_text(new_content, encoding="utf-8")
381 print(f"Blog year separators injected into {blog_index_path}")
382
383
339def deploy_to_server(build_dir, server): 384def deploy_to_server(build_dir, server):
340 remote_path = f"{server}:/var/www/cleberg.net/" 385 remote_path = f"{server}:/var/www/cleberg.net/"
341 print(f"Deploying .build/ → {remote_path}") 386 print(f"Deploying .build/ → {remote_path}")
@@ -384,6 +429,7 @@ def main():
384 run_emacs_publish(dev_mode=False) 429 run_emacs_publish(dev_mode=False)
385 copy_org_sources() 430 copy_org_sources()
386 update_index_html(html_snippet) 431 update_index_html(html_snippet)
432 inject_blog_year_separators()
387 minify_html("./.build/index.html", "./.build/index.html") 433 minify_html("./.build/index.html", "./.build/index.html")
388 generate_sitemap() 434 generate_sitemap()
389 if deploy: 435 if deploy:
@@ -398,6 +444,7 @@ def main():
398 run_emacs_publish(dev_mode=True) 444 run_emacs_publish(dev_mode=True)
399 copy_org_sources() 445 copy_org_sources()
400 update_index_html(html_snippet) 446 update_index_html(html_snippet)
447 inject_blog_year_separators()
401 minify_html("./.build/index.html", "./.build/index.html") 448 minify_html("./.build/index.html", "./.build/index.html")
402 generate_sitemap() 449 generate_sitemap()
403 if deploy: 450 if deploy: