build.py
Python script, Unicode text, UTF-8 text executable
1from Renderers import RenderTemplate, RenderMarkdown 2from sys import argv 3from shutil import rmtree as DeleteDirectory 4from os import mkdir as CreateDirectory, listdir as ListDirectory, unlink as DeleteFile 5from os.path import isfile as IsFile, exists as PathExists 6from distutils.dir_util import copy_tree as CopyDirectory 7from datetime import datetime 8from json import dump as DumpJSON 9from yaml import safe_load as LoadYML 10from re import sub as RegReplace 11from typing import Literal 12 13DEPLOY_BUILD_DIR = { 14"gh-pages-deploy": "build", 15"gl-pages-deploy": "public", 16"cb-pages": "build", 17} # Separate because this site is built with an action that won't work if they aren't 18LOCAL_BUILD_DIR = "build" 19 20PAGES_DEPLOY = argv[1] if len(argv) > 1 else None 21BUILD_DIRECTORY = DEPLOY_BUILD_DIR[PAGES_DEPLOY] if PAGES_DEPLOY is not None else LOCAL_BUILD_DIR 22 23PAGES = { 24"index.html": "index.html", 25"blog-list.html": "blog/index.html", 26"blog-feed.rss": "blog/feed.rss", 27"blog-feed.atom": "blog/feed.atom", 28"404.html": "404.html", 29} 30 31DISALLOWED_SITEMAP = { 32"404.html", 33"blog-feed.rss", 34"blog-feed.atom", 35"blog-list.html", 36"index.html", 37} 38 39REDIRECTS = { 40"link-tree.html": "list/link-tree.html", # Old location -> new location 41} 42 43SITEMAP_HREF = "https://steve0greatness.github.io/" 44sitemap = [ 45SITEMAP_HREF + "blog/", 46SITEMAP_HREF, 47] 48 49def EscapeHTMLForRSS(HTML: str) -> str: 50values = { 51"&": "&", # this is here for a reason, do not change order. 52"<": "<", 53">": ">", 54"§": "§", 55} 56RssHtml = HTML 57for old, new in values.items(): 58RssHtml = RssHtml.replace(old, new) 59return RssHtml 60 61def WipeFinalDir(): 62if not PathExists(BUILD_DIRECTORY): 63print("Directory didn't existing, creating it...") 64CreateDirectory(BUILD_DIRECTORY) 65return 66print("Directory exists, wiping it...") 67for item in ListDirectory(BUILD_DIRECTORY): 68path = BUILD_DIRECTORY + "/" + item 69if IsFile(path): 70DeleteFile(path) 71continue 72DeleteDirectory(path) 73 74def PostDateToDateObj(Date): 75return datetime.strptime(Date, "%Y %b %d") 76 77def PostSortHelper(Post): 78return PostDateToDateObj(Post["date"]) 79 80def GetBlogList(): 81print("Grabbing post list") 82PostSlugs: tuple[tuple[Literal["blog-posts", "drafts"], str], ...] = tuple( ("blog-posts", file) for file in ListDirectory("blog-posts") ) 83if not PAGES_DEPLOY and PathExists("drafts"): 84PostSlugs = PostSlugs + tuple( ("drafts", file) for file in ListDirectory("drafts") ) 85Posts = [] 86for dir, slug in PostSlugs: 87if not slug.endswith(".md"): 88continue 89print("Grabbing post list blog-posts/%s" % (slug)) 90with open(dir + "/" + slug, encoding="utf-8") as MDFile: 91RawMD = MDFile.read() 92PostHTML = RenderMarkdown(RawMD) 93Item = PostHTML.metadata 94Item["content"] = PostHTML 95Item["raw-content"] = RawMD 96Item["rss-content"] = EscapeHTMLForRSS(PostHTML) 97Item["rss-post-time"] = PostDateToDateObj(Item["date"]).strftime("%a, %d %b %Y") + " 00:00:00 GMT" 98Item["atom-post-time"] = PostDateToDateObj(Item["date"]).strftime("%Y-%m-%d") + "T00:00:00Z" 99Item["opengraph-date"] = PostDateToDateObj(Item["date"]).strftime("%Y-%m-%d") 100Item["opengraph-update"] = Item["opengraph-date"] 101Item["atom-update-time"] = Item["atom-post-time"] 102if "updated" in Item: 103Item["atom-update-time"] = PostDateToDateObj(Item["updated"]).strftime("%Y-%m-%d") + "T00:00:00Z" 104Item["opengraph-update"] = PostDateToDateObj(Item["updated"]).strftime("%Y-%m-%d") 105Item["pathname"] = slug.replace(".md", ".html") 106Item["plaintext"] = slug.replace(".md", ".txt") 107Item["origin"] = slug 108Item["is-draft"] = dir == "drafts" 109Posts.append(Item) 110PostsByDate = sorted(Posts, key=PostSortHelper, reverse=True) 111return PostsByDate 112 113PostList = [] 114 115 116def ListParseCategory(Obj, depth): 117html = "<h%d id=\"%s\">%s</h%d>" % (2+depth, Obj["id"], Obj["title"], 2+depth) 118if "paragraph" in Obj: 119html += "<p>%s</p>" % Obj["paragraph"] 120listType = "ul" 121if "list-type" in Obj and Obj["list-type"] == "ordered": 122listType = "ol" 123html += "<%s>" % listType 124for item in Obj["list"]: 125html += "<li>" + LIST_PARSER_DICT[item["type"]](item, depth + 1) + "</li>" 126html += "</%s>" % listType 127return html 128 129def ListParseLink(Obj, depth): 130html = "<a href=\"%s\">" % Obj["href"] 131text = Obj["text"] 132if "text-type" in Obj and Obj["text-type"] == "text/markdown": 133text = RenderMarkdown(text).replace("<p>", "").replace("</p>", "") 134html += text + "</a>" 135if "comment" in Obj: 136html += "(%s)" % Obj["comment"] 137return html 138 139def ListParseText(Obj, depth): 140text = Obj["text"] 141# if "text-type" in Obj and Obj["text-type"] == "text/markdown": 142# print(RenderMarkdown(text)) 143# text = RenderMarkdown(text) # this doesn't work??? 144if "comment" in Obj: 145text += "(%s)" % Obj["comment"] 146return text 147 148LIST_PARSER_DICT = { 149"category": ListParseCategory, 150"link": ListParseLink, 151"text": ListParseText, 152} 153 154def GetLists(): 155ListSlugs = ListDirectory("lists") 156Lists = [] 157for slug in ListSlugs: 158List = { 159"title": "", 160"content": "", 161"filename": slug 162} 163with open("lists/" + slug) as ListYML: 164ListDict = LoadYML(ListYML.read()) 165List["title"] = ListDict["title"] 166if "paragraph" in ListDict: 167List["content"] += "<p>%s</p>" % ListDict["paragraph"] 168List["content"] += "<ul>" 169for item in ListDict["list"]: 170List["content"] += "<li>" + LIST_PARSER_DICT[item["type"]](item, 0) + "</li>" 171List["content"] += "</ul>" 172Lists.append(List) 173sitemap.append(SITEMAP_HREF + "list/" + slug) 174sitemap.append(SITEMAP_HREF + "list/") 175return Lists 176 177def RenderPosts(): 178global PostList 179for post in PostList: 180Revised = post["updated"] if "updated" in post else False 181RenderedHTML = RenderTemplate( 182"blog-post.html", 183Revised=Revised, 184Title=post["title"], 185PostDate=post["date"], 186Content=post["content"], 187PostPath=post["pathname"], 188PlaintextPath=post["plaintext"], 189IsDraft=post["is-draft"], 190OpenGraphDate=post["opengraph-date"], 191post=post 192) 193print("Building blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["pathname"])) 194with open(BUILD_DIRECTORY + "/blog/" + post["pathname"], "w", encoding="utf-8") as PostHTMLFile: 195PostHTMLFile.write(RenderedHTML) 196print("Copying blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["plaintext"])) 197with open(BUILD_DIRECTORY + "/blog/" + post["plaintext"], "w", encoding="utf-8") as PostHTMLFile: 198PostHTMLFile.write(post["raw-content"]) 199sitemap.append(SITEMAP_HREF + "blog/" + post["pathname"]) 200 201def RenderPage(PageInput: str, ContentDest: str, AllowSitemap: bool = True, **kwargs): 202print("Building views/%s to %s/%s" % (PageInput, BUILD_DIRECTORY, ContentDest)) 203if AllowSitemap: 204sitemap.append(SITEMAP_HREF + ContentDest) 205with open(BUILD_DIRECTORY + "/" + ContentDest, "w", encoding="utf-8") as DestLocation: 206DestLocation.write(RenderTemplate(PageInput, **kwargs)) 207 208def CreateJSONFeed(): 209global PostList 210CreatedJSON = { 211"version": "https://jsonfeed.org/version/1", 212"title": "Steve0Greatness' Blog", 213"home_page_url": "https://steve0greatness.github.io", 214"feed_url": "https://steve0greatness.github.io/blog/feed.rss", 215"language": "en-US", 216"favicon": "https://steve0greatness.github.io/favicon.ico", 217"description": "A blog by a human being.", 218"authors": [ 219{ 220"name": "Steve0Greatness", 221"url": "https://steve0greatness.github.io" 222} 223], 224"items": [] 225} 226for post in PostList: 227CreatedJSON["items"].append({ 228"id": "https://steve0greatness.github.io/blog/" + post["pathname"], 229"title": "JSON Feed version 1.1", 230"icon": "https://steve0greatness.github.io/favicon.ico", 231"content_html": post["content"], 232"date_published": post["atom-post-time"], 233"date_modified": post["atom-update-time"], 234"url": "https://steve0greatness.github.io/blog/" + post["pathname"] 235}) 236with open(BUILD_DIRECTORY + "/blog/feed.json", "w") as JSONFeedFile: 237DumpJSON(CreatedJSON, JSONFeedFile) 238 239def RenderLists(): 240Lists = GetLists() 241CreateDirectory(BUILD_DIRECTORY + "/list/") 242ListIndex = "<ul>" 243for List in Lists: 244FileLocation = "/list/" + List["filename"].replace(".yml", ".html") 245Title = List["title"] 246print("%s -> %s" % ("lists/" + List["filename"], BUILD_DIRECTORY + FileLocation)) 247with open(BUILD_DIRECTORY + FileLocation, "w") as file: 248file.write(RenderTemplate("list.html", Content=List["content"], Title=Title, Location=FileLocation)) 249ListIndex += "<li><a href=\"%s\">%s</a></li>" % (FileLocation, Title) 250ListIndex += "</ul>" 251print("Building list index") 252with open(BUILD_DIRECTORY + "/list/index.html", "w") as file: 253file.write(RenderTemplate("list-index.html", Content=ListIndex)) 254 255def main(): 256global PostList 257PostList = GetBlogList() 258print("Wiping directory") 259WipeFinalDir() 260print("Creating blog holder") 261CreateDirectory(BUILD_DIRECTORY + "/blog") 262print("Rendering posts") 263RenderPosts() 264CreateJSONFeed() 265print("Copying static directory") 266CopyDirectory("static", BUILD_DIRECTORY) 267print("Creating lists") 268RenderLists() 269 270print("Building pages") 271for file, path in PAGES.items(): 272AllowSitemap = file not in DISALLOWED_SITEMAP 273RenderPage(file, path, AllowSitemap, PostList=PostList) 274 275print("Building redirects") 276for OldLocation, NewLocation in REDIRECTS.items(): 277RenderPage("redirect.html", OldLocation, False, redirect=NewLocation, old=OldLocation) 278 279with open(BUILD_DIRECTORY + "/sitemap.txt", "w") as SitemapFile: 280SitemapFile.write("\n".join(sitemap)) 281 282if __name__ == "__main__": 283main() 284 285