A mirror of my website's source code.

Important information: Google announced that, from September 2026, Android devices will require ALL apps to be signed by Google, effectively leading to an iOS situation. Value your right to a computer that does what you want; do not tolerate this monopolistic practice! Contact me if you don't understand why it is bad. Click to learn more.

 build.py

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