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