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: 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"] 149for item in ListDict["list"]: 150List["content"] += LIST_PARSER_DICT[item["type"]](item, 0) 151Lists.append(List) 152sitemap.append(SITEMAP_HREF + "list/" + slug) 153sitemap.append(SITEMAP_HREF + "list/") 154return Lists 155 156def RenderPosts(): 157global PostList 158for post in PostList: 159Revised = post["updated"] if "updated" in post else False 160RenderedHTML = RenderTemplate( 161"blog-post.html", 162Revised=Revised, 163Title=post["title"], 164PostDate=post["date"], 165Content=post["content"], 166PostPath=post["pathname"], 167PlaintextPath=post["plaintext"], 168IsDraft=post["is-draft"], 169) 170print("Building blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["pathname"])) 171with open(BUILD_DIRECTORY + "/blog/" + post["pathname"], "w", encoding="utf-8") as PostHTMLFile: 172PostHTMLFile.write(RenderedHTML) 173print("Copying blog-posts/%s to %s/blog/%s" % (post["origin"], BUILD_DIRECTORY, post["plaintext"])) 174with open(BUILD_DIRECTORY + "/blog/" + post["plaintext"], "w", encoding="utf-8") as PostHTMLFile: 175PostHTMLFile.write(post["raw-content"]) 176sitemap.append(SITEMAP_HREF + "blog/" + post["pathname"]) 177 178def RenderPage(PageInput: str, ContentDest: str, AllowSitemap: bool = True, **kwargs): 179print("Building views/%s to %s/%s" % (PageInput, BUILD_DIRECTORY, ContentDest)) 180if AllowSitemap: 181sitemap.append(SITEMAP_HREF + ContentDest) 182with open(BUILD_DIRECTORY + "/" + ContentDest, "w", encoding="utf-8") as DestLocation: 183DestLocation.write(RenderTemplate(PageInput, **kwargs)) 184 185def CreateJSONFeed(): 186global PostList 187CreatedJSON = { 188"version": "https://jsonfeed.org/version/1", 189"title": "Steve0Greatness' Blog", 190"home_page_url": "https://steve0greatness.github.io", 191"feed_url": "https://steve0greatness.github.io/blog/feed.rss", 192"language": "en-US", 193"favicon": "https://steve0greatness.github.io/favicon.ico", 194"description": "A blog by a human being.", 195"authors": [ 196{ 197"name": "Steve0Greatness", 198"url": "https://steve0greatness.github.io" 199} 200], 201"items": [] 202} 203for post in PostList: 204CreatedJSON["items"].append({ 205"id": "https://steve0greatness.github.io/blog/" + post["pathname"], 206"title": "JSON Feed version 1.1", 207"icon": "https://steve0greatness.github.io/favicon.ico", 208"content_html": post["content"], 209"date_published": post["atom-post-time"], 210"date_modified": post["atom-update-time"], 211"url": "https://steve0greatness.github.io/blog/" + post["pathname"] 212}) 213with open(BUILD_DIRECTORY + "/blog/feed.json", "w") as JSONFeedFile: 214DumpJSON(CreatedJSON, JSONFeedFile) 215 216def RenderLists(): 217Lists = GetLists() 218CreateDirectory(BUILD_DIRECTORY + "/list/") 219ListIndex = "<ul>" 220for List in Lists: 221FileLocation = "/list/" + List["filename"].replace(".yml", ".html") 222Title = List["title"] 223print("%s -> %s" % ("lists/" + List["filename"], BUILD_DIRECTORY + FileLocation)) 224with open(BUILD_DIRECTORY + FileLocation, "w") as file: 225file.write(RenderTemplate("list.html", Content=List["content"], Title=Title, Location=FileLocation)) 226ListIndex += "<li><a href=\"%s\">%s</a></li>" % (FileLocation, Title) 227ListIndex += "</ul>" 228print("Building list index") 229with open(BUILD_DIRECTORY + "/list/index.html", "w") as file: 230file.write(RenderTemplate("list-index.html", Content=ListIndex)) 231 232def main(): 233global PostList 234PostList = GetBlogList() 235print("Wiping directory") 236WipeFinalDir() 237print("Creating blog holder") 238CreateDirectory(BUILD_DIRECTORY + "/blog") 239print("Rendering posts") 240RenderPosts() 241CreateJSONFeed() 242print("Copying static directory") 243CopyDirectory("static", BUILD_DIRECTORY) 244print("Creating lists") 245RenderLists() 246 247print("Building pages") 248for file, path in PAGES.items(): 249AllowSitemap = file not in DISALLOWED_SITEMAP 250RenderPage(file, path, AllowSitemap, PostList=PostList) 251 252print("Building redirects") 253for OldLocation, NewLocation in REDIRECTS.items(): 254RenderPage("redirect.html", OldLocation, False, redirect=NewLocation, old=OldLocation) 255 256with open(BUILD_DIRECTORY + "/sitemap.txt", "w") as SitemapFile: 257SitemapFile.write("\n".join(sitemap)) 258 259if __name__ == "__main__": 260main()