2e162edf88b2bf3acb3d7dcaa6528028234d730b
Some checks failed
build / Build & Push & Dispatch (push) Has been cancelled
Gitea 1.25 expression parser does NOT support GitHub's ${VAR::N}
string-slicing shorthand inside an interpolation context. The previous
build.yml used ${{ github.sha::7 }} inline inside the build-push-action
tags: block, which failed with:
Failed to parse: got unexpected character ':' while lexing expression
Fix: add a dedicated "Compute image tag" step that writes the short SHA to
$GITHUB_OUTPUT, then reference it as ${{ steps.tag.outputs.short_sha }}
from both the build-push-action tags block and the dispatch curl payload.
Also, the previous workflow referenced certs/rootCA.pem but the file was
never committed to this repo (only to gitea-deploy-demo). Adding
certs/rootCA.pem in the same commit.
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%