Massive Refactor: Architecture Redesign and Technical Debt Cleanup (#528)
* Rename `data` folder with schema * Start refactoring data models * Work on entry models * Keep working on entries * Keep working on data models * Push old data files * Keep working on data models * First draft of schema.cv * Keep working on schema * Keep working on schema * Improve schema.models * Keep working on rendercv.schema * Work on schema.design * Keep working on rendercv.schema * Complete variant_class_generator * Keep working rendercv.schema * Keep working on rendercv.schema * Final touches to rendercv.schema * Improve json schema descriptions in rendercv.schema * Start working on rendercv.schema tests * Keep implementing rendercv.schema tests * Add more tests for rendercv.schema * Improve rendercv.schema * Improve docstrings and comments in rendercv.schema * Implement better pydantic error handling in `rendercv.schema` * Improve variant class system * Fix rendercv.schema tests * Start working on rendercv.templater * Update template names * Switching to new rendercv typst template soon * Work on new templater * Rename renderer with renderer_old * Don't use utils in rendercv.schema * Complete connections * Update renderer folder structure * Work on new renderer * Work on new renderer * Date processing on new renderer * Improve date processing, support multiple emails, phones, and websites * Improve markdown to Typst * Complete entry template processing * Time span computation in new renderer * Better entry templates * Setup new templates * Improve rendercv.schema * Start adding tests for rendercv.renderer * New markdown parser! * Improve markdown to typst conversion * Finalize markdown parser * Add new test files for rendercv.renderer * Fix cv and connections * Add connections test * Improve connection tests * Improve entry templates * Add model processor tests * Improve templater * Rename old folders * Improve schema * Add file generation logic to renderer * Fix naming issues * Fix schema tests * Add path type tests * Add font family and typst dimension type tests * Rename old tests * Fix design tests * Start integration testing of renderer * Improve entry tempates * Handle nested highlights properly * Finalize Typst preamble template * Start working on new CLI * Remove old test files * Implement override dictionary in new schema * Start working on new CLI * Better prints on render command * New structure * New render printer * Add all the commands to new CLI * Work on new command in new cli * Improve new command * Add error handler to new cli * Work on create theme command * Complete create theme command * Remove old source files * Improve exceptions * Create new docs * Add writing tests guide * Fix cli printer and write tests * Test copy templates * Add app tests * Bring back accidentally removed files * Imporve cli and tests * Fix path issues * Improve * Improve * Add reference file comparison tests * Fix path resolver * Start working on test_pdf_png * Implement comparison of multiple files (png) * Start testing typst * Fix templating issues * Fix header and entry templates issues * Implement short second rows * Fix date issues * Fix nested bullets and add summary * Update testdata * Implement footer * Update testdata * Reimagined design and locale schema, first iteration * Reimagined design and locale second iteration * Update design and locale schemas * Adapt templater to the new design and locale * Fix tests * Update lib.typ and testdata for the new locale and design * Implement proper tests with all combinations of entries * Remove some docstrings * fix connections logic * Improve * Start working on examples * Update testdata * Fix long second row issue * fix templating issues * Fix lib.typ issues * Update testdata * Fix clean_trailing_parts * Update test cv * update test cv * Update theme defaults * update schema and fix moderncv * Fix moderncv issues * Update testdata * Update testdata and examples * Fix issues about photo * Fix typst photo path issues * improve entry templates from yaml * add new locale * Rename writing tests doc * Update writing tests * Improve tests * Add more cli tests * Increase test coverage * Rename variant pydantic model generator * Improve tests * Update testdata and improve tests * Format, fix pre-commit errors * Fix scripts and update entry figures * Improve tests * Write docstrings of schema * Write schema docstrings * Setup api reference * Start working on new docs * Work on docs * Improve progress panel of render command * Finalize new docs index * Complete CLI docs * Work on YAML input structure page * Finalize user guide * Start working on developer guide * Improve api reference * Improve developer guide * Improve developer guide * Improve developer gide * Improve developer guide * Improve developer guide * Update developer guide * Improve developer guide * Improve developer guide * Improve developer guide * Developer guide first draft * update developer guide * Update examples * Update testdata * Handle wrong installation (rendercv instead of rendercv[full]) * Remove unnecessary files * Write set up vs code page * Update README.md * Change docs description * Compress design options gif * minor updates * Polish all the json schema descriptions * Update testdata and examples * Remove some emdashed from docs * Add whatsapp support * Add TestEscapeTypstCharacters to tests * Implement custom connections * Add page break before sections feature * Revert page break before sections feature * Rebase to main * Fix social network tests, update schema
4
.github/workflows/create-executables.yaml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
uses: taiki-e/install-action@just
|
uses: taiki-e/install-action@just
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --locked --all-extras --all-groups
|
run: just install
|
||||||
|
|
||||||
- name: Create executable
|
- name: Create executable
|
||||||
run: just create-executable
|
run: just create-executable
|
||||||
|
|||||||
4
.github/workflows/deploy-docs.yaml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
@@ -35,7 +35,7 @@ jobs:
|
|||||||
uses: taiki-e/install-action@just
|
uses: taiki-e/install-action@just
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --locked --all-extras --all-groups
|
run: just install
|
||||||
|
|
||||||
- name: Build docs
|
- name: Build docs
|
||||||
run: just build-docs
|
run: just build-docs
|
||||||
|
|||||||
8
.github/workflows/release.yaml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- update_files
|
- update_files
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
@@ -96,8 +96,8 @@ jobs:
|
|||||||
- name: Upload package to PyPI
|
- name: Upload package to PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
|
|
||||||
publish_to_dockerhub_and_ghcr:
|
publish_docker_to_ghcr:
|
||||||
name: Push Docker image to Docker Hub and GitHub Container Registry
|
name: Push Docker image to GitHub Container Registry
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- publish_to_pypi
|
- publish_to_pypi
|
||||||
@@ -108,7 +108,7 @@ jobs:
|
|||||||
id-token: write
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the repo
|
- name: Check out the repo
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Log in to the Container registry
|
- name: Log in to the Container registry
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
|
|||||||
18
.github/workflows/test.yaml
vendored
@@ -20,25 +20,23 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu, windows, macos]
|
os: [ubuntu, windows, macos]
|
||||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
python-version: ["3.12", "3.13", "3.14"]
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
with:
|
|
||||||
python-version: "${{ matrix.python-version}}"
|
|
||||||
|
|
||||||
- name: Install just
|
- name: Install just
|
||||||
uses: taiki-e/install-action@just
|
uses: taiki-e/install-action@just
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --locked --all-extras --all-groups
|
run: just install
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: just test-with-coverage
|
run: just test-coverage
|
||||||
|
|
||||||
- name: Rename the coverage file
|
- name: Rename the coverage file
|
||||||
run: mv .coverage .coverage.${{ matrix.python-version }}.${{ matrix.os }}
|
run: mv .coverage .coverage.${{ matrix.python-version }}.${{ matrix.os }}
|
||||||
@@ -51,14 +49,12 @@ jobs:
|
|||||||
path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
|
path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}
|
||||||
|
|
||||||
report-coverage:
|
report-coverage:
|
||||||
# Run only if the workflow was triggered by a push event
|
|
||||||
if: github.event_name == 'push'
|
|
||||||
name: Generate the coverage report
|
name: Generate the coverage report
|
||||||
needs: [test]
|
needs: [test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Download coverage files
|
- name: Download coverage files
|
||||||
uses: actions/download-artifact@v6
|
uses: actions/download-artifact@v6
|
||||||
@@ -71,7 +67,7 @@ jobs:
|
|||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --locked --all-extras --all-groups
|
run: just install
|
||||||
|
|
||||||
- name: Install just
|
- name: Install just
|
||||||
uses: taiki-e/install-action@just
|
uses: taiki-e/install-action@just
|
||||||
@@ -86,6 +82,6 @@ jobs:
|
|||||||
run: uv tool run smokeshow==0.4.0 upload ./htmlcov
|
run: uv tool run smokeshow==0.4.0 upload ./htmlcov
|
||||||
env:
|
env:
|
||||||
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
|
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
|
||||||
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 20
|
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 90
|
||||||
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|||||||
4
.github/workflows/update-files.yaml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v5
|
- uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install uv
|
- name: Install uv
|
||||||
uses: astral-sh/setup-uv@v7
|
uses: astral-sh/setup-uv@v7
|
||||||
@@ -23,7 +23,7 @@ jobs:
|
|||||||
uses: taiki-e/install-action@just
|
uses: taiki-e/install-action@just
|
||||||
|
|
||||||
- name: Install the project
|
- name: Install the project
|
||||||
run: uv sync --locked --all-extras --all-groups
|
run: just install
|
||||||
|
|
||||||
- name: Set Git credentials
|
- name: Set Git credentials
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
10
.gitignore
vendored
@@ -165,10 +165,7 @@ cython_debug/
|
|||||||
# VSCode
|
# VSCode
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Personal CVs
|
# RenderCV output
|
||||||
*_CV.yaml
|
|
||||||
*_cv.py
|
|
||||||
*_CV.typ
|
|
||||||
rendercv_output/
|
rendercv_output/
|
||||||
|
|
||||||
# Include reference files
|
# Include reference files
|
||||||
@@ -189,4 +186,7 @@ rendercv_output/
|
|||||||
render_command.prof
|
render_command.prof
|
||||||
|
|
||||||
# Executables:
|
# Executables:
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
|
# Coverage:
|
||||||
|
coverage.md
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
rev: v0.11.2
|
rev: v0.14.8
|
||||||
hooks:
|
hooks:
|
||||||
- id: ruff
|
- id: ruff
|
||||||
|
- repo: https://github.com/RobertCraigie/pyright-python
|
||||||
|
rev: v1.1.407
|
||||||
|
hooks:
|
||||||
|
- id: pyright
|
||||||
- repo: https://github.com/codespell-project/codespell
|
- repo: https://github.com/codespell-project/codespell
|
||||||
rev: v2.4.1
|
rev: v2.4.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: codespell
|
- id: codespell
|
||||||
- repo: https://github.com/adrienverge/yamllint
|
args:
|
||||||
rev: v1.37.1
|
- --skip=src/rendercv/schema/models/locale/other_locales/*
|
||||||
hooks:
|
- --exclude-file=schema.json
|
||||||
- id: yamllint
|
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
extends: default
|
|
||||||
rules:
|
|
||||||
line-length: disable
|
|
||||||
56
Dockerfile
@@ -1,31 +1,49 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
# Use a Python image with uv pre-installed
|
||||||
|
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
|
||||||
|
|
||||||
ARG UID=1000
|
# Install the project into `/app`
|
||||||
ARG GID=1000
|
WORKDIR /app
|
||||||
|
|
||||||
FROM python:3.13-slim AS builder
|
# Enable bytecode compilation
|
||||||
|
ENV UV_COMPILE_BYTECODE=1
|
||||||
|
|
||||||
WORKDIR /build
|
# Copy from the cache instead of linking since it's a mounted volume
|
||||||
|
ENV UV_LINK_MODE=copy
|
||||||
|
|
||||||
RUN python -m venv /opt/rendercv-venv \
|
# Install the project's dependencies using the lockfile and settings
|
||||||
&& /opt/rendercv-venv/bin/pip install --no-cache-dir --upgrade pip \
|
# This layer is cached separately from the project code for faster rebuilds
|
||||||
&& /opt/rendercv-venv/bin/pip install --no-cache-dir "rendercv[full]"
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||||
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
|
uv sync --locked --no-install-project --no-editable --extra full
|
||||||
|
|
||||||
FROM python:3.13-slim
|
# Then, add the rest of the project source code and install it
|
||||||
|
# Installing separately from its dependencies allows optimal layer caching
|
||||||
|
COPY . /app
|
||||||
|
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||||
|
uv sync --locked --no-editable --extra full
|
||||||
|
|
||||||
ARG UID
|
# Final stage
|
||||||
ARG GID
|
FROM python:3.12-slim-bookworm
|
||||||
|
|
||||||
RUN groupadd --gid ${GID} rendercv \
|
# Setup a non-root user
|
||||||
&& useradd --uid ${UID} --gid ${GID} --create-home rendercv
|
RUN groupadd --system --gid 999 rendercv \
|
||||||
|
&& useradd --system --gid 999 --uid 999 --create-home rendercv
|
||||||
|
|
||||||
COPY --from=builder /opt/rendercv-venv /opt/rendercv-venv
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
ENV PATH="/opt/rendercv-venv/bin:${PATH}"
|
# Copy the virtual environment from the builder stage
|
||||||
|
COPY --from=builder --chown=rendercv:rendercv /app/.venv /app/.venv
|
||||||
|
|
||||||
WORKDIR /rendercv
|
# Place executables in the environment at the front of the path
|
||||||
|
ENV PATH="/app/.venv/bin:$PATH"
|
||||||
|
|
||||||
USER rendercv:rendercv
|
# Use the non-root user to run our application
|
||||||
|
USER rendercv
|
||||||
|
|
||||||
ENTRYPOINT ["/bin/bash"]
|
# Set the entrypoint to the rendercv CLI (installed via pyproject.toml entry point)
|
||||||
|
ENTRYPOINT ["rendercv"]
|
||||||
|
|
||||||
|
# Default command shows help
|
||||||
|
CMD ["--help"]
|
||||||
|
|||||||
157
README.md
@@ -1,7 +1,7 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>RenderCV</h1>
|
<h1>RenderCV</h1>
|
||||||
|
|
||||||
_The engine of the [RenderCV App](https://rendercv.com)_
|
_CV/resume generator for academics and engineers_
|
||||||
|
|
||||||
[](https://github.com/rendercv/rendercv/actions/workflows/test.yaml)
|
[](https://github.com/rendercv/rendercv/actions/workflows/test.yaml)
|
||||||
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/rendercv/rendercv)
|
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/rendercv/rendercv)
|
||||||
@@ -11,68 +11,149 @@ _The engine of the [RenderCV App](https://rendercv.com)_
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
RenderCV engine is a Typst-based Python package with a command-line interface (CLI) that allows you to version-control your CV/resume as source code. It reads a CV written in a YAML file with Markdown syntax, converts it into a [Typst](https://typst.app) code, and generates a PDF.
|
Write your CV or resume as YAML, then run RenderCV,
|
||||||
|
|
||||||
RenderCV engine's focus is to provide these three features:
|
```bash
|
||||||
|
rendercv render John_Doe_CV.yaml
|
||||||
|
```
|
||||||
|
|
||||||
- **Content-first approach:** Users should be able to focus on the content instead of worrying about the formatting.
|
and get a PDF with perfect typography. No template wrestling. No broken layouts. Consistent spacing, every time.
|
||||||
- **A mechanism to version-control a CV's content and design separately:** The content and design of a CV are separate issues and they should be treated separately.
|
|
||||||
- **Robustness:** A PDF should be delivered if there aren't any errors. If errors exist, they should be clearly explained along with solutions.
|
|
||||||
|
|
||||||
|
With RenderCV, you can:
|
||||||
|
|
||||||
It takes a YAML file that looks like this:
|
- Version-control your CV — it's just text.
|
||||||
|
- Focus on content — don't wory about the formatting.
|
||||||
|
- Get perfect typography — kerning, spacing, hierarchy, all handled. No design skills required.
|
||||||
|
|
||||||
|
A YAML file like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
cv:
|
cv:
|
||||||
name: John Doe
|
name: John Doe
|
||||||
location: Location
|
location: San Francisco, CA
|
||||||
email: john.doe@example.com
|
email: john.doe@email.com
|
||||||
phone: tel:+1-609-999-9995
|
website: https://rendercv.com/
|
||||||
social_networks:
|
social_networks:
|
||||||
- network: LinkedIn
|
- network: LinkedIn
|
||||||
username: john.doe
|
username: rendercv
|
||||||
- network: GitHub
|
- network: GitHub
|
||||||
username: john.doe
|
username: rendercv
|
||||||
sections:
|
sections:
|
||||||
welcome_to_RenderCV!:
|
Welcome to RenderCV:
|
||||||
- '[RenderCV](https://rendercv.com) is a Typst-based CV
|
- RenderCV reads a CV written in a YAML file, and generates a PDF with professional typography.
|
||||||
framework designed for academics and engineers, with Markdown
|
- See the [documentation](https://docs.rendercv.com) for more details.
|
||||||
syntax support.'
|
|
||||||
- Each section title is arbitrary. Each section contains
|
|
||||||
a list of entries, and there are 7 different entry types
|
|
||||||
to choose from.
|
|
||||||
education:
|
education:
|
||||||
- institution: Stanford University
|
- institution: Princeton University
|
||||||
area: Computer Science
|
area: Computer Science
|
||||||
degree: PhD
|
degree: PhD
|
||||||
location: Stanford, CA, USA
|
date:
|
||||||
start_date: 2023-09
|
start_date: 2018-09
|
||||||
end_date: present
|
end_date: 2023-05
|
||||||
|
location: Princeton, NJ
|
||||||
|
summary:
|
||||||
highlights:
|
highlights:
|
||||||
- Working on the optimization of autonomous vehicles
|
- "Thesis: Efficient Neural Architecture Search for Resource-Constrained Deployment"
|
||||||
in urban environments
|
- "Advisor: Prof. Sanjeev Arora"
|
||||||
|
- NSF Graduate Research Fellowship, Siebel Scholar (Class of 2022)
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, it produces one of these PDFs with its corresponding Typst file, Markdown file, HTML file, and images as PNGs. Click on the images below to preview PDF files.
|
becomes one of these PDFs. Click on the images to preview.
|
||||||
|
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ClassicTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_Sb2novTheme_CV.pdf) |
|
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ClassicTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringresumesTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_Sb2novTheme_CV.pdf) |
|
||||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ModerncvTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringresumesTheme_CV.pdf) |
|
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ModerncvTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringclassicTheme_CV.pdf) |  |
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringclassicTheme_CV.pdf) |  |
|
|
||||||
|
|
||||||
RenderCV comes with a JSON Schema so that the YAML input file can be filled out interactively.
|
|
||||||
|
|
||||||

|
## JSON Schema
|
||||||
|
|
||||||
## Getting Started
|
RenderCV's JSON Schema lets you fill out the YAML interactively, with autocompletion and inline documentation.
|
||||||
|
|
||||||
RenderCV engine is very easy to install (`pip install "rendercv[full]"`) and easy to use (`rendercv new "John Doe"`). Follow the [user guide](https://docs.rendercv.com/user_guide) to get started.
|

|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
We are developing a [purpose-built app](https://rendercv.com) for writing CVs and resumes that will be available on mobile and web. This Python project is the foundation of that app. Check out [our blog post](https://rendercv.com/introducing-rendercv/) to learn more about why one would use such an app.
|
## Extensive Design Options
|
||||||
|
|
||||||
## Contributing
|
You have full control over every detail.
|
||||||
|
|
||||||
All contributions to RenderCV are welcome! To get started, please read [the developer guide](https://docs.rendercv.com/developer_guide).
|
```yaml
|
||||||
|
design:
|
||||||
|
theme: classic
|
||||||
|
page:
|
||||||
|
size: us-letter
|
||||||
|
top_margin: 0.7in
|
||||||
|
bottom_margin: 0.7in
|
||||||
|
left_margin: 0.7in
|
||||||
|
right_margin: 0.7in
|
||||||
|
show_footer: true
|
||||||
|
show_top_note: true
|
||||||
|
colors:
|
||||||
|
body: rgb(0, 0, 0)
|
||||||
|
name: rgb(0, 79, 144)
|
||||||
|
headline: rgb(0, 79, 144)
|
||||||
|
connections: rgb(0, 79, 144)
|
||||||
|
section_titles: rgb(0, 79, 144)
|
||||||
|
links: rgb(0, 79, 144)
|
||||||
|
footer: rgb(128, 128, 128)
|
||||||
|
top_note: rgb(128, 128, 128)
|
||||||
|
typography:
|
||||||
|
line_spacing: 0.6em
|
||||||
|
alignment: justified
|
||||||
|
date_and_location_column_alignment: right
|
||||||
|
font_family: Source Sans 3
|
||||||
|
# ...and more
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Want to set up a live preview environment like the one shown above? See [how to set up VS Code for RenderCV](https://docs.rendercv.com/user_guide/how_to/set_up_vs_code_for_rendercv.md).
|
||||||
|
|
||||||
|
## Strict Validation
|
||||||
|
|
||||||
|
No surprises. If something's wrong, you'll know exactly what and where. If it's valid, you get a perfect PDF.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Any Language
|
||||||
|
|
||||||
|
Fill out the locale field for your language.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
locale:
|
||||||
|
language: english
|
||||||
|
last_updated: Last updated in
|
||||||
|
month: month
|
||||||
|
months: months
|
||||||
|
year: year
|
||||||
|
years: years
|
||||||
|
present: present
|
||||||
|
month_abbreviations:
|
||||||
|
- Jan
|
||||||
|
- Feb
|
||||||
|
- Mar
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
Install RenderCV (Requires Python 3.12+):
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install "rendercv[full]"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new CV yaml file:
|
||||||
|
|
||||||
|
```
|
||||||
|
rendercv new "John Doe"
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit the YAML, then render:
|
||||||
|
|
||||||
|
```
|
||||||
|
rendercv render "John_Doe_CV.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details, see the [user guide](https://docs.rendercv.com/user_guide/index.md).
|
||||||
|
|||||||
37
docs/api_reference/api_reference.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import mkdocs_gen_files
|
||||||
|
|
||||||
|
nav = mkdocs_gen_files.Nav()
|
||||||
|
|
||||||
|
repository_root = Path(__file__).parent.parent.parent
|
||||||
|
api_reference = repository_root / "docs" / "api_reference"
|
||||||
|
src_rendercv = repository_root / "src" / "rendercv"
|
||||||
|
nav[("src", "rendercv")] = "index.md"
|
||||||
|
|
||||||
|
# Process each Python file in the objects directory
|
||||||
|
for path in sorted(src_rendercv.rglob("*.py")):
|
||||||
|
# Skip __init__.py files and __main__.py files
|
||||||
|
if path.name in ("__init__.py", "__main__.py"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Get the relative path from the objects directory
|
||||||
|
module_path = path.relative_to(src_rendercv).with_suffix("")
|
||||||
|
doc_path = module_path.with_suffix(".md")
|
||||||
|
parts = (f"rendercv.{module_path.parts[0]}", *module_path.parts[1:])
|
||||||
|
|
||||||
|
# Add to navigation
|
||||||
|
nav[("src", *parts)] = doc_path.as_posix()
|
||||||
|
|
||||||
|
# Generate the documentation page
|
||||||
|
with mkdocs_gen_files.open(f"api_reference/{doc_path}", "w") as fd:
|
||||||
|
module_ident = "rendercv." + ".".join(module_path.parts)
|
||||||
|
fd.write(f"::: {module_ident}\n")
|
||||||
|
|
||||||
|
# Set the edit path to the actual source file
|
||||||
|
# mkdocs_gen_files.set_edit_path(full_doc_path, full_doc_path.relative_to(docs_path))
|
||||||
|
|
||||||
|
|
||||||
|
# Write the navigation file
|
||||||
|
with mkdocs_gen_files.open("api_reference/SUMMARY.md", "w") as nav_file:
|
||||||
|
nav_file.writelines(nav.build_literate_nav())
|
||||||
7
docs/api_reference/index.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# API Reference
|
||||||
|
|
||||||
|
RenderCV is a CLI application, not a library. Its internal API is not guaranteed to be stable and may change without notice. However, for those who wish to use RenderCV programmatically in Python scripts, the complete API reference is provided here.
|
||||||
|
|
||||||
|
::: rendercv
|
||||||
|
options:
|
||||||
|
heading_level: 2
|
||||||
|
Before Width: | Height: | Size: 757 KiB After Width: | Height: | Size: 731 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
BIN
docs/assets/images/classic/classic.png
Normal file
|
After Width: | Height: | Size: 818 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 37 KiB |
BIN
docs/assets/images/design_options.gif
Normal file
|
After Width: | Height: | Size: 8.8 MiB |
|
Before Width: | Height: | Size: 702 KiB After Width: | Height: | Size: 759 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
BIN
docs/assets/images/engineeringclassic/engineeringclassic.png
Normal file
|
After Width: | Height: | Size: 866 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 702 KiB After Width: | Height: | Size: 752 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
BIN
docs/assets/images/engineeringresumes/engineeringresumes.png
Normal file
|
After Width: | Height: | Size: 859 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 41 KiB |
BIN
docs/assets/images/json_schema.gif
Normal file
|
After Width: | Height: | Size: 8.2 MiB |
|
Before Width: | Height: | Size: 698 KiB After Width: | Height: | Size: 681 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
BIN
docs/assets/images/moderncv/moderncv.png
Normal file
|
After Width: | Height: | Size: 642 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 615 KiB After Width: | Height: | Size: 710 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
BIN
docs/assets/images/sb2nov/sb2nov.png
Normal file
|
After Width: | Height: | Size: 733 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 2.0 MiB |
BIN
docs/assets/images/validation.gif
Normal file
|
After Width: | Height: | Size: 8.0 MiB |
@@ -1,5 +1,7 @@
|
|||||||
---
|
---
|
||||||
toc_depth: 1
|
toc_depth: 1
|
||||||
|
hide:
|
||||||
|
- navigation
|
||||||
---
|
---
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
@@ -92,8 +94,8 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- $\LaTeX$ has been replaced with Typst.
|
- $\LaTeX$ has been replaced with Typst.
|
||||||
- The `design` field has been changed completely. See the [documentation](https://docs.rendercv.com/user_guide/structure_of_the_yaml_input_file/#design-field) for details.
|
- The `design` field has been changed completely.
|
||||||
- The `locale_catalog` field has been renamed to `locale`, and some fields have been moved from `design` to `locale`. See the [documentation](https://docs.rendercv.com/user_guide/structure_of_the_yaml_input_file/#locale-field) for details.
|
- The `locale_catalog` field has been renamed to `locale`, and some fields have been moved from `design` to `locale`.
|
||||||
- The `moderncv` theme's header has been changed.
|
- The `moderncv` theme's header has been changed.
|
||||||
|
|
||||||
|
|
||||||
@@ -167,7 +169,7 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `rendercv_settings` field has been added to the YAML input file. For details, see [here](../user_guide/structure_of_the_yaml_input_file.md#rendercv_settings-field). It will be extended in the future.
|
- `rendercv_settings` field has been added to the YAML input file. It will be extended in the future.
|
||||||
|
|
||||||
|
|
||||||
## [1.13] - July 23, 2024
|
## [1.13] - July 23, 2024
|
||||||
@@ -176,8 +178,8 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Arbitrary keys are now allowed in the `cv` field. For details, see [here](../user_guide/structure_of_the_yaml_input_file.md#using-arbitrary-keys).
|
- Arbitrary keys are now allowed in the `cv` field.
|
||||||
- Two new fields have been added to the `locale` field: `phone_number_format` and `date_style` ([#130](https://github.com/rendercv/rendercv/issues/130)). For details, see [here](../user_guide/structure_of_the_yaml_input_file.md#locale-field).
|
- Two new fields have been added to the `locale` field: `phone_number_format` and `date_style` ([#130](https://github.com/rendercv/rendercv/issues/130)).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@@ -195,7 +197,7 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Arbitrary keys are now allowed in entry types. Users can use these keys in their templates. For details, see the [documentation](../user_guide/structure_of_the_yaml_input_file.md#using-arbitrary-keys).
|
- Arbitrary keys are now allowed in entry types. Users can use these keys in their templates.
|
||||||
- The `locale.full_names_of_months` field has been added to the data model ([#111](https://github.com/rendercv/rendercv/issues/111)).
|
- The `locale.full_names_of_months` field has been added to the data model ([#111](https://github.com/rendercv/rendercv/issues/111)).
|
||||||
- The `TODAY` placeholder can be used in the `design.page_numbering_style` field now.
|
- The `TODAY` placeholder can be used in the `design.page_numbering_style` field now.
|
||||||
|
|
||||||
@@ -214,7 +216,7 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- CLI options now have short versions. See the [CLI documentation](https://docs.rendercv.com/user_guide/cli/) for more information.
|
- CLI options now have short versions.
|
||||||
- CLI now notifies the user when a new version is available ([#89](https://github.com/rendercv/rendercv/issues/89)).
|
- CLI now notifies the user when a new version is available ([#89](https://github.com/rendercv/rendercv/issues/89)).
|
||||||
- `Google Scholar` has been added as a social network type ([#85](https://github.com/rendercv/rendercv/issues/85)).
|
- `Google Scholar` has been added as a social network type ([#85](https://github.com/rendercv/rendercv/issues/85)).
|
||||||
- Two new design options have been added to the `classic`, `sb2nov`, and `engineeringresumes` themes: `separator_between_connections` and `use_icons_for_connections`.
|
- Two new design options have been added to the `classic`, `sb2nov`, and `engineeringresumes` themes: `separator_between_connections` and `use_icons_for_connections`.
|
||||||
@@ -259,7 +261,7 @@ RenderCV has transitioned from using $\LaTeX$ to Typst. RenderCV is now much fas
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- RenderCV is now a multilingual tool. English strings can be overridden with `locale` section in the YAML input file ([#26](https://github.com/rendercv/rendercv/issues/26), [#20](https://github.com/rendercv/rendercv/pull/20)). See the [documentation](../user_guide/structure_of_the_yaml_input_file.md#locale-field) for more information.
|
- RenderCV is now a multilingual tool. English strings can be overridden with `locale` section in the YAML input file ([#26](https://github.com/rendercv/rendercv/issues/26), [#20](https://github.com/rendercv/rendercv/pull/20)).
|
||||||
- PNG files for each page can be generated now ([#57](https://github.com/rendercv/rendercv/issues/57)).
|
- PNG files for each page can be generated now ([#57](https://github.com/rendercv/rendercv/issues/57)).
|
||||||
- `rendercv new` command now generates Markdown and $\LaTeX$ source files in addition to the YAML input file so that the default templates can be modified easily.
|
- `rendercv new` command now generates Markdown and $\LaTeX$ source files in addition to the YAML input file so that the default templates can be modified easily.
|
||||||
- A new CLI command has been added, `rendercv create-theme`, to allow users to create their own themes easily.
|
- A new CLI command has been added, `rendercv create-theme`, to allow users to create their own themes easily.
|
||||||
57
docs/developer_guide/code_guidelines/source_code.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Guidelines for Writing Source Code
|
||||||
|
|
||||||
|
## Type Annotations
|
||||||
|
|
||||||
|
**Every function, variable, and class attribute must be strictly typed. No exceptions.**
|
||||||
|
|
||||||
|
Use modern Python 3.12+ syntax:
|
||||||
|
|
||||||
|
- Type aliases with `type` statement
|
||||||
|
- PEP 695 type parameters (`[T]`, `[**P]`)
|
||||||
|
- Pipe unions (`str | int`, not `Union[str, int]`)
|
||||||
|
- Proper optional types (`str | None`, not `Optional[str]`)
|
||||||
|
|
||||||
|
## Linting and Type Checking
|
||||||
|
|
||||||
|
Always run `just check` and `just format` before committing. `just check` must show **zero errors**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just format
|
||||||
|
just check
|
||||||
|
```
|
||||||
|
|
||||||
|
If there's absolutely no alternative, use `# pyright: ignore[errorCode]` or `#NOQA: errorCode` to ignore typing or linting errors.
|
||||||
|
|
||||||
|
## Docstrings
|
||||||
|
|
||||||
|
Use [Google-style docstrings](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings). Include a **"Why" section** and **"Example" section** when it adds value:
|
||||||
|
|
||||||
|
```python
|
||||||
|
def resolve_relative_path(
|
||||||
|
path: pathlib.Path, info: pydantic.ValidationInfo, must_exist: bool = True
|
||||||
|
) -> pathlib.Path:
|
||||||
|
"""Convert relative path to absolute path based on input file location.
|
||||||
|
|
||||||
|
Why:
|
||||||
|
Users reference files like `photo: profile.jpg` relative to their CV
|
||||||
|
YAML. This validator resolves such paths to absolute form and validates
|
||||||
|
existence, enabling file access during rendering.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: Path to resolve (may be relative or absolute).
|
||||||
|
info: Validation context containing input file path.
|
||||||
|
must_exist: Whether to raise error if path doesn't exist.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Absolute path.
|
||||||
|
"""
|
||||||
|
```
|
||||||
|
|
||||||
|
Docstring order:
|
||||||
|
|
||||||
|
1. Brief description (one line)
|
||||||
|
2. Why section (when it adds value)
|
||||||
|
3. Example section (when it adds value)
|
||||||
|
4. Args section (mandatory)
|
||||||
|
5. Returns section (mandatory)
|
||||||
|
6. Raises section (mandatory if function raises exceptions)
|
||||||
145
docs/developer_guide/code_guidelines/tests.md
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
# Guidelines for Writing Tests
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
Each test file tests all classes and functions in its corresponding source file. The structure mirrors `src/rendercv/`:
|
||||||
|
|
||||||
|
```
|
||||||
|
src/rendercv/renderer/templater/date.py
|
||||||
|
→ tests/renderer/templater/test_date.py
|
||||||
|
(tests all functions and classes in date.py)
|
||||||
|
|
||||||
|
src/rendercv/schema/models/cv/section.py
|
||||||
|
→ tests/schema/models/cv/test_section.py
|
||||||
|
(tests all functions and classes in section.py)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
Test names must include the name of the function or class being tested.
|
||||||
|
|
||||||
|
**When you need only one test**, use `test_` + the name:
|
||||||
|
|
||||||
|
- Testing `clean_url()` → `test_clean_url`
|
||||||
|
- Testing `Cv` → `test_cv`
|
||||||
|
|
||||||
|
**When you need multiple tests**, wrap them in a class using `Test` + PascalCase name:
|
||||||
|
|
||||||
|
- Testing `clean_url()` → `TestCleanUrl`
|
||||||
|
- Testing `Cv` → `TestCv`
|
||||||
|
|
||||||
|
Example with one test:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("url", "expected_clean_url"),
|
||||||
|
[
|
||||||
|
("https://example.com", "example.com"),
|
||||||
|
("https://example.com/", "example.com"),
|
||||||
|
("https://example.com/test", "example.com/test"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_clean_url(url, expected_clean_url):
|
||||||
|
assert clean_url(url) == expected_clean_url
|
||||||
|
```
|
||||||
|
|
||||||
|
Example with multiple tests:
|
||||||
|
|
||||||
|
```python
|
||||||
|
class TestComputeDateString:
|
||||||
|
@pytest.mark.parametrize(...)
|
||||||
|
def test_date_parameter_takes_precedence(self, ...):
|
||||||
|
...
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(...)
|
||||||
|
def test_date_ranges(self, ...):
|
||||||
|
...
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(...)
|
||||||
|
def test_returns_none_for_incomplete_data(self, ...):
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use Parametrize for Variations
|
||||||
|
|
||||||
|
Instead of writing multiple similar tests, use `@pytest.mark.parametrize`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("input_a", "input_b", "expected"),
|
||||||
|
[
|
||||||
|
("2020-01-01", "2021-01-01", "Jan 2020 – Jan 2021"),
|
||||||
|
("2020-01", "2021-02-01", "Jan 2020 – Feb 2021"),
|
||||||
|
(2020, 2021, "2020 – 2021"),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_date_ranges(self, input_a, input_b, expected):
|
||||||
|
result = compute_date_string(None, input_a, input_b, EnglishLocale())
|
||||||
|
assert result == expected
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shared Fixtures with conftest.py
|
||||||
|
|
||||||
|
Place shared fixtures in `conftest.py`. Use the closest one possible:
|
||||||
|
|
||||||
|
- Fixtures for one folder → that folder's `conftest.py`
|
||||||
|
- Fixtures for multiple folders → their closest common parent's `conftest.py`
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── conftest.py # Used across all tests
|
||||||
|
├── schema/
|
||||||
|
│ ├── conftest.py # Used by schema tests only
|
||||||
|
│ └── models/
|
||||||
|
│ └── cv/
|
||||||
|
│ ├── conftest.py # Used by CV model tests only
|
||||||
|
│ ├── test_section.py
|
||||||
|
│ └── test_cv.py
|
||||||
|
└── renderer/
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Principles
|
||||||
|
|
||||||
|
**Keep tests focused.** Test functions in isolation: input → output.
|
||||||
|
|
||||||
|
**Don't create unnecessary fixtures.** If setup is one clear line, inline it:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Don't:
|
||||||
|
@pytest.fixture
|
||||||
|
def locale(self):
|
||||||
|
return EnglishLocale()
|
||||||
|
|
||||||
|
def test_something(self, locale):
|
||||||
|
result = format_date(Date(2020, 1, 1), locale)
|
||||||
|
|
||||||
|
# Do:
|
||||||
|
def test_something(self):
|
||||||
|
result = format_date(Date(2020, 1, 1), EnglishLocale())
|
||||||
|
```
|
||||||
|
|
||||||
|
**Prefer real behavior over mocking.** Only mock when there's no practical alternative (external APIs, file system, etc.).
|
||||||
|
|
||||||
|
**Name tests by expected behavior, not by input:**
|
||||||
|
|
||||||
|
- Good: `test_returns_none_for_incomplete_data` - describes what should happen
|
||||||
|
- Bad: `test_function_with_none_input` - describes input but not behavior
|
||||||
|
|
||||||
|
**Keep tests simple:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
def test_something(self, input, expected):
|
||||||
|
result = function_under_test(input)
|
||||||
|
assert result == expected
|
||||||
|
```
|
||||||
|
|
||||||
|
**What to test:**
|
||||||
|
|
||||||
|
- Input → expected output
|
||||||
|
- Input → expected error
|
||||||
|
|
||||||
|
**What to avoid:**
|
||||||
|
|
||||||
|
- Testing implementation details instead of behavior
|
||||||
|
- Complex test setup when simple values work
|
||||||
40
docs/developer_guide/dockerfile.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Dockerfile
|
||||||
|
|
||||||
|
## What is Docker?
|
||||||
|
|
||||||
|
Docker lets software carry **its entire working environment** with it: the right language runtime, libraries, and configuration, all bundled into a single file called an *image*. Think of an image as a **frozen filesystem where everything is already installed and configured correctly**.
|
||||||
|
|
||||||
|
When you run an image, Docker creates a **container**: a live, isolated instance of that environment running on your machine. When you're done, you can delete it without a trace. Your actual system stays untouched.
|
||||||
|
|
||||||
|
## Why Docker Is Useful for RenderCV
|
||||||
|
|
||||||
|
## Why Docker for RenderCV?
|
||||||
|
|
||||||
|
RenderCV installs easily with `pip install rendercv` if you have Python. Most users don't need Docker.
|
||||||
|
|
||||||
|
But Docker makes sense if you want:
|
||||||
|
|
||||||
|
- **No installation at all** — no Python, no packages, nothing added to your system
|
||||||
|
- **A reproducible environment** — the exact same setup on every machine, every time
|
||||||
|
- **To bypass restrictions** — some systems block software installation but allow containers
|
||||||
|
|
||||||
|
The RenderCV Docker image is a ready-made environment with Python and RenderCV pre-installed. Just run:
|
||||||
|
```bash
|
||||||
|
docker run -v "$PWD":/work -w /work ghcr.io/rendercv/rendercv render Your_CV.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## How the Image Gets Published
|
||||||
|
|
||||||
|
Docker images are stored in **registries**, which are servers that host images so anyone can download and run them. Docker Hub is the most popular, but GitHub has its own called GitHub Container Registry (GHCR).
|
||||||
|
|
||||||
|
When you publish a GitHub release, the [`release.yaml` workflow](https://github.com/rendercv/rendercv/blob/main/.github/workflows/release.yaml) automatically builds and publishes the RenderCV image to GHCR at `ghcr.io/rendercv/rendercv`.
|
||||||
|
|
||||||
|
When users run `docker run ghcr.io/rendercv/rendercv`, Docker automatically pulls the image from the registry if it's not already on their machine.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
To learn more about writing `Dockerfile`, see the `uv`'s guide on [Docker](https://docs.astral.sh/uv/guides/integration/docker/).
|
||||||
91
docs/developer_guide/documentation.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
## The Goal
|
||||||
|
|
||||||
|
We want documentation at `docs.rendercv.com`, a proper website with navigation, search, theming, and interactive features.
|
||||||
|
|
||||||
|
**What is a website?** A collection of HTML, CSS, and JavaScript files. Browsers download these files and render them as the pages you see. To have a website, you need:
|
||||||
|
|
||||||
|
1. HTML/CSS/JavaScript files
|
||||||
|
2. A server hosting those files
|
||||||
|
3. A domain pointing to that server
|
||||||
|
|
||||||
|
**The problem:** Writing HTML/CSS/JavaScript manually for documentation is impractical. You want to write content in Markdown and have it become a professional website automatically.
|
||||||
|
|
||||||
|
**The solution:** [`mkdocs`](https://github.com/mkdocs/mkdocs) with [Material theme](https://github.com/squidfunk/mkdocs-material). You write Markdown in `docs/`, `mkdocs` generates HTML/CSS/JavaScript, and GitHub Pages hosts it at `docs.rendercv.com`.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[Markdown in docs/] --> B[MkDocs + Material]
|
||||||
|
B --> C[HTML/CSS/JS files]
|
||||||
|
C --> D[GitHub Pages hosting]
|
||||||
|
D --> E[docs.rendercv.com]
|
||||||
|
```
|
||||||
|
|
||||||
|
**This means:** Editing Markdown files in `docs/` updates the website at `docs.rendercv.com`.
|
||||||
|
|
||||||
|
## Configuration: [`mkdocs.yaml`](https://github.com/rendercv/rendercv/blob/main/mkdocs.yaml)
|
||||||
|
|
||||||
|
`mkdocs.yaml` controls how `mkdocs` builds the website:
|
||||||
|
|
||||||
|
- **Site metadata:** name, description, repository
|
||||||
|
- **Theme:** Material theme with colors and features
|
||||||
|
- **Navigation:** sidebar structure
|
||||||
|
- **Plugins:** see below
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
`mkdocs` plugins extend functionality beyond Markdown → HTML conversion.
|
||||||
|
|
||||||
|
### [`mkdocstrings`](https://github.com/mkdocstrings/mkdocstrings): API Reference
|
||||||
|
|
||||||
|
Generates API reference from Python docstrings. The entire [API Reference](../api_reference/index.md) section is auto-generated from `src/rendercv/`.
|
||||||
|
|
||||||
|
### [`mkdocs-macros-plugin`](https://mkdocs-macros-plugin.readthedocs.io/): Dynamic Content
|
||||||
|
|
||||||
|
Lets you inject code-generated values into Markdown. [`docs/docs_templating.py`](https://github.com/rendercv/rendercv/blob/main/docs/docs_templating.py) runs during build. It imports values directly from RenderCV's code and exposes them as variables. It's heavily used in [YAML Input Structure](../user_guide/yaml_input_structure.md) page.
|
||||||
|
|
||||||
|
## Entry Type Figures
|
||||||
|
|
||||||
|
The [YAML Input Structure](../user_guide/yaml_input_structure.md) page shows visual examples of each entry type rendered in each theme.
|
||||||
|
|
||||||
|
These are auto-generated PNG images. Run `just update-entry-figures` to regenerate them from [`docs/user_guide/sample_entries.yaml`](https://github.com/rendercv/rendercv/blob/main/docs/user_guide/sample_entries.yaml).
|
||||||
|
|
||||||
|
## Local Preview
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just serve-docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Starts a local server at `http://127.0.0.1:8000` with live reload. Edit Markdown files and see changes instantly.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just build-docs
|
||||||
|
```
|
||||||
|
|
||||||
|
Generates the final website in `site/` directory. Mainly used by GitHub workflows for final deployment (see [GitHub Workflows](github_workflows.md)).
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Every push to `main` triggers automatic deployment.
|
||||||
|
|
||||||
|
**The workflow** ([`.github/workflows/deploy-docs.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/deploy-docs.yaml)):
|
||||||
|
|
||||||
|
1. **Trigger:** Runs on every push to `main`
|
||||||
|
2. **Build step:**
|
||||||
|
- Installs dependencies (`uv`, `just`)
|
||||||
|
- Runs `just build-docs` to generate the website
|
||||||
|
- Uploads the `site/` directory as an artifact
|
||||||
|
3. **Deploy step:**
|
||||||
|
- Takes the uploaded artifact
|
||||||
|
- Deploys it to GitHub Pages (a free static website hosting service)
|
||||||
|
- Makes it available at `docs.rendercv.com`
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
See the [MkDocs Material documentation](https://squidfunk.github.io/mkdocs-material/) for more information.
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
# Frequently Asked Questions (FAQ)
|
|
||||||
|
|
||||||
## How can I add a new social network to RenderCV?
|
|
||||||
|
|
||||||
To add a new social network to RenderCV, go to the `rendercv/data/models/curriculum_vitae.py` file and follow these steps:
|
|
||||||
|
|
||||||
1. Append the social network name (for example, "Facebook") to the `SocialNetworkName` type.
|
|
||||||
2. If necessary, implement its username validation in the `SocialNetwork.check_username` method.
|
|
||||||
3. Implement its URL generation using the `SocialNetwork.url` method. If the URL can be generated by appending the username to a hostname, only update `url_dictionary`.
|
|
||||||
4. Finally, include the Typst icon of the social network to the `icon_dictionary` in the `CurriculumVitae.connections` method. RenderCV uses the [`fontawesome`](https://typst.app/universe/package/fontawesome/) package.
|
|
||||||
|
|
||||||
Then, the tests should be implemented for the new social network with the following steps:
|
|
||||||
|
|
||||||
1. Go to `tests/test_data.py` and update `test_social_network_url` accordingly, i.e., add a new `(network, username, expected_url)` tuple to the `pytest.mark.parametrize` decorator.
|
|
||||||
2. Go to `tests/conftest.py` and add the new social network to `rendercv_filled_curriculum_vitae_data_model`.
|
|
||||||
3. Set `update_testdata` to `True` in `conftest.py` and run the tests to update the `testdata` folder.
|
|
||||||
4. Review the updated `testdata` folder manually to ensure everything works as expected. Then, set `update_testdata` to `False` and push the changes.
|
|
||||||
|
|
||||||
## When should we consider adding a new entry type to RenderCV?
|
|
||||||
|
|
||||||
We should add a new entry type if and only if the proposed design of the entry type cannot be achieved using any of the existing entry types. This is because RenderCV's entry types are not designed to function as data models. Their purpose is not to store specific data but rather to determine how a given set of strings will appear in the PDF.
|
|
||||||
|
|
||||||
For example, JSON Resume follows a data-oriented approach. In JSON Resume, each entry type acts as a data model specifically designed to store structured information. RenderCV takes a design-oriented approach for two reasons:
|
|
||||||
|
|
||||||
- There would be too many different data models that would ultimately look more or less the same in the PDF.
|
|
||||||
- It would be impossible to provide all the different data models people might need.
|
|
||||||
|
|
||||||
Therefore, we decided to create entry types solely for their design output.
|
|
||||||
123
docs/developer_guide/github_workflows.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# GitHub Actions
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
|
||||||
|
Every software project has repetitive tasks that must run consistently:
|
||||||
|
|
||||||
|
- **On every update:** Run tests, redeploy documentation
|
||||||
|
- **On every release:** Run tests, update `schema.json` and examples, build executables for 4 platforms, build package, upload to PyPI, push Docker image
|
||||||
|
|
||||||
|
You could do these manually. But manual means:
|
||||||
|
|
||||||
|
- Forgetting steps ("Did I update `schema.json`? Did I build the Windows executable?")
|
||||||
|
- Wasted time ("Why am I doing the same 15 steps every release?")
|
||||||
|
|
||||||
|
**What if you could write down these tasks once, and have them run automatically every time?**
|
||||||
|
|
||||||
|
That's what **CI/CD (Continuous Integration/Continuous Deployment)** is. And **GitHub Actions** is GitHub's system for it.
|
||||||
|
|
||||||
|
## What are GitHub Actions?
|
||||||
|
|
||||||
|
GitHub actions are **automation scripts that run on GitHub's servers when certain events happen**.
|
||||||
|
|
||||||
|
You define them in `.github/workflows/*.yaml` files. Each file describes:
|
||||||
|
|
||||||
|
1. **When to run:** push to main? Pull request? New release?
|
||||||
|
2. **What to do:** Run tests? Build docs? Publish package?
|
||||||
|
3. **Where to run:** Linux? Windows? macOS? Multiple versions?
|
||||||
|
|
||||||
|
GitHub reads these files and executes them automatically when the triggering events occur.
|
||||||
|
|
||||||
|
**Why GitHub's servers?** Because you don't want to worry about it. Push your code, turn off your computer, you're done. GitHub handles the rest (running tests, deploying docs, building packages) without you having to keep your machine on or manually run anything.
|
||||||
|
|
||||||
|
## RenderCV's Workflows
|
||||||
|
|
||||||
|
RenderCV has 5 workflows. Each handles a specific automation task.
|
||||||
|
|
||||||
|
**How workflows start:** Every workflow begins the same way: clone the repository, install `uv`, install `just`, then run some `just` commands. This recreates the same environment you'd have locally (see [Setup](index.md)).
|
||||||
|
|
||||||
|
### 1. [`test.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/test.yaml): Run Tests
|
||||||
|
|
||||||
|
**When it runs:**
|
||||||
|
|
||||||
|
- Every push to `main` branch
|
||||||
|
- Every pull request
|
||||||
|
- Manually (via GitHub UI)
|
||||||
|
- When called by other workflows
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
|
||||||
|
1. Runs `just test-coverage` across **9 different environments** (3 operating systems × 3 Python versions: 3.12, 3.13, 3.14)
|
||||||
|
2. Combines all coverage reports and uploads them to show the coverage report
|
||||||
|
|
||||||
|
### 2. [`deploy-docs.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/deploy-docs.yaml): Deploy Documentation
|
||||||
|
|
||||||
|
**When it runs:**
|
||||||
|
|
||||||
|
- Every push to `main` branch
|
||||||
|
- Manually (via GitHub UI)
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
|
||||||
|
1. Builds the documentation website using `just build-docs`
|
||||||
|
2. Uploads it to GitHub Pages
|
||||||
|
3. Documentation is now live at https://docs.rendercv.com
|
||||||
|
|
||||||
|
### 3. [`update-files.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/update-files.yaml): Update Generated Files
|
||||||
|
|
||||||
|
**When it runs:**
|
||||||
|
|
||||||
|
- Manually (via GitHub UI)
|
||||||
|
- When called by the release workflow
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
|
||||||
|
1. Regenerates files derived from code:
|
||||||
|
- `schema.json` using `just update-schema`
|
||||||
|
- Example YAML files and PDFs in `examples/` folder using `just update-examples`
|
||||||
|
- Entry figures using `just update-entry-figures`
|
||||||
|
2. Commits and pushes these changes to the repository
|
||||||
|
|
||||||
|
### 4. [`create-executables.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/create-executables.yaml): Create Executables
|
||||||
|
|
||||||
|
**When it runs:**
|
||||||
|
|
||||||
|
- Manually (via GitHub UI)
|
||||||
|
- When called by the release workflow
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
|
||||||
|
1. Builds standalone executables using `just create-executable` for 4 platforms:
|
||||||
|
- Linux (x86_64 and ARM64)
|
||||||
|
- macOS (ARM64)
|
||||||
|
- Windows (x86_64)
|
||||||
|
2. Uploads executables as artifacts
|
||||||
|
|
||||||
|
These are single-file executables that users can download and run without installing Python.
|
||||||
|
|
||||||
|
### 5. [`release.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/release.yaml): Publish a Release
|
||||||
|
|
||||||
|
**When it runs:**
|
||||||
|
|
||||||
|
- When a new GitHub release is published
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
|
||||||
|
This is the complete release pipeline. It orchestrates everything:
|
||||||
|
|
||||||
|
1. **Run tests:** Calls `test.yaml` to ensure everything works
|
||||||
|
2. **Update files:** Calls `update-files.yaml` to regenerate schema/examples
|
||||||
|
3. **Build package:** Installs `uv`, builds Python wheel and source distribution using `uv build`
|
||||||
|
4. **Create executables:** Calls `create-executables.yaml` for all platforms
|
||||||
|
5. **Create GitHub release:** Downloads and uploads executables and wheel to the release
|
||||||
|
6. **Publish to PyPI:** Downloads and uploads package so users can `pip install rendercv`
|
||||||
|
7. **Publish Docker image:** Builds and pushes Docker image to GitHub Container Registry
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
- [GitHub Actions Documentation](https://docs.github.com/en/actions): Official docs
|
||||||
|
- [`.github/workflows/`](https://github.com/rendercv/rendercv/tree/main/.github/workflows): RenderCV's workflow files
|
||||||
65
docs/developer_guide/how_to/add_locale.md
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# Add a New Locale
|
||||||
|
|
||||||
|
1. Create a YAML file in `src/rendercv/schema/models/locale/other_locales/`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
touch src/rendercv/schema/models/locale/other_locales/mylanguage.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add the schema reference and provide translations
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# yaml-language-server: $schema=../../../../../../schema.json
|
||||||
|
locale:
|
||||||
|
language: mylanguage
|
||||||
|
last_updated: "Your translation"
|
||||||
|
month: "Your translation"
|
||||||
|
months: "Your translation"
|
||||||
|
year: "Your translation"
|
||||||
|
years: "Your translation"
|
||||||
|
present: "Your translation"
|
||||||
|
month_abbreviations:
|
||||||
|
- Jan
|
||||||
|
- Feb
|
||||||
|
- Mar
|
||||||
|
- Apr
|
||||||
|
- May
|
||||||
|
- Jun
|
||||||
|
- Jul
|
||||||
|
- Aug
|
||||||
|
- Sep
|
||||||
|
- Oct
|
||||||
|
- Nov
|
||||||
|
- Dec
|
||||||
|
month_names:
|
||||||
|
- January
|
||||||
|
- February
|
||||||
|
- March
|
||||||
|
- April
|
||||||
|
- May
|
||||||
|
- June
|
||||||
|
- July
|
||||||
|
- August
|
||||||
|
- September
|
||||||
|
- October
|
||||||
|
- November
|
||||||
|
- December
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Add ISO 639-1 language code to `english_locale.py`
|
||||||
|
|
||||||
|
Edit `src/rendercv/schema/models/locale/english_locale.py` line 95-108:
|
||||||
|
|
||||||
|
```python
|
||||||
|
return {
|
||||||
|
"english": "en",
|
||||||
|
# ... existing languages
|
||||||
|
"mylanguage": "xx", # Add your two-letter ISO 639-1 code
|
||||||
|
}[self.language]
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Done. Use it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rendercv new "John Doe" --locale mylanguage
|
||||||
|
```
|
||||||
114
docs/developer_guide/how_to/add_social_network.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Add a New Social Network
|
||||||
|
|
||||||
|
1. Add network name to `SocialNetworkName` type
|
||||||
|
|
||||||
|
Edit `src/rendercv/schema/models/cv/social_network.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
type SocialNetworkName = Literal[
|
||||||
|
"LinkedIn",
|
||||||
|
"GitHub",
|
||||||
|
# ... existing networks
|
||||||
|
"MyNetwork", # Add your network here
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add URL pattern to `url_dictionary`
|
||||||
|
|
||||||
|
Edit `src/rendercv/schema/models/cv/social_network.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
url_dictionary: dict[SocialNetworkName, str] = {
|
||||||
|
"LinkedIn": "https://linkedin.com/in/",
|
||||||
|
# ... existing networks
|
||||||
|
"MyNetwork": "https://mynetwork.com/profile/", # Add URL base here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. (Optional) Add username validation
|
||||||
|
|
||||||
|
If your network has special username format requirements, edit `src/rendercv/schema/models/cv/social_network.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
match network:
|
||||||
|
case "Mastodon":
|
||||||
|
# ... existing validations
|
||||||
|
case "MyNetwork":
|
||||||
|
# ... your custom validation logic
|
||||||
|
```
|
||||||
|
|
||||||
|
4. (Optional) Add custom URL generation
|
||||||
|
|
||||||
|
If URL generation requires special logic (not just base + username), edit `src/rendercv/schema/models/cv/social_network.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@functools.cached_property
|
||||||
|
def url(self) -> str:
|
||||||
|
if self.network == "Mastodon":
|
||||||
|
# ... existing custom logic
|
||||||
|
elif self.network == "MyNetwork":
|
||||||
|
# ... your custom URL generation logic
|
||||||
|
else:
|
||||||
|
url = url_dictionary[self.network] + self.username
|
||||||
|
return url
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Add Font Awesome icon
|
||||||
|
|
||||||
|
Edit `src/rendercv/renderer/templater/connections.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
typst_fa_icons = {
|
||||||
|
"LinkedIn": "linkedin",
|
||||||
|
# ... existing icons
|
||||||
|
"MyNetwork": "my-icon-name", # Add your icon name here
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
See available icons at: [fontawesome.com/search](https://fontawesome.com/search)
|
||||||
|
|
||||||
|
6. Add test for URL generation
|
||||||
|
|
||||||
|
Edit `tests/schema/models/cv/test_social_network.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("network", "username", "expected_url"),
|
||||||
|
[
|
||||||
|
# ... existing tests
|
||||||
|
(
|
||||||
|
"MyNetwork",
|
||||||
|
"myusername",
|
||||||
|
"https://mynetwork.com/profile/myusername",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_url(self, network, username, expected_url):
|
||||||
|
# test implementation
|
||||||
|
```
|
||||||
|
|
||||||
|
7. Add network to test fixtures
|
||||||
|
|
||||||
|
Edit `tests/renderer/conftest.py`, add your network to the `social_networks` list:
|
||||||
|
|
||||||
|
```python
|
||||||
|
social_networks=[
|
||||||
|
SocialNetwork(network="LinkedIn", username="johndoe"),
|
||||||
|
# ... existing networks
|
||||||
|
SocialNetwork(network="MyNetwork", username="johndoe"),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
8. Update test data and verify visual output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just update-testdata
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the generated PDFs in `tests/renderer/testdata/test_pdf_png/` to ensure your network appears correctly with the icon.
|
||||||
|
|
||||||
|
9. Run tests to verify everything passes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just test
|
||||||
|
```
|
||||||
27
docs/developer_guide/how_to/add_theme.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Add a New Theme
|
||||||
|
|
||||||
|
1. Create a YAML file in `src/rendercv/schema/models/design/other_themes/`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
touch src/rendercv/schema/models/design/other_themes/mytheme.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Add the schema reference and override Classic theme defaults
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# yaml-language-server: $schema=../../../../../../schema.json
|
||||||
|
design:
|
||||||
|
theme: mytheme
|
||||||
|
# Override any defaults from classic_theme.py here
|
||||||
|
colors:
|
||||||
|
name: rgb(0,0,0)
|
||||||
|
typography:
|
||||||
|
font_family: New Computer Modern
|
||||||
|
# ... add any other overrides
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Done. Use it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rendercv new "John Doe" --theme mytheme
|
||||||
|
```
|
||||||
@@ -1,114 +1,78 @@
|
|||||||
# Developer Guide
|
---
|
||||||
|
toc_depth: 1
|
||||||
|
---
|
||||||
|
|
||||||
All contributions to RenderCV are welcome!
|
# Setup
|
||||||
|
|
||||||
The source code is thoroughly documented and well-commented, making it an enjoyable read and easy to understand. A detailed documentation of the source code is available in the [API reference](../reference/index.md).
|
## Prerequisites
|
||||||
|
|
||||||
|
You need two tools to develop RenderCV:
|
||||||
|
|
||||||
## Getting Started
|
- **[`uv`](https://docs.astral.sh/uv/)**: Package and project manager. RenderCV uses `uv` to manage dependencies. It also handles Python installations, so you don't need to install Python separately.
|
||||||
|
- **[`just`](https://github.com/casey/just)**: Command runner. Development commands are defined in the [`justfile`](https://github.com/rendercv/rendercv/blob/main/justfile), and you need `just` to run them.
|
||||||
|
|
||||||
There are two ways of developing RenderCV: [locally](#develop-locally) or [with GitHub Codespaces](#develop-with-github-codespaces).
|
Install them by following their official installation guides:
|
||||||
|
|
||||||
### Develop Locally
|
- [Install `uv`](https://docs.astral.sh/uv/getting-started/installation/)
|
||||||
|
- [Install `just`](https://github.com/casey/just#installation)
|
||||||
|
|
||||||
1. Install [Hatch](https://hatch.pypa.io/latest/). The installation guide for Hatch can be found [here](https://hatch.pypa.io/latest/install/#installation).
|
## Setting Up the Development Environment
|
||||||
|
|
||||||
Hatch is a Python project manager. It mainly allows you to define the virtual environments you need in [`pyproject.toml`](https://github.com/rendercv/rendercv/blob/main/pyproject.toml). Then, it takes care of the rest. Also, you don't need to install Python. Hatch will install it when you follow the steps below.
|
|
||||||
|
|
||||||
2. Clone the repository.
|
1. Clone the repository:
|
||||||
```
|
|
||||||
|
```bash
|
||||||
git clone https://github.com/rendercv/rendercv.git
|
git clone https://github.com/rendercv/rendercv.git
|
||||||
```
|
```
|
||||||
3. Go to the `rendercv` directory.
|
|
||||||
```
|
and change to the repository directory:
|
||||||
|
|
||||||
|
```bash
|
||||||
cd rendercv
|
cd rendercv
|
||||||
```
|
```
|
||||||
4. Start using one of the virtual environments by activating it in the terminal.
|
|
||||||
|
|
||||||
Default development environment with Python 3.13:
|
2. Set up the development environment (creates a virtual environment in `./.venv` with all dependencies):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hatch shell default
|
just sync
|
||||||
```
|
```
|
||||||
|
|
||||||
The same environment, but with Python 3.10 (or 3.11, 3.12, 3.13):
|
3. Run `just test` to verify all tests pass and everything is set up correctly.
|
||||||
```bash
|
|
||||||
hatch shell test.py3.10
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Finally, activate the virtual environment in your integrated development environment (IDE). In Visual Studio Code:
|
4. Finally, activate the virtual environment in your integrated development environment (IDE). In Visual Studio Code:
|
||||||
|
|
||||||
- Press `Ctrl+Shift+P`.
|
- Press `Ctrl+Shift+P`.
|
||||||
- Type `Python: Select Interpreter`.
|
- Type `Python: Select Interpreter`.
|
||||||
- Select one of the virtual environments created by Hatch.
|
- Select the one in `./.venv`.
|
||||||
|
|
||||||
|
|
||||||
### Develop with GitHub Codespaces
|
That's it! You're now ready to start developing RenderCV.
|
||||||
|
|
||||||
1. [Fork](https://github.com/rendercv/rendercv/fork) the repository.
|
|
||||||
2. Navigate to the forked repository.
|
|
||||||
3. Click the <> **Code** button, then click the **Codespaces** tab, and then click **Create codespace on main**.
|
|
||||||
|
|
||||||
Then, [Visual Studio Code for the Web](https://code.visualstudio.com/docs/editor/vscode-web) will be opened with a ready-to-use development environment.
|
|
||||||
|
|
||||||
This is done with [Development containers](https://containers.dev/), and the environment is defined in the [`.devcontainer/devcontainer.json`](https://github.com/rendercv/rendercv/blob/main/.devcontainer/devcontainer.json) file. Dev containers can also be run locally using various [supporting tools and editors](https://containers.dev/supporting).
|
|
||||||
|
|
||||||
## Available Commands
|
## Available Commands
|
||||||
|
|
||||||
These commands are defined in the [`pyproject.toml`](https://github.com/rendercv/rendercv/blob/main/pyproject.toml) file.
|
### Development
|
||||||
|
|
||||||
- Build the package
|
- `just sync`: Sync all dependencies (including extras and dev groups)
|
||||||
```bash
|
- `just format`: Format code with black and ruff
|
||||||
hatch run build
|
- `just check`: Run all checks (ruff, pyright, pre-commit)
|
||||||
```
|
|
||||||
- Format the code with [Black](https://github.com/psf/black) and [Ruff](https://github.com/astral-sh/ruff)
|
|
||||||
```bash
|
|
||||||
hatch run format
|
|
||||||
```
|
|
||||||
- Lint the code with [Ruff](https://github.com/astral-sh/ruff)
|
|
||||||
```bash
|
|
||||||
hatch run lint
|
|
||||||
```
|
|
||||||
- Run [pre-commit](https://pre-commit.com/)
|
|
||||||
```bash
|
|
||||||
hatch run precommit
|
|
||||||
```
|
|
||||||
- Check the types with [Pyright](https://github.com/RobertCraigie/pyright-python)
|
|
||||||
```bash
|
|
||||||
hatch run check-types
|
|
||||||
```
|
|
||||||
- Run the tests with Python 3.13
|
|
||||||
```bash
|
|
||||||
hatch run test
|
|
||||||
```
|
|
||||||
- Run the tests with Python 3.13 and generate the coverage report
|
|
||||||
```bash
|
|
||||||
hatch run test-and-report
|
|
||||||
```
|
|
||||||
- Update [schema.json](https://github.com/rendercv/rendercv/blob/main/schema.json)
|
|
||||||
```bash
|
|
||||||
hatch run update-schema
|
|
||||||
```
|
|
||||||
- Update [`examples`](https://github.com/rendercv/rendercv/tree/main/examples) folder
|
|
||||||
```bash
|
|
||||||
hatch run update-examples
|
|
||||||
```
|
|
||||||
- Create an executable version of RenderCV with [PyInstaller](https://www.pyinstaller.org/)
|
|
||||||
```bash
|
|
||||||
hatch run exe:create
|
|
||||||
```
|
|
||||||
- Preview the documentation as you write it
|
|
||||||
```bash
|
|
||||||
hatch run docs:serve
|
|
||||||
```
|
|
||||||
- Build the documentation
|
|
||||||
```bash
|
|
||||||
hatch run docs:build
|
|
||||||
```
|
|
||||||
- Update figures of the entry types in the "[Structure of the YAML Input File](../user_guide/structure_of_the_yaml_input_file.md)"
|
|
||||||
```bash
|
|
||||||
hatch run docs:update-entry-figures
|
|
||||||
```
|
|
||||||
|
|
||||||
## About [`pyproject.toml`](https://github.com/rendercv/rendercv/blob/main/pyproject.toml)
|
### Testing
|
||||||
|
|
||||||
[`pyproject.toml`](https://github.com/rendercv/rendercv/blob/main/pyproject.toml) contains the metadata, dependencies, and tools required for the project. Please read through the file to understand the project's technical details.
|
- `just test`: Run tests with pytest
|
||||||
|
- `just test-coverage`: Run tests with coverage report
|
||||||
|
- `just update-testdata`: Update test data files (see [Testing](testing.md) for more details)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- `just build-docs`: Build documentation
|
||||||
|
- `just serve-docs`: Serve documentation locally with live reload
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
- `just update-schema`: Update JSON schema
|
||||||
|
- `just update-entry-figures`: Update entry figures for documentation
|
||||||
|
- `just update-examples`: Update example files
|
||||||
|
- `just create-executable`: Create standalone executable
|
||||||
|
|
||||||
|
### Utilities
|
||||||
|
|
||||||
|
- `just count-lines`: Count lines of Python code in the `src/` directory
|
||||||
|
|||||||
149
docs/developer_guide/json_schema.md
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# JSON Schema
|
||||||
|
|
||||||
|
## The Problem
|
||||||
|
|
||||||
|
You've encountered this everywhere, even if you didn't realize it was the same problem:
|
||||||
|
|
||||||
|
**VS Code settings** (`settings.json`):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"editor.fontSize": 14,
|
||||||
|
"editor.tabSiz": 4 // ← Typo! VS Code highlights it red immediately
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**GitHub Actions workflows** (`.github/workflows/test.yaml`):
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branchs: # ← Typo! Your editor underlines it, suggests "branches"
|
||||||
|
- main
|
||||||
|
```
|
||||||
|
|
||||||
|
**These files are completely different (VS Code settings, GitHub workflows). But you get autocomplete and validation in both.** How?
|
||||||
|
|
||||||
|
VS Code doesn't just "know" what's valid in `settings.json`. GitHub Actions workflows don't magically get autocomplete.
|
||||||
|
|
||||||
|
**Someone had to tell your editor:** "Here are all the valid fields, their types, and what they mean."
|
||||||
|
|
||||||
|
That "someone" is **JSON Schema**.
|
||||||
|
|
||||||
|
## What is JSON Schema?
|
||||||
|
|
||||||
|
JSON Schema is a **standard way to describe the structure of JSON/YAML documents**.
|
||||||
|
|
||||||
|
Think of it as a specification, a formal description of what's valid:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Your full name"
|
||||||
|
},
|
||||||
|
"age": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"description": "Your age in years"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "email"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This schema says:
|
||||||
|
|
||||||
|
- A valid document is an object
|
||||||
|
- It must have a `name` field (string, required)
|
||||||
|
- It can have an `age` field (non-negative integer, optional)
|
||||||
|
- It can have an `email` field (string matching email format, optional)
|
||||||
|
|
||||||
|
**Why does JSON Schema exist?**
|
||||||
|
|
||||||
|
Because JSON and YAML files are **everywhere**: configuration files, API requests/responses, CI/CD workflows, application settings, data files. They all share the same problem:
|
||||||
|
|
||||||
|
**How do you communicate what's valid?**
|
||||||
|
|
||||||
|
You could write documentation: "The `name` field is required and must be a string. The `age` field is optional and must be a non-negative integer." But **documentation is for humans to read, not machines**.
|
||||||
|
|
||||||
|
JSON Schema is the **same information in machine-readable format** so editors can understand it.
|
||||||
|
|
||||||
|
Once your editor has a schema, it can provide autocomplete, catch typos, and show inline documentation as you type.
|
||||||
|
|
||||||
|
This is why:
|
||||||
|
|
||||||
|
- **Microsoft publishes a JSON Schema for VS Code settings:** your editor fetches it and provides autocomplete
|
||||||
|
- **GitHub publishes a JSON Schema for Actions workflows:** that's how you get field suggestions
|
||||||
|
- **Thousands of tools do the same:** Kubernetes, Docker, Terraform, ESLint, package.json, tsconfig.json, the list goes on
|
||||||
|
|
||||||
|
JSON Schema is **infrastructure for editor tooling**.
|
||||||
|
|
||||||
|
## RenderCV's JSON Schema
|
||||||
|
|
||||||
|
RenderCV has the same problem. Users write their CVs in YAML, and we want them to have a smooth editor experience with autocomplete, typo detection, and inline documentation.
|
||||||
|
|
||||||
|
**Solution:** Publish a JSON Schema for RenderCV YAML files.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
That's why [`schema.json`](https://github.com/rendercv/rendercv/blob/main/schema.json) exists in the repository. Same universal problem, same universal solution.
|
||||||
|
|
||||||
|
## How the Schema is Generated
|
||||||
|
|
||||||
|
We don't write `schema.json` by hand. **It's automatically generated from Pydantic models.**
|
||||||
|
|
||||||
|
RenderCV's entire data structure is defined using Pydantic models (see [Understanding RenderCV](understanding_rendercv.md) for details). Pydantic has a built-in feature: `model_json_schema()`, which generates JSON Schema from your models.
|
||||||
|
|
||||||
|
That's what [`src/rendercv/schema/json_schema_generator.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/schema/json_schema_generator.py) does. It calls `model_json_schema()` on our top-level model and writes the result to `schema.json`.
|
||||||
|
|
||||||
|
## How Editors Know to Use RenderCV's Schema
|
||||||
|
|
||||||
|
There are two ways editors discover and use RenderCV's schema:
|
||||||
|
|
||||||
|
### 1. Manual Declaration
|
||||||
|
|
||||||
|
Add a special comment at the top of your YAML file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/rendercv/rendercv/refs/tags/v2.4/schema.json
|
||||||
|
|
||||||
|
cv:
|
||||||
|
name: John Doe
|
||||||
|
```
|
||||||
|
|
||||||
|
This tells the editor: "Use RenderCV's schema for this file." Note the version tag in the URL, which ensures you get the schema matching your RenderCV version.
|
||||||
|
|
||||||
|
**Requirements:** Your editor needs to support this. For VS Code, install the [YAML extension](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml).
|
||||||
|
|
||||||
|
### 2. Schema Store (Automatic)
|
||||||
|
|
||||||
|
RenderCV's schema is listed in [SchemaStore](https://github.com/SchemaStore/schemastore), a central registry of schemas that most IDEs use.
|
||||||
|
|
||||||
|
In SchemaStore, RenderCV's schema is configured to automatically activate for files ending with `_CV.yaml`. This means:
|
||||||
|
|
||||||
|
- If your file is named `John_Doe_CV.yaml`
|
||||||
|
- And your editor uses SchemaStore (VS Code with YAML extension does)
|
||||||
|
- You get autocomplete automatically, no comment needed
|
||||||
|
|
||||||
|
## When is the Schema Generated?
|
||||||
|
|
||||||
|
During development, whenever data models change, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just update-schema
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs [`scripts/update_schema.py`](https://github.com/rendercv/rendercv/blob/main/scripts/update_schema.py), which regenerates `schema.json`.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
- [Pydantic JSON Schema](https://docs.pydantic.dev/latest/concepts/json_schema/): How Pydantic generates schemas from models
|
||||||
138
docs/developer_guide/project_management.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Project Management
|
||||||
|
|
||||||
|
## What is "Project Management"?
|
||||||
|
|
||||||
|
When you look at RenderCV's repository, you see:
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── src/ ← The actual RenderCV code
|
||||||
|
├── pyproject.toml ← Project configuration
|
||||||
|
├── justfile ← Command shortcuts
|
||||||
|
├── scripts/ ← Supplementary Python scripts
|
||||||
|
├── .pre-commit-config.yaml ← Pre-commit configuration
|
||||||
|
└── uv.lock ← Dependency lock file
|
||||||
|
```
|
||||||
|
|
||||||
|
**Project management is everything except `src/`.** It's all the infrastructure that lets us:
|
||||||
|
|
||||||
|
- Share RenderCV with users (`pip install rendercv`)
|
||||||
|
- Manage dependencies consistently
|
||||||
|
- Automate testing, building, and releases
|
||||||
|
- Ensure reproducibility across machines and time
|
||||||
|
|
||||||
|
## Why Can't We Just Write Python Code?
|
||||||
|
|
||||||
|
RenderCV is a Python project. The actual source code lives in `src/rendercv/`. Why do we need all these other files - `pyproject.toml`, `uv.lock`, `justfile`, `.github/workflows/`, etc.?
|
||||||
|
|
||||||
|
**Because code alone doesn't solve two critical problems: distribution and development environment.**
|
||||||
|
|
||||||
|
### Problem 1: Distribution
|
||||||
|
|
||||||
|
**How do users get your code?**
|
||||||
|
|
||||||
|
You could tell them "download these files, install dependencies with `pip install -r requirements.txt`, and run them with `python main.py`". But users want `pip install rendercv` and have it work instantly with `rendercv` command.
|
||||||
|
|
||||||
|
**This requires:** Packaging your code and uploading to [PyPI](https://pypi.org) (Python Package Index).
|
||||||
|
|
||||||
|
### Problem 2: Development Environment
|
||||||
|
|
||||||
|
You have the source code. Two developers want to contribute.
|
||||||
|
|
||||||
|
Developer A installs today with Python 3.11 and gets `pydantic==2.10`. Tests pass. Developer B installs one month later with Python 3.12 and gets `pydantic==2.11` which has breaking changes. Tests fail. "Works on A's machine" but not B's. B asks: "What formatter do you use? What settings? How do I run tests?"
|
||||||
|
|
||||||
|
**What needs to happen:** Everyone gets the same Python version, same package versions (locked, not "latest"), same development tools with same settings. All in one command.
|
||||||
|
|
||||||
|
**This requires:** Locking dependencies (Python version, every package, frozen in time), configuring all tools in one place, and automating setup so it's identical for everyone.
|
||||||
|
|
||||||
|
### The Solution
|
||||||
|
|
||||||
|
All those files you see in the repository (`pyproject.toml`, `uv.lock`, `justfile`, and more) work together to solve these problems. The result:
|
||||||
|
|
||||||
|
**For users:**
|
||||||
|
```bash
|
||||||
|
pip install rendercv
|
||||||
|
```
|
||||||
|
|
||||||
|
Works instantly. Every time. Anywhere. All dependencies installed automatically.
|
||||||
|
|
||||||
|
**For developers** ([Setup](index.md)):
|
||||||
|
```bash
|
||||||
|
just sync
|
||||||
|
```
|
||||||
|
|
||||||
|
One command. Identical environment for everyone: correct Python version, exact dependency versions, all dev tools ready. Works today, works in 2027. Bug from 6 months ago? Check out that commit, run `just sync`, exact environment recreated.
|
||||||
|
|
||||||
|
The rest of this guide explains what each file does.
|
||||||
|
|
||||||
|
## Files and Folders in the Root
|
||||||
|
|
||||||
|
### [`pyproject.toml`](https://github.com/rendercv/rendercv/blob/main/pyproject.toml)
|
||||||
|
|
||||||
|
The project definition file. This is the standard way to configure a Python project.
|
||||||
|
|
||||||
|
This file defines:
|
||||||
|
|
||||||
|
- Project metadata (name, version, description)
|
||||||
|
- Dependencies (what packages RenderCV needs)
|
||||||
|
- Entry points (makes `rendercv` a command)
|
||||||
|
- Build configuration (how to package RenderCV)
|
||||||
|
- Tool settings (ruff, pyright, pytest, etc.)
|
||||||
|
|
||||||
|
Open the file to see the full configuration with detailed comments.
|
||||||
|
|
||||||
|
### [`justfile`](https://github.com/rendercv/rendercv/blob/main/justfile)
|
||||||
|
|
||||||
|
[just](https://github.com/casey/just) is a command runner, a tool that lets you define terminal commands in a file and run them easily.
|
||||||
|
|
||||||
|
**Why do we need it?** During development, you constantly run commands like "run tests with coverage", "format all code", "build and serve docs". Without standardization:
|
||||||
|
|
||||||
|
- Everyone types different commands with different options
|
||||||
|
- You have to remember long command strings
|
||||||
|
|
||||||
|
The `justfile` solves this: define each command once, and everyone runs the same thing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just test # Runs pytest with the right options
|
||||||
|
just format # Formats code with ruff
|
||||||
|
just serve-docs # Builds and serves documentation locally
|
||||||
|
just update-schema # Regenerates schema.json
|
||||||
|
```
|
||||||
|
|
||||||
|
This is why `just sync` works so elegantly. It's a standardized command that does exactly the same thing for everyone.
|
||||||
|
|
||||||
|
### [`scripts/`](https://github.com/rendercv/rendercv/tree/main/scripts)
|
||||||
|
|
||||||
|
Python scripts that automate some repetitive tasks.
|
||||||
|
|
||||||
|
**Why do we need it?** Some tasks need to be done repeatedly but are too complex for simple shell commands:
|
||||||
|
|
||||||
|
- `update_schema.py`: Generate `schema.json` from pydantic models
|
||||||
|
- `update_examples.py`: Regenerate all example YAML files and PDFs in `examples/` folder
|
||||||
|
- `create_executable.py`: Build standalone executable of RenderCV
|
||||||
|
|
||||||
|
These scripts are called by `just` commands (`just update-schema`, `just update-examples`, etc.).
|
||||||
|
|
||||||
|
### [`.pre-commit-config.yaml`](https://github.com/rendercv/rendercv/blob/main/.pre-commit-config.yaml)
|
||||||
|
|
||||||
|
Configuration file for [`pre-commit`](https://pre-commit.com/), a tool that runs code quality checks.
|
||||||
|
|
||||||
|
**Why do we need it?** Pre-commit's value is **fast CI/CD**. [pre-commit.ci](https://pre-commit.ci/) (free for open-source projects) automatically runs checks on every push and pull request. Forgot to format your code? The workflow fails, making it immediately obvious. Without pre-commit, we'd have to set up our own workflow to run these checks.
|
||||||
|
|
||||||
|
Run `just check` locally to check your code before committing. We don't use pre-commit as git hooks (that run before every commit). We prefer manual checks when ready.
|
||||||
|
|
||||||
|
### [`uv.lock`](https://github.com/rendercv/rendercv/blob/main/uv.lock)
|
||||||
|
|
||||||
|
A dependency lock file. This is a record of the exact version of every package RenderCV uses (including dependencies of dependencies).
|
||||||
|
|
||||||
|
**Why do we need it?** Remember development environment problem? This file solves it. When you run `just sync`, `uv` reads this file and installs the exact same versions everyone else has, not "the latest version", but "the exact version that's known to work". Without this file, developers would get different package versions and environments would drift apart.
|
||||||
|
|
||||||
|
**Never edit this manually.** `uv` generates and updates it automatically. **Always commit it to git.** That's how everyone gets identical environments.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
See the [`uv` documentation](https://docs.astral.sh/uv/) for more information on project management.
|
||||||
@@ -1,36 +1,79 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 2
|
||||||
|
---
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
||||||
After implementing a new feature or fixing a bug, one can run all the tests to see if anything is broken.
|
Tests check if your code does what it's supposed to do. Every time you change something, you need to verify it still works. Instead of manually checking everything, you write test code once and rerun it automatically.
|
||||||
|
|
||||||
To run all the tests with each Python version (3.10, 3.11, 3.12, and 3.13), use the following command.
|
Here's a simple example:
|
||||||
|
|
||||||
```bash
|
```python
|
||||||
hatch run test:test
|
def sum(a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
def test_sum():
|
||||||
|
assert sum(2, 3) == 5
|
||||||
|
assert sum(-1, 1) == 0
|
||||||
|
assert sum(0, 0) == 0
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the tests with a specific Python version, use the following command.
|
If you change something in `sum`, you can run `test_sum` again to see if it's still working.
|
||||||
|
|
||||||
|
All of the tests of RenderCV are written in [`tests/`](https://github.com/rendercv/rendercv/tree/main/tests) directory.
|
||||||
|
|
||||||
|
## [`pytest`](https://github.com/pytest-dev/pytest): Testing Framework
|
||||||
|
|
||||||
|
`pytest` is a Python library that provides utilities to write and run tests.
|
||||||
|
|
||||||
|
**How does it work?** When you run `pytest`, it searches for files matching `test_*.py` in the `tests/` directory and executes all functions starting with `test_`.
|
||||||
|
|
||||||
|
**Configuration:** `pytest` reads settings from `pyproject.toml` under `[tool.pytest.ini_options]`.
|
||||||
|
|
||||||
|
## Running RenderCV Tests
|
||||||
|
|
||||||
|
Whenever you make changes to RenderCV's source code, run the tests to ensure everything still works. If all tests pass, your changes didn't break anything.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hatch run test.py3.10:test
|
just test
|
||||||
```
|
```
|
||||||
|
|
||||||
To run the tests with Python 3.13 and generate a coverage report, use the following command.
|
## Reference File Comparison
|
||||||
|
|
||||||
|
Some tests in [`tests/renderer/`](https://github.com/rendercv/rendercv/tree/main/tests/renderer) (specifically [`test_pdf_png.py`](https://github.com/rendercv/rendercv/blob/main/tests/renderer/test_pdf_png.py), [`test_typst.py`](https://github.com/rendercv/rendercv/blob/main/tests/renderer/test_typst.py), [`test_markdown.py`](https://github.com/rendercv/rendercv/blob/main/tests/renderer/test_markdown.py), and [`test_html.py`](https://github.com/rendercv/rendercv/blob/main/tests/renderer/test_html.py)) use reference file comparison:
|
||||||
|
|
||||||
|
1. Tests generate output files by running RenderCV
|
||||||
|
2. Generated files are compared against reference files in `tests/renderer/testdata/`
|
||||||
|
3. If they match exactly, the test passes. Any difference fails the test.
|
||||||
|
|
||||||
|
### Updating Reference Files
|
||||||
|
|
||||||
|
You fix a bug that changes RenderCV's output. Tests fail because the new output doesn't match old reference files.
|
||||||
|
|
||||||
|
**This is expected.** You intentionally changed the output. You need to update the reference files:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
hatch run test-and-report
|
just update-testdata
|
||||||
```
|
```
|
||||||
Once new commits are pushed to the `main` branch, the [`test.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/test.yaml) workflow will be automatically triggered, and the tests will run.
|
|
||||||
|
|
||||||
## About [`testdata`](https://github.com/rendercv/rendercv/tree/main/tests/testdata) folder
|
|
||||||
|
|
||||||
In some of the tests:
|
|
||||||
|
|
||||||
- RenderCV generates an output with a sample input.
|
|
||||||
- Then, the output is compared with a reference output, which has been manually generated and stored in `testdata`. If the files differ, the tests fail.
|
|
||||||
|
|
||||||
|
|
||||||
When the `testdata` folder needs to be updated, it can be manually regenerated by setting `update_testdata` to `True` in `conftest.py` and running the tests.
|
|
||||||
|
|
||||||
!!! warning
|
!!! warning
|
||||||
- Whenever the `testdata` folder is generated, the files should be reviewed manually to ensure everything works as expected.
|
**Manually verify new reference files before committing.** These become the source of truth. If you commit broken reference files, tests will pass even when RenderCV produces bad output. Always check generated PDFs and PNGs carefully.
|
||||||
- `update_testdata` should be set to `False` before committing the changes.
|
|
||||||
|
## [`pytest-cov`](https://github.com/pytest-dev/pytest-cov): Coverage Plugin for `pytest`
|
||||||
|
|
||||||
|
Coverage is a measure of which code lines are executed when tests run. If tests execute a line, it's included in coverage. If tests execute all lines in `src/rendercv/`, coverage is 100%.
|
||||||
|
|
||||||
|
**Why does it matter?** Coverage reports show you which parts of your code aren't tested yet, so you know where to write more tests.
|
||||||
|
|
||||||
|
Run tests with coverage:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just test-coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates two outputs:
|
||||||
|
|
||||||
|
- **Terminal:** Overall coverage percentage
|
||||||
|
- **HTML report:** Open `htmlcov/index.html` to see exactly which lines are covered (green) and which aren't (red)
|
||||||
|
|
||||||
|
**Configuration:** Coverage settings are in `pyproject.toml` under `[tool.coverage.run]` and `[tool.coverage.report]`.
|
||||||
|
|||||||
311
docs/developer_guide/understanding_rendercv.md
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
---
|
||||||
|
toc_depth: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Understanding RenderCV
|
||||||
|
|
||||||
|
This guide walks you through how RenderCV works, explaining each step and the tools we use.
|
||||||
|
|
||||||
|
## The Core Workflow
|
||||||
|
|
||||||
|
RenderCV does more than this (Markdown, HTML, PNG outputs, watching files, etc.), but at its core, what happens is:
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[YAML file] --> B[Typst file]
|
||||||
|
B --> C[PDF]
|
||||||
|
```
|
||||||
|
|
||||||
|
Read a YAML file, generate a Typst file, compile it to PDF. Everything else is built on top of this foundation.
|
||||||
|
|
||||||
|
Let's understand each step.
|
||||||
|
|
||||||
|
## Step 1: Reading the YAML File
|
||||||
|
|
||||||
|
When a user gives us a YAML file like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cv:
|
||||||
|
name: John Doe
|
||||||
|
location: San Francisco, CA
|
||||||
|
sections:
|
||||||
|
education:
|
||||||
|
- institution: MIT
|
||||||
|
degree: PhD
|
||||||
|
start_date: 2020-09
|
||||||
|
end_date: 2024-05
|
||||||
|
```
|
||||||
|
|
||||||
|
We need to:
|
||||||
|
|
||||||
|
1. Parse the YAML into Python dictionaries
|
||||||
|
2. Validate the data (Does `start_date` come before `end_date`? Is `name` actually provided and is it a string?)
|
||||||
|
|
||||||
|
### [`ruamel.yaml`](https://github.com/pycontribs/ruamel-yaml): YAML Parser
|
||||||
|
|
||||||
|
First problem: reading YAML files.
|
||||||
|
|
||||||
|
Python doesn't have a built-in YAML library. To read YAML files, you need a library. **We use `ruamel.yaml`**, one of the best YAML parsers available.
|
||||||
|
|
||||||
|
What does it do? Simple: **converts YAML text into Python dictionaries.**
|
||||||
|
|
||||||
|
**YAML file** (`cv.yaml`):
|
||||||
|
```yaml
|
||||||
|
cv:
|
||||||
|
name: John Doe
|
||||||
|
location: San Francisco, CA
|
||||||
|
sections:
|
||||||
|
education:
|
||||||
|
- institution: MIT
|
||||||
|
degree: PhD
|
||||||
|
start_date: 2020-09
|
||||||
|
```
|
||||||
|
|
||||||
|
**After parsing with `ruamel.yaml`:**
|
||||||
|
```python
|
||||||
|
from ruamel.yaml import YAML
|
||||||
|
|
||||||
|
yaml = YAML()
|
||||||
|
data = yaml.load(open("cv.yaml"))
|
||||||
|
|
||||||
|
# Now data is a Python dictionary:
|
||||||
|
{
|
||||||
|
"cv": {
|
||||||
|
"name": "John Doe",
|
||||||
|
"location": "San Francisco, CA",
|
||||||
|
"sections": {
|
||||||
|
"education": [
|
||||||
|
{
|
||||||
|
"institution": "MIT",
|
||||||
|
"degree": "PhD",
|
||||||
|
"start_date": "2020-09"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# You can access it like any Python dict:
|
||||||
|
data["cv"]["name"] # "John Doe"
|
||||||
|
data["cv"]["sections"]["education"][0]["institution"] # "MIT"
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. YAML text becomes a Python dictionary we can work with.
|
||||||
|
|
||||||
|
`ruamel.yaml` is being called in [`src/rendercv/schema/yaml_reader.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/schema/yaml_reader.py).
|
||||||
|
|
||||||
|
### [`pydantic`](https://github.com/pydantic/pydantic): Python Dictionary Validator
|
||||||
|
|
||||||
|
Now we have a dictionary. We need to validate it. Without a library, you'd write:
|
||||||
|
|
||||||
|
```python
|
||||||
|
if "name" not in data["cv"]:
|
||||||
|
raise ValueError("Missing 'name' field")
|
||||||
|
|
||||||
|
if not isinstance(data["cv"]["name"], str):
|
||||||
|
raise ValueError("name must be a string")
|
||||||
|
|
||||||
|
if "sections" in data["cv"]:
|
||||||
|
for section_name, entries in data["cv"]["sections"].items():
|
||||||
|
for entry in entries:
|
||||||
|
if "start_date" in entry and "end_date" in entry:
|
||||||
|
# Parse dates, compare them...
|
||||||
|
# This is already hundreds of lines and we're barely started
|
||||||
|
```
|
||||||
|
|
||||||
|
With `pydantic`, we can define the structure once:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from datetime import date as Date
|
||||||
|
|
||||||
|
class Education(BaseModel):
|
||||||
|
institution: str
|
||||||
|
start_date: Date
|
||||||
|
end_date: Date
|
||||||
|
|
||||||
|
@pydantic.model_validator(mode="after")
|
||||||
|
def check_dates(self):
|
||||||
|
if self.start_date > self.end_date:
|
||||||
|
raise ValueError("start_date cannot be after end_date")
|
||||||
|
return self
|
||||||
|
|
||||||
|
class Cv(BaseModel):
|
||||||
|
name: str
|
||||||
|
location: str | None = None
|
||||||
|
education: list[Education]
|
||||||
|
```
|
||||||
|
|
||||||
|
Then validate:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# This dictionary (from ruamel.yaml):
|
||||||
|
data = {
|
||||||
|
"name": "John Doe",
|
||||||
|
"location": "San Francisco",
|
||||||
|
"education": [
|
||||||
|
{
|
||||||
|
"institution": "MIT",
|
||||||
|
"start_date": "2020-09",
|
||||||
|
"end_date": "2024-05"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Becomes this validated object:
|
||||||
|
cv = Cv.model_validate(data)
|
||||||
|
|
||||||
|
# Now you have clean, validated objects:
|
||||||
|
cv.name # "John Doe"
|
||||||
|
cv.education[0].institution # "MIT"
|
||||||
|
cv.education[0].start_date # "2020-09", guaranteed dates are valid
|
||||||
|
```
|
||||||
|
|
||||||
|
That's the power. Dictionary goes in, `pydantic` checks everything, clean Python object comes out.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
**RenderCV's entire data model is `pydantic` models all the way down:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
class RenderCVModel(BaseModel):
|
||||||
|
cv: Cv # ← pydantic model
|
||||||
|
design: Design # ← pydantic model
|
||||||
|
locale: Locale # ← pydantic model
|
||||||
|
settings: Settings # ← pydantic model
|
||||||
|
```
|
||||||
|
|
||||||
|
Each field is another `pydantic` model. `Cv` contains more `pydantic` models like `EducationEntry`, `ExperienceEntry`, etc. It's nested validation: when you validate `RenderCVModel`, `pydantic` automatically validates every nested model too. One `model_validate()` call checks the entire structure.
|
||||||
|
|
||||||
|
See [`src/rendercv/schema/models/rendercv_model.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/schema/models/rendercv_model.py) for the top-level model.
|
||||||
|
|
||||||
|
## Step 2: Generating the Typst File
|
||||||
|
|
||||||
|
Now we need to generate a Typst file:
|
||||||
|
|
||||||
|
```typst
|
||||||
|
= John Doe
|
||||||
|
San Francisco, CA
|
||||||
|
|
||||||
|
== Education
|
||||||
|
#strong[MIT] #h(1fr) 2020 – 2024
|
||||||
|
PhD in Computer Science
|
||||||
|
```
|
||||||
|
|
||||||
|
You could try string concatenation:
|
||||||
|
|
||||||
|
```python
|
||||||
|
typst = f"= {cv.name}\n"
|
||||||
|
if cv.location:
|
||||||
|
typst += f"{cv.location}\n"
|
||||||
|
typst += "\n"
|
||||||
|
|
||||||
|
for section_title, entries in cv.sections.items():
|
||||||
|
typst += f"== {section_title}\n"
|
||||||
|
for entry in entries:
|
||||||
|
typst += f"#strong[{entry.institution}]"
|
||||||
|
# What about optional fields? Spacing? Line breaks?
|
||||||
|
# Multiple themes with different layouts?
|
||||||
|
# This is impossible to maintain!
|
||||||
|
```
|
||||||
|
|
||||||
|
This doesn't work. You're building hundreds of lines of string concatenation logic, handling conditionals, managing whitespace. It's unworkable.
|
||||||
|
|
||||||
|
This is why **templating engines were invented**. When you need to programmatically generate complex text files, you need templates.
|
||||||
|
|
||||||
|
### [`jinja2`](https://github.com/pallets/jinja): Templating Engine
|
||||||
|
|
||||||
|
`jinja2` is the most famous templating engine for Python.
|
||||||
|
|
||||||
|
**Template file** (`Header.j2.typ`):
|
||||||
|
```jinja2
|
||||||
|
= {{ cv.name }}
|
||||||
|
{% if cv.location %}
|
||||||
|
{{ cv.location }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if cv.email %}
|
||||||
|
#link("mailto:{{ cv.email }}")
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Python code:**
|
||||||
|
```python
|
||||||
|
template = jinja2_env.get_template("Header.j2.typ")
|
||||||
|
output = template.render(cv=cv)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
```typst
|
||||||
|
= John Doe
|
||||||
|
San Francisco, CA
|
||||||
|
|
||||||
|
#link("mailto:john@example.com")
|
||||||
|
```
|
||||||
|
|
||||||
|
Clean separation: templates define layout, Python code provides data. Users can override templates to customize their CV without touching Python code.
|
||||||
|
|
||||||
|
Typst templates live in [`src/rendercv/renderer/templater/templates/typst/`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/renderer/templater/templates/typst/).
|
||||||
|
|
||||||
|
`jinja2` is being called in [`src/rendercv/renderer/templater/templater.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/renderer/templater/templater.py).
|
||||||
|
|
||||||
|
### [`markdown`](https://github.com/Python-Markdown/markdown): Markdown to Typst
|
||||||
|
|
||||||
|
Users want to write Markdown in their YAML:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
highlights:
|
||||||
|
- "**Published** [3 papers](https://example.com) on neural networks"
|
||||||
|
- "Collaborated with *Professor Smith*"
|
||||||
|
```
|
||||||
|
|
||||||
|
But Typst doesn't understand `**bold**` or `[links](url)`. We need Typst syntax: `#strong[bold]` and `#link("url")[text]`.
|
||||||
|
|
||||||
|
**We use the `markdown` library.** It parses Markdown into an XML tree. Then we walk the tree and convert each element to Typst:
|
||||||
|
|
||||||
|
```python
|
||||||
|
match element.tag:
|
||||||
|
case "strong":
|
||||||
|
return f"#strong[{content}]"
|
||||||
|
case "em":
|
||||||
|
return f"#emph[{content}]"
|
||||||
|
case "a":
|
||||||
|
href = element.get("href")
|
||||||
|
return f'#link("{href}")[{content}]'
|
||||||
|
```
|
||||||
|
|
||||||
|
Result: `#strong[Published] #link("https://example.com")[3 papers]`
|
||||||
|
|
||||||
|
See [`src/rendercv/renderer/templater/markdown_parser.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/renderer/templater/markdown_parser.py). The `markdown_to_typst()` function does this conversion.
|
||||||
|
|
||||||
|
## Step 3: Compiling to PDF
|
||||||
|
|
||||||
|
### [`typst`](https://github.com/messense/typst-py): Typst Compiler
|
||||||
|
|
||||||
|
`typst` library is the Python bindings for the Typst compiler.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from typst import compile
|
||||||
|
compile("cv.typ", output="cv.pdf")
|
||||||
|
```
|
||||||
|
|
||||||
|
Done. Typst file has been compiled to PDF.
|
||||||
|
|
||||||
|
`typst` is being called in [`src/rendercv/renderer/pdf_png.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/renderer/pdf_png.py).
|
||||||
|
|
||||||
|
## The Complete Pipeline
|
||||||
|
|
||||||
|
When you run `rendercv render cv.yaml`:
|
||||||
|
|
||||||
|
1. **Parse** - `ruamel.yaml` reads YAML → Python dict
|
||||||
|
2. **Validate** - `pydantic` validates dict → `RenderCVModel` object
|
||||||
|
3. **Generate** - `jinja2` renders templates with data → Typst file
|
||||||
|
4. **Compile** - `typst` compiles Typst → PDF
|
||||||
|
|
||||||
|
Everything else (Markdown support, watch mode, PNG output, HTML export) builds on this core.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
1. [`src/rendercv/cli/render_command/run_rendercv.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/cli/render_command/run_rendercv.py): The complete flow
|
||||||
|
2. [`src/rendercv/schema/models/rendercv_model.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/schema/models/rendercv_model.py): The top-level Pydantic model
|
||||||
|
3. [`src/rendercv/renderer/templater/templater.py`](https://github.com/rendercv/rendercv/blob/main/src/rendercv/renderer/templater/templater.py): Template rendering
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# Writing Documentation
|
|
||||||
|
|
||||||
The documentation's source files are located in the [`docs`](https://github.com/rendercv/rendercv/tree/main/docs) directory and it is built using the [MkDocs](https://github.com/mkdocs/mkdocs) package. To work on the documentation and see the changes in real-time, run the following command.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hatch run docs:serve
|
|
||||||
```
|
|
||||||
|
|
||||||
Once the changes are pushed to the `main` branch, the [`deploy-docs.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/deploy-docs.yaml) workflow will be automatically triggered, and [docs.rendercv.com](https://docs.rendercv.com/) will be updated to the most recent version.
|
|
||||||
|
|
||||||
|
|
||||||
## Updating the [`examples`](https://github.com/rendercv/rendercv/tree/main/examples) folder
|
|
||||||
|
|
||||||
The `examples` folder includes example YAML files for all the built-in themes, along with their corresponding PDF outputs. Also, there are PNG files of the first pages of each theme in [`docs/assets/images`](https://github.com/rendercv/rendercv/tree/main/docs/assets/images). These examples are shown in [`README.md`](https://github.com/rendercv/rendercv/blob/main/README.md).
|
|
||||||
|
|
||||||
These files are generated using [`scripts/update_examples.py`](https://github.com/rendercv/rendercv/blob/main/scripts/update_examples.py). The contents of the examples are taken from the [`create_a_sample_data_model`](https://docs.rendercv.com/reference/data/#rendercv.data.create_a_sample_data_model) function from [`rendercv.data`](https://docs.rendercv.com/reference/data/).
|
|
||||||
|
|
||||||
Run the following command to update the `examples` folder.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hatch run update-examples
|
|
||||||
```
|
|
||||||
|
|
||||||
Once a new release is created on GitHub, the [`publish-to-pypi.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/publish-to-pypi.yaml) workflow will be automatically triggered, and the `examples` folder will be updated to the most recent version.
|
|
||||||
|
|
||||||
## Updating figures of the entry types in the "[Structure of the YAML Input File](../user_guide/structure_of_the_yaml_input_file.md)"
|
|
||||||
|
|
||||||
There are example figures for each entry type for each theme in the "[Structure of the YAML Input File](../user_guide/structure_of_the_yaml_input_file.md)" page.
|
|
||||||
|
|
||||||
The figures are generated using [`scripts/update_entry_figures.py`](https://github.com/rendercv/rendercv/blob/main/scripts/update_entry_figures.py).
|
|
||||||
|
|
||||||
Run the following command to update the figures.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hatch run docs:update-entry-figures
|
|
||||||
```
|
|
||||||
|
|
||||||
Once a new release is created on GitHub, the [`publish-to-pypi.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/publish-to-pypi.yaml) workflow will be automatically triggered, and the figures will be updated to the most recent version.
|
|
||||||
|
|
||||||
## Updating the JSON Schema ([`schema.json`](https://github.com/rendercv/rendercv/blob/main/schema.json))
|
|
||||||
|
|
||||||
The schema of RenderCV's input file is defined using [Pydantic](https://docs.pydantic.dev/latest/). Pydantic allows automatic creation and customization of JSON schemas from Pydantic models.
|
|
||||||
|
|
||||||
The JSON Schema is also generated using [`scripts/update_schema.py`](https://github.com/rendercv/rendercv/blob/main/scripts/update_schema.py). It uses [`generate_json_schema`](https://docs.rendercv.com/reference/data/#rendercv.data.generate_json_schema) function from [`rendercv.data`](https://docs.rendercv.com/reference/data/).
|
|
||||||
|
|
||||||
Run the following command to update the JSON Schema.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
hatch run update-schema
|
|
||||||
```
|
|
||||||
|
|
||||||
Once a new release is created on GitHub, the [`publish-to-pypi.yaml`](https://github.com/rendercv/rendercv/blob/main/.github/workflows/publish-to-pypi.yaml) workflow will be automatically triggered, and `schema.json` will be updated to the most recent version.
|
|
||||||
142
docs/docs_templating.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
"""This script generates the example entry figures and creates an environment for
|
||||||
|
documentation templates using `mkdocs-macros-plugin`. For example, the content of the
|
||||||
|
example entries found in
|
||||||
|
"[Structure of the YAML Input File](https://docs.rendercv.com/user_guide/structure_of_the_yaml_input_file/)"
|
||||||
|
are coming from this script.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import io
|
||||||
|
import pathlib
|
||||||
|
from typing import get_args
|
||||||
|
|
||||||
|
import pydantic
|
||||||
|
import ruamel.yaml
|
||||||
|
|
||||||
|
from rendercv.schema.models.cv.section import (
|
||||||
|
BulletEntry,
|
||||||
|
EducationEntry,
|
||||||
|
ExperienceEntry,
|
||||||
|
NormalEntry,
|
||||||
|
NumberedEntry,
|
||||||
|
OneLineEntry,
|
||||||
|
PublicationEntry,
|
||||||
|
ReversedNumberedEntry,
|
||||||
|
)
|
||||||
|
from rendercv.schema.models.cv.social_network import available_social_networks
|
||||||
|
from rendercv.schema.models.design.built_in_design import available_themes
|
||||||
|
from rendercv.schema.models.design.classic_theme import (
|
||||||
|
Alignment,
|
||||||
|
BodyAlignment,
|
||||||
|
Bullet,
|
||||||
|
PageSize,
|
||||||
|
PhoneNumberFormatType,
|
||||||
|
SectionTitleType,
|
||||||
|
)
|
||||||
|
from rendercv.schema.models.design.font_family import available_font_families
|
||||||
|
from rendercv.schema.models.locale.locale import available_locales
|
||||||
|
from rendercv.schema.yaml_reader import read_yaml
|
||||||
|
|
||||||
|
repository_root = pathlib.Path(__file__).parent.parent
|
||||||
|
rendercv_path = repository_root / "src" / "rendercv"
|
||||||
|
image_assets_directory = pathlib.Path(__file__).parent / "assets" / "images"
|
||||||
|
|
||||||
|
|
||||||
|
class SampleEntries(pydantic.BaseModel):
|
||||||
|
education_entry: EducationEntry
|
||||||
|
experience_entry: ExperienceEntry
|
||||||
|
normal_entry: NormalEntry
|
||||||
|
publication_entry: PublicationEntry
|
||||||
|
one_line_entry: OneLineEntry
|
||||||
|
bullet_entry: BulletEntry
|
||||||
|
numbered_entry: NumberedEntry
|
||||||
|
reversed_numbered_entry: ReversedNumberedEntry
|
||||||
|
text_entry: str
|
||||||
|
|
||||||
|
|
||||||
|
def dictionary_to_yaml(dictionary: dict):
|
||||||
|
"""Converts a dictionary to a YAML string.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dictionary: The dictionary to be converted to YAML.
|
||||||
|
Returns:
|
||||||
|
The YAML string.
|
||||||
|
"""
|
||||||
|
yaml_object = ruamel.yaml.YAML()
|
||||||
|
yaml_object.width = 60
|
||||||
|
yaml_object.indent(mapping=2, sequence=4, offset=2)
|
||||||
|
with io.StringIO() as string_stream:
|
||||||
|
yaml_object.dump(dictionary, string_stream)
|
||||||
|
return string_stream.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
def define_env(env):
|
||||||
|
# See https://mkdocs-macros-plugin.readthedocs.io/en/latest/macros/
|
||||||
|
sample_entries = read_yaml(
|
||||||
|
repository_root / "docs" / "user_guide" / "sample_entries.yaml"
|
||||||
|
)
|
||||||
|
# validate the parsed dictionary by creating an instance of SampleEntries:
|
||||||
|
sample_entries = SampleEntries(**sample_entries).model_dump()
|
||||||
|
|
||||||
|
entries_showcase = {}
|
||||||
|
for entry_name, entry in sample_entries.items():
|
||||||
|
proper_entry_name = entry_name.replace("_", " ").title().replace(" ", "")
|
||||||
|
entries_showcase[proper_entry_name] = {
|
||||||
|
"yaml": dictionary_to_yaml(entry),
|
||||||
|
"figures": [
|
||||||
|
{
|
||||||
|
"path": f"../assets/images/{theme}/{entry_name}.png",
|
||||||
|
"alt_text": f"{proper_entry_name} in {theme}",
|
||||||
|
"theme": theme,
|
||||||
|
}
|
||||||
|
for theme in available_themes
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
env.variables["sample_entries"] = entries_showcase
|
||||||
|
env.variables["entry_count"] = len(sample_entries)
|
||||||
|
env.variables["entry_names"] = [
|
||||||
|
f"[{entry_name}](#{entry_name.lower()})" for entry_name in entries_showcase
|
||||||
|
]
|
||||||
|
|
||||||
|
# Available themes strings (put available themes between ``)
|
||||||
|
themes = [f"`{theme}`" for theme in available_themes]
|
||||||
|
env.variables["available_themes"] = ", ".join(themes)
|
||||||
|
|
||||||
|
# Available locales string
|
||||||
|
locales = [f"`{locale}`" for locale in available_locales]
|
||||||
|
env.variables["available_locales"] = ", ".join(locales)
|
||||||
|
|
||||||
|
# Available social networks strings (put available social networks between ``)
|
||||||
|
social_networks = [
|
||||||
|
f"`{social_network}`" for social_network in available_social_networks
|
||||||
|
]
|
||||||
|
env.variables["available_social_networks"] = ", ".join(social_networks)
|
||||||
|
|
||||||
|
# Others:
|
||||||
|
env.variables["available_page_sizes"] = ", ".join(
|
||||||
|
[f"`{page_size}`" for page_size in get_args(PageSize.__value__)]
|
||||||
|
)
|
||||||
|
env.variables["available_font_families"] = ", ".join(
|
||||||
|
[f"`{font_family}`" for font_family in available_font_families]
|
||||||
|
)
|
||||||
|
env.variables["available_body_alignments"] = ", ".join(
|
||||||
|
[f"`{text_alignment}`" for text_alignment in get_args(BodyAlignment.__value__)]
|
||||||
|
)
|
||||||
|
env.variables["available_phone_number_formats"] = ", ".join(
|
||||||
|
[
|
||||||
|
f"`{phone_number_format}`"
|
||||||
|
for phone_number_format in get_args(PhoneNumberFormatType.__value__)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
env.variables["available_alignments"] = ", ".join(
|
||||||
|
[f"`{alignment}`" for alignment in get_args(Alignment.__value__)]
|
||||||
|
)
|
||||||
|
env.variables["available_section_title_types"] = ", ".join(
|
||||||
|
[
|
||||||
|
f"`{section_title_type}`"
|
||||||
|
for section_title_type in get_args(SectionTitleType.__value__)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
env.variables["available_bullets"] = ", ".join(
|
||||||
|
[f"`{bullet}`" for bullet in get_args(Bullet.__value__)]
|
||||||
|
)
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
"""This script generates the example entry figures and creates an environment for
|
|
||||||
documentation templates using `mkdocs-macros-plugin`. For example, the content of the
|
|
||||||
example entries found in
|
|
||||||
"[Structure of the YAML Input File](https://docs.rendercv.com/user_guide/structure_of_the_yaml_input_file/)"
|
|
||||||
are coming from this script.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import io
|
|
||||||
import pathlib
|
|
||||||
from typing import get_args
|
|
||||||
|
|
||||||
import pydantic
|
|
||||||
import ruamel.yaml
|
|
||||||
|
|
||||||
import rendercv.data as data
|
|
||||||
import rendercv.themes.options as theme_options
|
|
||||||
|
|
||||||
repository_root = pathlib.Path(__file__).parent.parent
|
|
||||||
rendercv_path = repository_root / "src" / "rendercv"
|
|
||||||
image_assets_directory = pathlib.Path(__file__).parent / "assets" / "images"
|
|
||||||
|
|
||||||
|
|
||||||
class SampleEntries(pydantic.BaseModel):
|
|
||||||
education_entry: data.EducationEntry
|
|
||||||
experience_entry: data.ExperienceEntry
|
|
||||||
normal_entry: data.NormalEntry
|
|
||||||
publication_entry: data.PublicationEntry
|
|
||||||
one_line_entry: data.OneLineEntry
|
|
||||||
bullet_entry: data.BulletEntry
|
|
||||||
numbered_entry: data.NumberedEntry
|
|
||||||
reversed_numbered_entry: data.ReversedNumberedEntry
|
|
||||||
text_entry: str
|
|
||||||
|
|
||||||
|
|
||||||
def dictionary_to_yaml(dictionary: dict):
|
|
||||||
"""Converts a dictionary to a YAML string.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
dictionary: The dictionary to be converted to YAML.
|
|
||||||
Returns:
|
|
||||||
The YAML string.
|
|
||||||
"""
|
|
||||||
yaml_object = ruamel.yaml.YAML()
|
|
||||||
yaml_object.width = 60
|
|
||||||
yaml_object.indent(mapping=2, sequence=4, offset=2)
|
|
||||||
with io.StringIO() as string_stream:
|
|
||||||
yaml_object.dump(dictionary, string_stream)
|
|
||||||
return string_stream.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
def define_env(env):
|
|
||||||
# See https://mkdocs-macros-plugin.readthedocs.io/en/latest/macros/
|
|
||||||
sample_entries = data.read_a_yaml_file(
|
|
||||||
repository_root / "docs" / "user_guide" / "sample_entries.yaml"
|
|
||||||
)
|
|
||||||
# validate the parsed dictionary by creating an instance of SampleEntries:
|
|
||||||
SampleEntries(**sample_entries)
|
|
||||||
|
|
||||||
entries_showcase = {}
|
|
||||||
for entry_name, entry in sample_entries.items():
|
|
||||||
proper_entry_name = entry_name.replace("_", " ").title().replace(" ", "")
|
|
||||||
entries_showcase[proper_entry_name] = {
|
|
||||||
"yaml": dictionary_to_yaml(entry),
|
|
||||||
"figures": [
|
|
||||||
{
|
|
||||||
"path": f"../assets/images/{theme}/{entry_name}.png",
|
|
||||||
"alt_text": f"{proper_entry_name} in {theme}",
|
|
||||||
"theme": theme,
|
|
||||||
}
|
|
||||||
for theme in data.available_themes
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
env.variables["sample_entries"] = entries_showcase
|
|
||||||
|
|
||||||
# For theme templates reference docs
|
|
||||||
themes_path = rendercv_path / "themes"
|
|
||||||
theme_templates = {}
|
|
||||||
for theme in data.available_themes:
|
|
||||||
theme_templates[theme] = {}
|
|
||||||
for theme_file in themes_path.glob(f"{theme}/*.typ"):
|
|
||||||
theme_templates[theme][theme_file.stem] = theme_file.read_text()
|
|
||||||
|
|
||||||
# Update ordering of theme templates, if there are more files, add them to the
|
|
||||||
# end
|
|
||||||
order = [
|
|
||||||
"Preamble.j2",
|
|
||||||
"Header.j2",
|
|
||||||
"SectionBeginning.j2",
|
|
||||||
"SectionEnding.j2",
|
|
||||||
"TextEntry.j2",
|
|
||||||
"BulletEntry.j2",
|
|
||||||
"NumberedEntry.j2",
|
|
||||||
"ReversedNumberedEntry.j2",
|
|
||||||
"OneLineEntry.j2",
|
|
||||||
"EducationEntry.j2",
|
|
||||||
"ExperienceEntry.j2",
|
|
||||||
"NormalEntry.j2",
|
|
||||||
"PublicationEntry.j2",
|
|
||||||
]
|
|
||||||
remaining_files = set(theme_templates[theme].keys()) - set(order)
|
|
||||||
order += list(remaining_files)
|
|
||||||
theme_templates[theme] = {key: theme_templates[theme][key] for key in order}
|
|
||||||
|
|
||||||
if theme != "markdown":
|
|
||||||
theme_templates[theme] = {
|
|
||||||
f"{key}.typ": value for key, value in theme_templates[theme].items()
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
theme_templates[theme] = {
|
|
||||||
f"{key}.md": value for key, value in theme_templates[theme].items()
|
|
||||||
}
|
|
||||||
|
|
||||||
env.variables["theme_templates"] = theme_templates
|
|
||||||
|
|
||||||
theme_components = {}
|
|
||||||
for theme_file in themes_path.glob("components/*.typ"):
|
|
||||||
theme_components[theme_file.stem] = theme_file.read_text()
|
|
||||||
theme_components = {f"{key}.typ": value for key, value in theme_components.items()}
|
|
||||||
|
|
||||||
env.variables["theme_components"] = theme_components
|
|
||||||
|
|
||||||
# Available themes strings (put available themes between ``)
|
|
||||||
themes = [f"`{theme}`" for theme in data.available_themes]
|
|
||||||
env.variables["available_themes"] = ", ".join(themes)
|
|
||||||
|
|
||||||
# Available social networks strings (put available social networks between ``)
|
|
||||||
social_networks = [
|
|
||||||
f"`{social_network}`" for social_network in data.available_social_networks
|
|
||||||
]
|
|
||||||
env.variables["available_social_networks"] = ", ".join(social_networks)
|
|
||||||
|
|
||||||
# Others:
|
|
||||||
env.variables["available_page_sizes"] = ", ".join(
|
|
||||||
[f"`{page_size}`" for page_size in get_args(theme_options.PageSize)]
|
|
||||||
)
|
|
||||||
env.variables["available_font_families"] = ", ".join(
|
|
||||||
[f"`{font_family}`" for font_family in get_args(theme_options.FontFamily)]
|
|
||||||
)
|
|
||||||
env.variables["available_text_alignments"] = ", ".join(
|
|
||||||
[
|
|
||||||
f"`{text_alignment}`"
|
|
||||||
for text_alignment in get_args(theme_options.TextAlignment)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
env.variables["available_header_alignments"] = ", ".join(
|
|
||||||
[
|
|
||||||
f"`{header_alignment}`"
|
|
||||||
for header_alignment in get_args(theme_options.Alignment)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
env.variables["available_section_title_types"] = ", ".join(
|
|
||||||
[
|
|
||||||
f"`{section_title_type}`"
|
|
||||||
for section_title_type in get_args(
|
|
||||||
get_args(theme_options.SectionTitleType)[0]
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
env.variables["available_bullets"] = ", ".join(
|
|
||||||
[f"`{bullet}`" for bullet in get_args(theme_options.BulletPoint)]
|
|
||||||
)
|
|
||||||
161
docs/index.md
@@ -1,73 +1,158 @@
|
|||||||
# The engine of the [RenderCV App](https://rendercv.com)
|
# RenderCV
|
||||||
|
|
||||||
|
<div align="center" markdown>
|
||||||
|
*CV/resume generator for academics and engineers*
|
||||||
|
|
||||||
[](https://github.com/rendercv/rendercv/actions/workflows/test.yaml)
|
[](https://github.com/rendercv/rendercv/actions/workflows/test.yaml)
|
||||||
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/rendercv/rendercv)
|
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/rendercv/rendercv)
|
||||||
[)](https://docs.rendercv.com)
|
[)](https://docs.rendercv.com)
|
||||||
[)](https://pypi.python.org/pypi/rendercv)
|
[)](https://pypi.python.org/pypi/rendercv)
|
||||||
[)](https://pypistats.org/packages/rendercv)
|
[)](https://pypistats.org/packages/rendercv)
|
||||||
|
</div>
|
||||||
|
|
||||||
RenderCV engine is a Typst-based Python package with a command-line interface (CLI) that allows you to version-control your CV/resume as source code. It reads a CV written in a YAML file with Markdown syntax, converts it into a [Typst](https://typst.app) code, and generates a PDF.
|
Write your CV or resume as YAML, then run RenderCV,
|
||||||
|
|
||||||
RenderCV engine's focus is to provide these three features:
|
```bash
|
||||||
|
rendercv render John_Doe_CV.yaml
|
||||||
|
```
|
||||||
|
|
||||||
- **Content-first approach:** Users should be able to focus on the content instead of worrying about the formatting.
|
and get a PDF with perfect typography. No template wrestling. No broken layouts. Consistent spacing, every time.
|
||||||
- **A mechanism to version-control a CV's content and design separately:** The content and design of a CV are separate issues and they should be treated separately.
|
|
||||||
- **Robustness:** A PDF should be delivered if there aren't any errors. If errors exist, they should be clearly explained along with solutions.
|
|
||||||
|
|
||||||
|
With RenderCV, you can:
|
||||||
|
|
||||||
It takes a YAML file that looks like this:
|
- Version-control your CV — it's just text.
|
||||||
|
- Focus on content — don't wory about the formatting.
|
||||||
|
- Get perfect typography — kerning, spacing, hierarchy, all handled. No design skills required.
|
||||||
|
|
||||||
|
A YAML file like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
cv:
|
cv:
|
||||||
name: John Doe
|
name: John Doe
|
||||||
location: Location
|
location: San Francisco, CA
|
||||||
email: john.doe@example.com
|
email: john.doe@email.com
|
||||||
phone: tel:+1-609-999-9995
|
website: https://rendercv.com/
|
||||||
social_networks:
|
social_networks:
|
||||||
- network: LinkedIn
|
- network: LinkedIn
|
||||||
username: john.doe
|
username: rendercv
|
||||||
- network: GitHub
|
- network: GitHub
|
||||||
username: john.doe
|
username: rendercv
|
||||||
sections:
|
sections:
|
||||||
welcome_to_RenderCV!:
|
Welcome to RenderCV:
|
||||||
- '[RenderCV](https://rendercv.com) is a Typst-based CV
|
- RenderCV reads a CV written in a YAML file, and generates a PDF with professional typography.
|
||||||
framework designed for academics and engineers, with Markdown
|
- See the [documentation](https://docs.rendercv.com) for more details.
|
||||||
syntax support.'
|
|
||||||
- Each section title is arbitrary. Each section contains
|
|
||||||
a list of entries, and there are 7 different entry types
|
|
||||||
to choose from.
|
|
||||||
education:
|
education:
|
||||||
- institution: Stanford University
|
- institution: Princeton University
|
||||||
area: Computer Science
|
area: Computer Science
|
||||||
degree: PhD
|
degree: PhD
|
||||||
location: Stanford, CA, USA
|
date:
|
||||||
start_date: 2023-09
|
start_date: 2018-09
|
||||||
end_date: present
|
end_date: 2023-05
|
||||||
|
location: Princeton, NJ
|
||||||
|
summary:
|
||||||
highlights:
|
highlights:
|
||||||
- Working on the optimization of autonomous vehicles
|
- "Thesis: Efficient Neural Architecture Search for Resource-Constrained Deployment"
|
||||||
in urban environments
|
- "Advisor: Prof. Sanjeev Arora"
|
||||||
|
- NSF Graduate Research Fellowship, Siebel Scholar (Class of 2022)
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, it produces one of these PDFs with its corresponding Typst file, Markdown file, HTML file, and images as PNGs. Click on the images below to preview PDF files.
|
becomes one of these PDFs. Click on the images to preview.
|
||||||
|
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ClassicTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_Sb2novTheme_CV.pdf) |
|
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ClassicTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringresumesTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_Sb2novTheme_CV.pdf) |
|
||||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ModerncvTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringresumesTheme_CV.pdf) |
|
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_ModerncvTheme_CV.pdf) | [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringclassicTheme_CV.pdf) |  |
|
||||||
| [](https://github.com/rendercv/rendercv/blob/main/examples/John_Doe_EngineeringclassicTheme_CV.pdf) |  |
|
|
||||||
|
|
||||||
RenderCV comes with a JSON Schema so that the YAML input file can be filled out interactively.
|
|
||||||
|
|
||||||

|
## JSON Schema
|
||||||
|
|
||||||
## Getting Started
|
RenderCV's JSON Schema lets you fill out the YAML interactively, with autocompletion and inline documentation.
|
||||||
|
|
||||||
RenderCV engine is very easy to install (`pip install "rendercv[full]"`) and easy to use (`rendercv new "John Doe"`). Follow the [user guide](https://docs.rendercv.com/user_guide) to get started.
|

|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
We are developing a [purpose-built app](https://rendercv.com) for writing CVs and resumes that will be available on mobile and web. This Python project is the foundation of that app. Check out [our blog post](https://rendercv.com/introducing-rendercv/) to learn more about why one would use such an app.
|
## Extensive Design Options
|
||||||
|
|
||||||
## Contributing
|
You have full control over every detail.
|
||||||
|
|
||||||
All contributions to RenderCV are welcome! To get started, please read [the developer guide](https://docs.rendercv.com/developer_guide).
|
```yaml
|
||||||
|
design:
|
||||||
|
theme: classic
|
||||||
|
page:
|
||||||
|
size: us-letter
|
||||||
|
top_margin: 0.7in
|
||||||
|
bottom_margin: 0.7in
|
||||||
|
left_margin: 0.7in
|
||||||
|
right_margin: 0.7in
|
||||||
|
show_footer: true
|
||||||
|
show_top_note: true
|
||||||
|
colors:
|
||||||
|
body: rgb(0, 0, 0)
|
||||||
|
name: rgb(0, 79, 144)
|
||||||
|
headline: rgb(0, 79, 144)
|
||||||
|
connections: rgb(0, 79, 144)
|
||||||
|
section_titles: rgb(0, 79, 144)
|
||||||
|
links: rgb(0, 79, 144)
|
||||||
|
footer: rgb(128, 128, 128)
|
||||||
|
top_note: rgb(128, 128, 128)
|
||||||
|
typography:
|
||||||
|
line_spacing: 0.6em
|
||||||
|
alignment: justified
|
||||||
|
date_and_location_column_alignment: right
|
||||||
|
font_family: Source Sans 3
|
||||||
|
# ...and more
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> Want to set up a live preview environment like the one shown above? See [how to set up VS Code for RenderCV](user_guide/how_to/set_up_vs_code_for_rendercv.md).
|
||||||
|
|
||||||
|
## Strict Validation
|
||||||
|
|
||||||
|
No surprises. If something's wrong, you'll know exactly what and where. If it's valid, you get a perfect PDF.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
## Any Language
|
||||||
|
|
||||||
|
Fill out the locale field for your language.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
locale:
|
||||||
|
language: english
|
||||||
|
last_updated: Last updated in
|
||||||
|
month: month
|
||||||
|
months: months
|
||||||
|
year: year
|
||||||
|
years: years
|
||||||
|
present: present
|
||||||
|
month_abbreviations:
|
||||||
|
- Jan
|
||||||
|
- Feb
|
||||||
|
- Mar
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
Install RenderCV (Requires Python 3.12+):
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install "rendercv[full]"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new CV yaml file:
|
||||||
|
|
||||||
|
```
|
||||||
|
rendercv new "John Doe"
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit the YAML, then render:
|
||||||
|
|
||||||
|
```
|
||||||
|
rendercv render "John_Doe_CV.yaml"
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details, see the [user guide](user_guide/index.md).
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.api.functions`
|
|
||||||
|
|
||||||
::: rendercv.api.functions
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.api`
|
|
||||||
|
|
||||||
::: rendercv.api
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.cli.commands`
|
|
||||||
|
|
||||||
::: rendercv.cli.commands
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.cli`
|
|
||||||
|
|
||||||
::: rendercv.cli
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.cli.printer`
|
|
||||||
|
|
||||||
::: rendercv.cli.printer
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.cli.utilities`
|
|
||||||
|
|
||||||
::: rendercv.cli.utilities
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.data.generator`
|
|
||||||
|
|
||||||
::: rendercv.data.generator
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.data`
|
|
||||||
|
|
||||||
::: rendercv.data
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.data.models.base`
|
|
||||||
|
|
||||||
::: rendercv.data.models.base
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# `rendercv.data.models.computers`
|
|
||||||
|
|
||||||
::: rendercv.data.models.computers
|
|
||||||