The Problem
Maintaining a static blog that automatically enriches posts with related‑content data and search indexes can become brittle when the CI pipelines do not actually run tests, use mutable action versions, or expose overly‑privileged tokens. The repo also ships large binary blobs that bloat every clone.
What This Does
The repository builds a Jekyll site under docs/ (see docs/_posts/, docs/assets/, docs/_layouts/). On every push that touches docs/_posts/** a workflow Generate Related Data recomputes a lightweight related‑post graph and updates docs/assets/search-index.json. A scheduled workflow runs every 30 minutes, loads pre‑computed embeddings from docs/_data/embeddings.npz, computes missing neighbours, and writes docs/_data/related.json and docs/_data/status.json. The final site is deployed to GitHub Pages by the workflow Deploy Jekyll site.
Key files:
.github/workflows/generate-related-min.yml– scheduled data generation..github/workflows/e2e-semantic.yml– example end‑to‑end test workflow (currently empty of test steps).docs/vendor/bundle/.../Dockerfile– builds a Docker image used by the generation jobs.- Large data blobs in
docs/_data/(related.json,embeddings.f32) that exceed 5 MiB each.
How It Is Wired
Entry point: a push to main or the scheduled cron triggers the GitHub Actions defined in .github/workflows/*.yml.
Workflow flow (Generate Related Data):
actions/checkout@v3pulls the repo.docker/build-push-action(or a custom Docker step) builds the image defined indocs/vendor/bundle/.../Dockerfile.- Inside the container a Ruby script (part of the Jekyll site or a custom plugin) reads
docs/_data/embeddings.npz, computes neighbour vectors, and writesrelated.jsonandstatus.json. - The workflow commits the updated JSON files back to the repo using
git(the token fromGITHUB_TOKEN).
Deploy Jekyll site workflow:
- Checkout.
- Set up Ruby (
ruby/setup-ruby). - Run
bundle installandbundle exec jekyll build. - Push the generated
_site/directory to thegh-pagesenvironment via the built‑in Pages deployment action.
Permissions & token usage: All workflows rely on the default GITHUB_TOKEN. No explicit permissions: block is present, so the token inherits repository defaults (full write). The checkout step retains the token in .git/config, exposing it to any later npm/gem post‑install script.
Docker image: Dockerfile in docs/vendor/bundle/... starts from ruby:$RUBY_VERSION-slim and runs Jekyll commands as root, with no non‑root USER directive.
Data handling: Large files (related.json ≈ 20 MiB, embeddings.f32 ≈ 20 MiB) live directly in the Git history, causing every clone and CI checkout to transfer > 40 MiB of binary data.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/repo_posts.git
cd repo_posts
# Install Ruby dependencies (requires Ruby 3.2)
bundle install --path vendor/bundle
# Build the site locally
bundle exec jekyll build --source docs --destination _site
# Serve locally for preview
bundle exec jekyll serve --source docs --destination _site --watch
No explicit configuration files (e.g., config.yml) are present; the site relies on Jekyll defaults and the docs/_config.yml that ships with the Minimal theme. To trigger the data‑generation pipeline locally you would need to run the same Docker image used in the workflow, but the repo does not provide a ready‑made CLI script.
Real‑World Use
A marketing team could host a curated blog where each article automatically shows “related posts” based on vector similarity. A nightly CI job updates the similarity graph without manual intervention, and the published site reflects the latest relationships immediately after each push.
# Example snippet that could be added to a downstream repo
on:
schedule:
- cron: "*/30 * * * *"
Code Health & Issues
- High – Pin third‑party GitHub Actions to a commit SHA –
.github/workflows/*usesstefanzweifel/git-auto-commit-action@v5. Replace@v5with a 40‑character SHA and keep the version comment. - High – Make CI invoke the test suite it has –
.github/workflows/*contain four test files but norun: bundle exec rake test(or similar). Add a test step to the existing workflow. - Medium – Declare least‑privilege permissions for GITHUB_TOKEN –
.github/workflows/e2e-semantic.ymllacks apermissions:block. Addpermissions: contents: readand widen only where needed. - Medium – Pin the container base image by digest – Dockerfile in
docs/vendor/bundle/...usesruby:$RUBY_VERSION-slim. Replace withruby@sha256:<digest>. - Medium – Move large binaries to Git LFS or external storage –
docs/_data/related.jsonandembeddings.f32each exceed 5 MiB. Track them with LFS or fetch from object storage in a setup step. - Medium – Set
persist-credentials: falseon checkout –.github/workflows/e2e-semantic.ymlkeeps the token in.git/config. Addwith: persist-credentials: false. - Medium – Add a non‑root USER to the image – Dockerfile lacks a
USERdirective. Create an unprivileged user and switch to it beforeCMD. - Low – Set
timeout-minuteson workflow jobs –.github/workflows/e2e-semantic.ymldefines no job timeout. Add a reasonabletimeout-minutes:(e.g., 30).
Additional hygiene gaps: no LICENSE file, no README describing onboarding, no test suite executed by CI, and no Dockerfile at the repository root.
The Bottom Line
The repo delivers a functional Jekyll‑based blog with automated related‑post updates, but its CI pipelines are insecure and untested, and the repository is bloated with large binary assets. It is suitable for teams that need a quick static‑site showcase and are willing to tighten CI security, add proper testing, and offload large data files to LFS or external storage.