Publishing a TIL collection as a static GitHub Pages site
I forked simonw/til to start my own "Today I Learned" collection, but its publishing pipeline was built around Simon's own infrastructure: build_database.py compiled every entry into a sqlite database, generate_screenshots.py rendered preview images via Playwright, and the GitHub Actions workflow pushed the result to an S3 bucket and deployed a Datasette instance on Fly.io. None of that infrastructure was mine, so the workflow just failed without those secrets configured.
Since I only wanted a browsable index of my own notes, I replaced the whole pipeline with a much smaller one:
update_readme.pyscans the topic directories directly and rewrites the README's index between<!-- index starts -->/<!-- index ends -->markers. Instead of reading dates from a database, it shells out togit log --follow --diff-filter=A --date=shortper file to find when it was actually added.build_site.pyconverts everytopic/entry.mdinto a standalone HTML page (via themarkdownpackage) plus oneindex.htmlgrouping everything by topic, and writes it all to_site/..github/workflows/publish.ymlruns both scripts on every push tomain, commits the refreshed README if it changed, then uploads_site/withactions/upload-pages-artifactand deploys it withactions/deploy-pages.
That last part is a pattern Simon documented himself, in his own TIL: github-actions/github-pages.md. The minimal recipe is just:
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
mkdir _site
echo '<h1>Hello, world!</h1>' > _site/index.html
- uses: actions/upload-pages-artifact@v3
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
Anything written into _site/ before the upload-pages-artifact step gets published. The three permissions are all required: contents: read for checkout, pages: write to publish, and id-token: write because deploy-pages needs it to authenticate.
One gotcha I hit: my repo has a topic folder literally named markdown/, which shadowed the pip markdown package the build script imports — Python adds the script's own directory to sys.path, so import markdown found the empty local folder instead of the real library. Fixed by stripping the script's directory from sys.path before importing it.
Enabling Pages with "build from GitHub Actions" as the source (rather than a branch) can be done from the repo settings UI, or via the API:
gh api -X POST repos/OWNER/REPO/pages -f 'build_type=workflow'
The site now lives at https://til.housni.eu/, with no Fly, S3, or Datasette involved. It started at abdelhousni.github.io/til/; with a custom domain set in the repository's Pages settings, GitHub redirects that address to the new one.
Created 2026-09-05T02:32:25+02:00, updated 2026-09-29T22:48:40+02:00 · History · Edit