Static blogging using the Python pelican static site generator under the hood.
Markdown files under /content/posts and /content/pages are converted to HTML
by Pelican as part of a Github Action.
Local development settings are in pelicanconf.py while the live settings are in publishconf.py.
publishconf.py imports everything from pelicanconf.py and overrides the production-specific values
(live SITEURL, feeds enabled, DELETE_OUTPUT_DIRECTORY = True). You should never need to edit
publishconf.py for local dev.
Deployment consists of committing changes to main and pushing up to GitHub.
cd ~/Documents/work/pelicantpc
# Build once and serve on http://localhost:8000
uv run pelican content -s pelicanconf.py
python -m http.server 8000 --directory output# In one terminal: watch and rebuild on every content/template/CSS change
uv run pelican --autoreload -s pelicanconf.py
# In another terminal: serve the output
python -m http.server 8000 --directory outputOr with the Makefile (if pelican is on your PATH):
make devserver # starts both watcher + HTTP server together| File | Purpose |
|---|---|
pelicanconf.py |
Local dev — SITEURL = "http://localhost:8000", feeds off |
publishconf.py |
Production — imports pelicanconf, sets SITEURL = "https://tomclancy.info", feeds on |
The GitHub Action runs pelican content -s publishconf.py on push to main.
- Work in progress: we need to split up the
article.contentso headers can appear properly. - Need to test footnotes
- Test subheaders and then reformat some docs (plus update Thunderbolt theme)
- Will need to be updated for changes above before switching can work
All active plugins are listed in pelicanconf.py under PLUGINS.
| Plugin | Install | Purpose |
|---|---|---|
plugins.oembed |
local (plugins/oembed.py) |
Embeds YouTube, Twitter/X, and other oEmbed providers |
pelican.plugins.simple_footnotes |
PyPI pelican-simple-footnotes |
Footnote syntax: [^1: text] renders inline footnotes |
pelican.plugins.deadlinks |
GitHub (no PyPI release) | Detects broken external links; set DEADLINKS_VALIDATION = True for full HTTP checks |
Because pelican-deadlinks has no PyPI release, it must be installed from GitHub:
uv add "git+https://github.com/pelican-plugins/deadlinks.git"Enable validation once, build, then disable again to avoid slowing down normal builds:
# In pelicanconf.py: set DEADLINKS_VALIDATION = True
uv run pelican content -s pelicanconf.py 2>&1 | grep -i dead
# Re-set DEADLINKS_VALIDATION = False when done- Markdown linter for consistency between writing environments?
- oEmbed support in importers: https://micawber.readthedocs.io/en/latest/index.html
- Not sure if I need it; if so, here's an example content/posts/2013-06-12-parochialism.md
- problem is there are lots of embedded youtube and similar in things, so it would need some testing
- Swap ://twitter.com to ://x.com?
- Add requirement to hook
- Not sure if I need it; if so, here's an example content/posts/2013-06-12-parochialism.md
- Add a tag to any post starting with "Twitter Updates" or just put a "hide" status on them
- Music blogging for favorites -- create a landing page
- Do it in sync with A-Team portfolio rebuild
- Automatically add media from Django if possible, remove things like
thumb:1
- Media consumed posts: podcasts, books, etc
- Pelican docs
- Plugins and repo
- UV docs: Using this as a project to get used to
uv