BLOOD CHURCH

by cassandra lugo // portfolio // resume // email // RSS

new blog, new me

20240921

this blog may look exactly the same for you, but it’s in fact totally different!

i intend to post here more now that cohost is going away. expect a lot of the more effortful posts that i used to post on there to be here instead.

i used to host blood.church on neocities, and it was built with a very simple static site generator called cobalt. this setup required some really annoying manual uploading whenever i wanted to post. neocities has some kind of automatic way to mount the site as a drive on your machine so you can sync automatically but i never got it to work.

when cohost announced it was closing, i then started working on a very ambitious project: a dynamic website, with a database to store posts, where instead of using a web-based post editor i would just use curl to send POST requests! that way i could use whatever text editor i wanted. i started writing a basic CRUD API in gleam (which is a very cool new programming language) with sqlite.

i had a lot of fun plugging away at this for two weeks, at which point i realized i could get 90% of what i wanted by just stapling together git and a static site generator like everyone else does. the only thing i wouldn’t be able to get is full-text search: you can do this on a static site, but it requires shipping javascript, which in my mind defeats the entire purpose of a static site.

the main issue that i ran into with the dynamic site idea was uploading images, which i actually got to work by writing an emacs lisp function that parsed all the image links out of an org-mode file and passed them as separate form fields to the server with curl.

anyway, what i’m doing now is this: i spun up a droplet on digitalocean and installed nginx and hugo. i chose nginx because it is clearly and obviously the best web server, it’s extraordinarily fast and easy to configure. i didn’t put too much thought into choosing hugo, it’s just the only popular static site generator that’s written in a statically typed, compiled langauge. the other big ones are 11ty (javascript) and jekyll (ruby), and while i don’t really envision myself ever contributing to any of these projects i figured i might as well support the forces of static type systems against the dynamic hordes.

anyway, all i did was create a hugo site in a git repo, and then set up a git repo on the digitalocean droplet and make it a remote of my local repo so i could freely push to it. normally when you have a git server, you set it up as a bare repo, but for this to work i need the working tree to be there. git normally doesn’t like when you try to push to a non-bare repo, beacuse the working tree on the remote isn’t automatically updated, you have to run git reset --hard HEAD to sync it with the changes stored in the .git directory. but git has a configuration option, git config receive.denyCurrentBranch updateInstead, which makes it automatically update the working tree whenever it receives a push.

then i made use of yet another git feature i’ve never used before: githooks. there’s a directory inside .git, .git/hooks/, where you can put shell scripts with special names that get executed at various phases of the git lifecycle. the one i’m using is post-receive, which activates in remotes whenever they receive a push. the contents of that file are super simple:

cd /home/cass/blog/
rm -rf public
hugo

move to the folder where the blog is stored, remove the public folder that stores all the hugo generated files, then run hugo to regenerate the site.

the main pain points weren’t anything related to what i was doing, it was all Security Enhanced Linux bullshit causing nginx to 403 when it tried to retrieve the site files, and also causing the server to reject git ssh pushes.

this setup is really simple, doesn’t require me to babysit any running software except nginx, only requires the cheapest droplet on digitalocean and because of nginx can still handle a lot of traffic very quickly. using git gives me, for free, many features that i would have to implement manually with a dynamic site. not only can i just upload things to the site easily with a few commands, but i get versioning, updating posts, deleting posts, and just in general all the things people use git for. the power of

that was it for the server side, but now i wanted to make some stuff to make posting as easy as possible for the client side.

another big change i made recently was that i switched text editors from the ever-popular visual studio code to… emacs.

for the uninitiated (i don’t know why the uninitiated would still be reading but whatever), emacs is a text editor from the 70s that’s still used by all manner of freaks and weirdos. it’s one of the two editors of the traditional unix editor wars, the other being vi/vim (vim stands for “vi improved”, vi is short for “visual” which it was at the time, compared to ed).

so why did i do this? i’d been wanting to switch away from visual studio code for some time for two reasons. one was opposition to vs code: it’s a memory hog because it’s an electron app, it’s weirdly brittle and breaks all the time, and i’ve been wanting to get microsoft out of my life because they’ve been leaning so hard into gross AI shit.

