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