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