blumeops/docs/reference/services/cv.md
Erich Blume 5b52f18356 cv+docs: relocate content under ~/blumeops/{cv,docs}
Keeps blumeops-managed state grouped under a single namespace in the
home dir rather than scattered top-level dirs. Caddy block paths are
derived from cv_content_dir / docs_content_dir, so the role-defaults
edit propagates automatically.

Validated end-to-end on indri: tarballs extracted to the new paths,
sentinels written, second run is idempotent. Old ~/cv and ~/docs from
the earlier validation run were removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 14:54:01 -07:00

69 lines
2.6 KiB
Markdown

---
title: CV
modified: 2026-04-29
last-reviewed: 2026-04-29
tags:
- service
- resume
---
# CV (Resume)
Personal resume/CV served as a static HTML page with PDF download, built from YAML source via Jinja2 and WeasyPrint.
## Quick Reference
| Property | Value |
|----------|-------|
| **Public URL** | `cv.eblu.me` (via [[flyio-proxy]]) |
| **Private URL** | `cv.ops.eblu.me` (Caddy on indri) |
| **Deployment** | Ansible role `cv` on indri (no daemon — Caddy serves files directly) |
| **Content dir** | `~/blumeops/cv/content/` on indri |
| **Source repo** | `forge.eblu.me/eblume/cv` (private, not mirrored to GitHub) |
| **Content packages** | `forge.eblu.me/eblume/-/packages` (generic package `cv`) |
Migrated from minikube to indri-native on 2026-04-29 (see [[cv-on-indri]]).
## Architecture
1. **Source**: `resume.yaml` (content) + `template.html` (Jinja2) + `style.css` in the cv repo
2. **Build**: `render.py` (uv script runner) generates `index.html`; WeasyPrint generates `resume.pdf`
3. **Release**: Dagger `build` function packages `index.html`, `style.css`, `resume.pdf` into a tarball, uploaded to Forgejo generic packages
4. **Deploy**: ansible role downloads the tarball into `~/blumeops/cv/content/` on indri; Caddy serves the directory directly
## Endpoints
| Path | Description |
|------|-------------|
| `/` | Resume HTML page |
| `/resume.pdf` | PDF download (Caddy adds `Content-Disposition: attachment`) |
## Configuration
**Key files (blumeops):**
- `ansible/roles/cv/defaults/main.yml` — pinned `cv_version` and tarball URL
- `ansible/roles/cv/tasks/main.yml` — sentinel-gated download + extract
- `ansible/roles/caddy/defaults/main.yml``cv` service entry (`kind: static`, `download_paths` for the PDF)
**Key files (cv repo):**
- `resume.yaml` — Resume content (YAML)
- `template.html` — Jinja2 HTML template
- `style.css` — CSS with screen/print media queries
- `render.py` — uv script runner (PEP 723) that renders YAML → HTML
- `src/cv_ci/main.py` — Dagger pipeline (alpine + uv + WeasyPrint)
- `.forgejo/workflows/cv-release.yaml` — Release workflow
## Release flow
1. Release a new package from the cv repo (`Release CV` workflow)
2. Run the blumeops `Deploy CV` workflow → bumps `cv_version` in the ansible role and pushes
3. Run `mise run provision-indri -- --tags cv` from gilbert
4. Purge the Fly.io proxy cache so the new content is fetched
## Related
- [[cv-on-indri]] — Operations how-to
- [[docs]] — Similar architecture (Caddy serving a tarball-extracted dir)
- [[flyio-proxy]] — Exposes `cv.eblu.me` publicly via Tailscale tunnel