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