This is the website for Papers We Love, a community dedicated to reading and discussing computer science papers. The site showcases meetup chapters worldwide, blog posts about paper presentations, and conference information.
Live site: http://paperswelove.org/
Hosted on: GitHub Pages (static files in main branch)
Source branch: middleman (development happens here)
- Static Site Generator: Middleman v4.6.x
- Ruby Version: 3.3 (via Docker)
- CSS: Vanilla CSS (migrated from SASS/Bourbon/Neat)
- Templating: ERB templates with Markdown content
- Blog Plugin: middleman-blog 4.x
- Search: Pagefind — static search index built after Middleman build via
npx pagefind --site build - Deployment: Manual git push to
mainbranch (via GitHub Actions ormake docker-deploy)
We will store specific plans for features and maintenance in project-docs. This is also where we will store a CHANGELOG.md going forward with a summary of all of working sessions.
├── config.rb # Middleman configuration
├── Gemfile / Gemfile.lock # Ruby dependencies
├── Makefile # Build shortcuts
├── Rakefile # Rake tasks for build/deploy
├── data/ # JSON data for chapters, papers (from Meetup.com API, Cuttlefish)
│ ├── chapters.json # Chapter configuration with data adapters
│ ├── ogp/ # OpenGraph metadata (fb.yml, og.yml)
│ ├── json/ # Paper data
│ │ ├── categories.json # Category-to-paper index
│ │ ├── keywords.json # Keyword-to-paper index
│ │ └── papers/ # Individual paper JSON files (by UUID)
│ └── [city].json # Event data per chapter
├── lib/ # Custom Middleman extensions
│ ├── build_cleaner.rb # Clears build dir before deploys
│ ├── event-handler.rb # Meetup event parsing helpers
│ └── templates/ # Article templates
├── source/ # Website source files
│ ├── layouts/ # ERB layouts (layout.erb, article.erb)
│ ├── partials/ # Reusable template partials
│ ├── stylesheets/ # CSS files (screen.css, font-awesome.min.css)
│ ├── javascripts/ # JS files
│ ├── images/ # Static images
│ ├── chapters.yml # Chapter list with meetup URLs
│ ├── *.html.erb # Page templates
│ └── *.html.markdown # Blog posts (dated articles)
└── tasks/ # Thor tasks for CLI commands
├── update_task.rb # Fetch data from Meetup.com
├── upcoming_task.rb # Generate upcoming meetups post
└── chapter_task.rb # Chapter management
Run make help to see all available commands.
# Install dependencies
make deps
# or: bundle install
# Run development server (http://localhost:4567)
make serve
# or: bundle exec middleman server# Build static site to /build directory (includes Pagefind indexing)
make build
# or: bundle exec middleman build && npx pagefind --site build
# or: rake build
# Deploy to GitHub Pages (builds first, pushes to main branch)
bundle exec middleman deploy
# or: rake deploy# Update Meetup.com API data (requires CUTTLEFISH_PATH env var)
rake update
# Generate upcoming meetups post for current month
rake upcoming
# Full refresh: pull, update data, generate upcoming, deploy
rake refresh# Build the Docker image (rebuilds all services including build/deploy)
make docker-build
# Run development server (http://localhost:4567)
make docker-serve
# Build static site in container
make docker-site-build
# Deploy to GitHub Pages (requires deploy key setup)
make docker-deploy
# Open a shell in the container
make docker-shell
# Follow container logs
make docker-logs
# Rebuild without cache (after Gemfile changes)
make docker-build-no-cache
# Clean up Docker resources
make docker-cleanThe Docker deploy requires a dedicated SSH deploy key:
-
Generate a deploy key (if not already done):
ssh-keygen -t ed25519 -C "papers-we-love-deploy" -f ~/.ssh/pwl_deploy -N ""
-
Add the public key to GitHub:
- Go to:
github.com/papers-we-love/papers-we-love.github.io/settings/keys - Click "Add deploy key"
- Paste contents of
~/.ssh/pwl_deploy.pub - Enable "Allow write access"
- Save
- Go to:
-
The Docker deploy service mounts
~/.ssh/pwl_deployautomatically
- Blog with pagination (10 per page)
- URL structure:
/{year}/{category}/{title}.html - Chapter pages dynamically generated from
data/chapters.json - Video pages dynamically generated from
data/videographer.json - Paper pages dynamically generated from
data/json/papers/*.jsonwith category and keyword indexes - Directory indexes enabled (clean URLs)
- CSS minification in production
- Git deployment to
mainbranch
---
title: Post Title
date: 2024-01-15 22:23 UTC
author: Author Name
category: news | meetup
tags: topic1, topic2
label: Label Text
label_url: http://example.com
presenter: Speaker Name # for meetup posts
presenter_url: http://... # for meetup posts
description: SEO description (150 chars)
---Configured in data/chapters.json with adapters:
- meetup - Meetup.com API (most chapters)
- eventbrite - Eventbrite API (e.g., Milano)
- facebook - Facebook events (e.g., Kathmandu, Singapore)
Data fetching is handled by Cuttlefish, a Racket application. Set CUTTLEFISH_PATH environment variable to use rake update.
The site is automatically built and deployed daily via GitHub Actions.
- When: Daily at midnight EST (5:00 UTC)
- What: Pulls latest
middlemanbranch, builds, deploys tomain - Workflow:
.github/workflows/scheduled-deploy.yml
You can also trigger a deploy manually:
- Go to:
github.com/papers-we-love/papers-we-love.github.io/actions - Select "Scheduled Deploy" workflow
- Click "Run workflow"
Add the deploy key as a GitHub secret:
- Copy the private key:
cat ~/.ssh/pwl_deploy - Go to:
github.com/papers-we-love/papers-we-love.github.io/settings/secrets/actions - Click "New repository secret"
- Name:
DEPLOY_KEY - Value: Paste the entire private key (including BEGIN/END lines)
- Save
- Branch workflow: Edit on
middlemanbranch, deploy pushes static files tomain - Docker: Uses Ruby 3.3-slim image with Bundler 2.5.x (recommended for development)
- Build artifacts:
/builddirectory is gitignored and recreated each build - Chapter pages: Dynamically proxied from
chapter.html.erbtemplate using data files - Video pages: Dynamically proxied from
video.html.erbwith tag indexes - Paper pages: Dynamically proxied from
paper.html.erbwith category and keyword indexes, plus an Atom feed at/papers_feed.xml. Papers with nil/empty titles in the JSON data are skipped. Markdown summaries are rendered via Kramdown. - Search: Pagefind indexes the built HTML. The search UI partial (
source/partials/_search.erb) is included on the homepage, paper category/keyword pages, and video pages. Content pages (papers, videos, blog articles) usedata-pagefind-bodyfor selective indexing anddata-pagefind-filterattributes for faceted filtering (type, author, category, keyword, tag). Pagefind assets only exist after build, so search won't work duringmiddleman server(the init is wrapped in try/catch). - Make commands: Run
make helpto see all available commands