7d84d8f00ded63abfb2f5a50208522f244de153e
This replaces the old 'manual docker run' workflow. Pushing to main now: 1. Builds the multi-stage Docker image (Node build + nginx runtime) 2. Tags with :latest and :<short-sha> for reproducibility 3. Pushes both to the Gitea container registry 4. Dispatches to gitea-deploy-orchestrator with the image+SHA+branch The orchestrator handles the actual deploy (Portainer, DNS, healthcheck). Single source of truth for 'where do we deploy' lives in the orchestrator repo, not in scattered docker run commands. Required secrets (Kay's manual step): - REGISTRY_TOKEN: Gitea API token with write:package scope - GITEA_DISPATCH_TOKEN: Gitea API token with repository scope (for posting repository_dispatch to the orchestrator) Refs: gitea-deploy-orchestrator#3 (Phase B5) Co-Authored-By: Claude <noreply@anthropic.com>
WH40K Points Comparator
Compare Warhammer 40,000 unit points across Munitorum Field Manual versions.
Features
- Multi-version comparison — Track points changes across 5 MFM versions (1.14 → current)
- Faction filtering — Browse by faction or view all 1,449 units
- Biggest movers — Top 5 price drops and rises for the current view
- Historical graph — Click any unit to see a points history chart across all MFM versions
- Shareable URLs — Filter state is stored in URL query params (
?faction=...&dir=...&q=...) - Mobile responsive — Flex-based layout that scales to any device
- Social embeds — OG/Twitter meta tags with grimdark favicon and preview image
Tech Stack
- React + MUI (Material UI DataGrid)
- Vite build
- nginx (Alpine) in Docker
- Traefik reverse proxy with Cloudflare TLS
Data Sources
- Current MFM — Scraped from warhammer-community.com (live data)
- MFM 4.3 (Jun 2026), 3.2 (Aug 2025), 2.3 (Mar 2025), 1.14 (Dec 2024) — Parsed from PDFs
Deployment
Using Docker Compose (recommended)
- Edit
docker-compose.yml— change theHost()rule to your domain - Build and deploy:
docker compose up -d --build
Without Traefik
Uncomment the ports section in docker-compose.yml:
ports:
- "8080:80"
Then access at http://localhost:8080.
Building from scratch
The Dockerfile is multi-stage:
- Build stage —
node:20-alpineinstalls deps and runsnpm run build - Runtime stage —
nginx:alpineserves the static files
No pre-built artifacts needed — just docker compose build.
Data Pipeline
PDFs + live scrape → per-faction JSON files
↓
build_deduped_data.py
↓
react-app/public/data.json
↓
React app (fetched at runtime)
Run python3 build_deduped_data.py to rebuild data.json from source data.
Project Structure
wh40k-factions/
├── Dockerfile # Multi-stage: node build → nginx serve
├── docker-compose.yml # Traefik + Cloudflare TLS config
├── build_deduped_data.py # Merges all MFM versions into data.json
├── parse_pdf_per_faction.py # PDF → per-faction JSON (MFM 4.3)
├── react-app/
│ ├── index.html # OG meta tags, favicon
│ ├── package.json
│ ├── vite.config.js
│ ├── public/
│ │ ├── data.json # Merged dataset (5 MFM versions)
│ │ ├── favicon.png # Grimdark Aquila icon
│ │ └── og-image.png # Social embed banner
│ └── src/
│ ├── main.jsx
│ └── App.jsx # Main app: filters, movers, DataGrid, graph modal
├── live/ # Current MFM scrape (per-faction JSON)
├── pdf/ # MFM 4.3 PDF parse (per-faction JSON)
├── pdf32/ # MFM 3.2 PDF parse
├── pdf23/ # MFM 2.3 PDF parse
└── pdf114/ # MFM 1.14 PDF parse
License
MIT
Description
Languages
Python
48.9%
JavaScript
38.2%
CSS
7.7%
HTML
4.1%
Dockerfile
1.1%