From 030b0d37675f3c188cb76b24617263a2e01a7672 Mon Sep 17 00:00:00 2001 From: Jesse Braham Date: Wed, 12 Feb 2025 17:07:27 +0100 Subject: [PATCH] Add pelican configuration --- pelicanconf.py | 98 ++++++++++++++++++++++++++++++ publishconf.py | 7 +++ requirements.txt | 11 ++++ tasks.py | 151 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 pelicanconf.py create mode 100644 publishconf.py create mode 100644 requirements.txt create mode 100644 tasks.py diff --git a/pelicanconf.py b/pelicanconf.py new file mode 100644 index 0000000..73be9ab --- /dev/null +++ b/pelicanconf.py @@ -0,0 +1,98 @@ +# https://docs.getpelican.com/en/latest/settings.html + +# Basic Settings + +USE_FOLDER_AS_CATEGORY = True +DELETE_OUTPUT_DIRECTORY = True + +JINJA_ENVIRONMENT = {"trim_blocks": True, "lstrip_blocks": True} + +MARKDOWN = { + "extension_configs": { + "markdown.extensions.codehilite": {"css_class": "highlight"}, + "markdown.extensions.extra": {}, + "markdown.extensions.nl2br": {}, + "markdown.extensions.toc": { + "title": "Table of Contents", + "anchorlink": True, + "baselevel": 2, + }, + }, + "output_format": "html5", +} + +PATH = "content" + +SITENAME = "Hate-Bit" +SITEURL = "" + +STATIC_PATHS = ["extra"] + + +# URL Settings + +AUTHOR_SAVE_AS = AUTHORS_SAVE_AS = "" +CATEGORY_SAVE_AS = CATEGORIES_SAVE_AS = "" +INDEX_SAVE_AS = "" +TAG_SAVE_AS = TAGS_SAVE_AS = "" + +ARCHIVES_SAVE_AS = "index.html" +ARTICLE_SAVE_AS = ARTICLE_URL = "{category}/{slug}.html" +PAGE_SAVE_AS = PAGE_URL = "{slug}.html" + +SLUGIFY_USE_UNICODE = True + + +# Time and Date + +TIMEZONE = "Europe/Prague" +DEFAULT_DATE_FORMAT = "%Y-%m-%d" + + +# Metadata + +EXTRA_PATH_METADATA = { + "extra/robots.txt": {"path": "robots.txt"}, +} + + +# Feed Settings + +FEED_ALL_ATOM = "feeds/all.atom.xml" + +AUTHOR_FEED_ATOM = None +AUTHOR_FEED_RSS = None +CATEGORY_FEED_ATOM = None +TRANSLATION_FEED_ATOM = None + + +# Translations + +DEFAULT_LANG = "en" + + +# Themes + +THEME = "theme" + +MENUITEMS = [ + ("posts", ""), + ("projects", "projects.html"), + ("about", "about.html"), +] + +SOCIAL = [ + ("email", "mailto:admin@hatebit.org"), + ("git", "https://hatebit.org/"), + ("feed", f"{SITEURL}/{FEED_ALL_ATOM}"), +] + + +# Plugins + +# https://github.com/pelican-plugins/sitemap +SITEMAP = { + "format": "xml", + "priorities": {"articles": 0.5, "indexes": 0.5, "pages": 0.5}, + "changefreqs": {"articles": "monthly", "indexes": "daily", "pages": "monthly"}, +} diff --git a/publishconf.py b/publishconf.py new file mode 100644 index 0000000..8f5e861 --- /dev/null +++ b/publishconf.py @@ -0,0 +1,7 @@ +import os +import sys + +sys.path.append(os.curdir) +from pelicanconf import * + +SITEURL = "https://blog.hatebit.org" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..15a2fa5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,11 @@ +black==25.1.0 +invoke==2.2.0 +isort==6.0.0 +livereload==2.7.1 +Markdown==3.7 +pelican==4.11.0 +pelican-neighbors==1.2.0 +pelican-sitemap==1.2.0 +pelican-statistics==1.0.0 +pelican-webassets==2.1.0 +pelican-yaml-metadata==2.1.2 diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000..1e12bbf --- /dev/null +++ b/tasks.py @@ -0,0 +1,151 @@ +import os +import shlex +import shutil +import sys + +from invoke import task +from invoke.main import program +from pelican import main as pelican_main +from pelican.server import ComplexHTTPRequestHandler, RootedHTTPServer +from pelican.settings import DEFAULT_CONFIG, get_settings_from_file + +OPEN_BROWSER_ON_SERVE = True +SETTINGS_FILE_BASE = "pelicanconf.py" +SETTINGS = {} +SETTINGS.update(DEFAULT_CONFIG) +LOCAL_SETTINGS = get_settings_from_file(SETTINGS_FILE_BASE) +SETTINGS.update(LOCAL_SETTINGS) + +CONFIG = { + "settings_base": SETTINGS_FILE_BASE, + "settings_publish": "publishconf.py", + # Output path. Can be absolute or relative to tasks.py. Default: 'output' + "deploy_path": SETTINGS["OUTPUT_PATH"], + # Remote server configuration + "ssh_user": os.environ.get("SSH_USER"), + "ssh_host": os.environ.get("SSH_HOST"), + "ssh_port": os.environ.get("SSH_PORT"), + "ssh_path": os.environ.get("SSH_PATH"), + # Host and port for `serve` + "host": "localhost", + "port": 8000, +} + + +@task +def clean(c): + """Remove generated files""" + if os.path.isdir(CONFIG["deploy_path"]): + shutil.rmtree(CONFIG["deploy_path"]) + os.makedirs(CONFIG["deploy_path"]) + + +@task +def build(c): + """Build local version of site""" + pelican_run("-s {settings_base}".format(**CONFIG)) + + +@task +def rebuild(c): + """`build` with the delete switch""" + pelican_run("-d -s {settings_base}".format(**CONFIG)) + + +@task +def regenerate(c): + """Automatically regenerate site upon file modification""" + pelican_run("-r -s {settings_base}".format(**CONFIG)) + + +@task +def serve(c): + """Serve site at http://$HOST:$PORT/ (default is localhost:8000)""" + + class AddressReuseTCPServer(RootedHTTPServer): + allow_reuse_address = True + + server = AddressReuseTCPServer( + CONFIG["deploy_path"], + (CONFIG["host"], CONFIG["port"]), + ComplexHTTPRequestHandler, + ) + + if OPEN_BROWSER_ON_SERVE: + # Open site in default browser + import webbrowser + + webbrowser.open("http://{host}:{port}".format(**CONFIG)) + + sys.stderr.write("Serving at {host}:{port} ...\n".format(**CONFIG)) + server.serve_forever() + + +@task +def reserve(c): + """`build`, then `serve`""" + build(c) + serve(c) + + +@task +def preview(c): + """Build production version of site""" + pelican_run("-s {settings_publish}".format(**CONFIG)) + + +@task +def livereload(c): + """Automatically reload browser tab upon file modification.""" + from livereload import Server + + def cached_build(): + cmd = "-s {settings_base} -e CACHE_CONTENT=true LOAD_CONTENT_CACHE=true" + pelican_run(cmd.format(**CONFIG)) + + cached_build() + server = Server() + theme_path = SETTINGS["THEME"] + watched_globs = [ + CONFIG["settings_base"], + f"{theme_path}/templates/**/*.html", + ] + + content_file_extensions = [".md", ".rst"] + for extension in content_file_extensions: + content_glob = "{}/**/*{}".format(SETTINGS["PATH"], extension) + watched_globs.append(content_glob) + + static_file_extensions = [".css", ".js"] + for extension in static_file_extensions: + static_file_glob = f"{theme_path}/static/**/*{extension}" + watched_globs.append(static_file_glob) + + for glob in watched_globs: + server.watch(glob, cached_build) + + if OPEN_BROWSER_ON_SERVE: + # Open site in default browser + import webbrowser + + webbrowser.open("http://{host}:{port}".format(**CONFIG)) + + server.serve(host=CONFIG["host"], port=CONFIG["port"], root=CONFIG["deploy_path"]) + + +@task +def publish(c): + """Publish to production via rsync""" + pelican_run("-s {settings_publish}".format(**CONFIG)) + c.run( + 'rsync --delete --exclude ".DS_Store" -pthrvz -c ' + '-e "ssh -p {ssh_port}" ' + "{} {ssh_user}@{ssh_host}:{ssh_path}".format( + CONFIG["deploy_path"].rstrip("/") + "/", **CONFIG + ) + ) + + +def pelican_run(cmd): + cmd += " " + program.core.remainder # allows to pass-through args to pelican + pelican_main(shlex.split(cmd))