This weekend I’ve been procrastinating playing the long game by getting some nice workflows set up for my TIL content repository and its associated website (cf Context, below). If all goes well, by the time I hit :wq on this TIL, it should invisibly trigger 2 Git commits, a Hugo build, and then appear on the site fully-birthed.

One of the more interesting patterns I noticed was the .nojekyll empty file which I had to build to get GH Pages to stop tussling with my Hugo Action for turf. .nojekyll is technically a dotfile, but it doesn’t actually contain any content - its mere existence is enough to change behavior. And that makes their existence a really interesting design affordance offered to us developers by the filesystems we use every day but rarely think about.

Some other examples I’ve seen:

  • .gitkeep is by long convention often created inside of folders which we want to exist in Git, but which have nothing in them yet. .gitkeep doesn’t actually need any text at all in it.
  • Two of the projects I lead at work use empty dotfiles in /etc/opt called things like is_sheared and is_god to perform some pretty wide-sweeping visual changes on system startup. It’s a little hacky, but it’s also really easy to explain (and script around): “If this file exists and you reboot the device, it starts in God mode.”

I propose calling these kinds of files dotflags, to make it explicit that they are intended to be explicitly empty (or at best that their content is irrelevant). It’s worth noticing as well that all 3 examples are hacks to varying degrees – 2 of them come from the needs of for-profit corporations, and one of them is a hack by people who just want to keep a directory structure in Git and get on with their day.


The full workflow of what I’m trying to do in SVG form, as a sequence diagram:

And the associated Mermaid code for the above diagram:

    participant User as User
    participant Command as til Command Line
    participant Editor as Neovim Editor
    participant Repo as GitHub Repo (til)
    participant GHAction1 as GitHub Action (update.yml)
    participant GHAction2 as GitHub Action (hugo.yml)
    participant HugoSite as Hugo Site

    User->>Command: Run `til` @ command line
    Command->>User: Title?
    User->>Command: Title Is This!
    Command->>Editor: `:e til/`
    Editor->>User: opened, begin writing.
    User->>Editor: (writing...)
    User->>Editor: `:wq`
    Editor->>Command: take the shot
    Command->>Repo: Add, commit, push to til Repo
    Command->>Repo: Local Post-Push Hook (hacky)
    Repo->>GHAction1: Trigger til-site/update.yml
    GHAction1->>Repo: Update submodules, commit, push
    Repo->>GHAction2: Trigger til/sitehugo.yml
    GHAction2->>HugoSite: Rebuild and deploy Hugo site
    HugoSite->>User: Display new content
  1. I run til at the command line.
  2. I get prompted for a Title.
  3. After I enter a Title, a Neovim window immediately opens up with properly-formatted frontmatter for me to type my TIL in one go.
  4. After I :wq, the file is immediately added, committed, and pushed to
  5. A local post-push hook (Git commit here) runs immediately after this push, and triggers a Github Action on called update.yml.
  6. update.yml pulls the latest commits for til-site’s Git submodules (currently only two – the theme, and til itself, which populates the content/posts folder). That itself forms a Git commit, which immediately gets added, committed, and pushed to til-site.
  7. A SECOND Github Workflow called hugo.yml is triggered by this til-site commit, and rebuilds and deploys the Hugo site.
  8. The Hugo site gets the new content!