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