Skip to content

Commit

Permalink
fix(project-setup): Switch from poetry to uv, bump all dependency…
Browse files Browse the repository at this point in the history
… versions to latest
  • Loading branch information
alexpovel committed Oct 23, 2024
1 parent dc4cf90 commit 4983dee
Show file tree
Hide file tree
Showing 13 changed files with 1,562 additions and 2,409 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ jobs:
enable-cache: true

- name: Build package
run: devbox run poetry build
run: devbox run uv build

- name: Publish package
uses: pypa/gh-action-pypi-publish@v1.10.2
Expand Down
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ depgraph.svg
# This is discardable output for debugging tests
tests/test_data/actual-outputs

# We're not using this directly, but `poetry.lock`. However, since Python dependencies
# are still a nightmare, we'll use `poetry.lock` to generate `requirements.txt` and
# use that temporarily.
requirements.txt

# Byte-compiled / optimized / DLL files
Expand Down
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This document is less about software architecture per-se, but rather about techn
- but since switched to [Google Cloud Run](https://cloud.google.com/run), which is based on regular OCI containers (see [Dockerfile](./Dockerfile)), hence resolving the hell that is dependency management in serverless environments.

Fully serverless is still interesting since it's such a fitting use-case.
The best solution seems to [vendor all dependencies](https://www.serverless.com/plugins/serverless-python-requirements), instead of trying our luck with the serverless provider reading and correctly installing the dependencies for us (which often requires a `requirements.txt` instead of a [`poetry.lock`](./poetry.lock) or similar).
The best solution seems to [vendor all dependencies](https://www.serverless.com/plugins/serverless-python-requirements), instead of trying our luck with the serverless provider reading and correctly installing the dependencies for us (which often requires a `requirements.txt` instead of a `poetry.lock` or similar).

However, since this project treats self-hosting as a first-class citizen, going full serverless and abandoning providing Docker images entirely isn't an option anyway.
Hosting serverlessly would be a split, required maintenance of two hosting options instead of just building one image and calling it a day.
Expand All @@ -23,7 +23,7 @@ This document is less about software architecture per-se, but rather about techn
- [`pydantic`](https://pydantic-docs.helpmanual.io/) for fully typed data validation (e.g., for APIs), facilitated by [automatic `pydantic` model generation](https://koxudaxi.github.io/datamodel-code-generator/) from e.g. OpenAPI specs like [GitHub's](https://github.com/github/rest-api-description/tree/main/descriptions/api.github.com) or [JSON Resume's](https://github.com/jsonresume/resume-schema/blob/master/schema.json), allowing full support from `mypy` and the IDE when using said validated data
- [12 Factor App](https://12factor.net/) conformance:
1. [Codebase](https://12factor.net/codebase): [GitHub-hosted repo](https://github.com/alexpovel/ancv/)
2. [Dependencies](https://12factor.net/dependencies): taken care of by [poetry](https://python-poetry.org/) and its standardized ([PEP 621](https://peps.python.org/pep-0621/)) [config](pyproject.toml) and [lock](poetry.lock) files, pinning all transient dependencies and providing virtual environments
2. [Dependencies](https://12factor.net/dependencies): taken care of by [uv](https://docs.astral.sh/uv/)
3. [Config](https://12factor.net/config): the app is configured using environment variables.
Although [problematic](https://news.ycombinator.com/item?id=31200132), this approach was chosen for its simplicity
4. [Backing Services](https://12factor.net/backing-services): not applicable for this very simple app
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

Use devbox to set up a development environment.
Refer to [the available `script`s](devbox.json) to see what's possible.
Generally, even when running in a `devbox shell`, `poetry run` is necessary:
Generally, even when running in a `devbox shell`, running `uv` is necessary:

- devbox sets up what used to be system-wide packages (Python, poetry, ...) deterministically and automatically
- within the devbox virtual environment, we still manage and use a Python virtual environment through `poetry run`
- devbox sets up what used to be system-wide packages (Python, uv, ...) deterministically and automatically
- within the devbox virtual environment, we still manage and use a Python virtual environment through `uv` commands

That way, we get the normal Python package management for normal Python packages (`ruff`, `pytest`, ...), and devbox for the overarching rest.

Expand Down
39 changes: 12 additions & 27 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,35 +1,20 @@
# Global ARG, available to all stages (if renewed)
ARG WORKDIR="/app"

FROM python:3.12 AS builder

# Renew (https://stackoverflow.com/a/53682110):
ARG WORKDIR

# Don't buffer `stdout`:
ENV PYTHONUNBUFFERED=1
# Don't create `.pyc` files:
ENV PYTHONDONTWRITEBYTECODE=1

RUN pip install poetry && poetry config virtualenvs.in-project true

WORKDIR ${WORKDIR}
COPY . .

RUN poetry install --only main

FROM python:3.12

ARG WORKDIR
FROM python:3.12-slim

ARG WORKDIR="/app"
WORKDIR ${WORKDIR}

COPY --from=builder ${WORKDIR} .

RUN useradd -u 1000 -d ${WORKDIR} -M app
RUN chown -R app:app ${WORKDIR}
USER 1000

# App-specific settings:
# https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# This just works... splitting this up for better caching is a pain with Python.
COPY . .
RUN uv sync --frozen

EXPOSE 8080
ENTRYPOINT [ "./.venv/bin/python", "-m", "ancv" ]

ENTRYPOINT [ "uv", "run", "ancv" ]
CMD [ "serve", "api", "--port", "8080" ]
4 changes: 0 additions & 4 deletions ancv/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import sys
from enum import IntEnum
from pathlib import Path

PROJECT_ROOT = Path(__file__).parent
PACKAGE = __package__

# See also https://github.com/python/typeshed/issues/3049
sys.stdout.reconfigure(encoding="utf-8") # type: ignore[attr-defined]


class SIPrefix(IntEnum):
KILO = 1e3
Expand Down
7 changes: 6 additions & 1 deletion ancv/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@ def generate_schema() -> None:
from ancv.visualization.themes import THEMES
from ancv.visualization.translations import TRANSLATIONS

if METADATA.project_url is not None:
homepage = METADATA.project_url[0].split(",")[-1].strip()
else:
homepage = None

schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"allOf": [
Expand All @@ -184,7 +189,7 @@ def generate_schema() -> None:
"properties": {
METADATA.name: {
"type": "object",
"description": f"{METADATA.name}-specific ({METADATA.home_page}) properties",
"description": f"{METADATA.name}-specific ({homepage}) properties",
"properties": {
"template": {
"type": "string",
Expand Down
22 changes: 12 additions & 10 deletions devbox.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"packages": [
"python@3.12",
"poetry@1.8",
"uv@0.4",
"ruff@0.4",
"pydeps@1.12",
"graphviz@8",
"pre-commit@3"
],
Expand All @@ -11,18 +13,18 @@
"shell": {
"init_hook": [
"echo 'Running command in devbox shell...'",
"poetry install"
"uv sync"
],
"scripts": {
"build-image": "docker build --progress=plain --tag \"$LIBRARY\"/\"$LIBRARY\":$(poetry version --short) .",
"format-check": "poetry run ruff format --check --diff",
"build-image": "docker build --progress=plain --tag \"$LIBRARY\"/\"$LIBRARY\":dev .",
"format-check": "ruff format --check --diff",
"install-hooks": "pre-commit install --hook-type pre-push --hook-type pre-commit --hook-type commit-msg",
"lint": "poetry run ruff --verbose .",
"make-depgraph.svg": "poetry run pydeps --max-bacon=4 --cluster -T svg -o depgraph.svg \"$LIBRARY\"",
"make-github.py": "poetry run datamodel-codegen --url \"https://raw.githubusercontent.com/github/rest-api-description/main/descriptions-next/api.github.com/dereferenced/api.github.com.deref.json\" --encoding utf-8 --input-file-type openapi --openapi-scopes paths --output github.py",
"make-resume.py": "poetry run datamodel-codegen --url \"https://raw.githubusercontent.com/jsonresume/resume-schema/master/schema.json\" --encoding utf-8 --input-file-type jsonschema --output resume.py",
"test": "poetry run pytest --cov=\"$LIBRARY\" --cov-report=html --cov-report=term --cov-report=xml",
"typecheck": "poetry run mypy -p \"$LIBRARY\""
"lint": "ruff check --verbose .",
"make-depgraph.svg": "pydeps --max-bacon=4 --cluster -T svg -o depgraph.svg \"$LIBRARY\"",
"make-github.py": "uv run datamodel-codegen --url \"https://raw.githubusercontent.com/github/rest-api-description/main/descriptions-next/api.github.com/dereferenced/api.github.com.deref.json\" --encoding utf-8 --input-file-type openapi --openapi-scopes paths --output github.py",
"make-resume.py": "uv run datamodel-codegen --url \"https://raw.githubusercontent.com/jsonresume/resume-schema/master/schema.json\" --encoding utf-8 --input-file-type jsonschema --output resume.py",
"test": "uv run pytest -vv --cov=\"$LIBRARY\" --cov-report=html --cov-report=term --cov-report=xml",
"typecheck": "uv run mypy -v -p \"$LIBRARY\""
}
}
}
Loading

0 comments on commit 4983dee

Please sign in to comment.