the other is attraction to vim and emacs: the people who like them really like them. they’re so old that they operate in very different ways to later text editors, but they’ve stuck around because they offer things that more recent text editors can’t.

for vim, this is an extremely challenging to learn but very rewarding set of elaborate keyboard shortcuts that provide facilities for manipulating text unparalleled by any graphical editor. for emacs, it’s the fact that the editor is implemented as a small C core that implements a lisp interpreter in which the rest of the system is implemented. this lisp environment provides the user with the opportunity to script and customize every single aspect of the editing environment.

for me, the choice between vim and emacs was pretty obvious. emacs extensibility means that its ecosystem features evil mode, a complete set of vim keybindings for use in emacs. it’s truly the best of both worlds.

the reason i switched now was because the opportunity presented itself to me. emacs on windows is a bizarre ugly slow nightmare, but i recently switched to macos as my daily driver OS, and emacs on macos is basically just as good as emacs on linux.

i’m using a popular emacs distribution called doom emacs, which not only comes with evil mode but also a bunch of other nice packages and configuration options designed to make using emacs as friendly as possible.

anyway, if you want to know by example why i switched to emacs, let’s talk baout the client side posting interface i made. first, i wanted to make creating a post as easy as hittin M-x and typing “make-blog-post”.

(defun make-url-safe (input)
  (let ((url-unsafe-regex "[^a-zA-Z0-9_.~-]"))
    (replace-regexp-in-string url-unsafe-regex "-" input)))


(defun make-blog-post ()
 (interactive)
 (let ((title (read-from-minibuffer "title: ")))
   (let ((slug (make-url-safe title)))
     (let ((path (concat "~/blog/content/posts/" slug ".md")))
       (cd "~/blog/")
       (shell-command (concat "hugo new content " path))
       (pop-to-buffer (generate-new-buffer path))
       (markdown-mode)
       (insert-file-contents path)
       (replace-regexp-in-region slug title)
       (write-file path)))))

i’m not much of a lisp andrew, my preference for functional languages leans much more in the direction of ML-family languages (i like types!), but emacs lisp is pretty friendly, especially for the limited use case of scripting and customizing a text editor.

i’m not going to do a full emacs lisp tutorial here (and i certainly wouldn’t be the person to do it justice), but i’l give a brief overview. i create a function called make-blog-post, and that (interactive) line means that this function can be used from emacs by pushing M-x and typing its name. then, i give myself a prompt with read-from-minibuffer to let me type in what i want the title of my new post to be, and generate a url slug and the destination path for it based on that by replacing all the url-unsafe characters with dashes.

then i run hugo new content with the path to create the blog post based on my hugo template for posts. something that betrays the sort of bizarre (by modern standards) way emacs conceptualizes files and text is the little dance i have to do to open this file: i have to create a new “buffer,” which is emacs’ concept of text separate from any place it might be displayed, use pop-to-buffer to open the buffer in a “window” (which is emacs concept of a subdivision of the larger emacs window, remember emacs predates the idea of a “window” as you and i know it considerably) set that buffer’s major mode to markdown-mode, then use insert-file-contents to put the contents of the file we just created in the buffer. but that’s not enough, because we just have a random buffer in a random window, and if we try to save the buffer it won’t know where to save it; we have to use write-file to re-save the file back to the place we pulled it from to connect the buffer we just created and the file on disk.

also i use replace-regexp-in-region to replace the slug with the actual post title in the hugo frontmatter metadata section.

anyway, this is why emacs is cool. visual studio code would let me do this, but it would be a lot more fiddly and stupid.

the publishing command is much simpler:

(defun publish-blog ()
  (interactive)
  (cd "~/blog/")
  (shell-command (concat "git add -A"))
  (shell-command (concat "git commit -m " buffer-file-name))
  (shell-command (concat "git push")))

doom emacs comes with the much-beloved magit package, which i’m still learning how to use, so i just combined the three necessary git commands into one function. maybe in the future i’ll rewrite this to use magit functions. or maybe not.

anyway, that’s what i’ve been up to this month. if you thought this was cool, you should hit up the rss feed, because this is the kind of thing i post about. i have a few more tweaks i want to make to the site (in particular i want to take more advantage of hugo’s ability to handle tags), but this is good enough for now.


personal emacs programming web development