Compare commits
No commits in common. "7aef7edf19e685074fb799c29f1f65587e748d56" and "c9b8cced5146b7ec3153d868797a0ff38fec5df7" have entirely different histories.
7aef7edf19
...
c9b8cced51
|
@ -1,3 +1 @@
|
||||||
out/
|
out/
|
||||||
srv/
|
|
||||||
venv
|
|
16
Makefile
16
Makefile
|
@ -1,23 +1,17 @@
|
||||||
.PHONY: *
|
.PHONY: *
|
||||||
|
|
||||||
build:
|
build:
|
||||||
./build.py out/
|
./build.py
|
||||||
pagefind --site out/
|
pagefind --site out/
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
test -e out && rm -rf out || true
|
test -e out && rm -rf out
|
||||||
test -e srv && rm -rf srv || true
|
|
||||||
|
|
||||||
watch:
|
watch:
|
||||||
while inotifywait -r -e modify -e move -e create -e delete build.py Makefile src/; do make build; done
|
while sleep 1; do find src/ build.py Makefile | entr -d make build; done
|
||||||
|
|
||||||
serve:
|
serve:
|
||||||
python -m http.server --directory out/
|
python -m http.server --directory out/
|
||||||
|
|
||||||
pubdate:
|
publish:
|
||||||
sed -i "s/pubdate: now/pubdate: $$(date -Isec)/" src/blog/**/*.md
|
rsync -av out/* ssh.alogoulogoi.com:/srv/www.alogoulogoi.com
|
||||||
|
|
||||||
upload:
|
|
||||||
./build.py srv/
|
|
||||||
pagefind --site srv/
|
|
||||||
rsync -av srv/* ssh.alogoulogoi.com:/srv/www.alogoulogoi.com
|
|
42
build.py
42
build.py
|
@ -1,32 +1,27 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
from datetime import datetime, timezone
|
|
||||||
import copy
|
import copy
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
import bs4
|
import bs4
|
||||||
from feedgen.feed import FeedGenerator
|
|
||||||
import markdown
|
import markdown
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("out", help="output directory")
|
parser.add_argument("src", help="source directory", nargs="?", default="./src")
|
||||||
|
parser.add_argument("out", help="output directory", nargs="?", default="./out")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
src = pathlib.Path("src")
|
src = pathlib.Path(args.src)
|
||||||
out = pathlib.Path(args.out)
|
out = pathlib.Path(args.out)
|
||||||
|
|
||||||
md = markdown.Markdown(extensions=["attr_list", "footnotes", "md_in_html", "meta"])
|
md = markdown.Markdown(extensions=["attr_list", "footnotes", "md_in_html", "meta"])
|
||||||
comment_md = markdown.Markdown()
|
comment_md = markdown.Markdown()
|
||||||
|
|
||||||
# Map of feed url -> FeedGenerator object
|
|
||||||
feeds = {}
|
|
||||||
build_date = datetime.now(timezone.utc)
|
|
||||||
|
|
||||||
# Load the template
|
# Load the template
|
||||||
template = bs4.BeautifulSoup(
|
template = bs4.BeautifulSoup(
|
||||||
(src / ".template.html").read_text(encoding="utf8"),
|
(src / ".template.html").read_text(encoding="utf8"),
|
||||||
|
@ -113,41 +108,10 @@ def main():
|
||||||
aside.extend(html.p.contents)
|
aside.extend(html.p.contents)
|
||||||
page.header.append(aside)
|
page.header.append(aside)
|
||||||
|
|
||||||
# RSS metadata
|
|
||||||
if "feed" in meta and "pubdate" in meta:
|
|
||||||
pubdate = datetime.fromisoformat(meta["pubdate"][0])
|
|
||||||
link = f"https://www.alogoulogoi.com/{dest.relative_to(out).as_posix()}"
|
|
||||||
for feed in meta["feed"]:
|
|
||||||
if feed not in feeds:
|
|
||||||
feeds[feed] = []
|
|
||||||
feeds[feed].append({
|
|
||||||
"title": meta_title[0],
|
|
||||||
"link": link,
|
|
||||||
"description": "",
|
|
||||||
"pubdate": pubdate,
|
|
||||||
})
|
|
||||||
|
|
||||||
# Write the fully templated page
|
# Write the fully templated page
|
||||||
print("Writing ", dest)
|
print("Writing ", dest)
|
||||||
dest.write_text(str(page))
|
dest.write_text(str(page))
|
||||||
|
|
||||||
for feed, items in feeds.items():
|
|
||||||
fg = FeedGenerator()
|
|
||||||
fg.title(f"alogoulogoi /{feed}/")
|
|
||||||
fg.link(href=f"https://www.alogoulogoi.com/{feed}/feed.xml")
|
|
||||||
fg.description("Blog posts from alogoulogoi")
|
|
||||||
fg.language("en-us")
|
|
||||||
fg.lastBuildDate(build_date)
|
|
||||||
for item in sorted(items, key=lambda i: i["pubdate"]):
|
|
||||||
entry = fg.add_entry()
|
|
||||||
entry.title(item["title"])
|
|
||||||
entry.link(href=item["link"])
|
|
||||||
entry.description(item["description"])
|
|
||||||
entry.published(item["pubdate"])
|
|
||||||
rss_path = (out / feed / "feed.xml")
|
|
||||||
os.makedirs(rss_path.parent, exist_ok=True)
|
|
||||||
rss_path.write_bytes(fg.rss_str(pretty=True))
|
|
||||||
|
|
||||||
print("Processed", count, "files")
|
print("Processed", count, "files")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,18 +17,13 @@
|
||||||
pyenv = pkgs.python3.withPackages (pypkgs: [
|
pyenv = pkgs.python3.withPackages (pypkgs: [
|
||||||
pypkgs.markdown
|
pypkgs.markdown
|
||||||
pypkgs.beautifulsoup4
|
pypkgs.beautifulsoup4
|
||||||
pypkgs.feedgen
|
|
||||||
]);
|
]);
|
||||||
in {
|
in {
|
||||||
packages.${system} = {
|
|
||||||
inherit pyenv;
|
|
||||||
};
|
|
||||||
|
|
||||||
devShells.${system} = {
|
devShells.${system} = {
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
packages = [
|
packages = [
|
||||||
pyenv
|
pyenv
|
||||||
pkgs.inotify-tools
|
pkgs.entr
|
||||||
pkgs.rsync
|
pkgs.rsync
|
||||||
pkgs.pagefind
|
pkgs.pagefind
|
||||||
];
|
];
|
||||||
|
|
|
@ -8,7 +8,7 @@ I self-host a Gitea instance at [git.alogoulogoi.com](https://git.alogoulogoi.co
|
||||||
|
|
||||||
# Colophon
|
# Colophon
|
||||||
|
|
||||||
This website is built from [Markdown and HTML sources](https://git.alogoulogoi.com/Jaculabilis/www) using [Python-Markdown](https://python-markdown.github.io/). The static files are deployed to a NixOS VPS via `rsync` and served by Nginx.
|
**Technology:** This website is built from [Markdown and HTML sources](https://git.alogoulogoi.com/Jaculabilis/www) using [Python-Markdown](https://python-markdown.github.io/). It is hosted on a NixOS VPS running Nginx and serving static files deployed via `rsync`.
|
||||||
|
|
||||||
Before the Internet was a deployment system for JavaScript, it was a web of interlinked hypertext documents. This site's design reflects that forgotten dream. The pages are self-contained, the styling is minimal, and the content is accessible without scripting enabled.
|
Before the Internet was a deployment system for JavaScript, it was a web of interlinked hypertext documents. This site's design reflects that forgotten dream. The pages are self-contained, the styling is minimal, and the content is accessible without scripting enabled.
|
||||||
|
|
||||||
|
@ -17,3 +17,5 @@ A selection of other sites used as design inspiration:
|
||||||
* [brutalist-web.design](https://brutalist-web.design/)
|
* [brutalist-web.design](https://brutalist-web.design/)
|
||||||
* [blog.aqwari.net](https://blog.aqwari.net/)
|
* [blog.aqwari.net](https://blog.aqwari.net/)
|
||||||
* [spectrum-os.org](https://spectrum-os.org/)
|
* [spectrum-os.org](https://spectrum-os.org/)
|
||||||
|
|
||||||
|
**Privacy:** Nginx keeps request logs, but I never check them. I don't know what I'd do with them, either.
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
---
|
|
||||||
title: The traditional first software engineer blog post
|
|
||||||
pubdate: 2023-09-28T15:39:59-07:00
|
|
||||||
feed: blog
|
|
||||||
---
|
|
||||||
|
|
||||||
It seems appropriate for a first post on a blog to explain why the blog exists and how it is built. For people other than software engineers, the latter question is typically not very interesting because the blog is on a blogging site like WordPress or built with a CMS like WordPress. Software engineers, however, have a habit of doing everything from scratch, so they also explain their hosting choice, Emacs plugins, operating system version, and WordPress deployment.
|
|
||||||
|
|
||||||
I do not have a profound answer to either question, so this will be short. I set up this website and this blog to be able to put things on the public Internet and share them with people. Some of those things might not have a particular audience. Someone, somewhere, said something to the effect that teaching something is the best way to learn it. Having to explain something to other people helps solidify your understanding as you are forced to articulate things you had only nebulously grasped before. If I write about how I solved some problem or used some tool, that might also help someone else in the same situation. People learn things differently and sometimes all you need is to see the information presented in the way that clicks for you.
|
|
||||||
|
|
||||||
The explanation of how the blog is built is similarly simple. A directory tree of markup text is parsed by a Python build script into lean HTML and written out to a destination directory tree, which is then uploaded to the server where the files are served. It'd be nice if I could say I used this project to learn a new technology or language, but none of the tools here are new to me. I would defend this decision by saying that I [chose boring technology](https://mcfunley.com/choose-boring-technology), but the reason to choose boring technology is to save your "innovation tokens" for the innovative technology your company is building. A static site and a blog are not innovative, so they do not benefit from token austerity.
|
|
||||||
|
|
||||||
Perhaps this explains the tendency of personal software blogs to sit atop overengineered tech stacks: setting up a blog does not interest the tinkerer unless there is something technologically interesting involved. I don't know if "technologically interesting" is always a virtue; "may you live in interesting times" is considered a curse. I don't want to go down a path where "debugging my blog" could describe my weekend.
|
|
||||||
|
|
||||||
With that said, here are some things I *did* use for the first time while building this:
|
|
||||||
|
|
||||||
* [Python-Markdown](https://python-markdown.github.io/), which has some built-in extensions and supports inline HTML. That lets me do things like insert short page-specific `<script>` or `<style>` blocks.
|
|
||||||
* [python-feedgen](https://feedgen.kiesow.be/), which generates the RSS feeds for this blog.
|
|
||||||
* [pagefind](https://pagefind.app/), which makes a static site searchable without requiring a backend. Neat!
|
|
||||||
* [inotifywait](https://linux.die.net/man/1/inotifywait), which I use to rebuild the site while I develop it. Responsive rebuilds are nothing new in the current year, but it's not complicated to use it in your own shell workflows.
|
|
||||||
* [Applying patches to nixpkgs on import.](https://git.alogoulogoi.com/Jaculabilis/www/commit/75a860a95ad3ea63b53878e5e378403816fd6b9f) I found pagefind while it was still in PR to nixpkgs, and I try to keep my flakes on NixOS releases so I don't have to re-download the entire Linux userspace for every project. Patching it in let me just build it from source instead of having to pull in `nixpkgs-unstable`. I had to include another change not from the PR to get the diff to apply correctly. The PR's merged now, so this only needs to work until 23.11 is released and I upgrade things.
|
|
||||||
|
|
||||||
I considered using [Hugo](https://gohugo.io/), but it seemed like it had a lot more control knobs than I needed, and I would need to learn how to create a theme before I could make a website as minimalist as I wanted. If I end up needing more features for page generation, I might reconsider using it or a similar tool, but I don't think there's a lot of marginal value in that right now.
|
|
|
@ -1 +0,0 @@
|
||||||
* [The traditional first software engineer blog post](./blog-start.md)
|
|
|
@ -1,7 +0,0 @@
|
||||||
---
|
|
||||||
title: Blog
|
|
||||||
---
|
|
||||||
|
|
||||||
[RSS](./feed.xml)
|
|
||||||
|
|
||||||
* [The traditional first software engineer blog post](./2023/blog-start.md)
|
|
|
@ -6,8 +6,6 @@ title: Home
|
||||||
|
|
||||||
=> [Projects I've worked on](./project/).
|
=> [Projects I've worked on](./project/).
|
||||||
|
|
||||||
=> [Blog posts](./blog/).
|
|
||||||
|
|
||||||
=> [About this site](./about/).
|
=> [About this site](./about/).
|
||||||
|
|
||||||
=> [Search this site](./search/).
|
=> [Search this site](./search/).
|
||||||
|
|
|
@ -9,6 +9,6 @@ Search results generated by [Pagefind](https://pagefind.app/).
|
||||||
<div id="search"></div>
|
<div id="search"></div>
|
||||||
<script>
|
<script>
|
||||||
window.addEventListener('DOMContentLoaded', (event) => {
|
window.addEventListener('DOMContentLoaded', (event) => {
|
||||||
new PagefindUI({ element: "#search", showSubResults: true, showImages: false });
|
new PagefindUI({ element: "#search", showSubResults: true });
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
Loading…
Reference in New Issue