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