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