update @ Thu 9 Feb 00:45:57 EET 2023

Signed-off-by: Ari Archer <ari.web.xyz@gmail.com>
This commit is contained in:
Ari Archer 2023-02-09 00:45:57 +02:00
parent 43e3b1d23f
commit 492c45ca58
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: A50D5B4B599AF8A2
4 changed files with 131 additions and 189 deletions

File diff suppressed because one or more lines are too long

View file

@ -203,11 +203,18 @@ blockquote * {
*[data-pl]:hover > a, *[data-pl]:hover > a,
*[data-pl]:focus > a { *[data-pl]:focus > a {
opacity: 0.6;
}
*[data-pl] > a:hover,
*[data-pl] > a:focus {
opacity: 1; opacity: 1;
} }
@media only screen and (max-width: 1200px) { @media only screen and (max-width: 1200px) {
*[data-pl] > a { *[data-pl] > a,
*[data-pl]:hover > a,
*[data-pl]:focus > a {
opacity: 1; opacity: 1;
} }
} }

View file

@ -1,5 +1,5 @@
[build] [build]
command = "python3 ./scripts/blog.py static" command = "python3 ./scripts/blog.py static && rm -rf ./scripts/ ./completions/"
[[redirects]] [[redirects]]
from = "/git/*" from = "/git/*"

View file

@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Manage blogs""" """manage blogs"""
import hashlib import hashlib
import os import os
@ -18,7 +18,8 @@ from shutil import rmtree
from tempfile import gettempdir from tempfile import gettempdir
from threading import Thread from threading import Thread
from timeit import default_timer as code_timer from timeit import default_timer as code_timer
from typing import Any, Callable, Collection, Dict, List, Optional, Set, Tuple from typing import (Any, Callable, Collection, Dict, List, Optional, Set,
Tuple, Union)
from warnings import filterwarnings as filter_warnings from warnings import filterwarnings as filter_warnings
import ujson # type: ignore import ujson # type: ignore
@ -32,6 +33,8 @@ from markdown.treeprocessors import Treeprocessor # type: ignore
from plumbum.commands.processes import ProcessExecutionError # type: ignore from plumbum.commands.processes import ProcessExecutionError # type: ignore
from pyfzf import FzfPrompt # type: ignore from pyfzf import FzfPrompt # type: ignore
__version__: int = 1
NOT_CI_BUILD: bool = not os.getenv("CI") NOT_CI_BUILD: bool = not os.getenv("CI")
if NOT_CI_BUILD: if NOT_CI_BUILD:
@ -64,9 +67,9 @@ DEFAULT_CONFIG: Dict[str, Any] = {
], ],
"default-keywords": ["website", "blog", "opinion", "article", "ari-web", "ari"], "default-keywords": ["website", "blog", "opinion", "article", "ari-web", "ari"],
"page-title": "Ari::web -> Blog", "page-title": "Ari::web -> Blog",
"page-description": "My blog page", "page-description": "my blog page",
"colourscheme-type": "dark", "colourscheme-type": "dark",
"short-name": "Ari's blogs", "short-name": "aris blogs",
"home-keywords": ["ari", "ari-web", "blog", "ari-archer", "foss", "free", "linux"], "home-keywords": ["ari", "ari-web", "blog", "ari-archer", "foss", "free", "linux"],
"base-homepage": "https://ari-web.xyz/", "base-homepage": "https://ari-web.xyz/",
"meta-icons": [{"src": "/favicon.ico", "sizes": "128x128", "type": "image/png"}], "meta-icons": [{"src": "/favicon.ico", "sizes": "128x128", "type": "image/png"}],
@ -74,13 +77,12 @@ DEFAULT_CONFIG: Dict[str, Any] = {
"background-colour": "#262220", "background-colour": "#262220",
"full-name": "Ari Archer", "full-name": "Ari Archer",
"locale": "en_GB", "locale": "en_GB",
"home-page-header": "My blogs", "home-page-header": "my blogs",
"comment-url": "/c", "comment-url": "/c",
"blogs": {}, "blogs": {},
} }
DEFAULT_CONFIG_FILE: str = "blog.json" DEFAULT_CONFIG_FILE: str = "blog.json"
HISTORY_FILE: str = ".blog_history" HISTORY_FILE: str = ".blog_history"
BLOG_VERSION: int = 1
CONTEXT_WORDS: Tuple[str, ...] = ( CONTEXT_WORDS: Tuple[str, ...] = (
"the", "the",
"a", "a",
@ -110,6 +112,7 @@ CONTEXT_WORDS: Tuple[str, ...] = (
"and", "and",
"cause", "cause",
"how", "how",
"what",
) )
BLOG_MARKDOWN_TEMPLATE: str = """<header role="group"> BLOG_MARKDOWN_TEMPLATE: str = """<header role="group">
@ -140,11 +143,11 @@ skip</a>
<article id="main"> <article id="main">
<!-- Main blog content: Begin --> <!-- main blog post content : begin -->
%s %s
<!-- Main blog content: End --> <!-- main blog post content : end -->
</article>""" </article>"""
@ -161,7 +164,7 @@ HTML_HEADER: str = f"""<head>
<meta name="robots" content="follow, index, max-snippet:-1, \ <meta name="robots" content="follow, index, max-snippet:-1, \
max-video-preview:-1, max-image-preview:large"/> max-video-preview:-1, max-image-preview:large"/>
<meta name="generator" \ <meta name="generator" \
content="Ari-web blog generator version {BLOG_VERSION}"/> content="ari-web blog generator version {__version__}"/>
<link <link
rel="stylesheet" rel="stylesheet"
@ -220,13 +223,13 @@ HOME_PAGE_HTML_TEMPLATE: str = f"""<!DOCTYPE html>
<span aria-hidden="true" role="seperator">|</span> <span aria-hidden="true" role="seperator">|</span>
<span role="menuitem"> <span role="menuitem">
latest update: <time>{{lastest_blog_time}}</time> GMT last posted : <time>{{lastest_blog_time}}</time> GMT
</span> </span>
<span aria-hidden="true" role="seperator">|</span> <span aria-hidden="true" role="seperator">|</span>
<span role="menuitem"> <span role="menuitem">
latest blog: \ latest post : \
<a href="{{latest_blog_url}}">{{latest_blog_title}}</a> <a href="{{latest_blog_url}}">{{latest_blog_title}}</a>
</span> </span>
@ -241,11 +244,11 @@ HOME_PAGE_HTML_TEMPLATE: str = f"""<!DOCTYPE html>
<main id="main"> <main id="main">
<!-- Main home page content: Begin --> <!-- main home page content : begin -->
{{content}} {{content}}
<!-- Main home page content: End --> <!-- main home page content : end -->
</main> </main>
</body> </body>
@ -257,15 +260,22 @@ def remove_basic_punct(s: str) -> str:
def sanitise_title( def sanitise_title(
title: str, titleset: Collection[str], /, nosep: bool = False title: str, titleset: Collection[str], *, nosep: bool = False, generic: bool = True
) -> str: ) -> str:
title = " ".join(
[ title = title.lower().strip()
w words: list[str] = []
for w in remove_basic_punct(title).lower().split()
if w not in CONTEXT_WORDS if generic:
][:8] for w in remove_basic_punct(title).split():
) if w not in CONTEXT_WORDS:
words.append(w)
elif len(words) >= 8:
break
if words:
title = " ".join(words)
_title: str = "" _title: str = ""
for char in title: for char in title:
@ -277,7 +287,7 @@ def sanitise_title(
else "" else ""
) )
_title = _title.rstrip("-") _title = _title.strip("-")
return ( return (
_title _title
@ -285,20 +295,21 @@ def sanitise_title(
else sanitise_title( else sanitise_title(
_title + ("" if nosep else "-") + random.choice(string.digits), _title + ("" if nosep else "-") + random.choice(string.digits),
titleset, titleset,
True, nosep=True,
generic=generic,
) )
) )
def truncate_str(string: str, length: int) -> str: def truncate_str(string: str, length: int) -> str:
return string if len(string) <= length else (string[:length] + "...") return string if len(string) <= length else (string[:length] + " ...")
class BetterHeaders(Treeprocessor): class BetterHeaders(Treeprocessor):
"""Better headers """better headers
- Downsizes headers from h1 -> h2 - downsizes headers from h1 -> h2
- Adds header links""" - adds header links"""
def run(self, root: etree.Element) -> None: def run(self, root: etree.Element) -> None:
ids: List[str] = [] ids: List[str] = []
@ -320,7 +331,7 @@ class BetterHeaders(Treeprocessor):
if elem.text is None: if elem.text is None:
elem.text = "" elem.text = ""
gen_id: str = sanitise_title(elem.text, ids) gen_id: str = sanitise_title(elem.text, ids, generic=False)
ids.append(gen_id) ids.append(gen_id)
heading_parent: etree.Element = elem.makeelement( heading_parent: etree.Element = elem.makeelement(
@ -362,42 +373,48 @@ class BetterHeaders(Treeprocessor):
root.insert(idx, heading_parent) root.insert(idx, heading_parent)
class AddIDLinks(InlineProcessor): class TitleLinks(InlineProcessor):
"""Add support for <#ID> links""" """add support for <#:title> links"""
def handleMatch( # pyright: ignore def handleMatch( # pyright: ignore
self, match: RegexMatch, *_ # pyright: ignore self, match: RegexMatch, *_ # pyright: ignore
) -> Tuple[etree.Element, Any, Any]: ) -> Union[Tuple[etree.Element, Any, Any], Tuple[None, ...]]:
text: str = match.group(1) # type: ignore
if not text:
return (None,) * 3
link: etree.Element = etree.Element("a") link: etree.Element = etree.Element("a")
link.text = match.group(1) or "#" link.text = f"# {text}"
link.set("href", link.text or "#") link.set("href", f"#{sanitise_title(text, [], generic=False)}") # type: ignore
return link, match.start(0), match.end(0) return link, match.start(0), match.end(0)
class AriMarkdownExts(Extension): class AriMarkdownExts(Extension):
"""Ari-web markdown extensions""" """ari-web markdown extensions"""
def extendMarkdown( def extendMarkdown(
self, self,
md: markdown_core.Markdown, md: markdown_core.Markdown,
key: str = "add_header_links", key: str = "add_header_links",
index: int = int(1e8), index: int = int(1e8),
): ) -> None:
md.registerExtension(self) md.registerExtension(self)
md.treeprocessors.register( md.treeprocessors.register(
BetterHeaders(md.parser), key, index # pyright: ignore BetterHeaders(md.parser), key, index # pyright: ignore
) )
md.inlinePatterns.register( md.inlinePatterns.register(
AddIDLinks(r"<(#.*)>", "a"), key, index # pyright: ignore TitleLinks(r"<#:(.*)>", "a"), key, index # pyright: ignore
) )
def log(message: str, header: str = "ERROR", code: int = EXIT_ERR) -> int: def log(message: str, header: str = "ERROR", code: int = EXIT_ERR) -> int:
if not (not NOT_CI_BUILD and header != "ERROR"): if not (not NOT_CI_BUILD and header != "ERROR"):
sys.stderr.write(f"{header}: {message}\n") sys.stderr.write(f"{header} : {message}\n")
return code return code
@ -443,7 +460,7 @@ def yn(prompt: str, default: str = "y", current_value: str = "") -> bool:
def new_config() -> None: def new_config() -> None:
log("Making new config...", "INFO") log("making new config ...", "INFO")
with open(DEFAULT_CONFIG_FILE, "w") as cfg: with open(DEFAULT_CONFIG_FILE, "w") as cfg:
ujson.dump(DEFAULT_CONFIG, cfg, indent=4) ujson.dump(DEFAULT_CONFIG, cfg, indent=4)
@ -458,25 +475,25 @@ def pick_blog(config: Dict[str, Any]) -> str:
lambda key: f"{key} | {b64decode(config['blogs'][key]['title']).decode()!r}", # pyright: ignore lambda key: f"{key} | {b64decode(config['blogs'][key]['title']).decode()!r}", # pyright: ignore
tuple(config["blogs"].keys())[::-1], tuple(config["blogs"].keys())[::-1],
), ),
"--prompt='Pick blog: '", "--prompt='pick a post : '",
)[0] )[0]
.split()[0] # pyright: ignore .split()[0] # pyright: ignore
) )
except ProcessExecutionError: except ProcessExecutionError:
log("Fzf process exited unexpectedly") log("fzf process exited unexpectedly")
return "" return ""
if blog_id not in config["blogs"]: if blog_id not in config["blogs"]:
log(f"Blog {blog_id!r} does not exist") log(f"blog post {blog_id!r} does not exist")
return "" return ""
return blog_id return blog_id
def new_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def new_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Make a new blog""" """make a new blog"""
if title := iinput("blog title"): if title := iinput("blog post title"):
readline.add_history((title := title[0].upper() + title[1:])) readline.add_history((title := title[0].upper() + title[1:]))
us_title: str = title us_title: str = title
@ -487,7 +504,6 @@ def new_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
blog: Dict[str, Any] = { blog: Dict[str, Any] = {
"title": b64encode(us_title.encode()).decode(), "title": b64encode(us_title.encode()).decode(),
"content": "", "content": "",
"version": BLOG_VERSION,
"time": 0.0, "time": 0.0,
"keywords": "", "keywords": "",
} }
@ -506,9 +522,9 @@ def new_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
os.remove(file) os.remove(file)
if not blog["content"].strip(): # type: ignore if not blog["content"].strip(): # type: ignore
return log("Blog cannot be empty"), config return log("blog post cannot be empty"), config
user_keywords: str = iinput("keywords (seperated by spaces)") user_keywords: str = iinput("keywords ( seperated by spaces )")
readline.add_history(user_keywords) readline.add_history(user_keywords)
blog["keywords"] = html_escape(user_keywords) blog["keywords"] = html_escape(user_keywords)
@ -520,9 +536,9 @@ def new_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def build_css(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def build_css(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Minify (build) the CSS""" """minify ( build ) the CSS"""
log("Minifying CSS...", "MINIFY") log("minifying CSS ...", "MINIFY")
saved_stdout = sys.stdout saved_stdout = sys.stdout
sys.stdout = open(os.devnull, "w") sys.stdout = open(os.devnull, "w")
@ -534,19 +550,19 @@ def build_css(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
css_threads[-1].start() css_threads[-1].start()
if os.path.isfile("content/styles.css"): if os.path.isfile("content/styles.css"):
log("Minifying main styles", "MINIFY") log("minifying main styles", "MINIFY")
_thread( _thread(
lambda: process_single_css_file("content/styles.css") # pyright: ignore lambda: process_single_css_file("content/styles.css") # pyright: ignore
) )
if os.path.isdir("content/fonts"): if os.path.isdir("content/fonts"):
log("Minifying fonts...", "MINIFY") log("minifying fonts ...", "MINIFY")
for font in iglob("content/fonts/*.css"): for font in iglob("content/fonts/*.css"):
if font.endswith(".min.css"): if font.endswith(".min.css"):
continue continue
log(f"Minifying font file: {font}", "MINIFY") log(f"minifying font file -- {font}", "MINIFY")
_thread(lambda: process_single_css_file(font)) # pyright: ignore _thread(lambda: process_single_css_file(font)) # pyright: ignore
for t in css_threads: for t in css_threads:
@ -555,16 +571,16 @@ def build_css(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
sys.stdout.close() sys.stdout.close()
sys.stdout = saved_stdout sys.stdout = saved_stdout
log("Done minifying CSS", "MINIFY") log("done minifying CSS", "MINIFY")
return EXIT_OK, config return EXIT_OK, config
def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Build, minimise and generate site""" """build, minimise and generate site"""
if not config["blogs"]: if not config["blogs"]:
return log("No blogs to build"), config return log("no blogs to build"), config
latest_blog_id: str = tuple(config["blogs"].keys())[-1] latest_blog_id: str = tuple(config["blogs"].keys())[-1]
@ -573,16 +589,9 @@ def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
os.makedirs(config["blog-dir"], exist_ok=True) os.makedirs(config["blog-dir"], exist_ok=True)
log("Building blogs...", "INFO") log("building blogs ...", "INFO")
def thread(blog_id: str, blog_meta: Dict[str, Any]): def thread(blog_id: str, blog_meta: Dict[str, Any]):
if blog_meta["version"] != BLOG_VERSION:
log(
f"{blog_id}: unmatching version between \
{blog_meta['version']} and {BLOG_VERSION}",
"WARNING",
)
blog_dir: str = os.path.join(config["blog-dir"], blog_id) blog_dir: str = os.path.join(config["blog-dir"], blog_id)
os.makedirs(blog_dir, exist_ok=True) os.makedirs(blog_dir, exist_ok=True)
@ -619,20 +628,20 @@ def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
keywords=blog_meta["keywords"].replace(" ", ", ") keywords=blog_meta["keywords"].replace(" ", ", ")
+ ", " + ", "
+ ", ".join(config["default-keywords"]), + ", ".join(config["default-keywords"]),
blog_description=f"Blog on {blog_time} GMT -- {blog_title}", blog_description=f"blog post on {blog_time} GMT -- {blog_title}",
blog_title=blog_title, blog_title=blog_title,
blog=blog_base_html, blog=blog_base_html,
author=config["full-name"], author=config["full-name"],
locale=config["locale"], locale=config["locale"],
) )
log(f"Minifying {blog_id!r} HTML", "MINIFY") log(f"minifying {blog_id!r} HTML", "MINIFY")
blog_html_full = html_minify(blog_html_full) blog_html_full = html_minify(blog_html_full)
log(f"Done minifying the HTML of {blog_id!r}", "MINIFY") log(f"done minifying the HTML of {blog_id!r}", "MINIFY")
blog_html.write(blog_html_full) blog_html.write(blog_html_full)
log(f"Finished building blog {blog_id!r}", "BUILD") log(f"finished building blog post {blog_id!r}", "BUILD")
_tmp_threads: List[Thread] = [] _tmp_threads: List[Thread] = []
@ -645,7 +654,7 @@ def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
for awaiting_thread in _tmp_threads: for awaiting_thread in _tmp_threads:
awaiting_thread.join() awaiting_thread.join()
log("Building blog index...", "INFO") log("building blog post index ...", "INFO")
with open("index.html", "w") as index: with open("index.html", "w") as index:
lastest_blog: Dict[str, Any] = config["blogs"][latest_blog_id] lastest_blog: Dict[str, Any] = config["blogs"][latest_blog_id]
@ -685,18 +694,17 @@ def build(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def list_blogs(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def list_blogs(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""List blogs""" """list blogs"""
if not config["blogs"]: if not config["blogs"]:
return log("No blogs to list"), config return log("no blogs to list"), config
for blog_id, blog_meta in config["blogs"].items(): for blog_id, blog_meta in config["blogs"].items():
print( print(
f"""ID: {blog_id} f"""ID : {blog_id}
Title: {b64decode(blog_meta["title"]).decode()!r} title : {b64decode(blog_meta["title"]).decode()!r}
Version: {blog_meta["version"]} time_of_creation : {format_time(blog_meta["time"])}
Time_of_creation: {format_time(blog_meta["time"])} keywords : {blog_meta['keywords'].replace(" ", ", ")}
Keywords: {blog_meta['keywords'].replace(" ", ", ")}
""" """
) )
@ -704,10 +712,10 @@ Keywords: {blog_meta['keywords'].replace(" ", ", ")}
def remove_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def remove_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Remove a blog page""" """remove a blog post"""
if not config["blogs"]: if not config["blogs"]:
return log("No blogs to remove"), config return log("no blogs to remove"), config
blog_id: str = pick_blog(config) blog_id: str = pick_blog(config)
@ -719,7 +727,7 @@ def remove_blog(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def dummy(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def dummy(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Print help/usage information""" """print help / usage information"""
return EXIT_OK, config return EXIT_OK, config
@ -730,7 +738,7 @@ def edit_title(blog: str, config: Dict[str, Any]) -> int:
) )
if not new_title.strip(): if not new_title.strip():
return log("New title cannot be empty") return log("new title cannot be empty")
# Made it not change the slug # Made it not change the slug
@ -750,7 +758,7 @@ def edit_keywords(blog: str, config: Dict[str, Any]) -> int:
new_keywords: str = iinput("edit keywords", config["blogs"][blog]["keywords"]) new_keywords: str = iinput("edit keywords", config["blogs"][blog]["keywords"])
if not new_keywords.strip(): if not new_keywords.strip():
return log("Keywords cannot be empty") return log("keywords cannot be empty")
config["blogs"][blog]["keywords"] = new_keywords config["blogs"][blog]["keywords"] = new_keywords
@ -770,7 +778,7 @@ def edit_content(blog: str, config: Dict[str, Any]) -> int:
if not content.strip(): if not content.strip():
blog_md_new.close() blog_md_new.close()
return log("Content of a blog cannot be empty") return log("content of a blog post cannot be empty")
config["blogs"][blog]["content"] = b64encode(content.encode()).decode() config["blogs"][blog]["content"] = b64encode(content.encode()).decode()
@ -786,10 +794,10 @@ EDIT_HOOKS: Dict[str, Callable[[str, Dict[str, Any]], int]] = {
def edit(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def edit(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Edit a blog""" """edit a blog"""
if not config["blogs"]: if not config["blogs"]:
return log("No blogs to edit"), config return log("no blogs to edit"), config
blog_id: str = pick_blog(config) blog_id: str = pick_blog(config)
@ -798,25 +806,25 @@ def edit(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
try: try:
hook: str = FzfPrompt().prompt( # pyright: ignore hook: str = FzfPrompt().prompt( # pyright: ignore
EDIT_HOOKS.keys(), "--prompt='What to edit: '" EDIT_HOOKS.keys(), "--prompt='what to edit : '"
)[0] )[0]
if hook not in EDIT_HOOKS: if hook not in EDIT_HOOKS:
return log(f"Hook {hook!r} does not exist"), config return log(f"hook {hook!r} does not exist"), config
EDIT_HOOKS[hook](blog_id, config) EDIT_HOOKS[hook](blog_id, config)
except ProcessExecutionError: except ProcessExecutionError:
return log("No blog selected"), config return log("no blog post selected"), config
return EXIT_OK, config return EXIT_OK, config
def gen_def_config(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def gen_def_config(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Generate default config""" """generate default config"""
if os.path.exists(DEFAULT_CONFIG_FILE): if os.path.exists(DEFAULT_CONFIG_FILE):
if iinput("Do you want to overwite config? (y/n)").lower()[0] != "y": if iinput("do you want to overwite config ? ( y / n )").lower()[0] != "y":
return log("Not overwritting config", "INFO", EXIT_OK), config return log("not overwritting config", "INFO", EXIT_OK), config
new_config() new_config()
@ -827,7 +835,7 @@ def gen_def_config(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def clean(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def clean(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Clean up current directory""" """clean up current directory"""
TRASH: Set[str] = { TRASH: Set[str] = {
HISTORY_FILE, HISTORY_FILE,
@ -840,7 +848,7 @@ def clean(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
} }
def remove(file: str) -> None: def remove(file: str) -> None:
log(f"Removing {file!r}", "REMOVE") log(f"removing {file!r}", "REMOVE")
try: try:
os.remove(file) os.remove(file)
@ -857,10 +865,10 @@ def clean(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def generate_metadata(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def generate_metadata(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Generate metadata""" """generate metadata"""
with open("manifest.json", "w") as manifest: with open("manifest.json", "w") as manifest:
log(f"Generating {manifest.name}...", "GENERATE") log(f"generating {manifest.name} ...", "GENERATE")
ujson.dump( ujson.dump(
{ {
"$schema": "https://json.schemastore.org/web-manifest-combined.json", "$schema": "https://json.schemastore.org/web-manifest-combined.json",
@ -877,7 +885,7 @@ def generate_metadata(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
) )
with open(DEFAULT_CONFIG_FILE, "rb") as blog_api_file: with open(DEFAULT_CONFIG_FILE, "rb") as blog_api_file:
log(f"Generating hash for {DEFAULT_CONFIG_FILE!r}", "HASH") log(f"generating hash for {DEFAULT_CONFIG_FILE!r}", "HASH")
with open( with open(
f"{DEFAULT_CONFIG_FILE.replace('.', '_')}_hash.txt", "w" f"{DEFAULT_CONFIG_FILE.replace('.', '_')}_hash.txt", "w"
@ -888,21 +896,21 @@ def generate_metadata(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
def generate_static_full(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]: def generate_static_full(config: Dict[str, Any]) -> Tuple[int, Dict[str, Any]]:
"""Generate full static site""" """generate full static site"""
BUILD_CFG: Dict[str, Callable[[Dict[str, Any]], Tuple[int, Dict[str, Any]]]] = { BUILD_CFG: Dict[str, Callable[[Dict[str, Any]], Tuple[int, Dict[str, Any]]]] = {
"Cleaning up": clean, "cleaning up": clean,
"Building CSS": build_css, "building CSS": build_css,
"Building static site": build, "building static site": build,
"Generating metatata": generate_metadata, "generating metatata": generate_metadata,
} }
for logger_msg, function in BUILD_CFG.items(): for logger_msg, function in BUILD_CFG.items():
log(f"{logger_msg}...", "STATIC") log(f"{logger_msg} ...", "STATIC")
code, config = function(config) code, config = function(config)
if code != EXIT_OK: if code != EXIT_OK:
log("Failed to generate static site") log("failed to generate static site")
return EXIT_ERR, config return EXIT_ERR, config
return EXIT_OK, config return EXIT_OK, config
@ -924,7 +932,7 @@ SUBCOMMANDS: Dict[str, Callable[[Dict[str, Any]], Tuple[int, Dict[str, Any]]]] =
def usage(code: int = EXIT_ERR, _: Optional[Dict[str, Any]] = None) -> int: def usage(code: int = EXIT_ERR, _: Optional[Dict[str, Any]] = None) -> int:
sys.stderr.write(f"Usage: {sys.argv[0]} <subcommand>\n") sys.stderr.write(f"usage : {sys.argv[0]} <subcommand>\n")
for subcommand, func in SUBCOMMANDS.items(): for subcommand, func in SUBCOMMANDS.items():
sys.stderr.write(f" {subcommand:20s}{func.__doc__ or ''}\n") sys.stderr.write(f" {subcommand:20s}{func.__doc__ or ''}\n")
@ -933,7 +941,7 @@ def usage(code: int = EXIT_ERR, _: Optional[Dict[str, Any]] = None) -> int:
def main() -> int: def main() -> int:
"""Entry/main function""" """entry / main function"""
if NOT_CI_BUILD: if NOT_CI_BUILD:
if not os.path.isfile(HISTORY_FILE): if not os.path.isfile(HISTORY_FILE):
@ -951,7 +959,7 @@ def main() -> int:
if not os.path.isfile(DEFAULT_CONFIG_FILE): if not os.path.isfile(DEFAULT_CONFIG_FILE):
new_config() new_config()
log(f"PLease configure {DEFAULT_CONFIG_FILE!r}") log(f"please configure {DEFAULT_CONFIG_FILE!r}")
return EXIT_ERR return EXIT_ERR
if len(sys.argv) != 2: if len(sys.argv) != 2:
@ -970,12 +978,12 @@ def main() -> int:
code, config = SUBCOMMANDS[sys.argv[1]](ujson.load(lcfg)) code, config = SUBCOMMANDS[sys.argv[1]](ujson.load(lcfg))
log( log(
f"Finished in {code_timer() - cmd_time_init} seconds with code {code}", f"finished in {code_timer() - cmd_time_init} seconds with code {code}",
"TIME", "TIME",
) )
if config["blogs"] and NOT_CI_BUILD: if config["blogs"] and NOT_CI_BUILD:
log("Sorting blogs by creation time...", "CLEANUP") log("Sorting blogs by creation time ...", "CLEANUP")
sort_timer = code_timer() sort_timer = code_timer()
@ -986,16 +994,16 @@ def main() -> int:
) )
) )
log(f"Sorted in {code_timer() - sort_timer} seconds", "TIME") log(f"sorted in {code_timer() - sort_timer} seconds", "TIME")
log("Redumping config", "CONFIG") log("redumping config", "CONFIG")
dump_timer = code_timer() dump_timer = code_timer()
with open(DEFAULT_CONFIG_FILE, "w") as dcfg: with open(DEFAULT_CONFIG_FILE, "w") as dcfg:
ujson.dump(config, dcfg, indent=(4 if NOT_CI_BUILD else 0)) ujson.dump(config, dcfg, indent=(4 if NOT_CI_BUILD else 0))
log(f"Dumped config in {code_timer() - dump_timer} seconds", "TIME") log(f"dumped config in {code_timer() - dump_timer} seconds", "TIME")
return code return code