diff --git a/.copier/.copier-answers.yml.jinja b/.copier/.copier-answers.yml.jinja new file mode 100644 index 0000000..0028a23 --- /dev/null +++ b/.copier/.copier-answers.yml.jinja @@ -0,0 +1 @@ +{{ _copier_answers|to_json -}} diff --git a/.devcontainer/devcontainer-start.sh b/.devcontainer/devcontainer-start.sh index b68e305..c490af4 100755 --- a/.devcontainer/devcontainer-start.sh +++ b/.devcontainer/devcontainer-start.sh @@ -4,4 +4,5 @@ set -xeo pipefail +uv lock uv sync --frozen --no-install-project --all-groups diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json.jinja similarity index 86% rename from .devcontainer/devcontainer.json rename to .devcontainer/devcontainer.json.jinja index db024b2..65250e2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json.jinja @@ -1,5 +1,5 @@ { - "name": "Python Template", + "name": "{{ project_name }}", "dockerComposeFile": "docker-compose.yaml", "service": "devcontainer", "runServices": [ @@ -10,9 +10,9 @@ "celery_worker", "otel-collector" ], - "workspaceFolder": "/opt/app/${localEnv:PROJECT_NAME:python-template}", + "workspaceFolder": "/opt/app/${localEnv:PROJECT_NAME:{{ project_name }}}", "containerEnv": { - "PROJECT": "${localEnv:PROJECT_NAME:python-template}", + "PROJECT": "${localEnv:PROJECT_NAME:{{ project_name }}}", "USER": "${localEnv:USER}" }, "features": { @@ -59,8 +59,8 @@ ], "unwantedRecommendations": [] }, - "postCreateCommand": "/opt/app/${localEnv:PROJECT_NAME:python-template}/.devcontainer/devcontainer-create.sh", - "postStartCommand": "/opt/app/${localEnv:PROJECT_NAME:python-template}/.devcontainer/devcontainer-start.sh", + "postCreateCommand": "/opt/app/${localEnv:PROJECT_NAME:{{ project_name }}}/.devcontainer/devcontainer-create.sh", + "postStartCommand": "/opt/app/${localEnv:PROJECT_NAME:{{ project_name }}}/.devcontainer/devcontainer-start.sh", "forwardPorts": [ // Backend API: // localhost:8000 for accessing on your host diff --git a/.devcontainer/docker-compose.yaml b/.devcontainer/docker-compose.yaml.jinja similarity index 90% rename from .devcontainer/docker-compose.yaml rename to .devcontainer/docker-compose.yaml.jinja index 285a2a4..e242cb7 100644 --- a/.devcontainer/docker-compose.yaml +++ b/.devcontainer/docker-compose.yaml.jinja @@ -4,13 +4,13 @@ services: context: ../ target: devcontainer args: - PROJECT_NAME: ${PROJECT_NAME:-python-template} + PROJECT_NAME: ${PROJECT_NAME:-{{ project_name }}} USER: ${USER} ports: - '8000:8000' volumes: - source: .. - target: /opt/app/${PROJECT_NAME:-python-template} + target: /opt/app/${PROJECT_NAME:-{{ project_name }}} type: bind - source: ./commandhistory target: /home/${USER}/.commandhistory diff --git a/.env.jinja b/.env.jinja new file mode 100644 index 0000000..b790781 --- /dev/null +++ b/.env.jinja @@ -0,0 +1,17 @@ +PROJECT_NAME={{project_name}} + +ASYNC_DATABASE_URL=postgresql+asyncpg://dev:dev@postgres:5432/dev +DATABASE_POOL_PRE_PING=True +DATABASE_POOL_SIZE=5 +DATABASE_POOL_RECYCLE=3600 +DATABASE_MAX_OVERFLOW=10 +SERVER_URL=example.com +ACCESS_TOKEN_EXPIRE_MINUTES=15 +JWT_SIGNING_KEY= + +# OpenTelemetry configuration +OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317 + +# Celery settings +CELERY_BROKER_URL=amqp://guest:guest@rabbitmq:5672 +CELERY_RESULT_BACKEND=redis://redis:6379/0 diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index 373e91c..00ff939 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -21,14 +21,24 @@ jobs: with: python-version: 3.13 + - name: Install Copier + run: pip install copier + + - name: Generate project from template + run: copier copy . generated --defaults --trust + - name: Install dependencies + working-directory: generated run: | pip install uv - uv sync --frozen --no-cache --no-install-project --all-groups + uv sync --no-cache --no-install-project --all-groups - name: Install pre-commit + working-directory: generated run: pip install pre-commit + - name: Run linters and formatters + working-directory: generated run: pre-commit run --all-files tests: @@ -73,12 +83,20 @@ jobs: with: python-version: 3.13 + - name: Install Copier + run: pip install copier + + - name: Generate project from template + run: copier copy . generated --defaults --trust + - name: Install dependencies + working-directory: generated run: | pip install uv - uv sync --frozen --no-cache --no-install-project --group dev --no-group types + uv sync --no-cache --no-install-project --group dev --no-group types - name: Run tests with coverage + working-directory: generated run: | uv run coverage run -m pytest uv run coverage report -m --fail-under=80 @@ -87,11 +105,16 @@ jobs: name: Build Docker Image runs-on: ubuntu-latest needs: tests - steps: - name: Checkout code uses: actions/checkout@v3 + - name: Install Copier + run: pip install copier + + - name: Generate project from template + run: copier copy . generated --defaults --trust + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -106,7 +129,7 @@ jobs: - name: Build image with cache uses: docker/build-push-action@v5 with: - context: . + context: ./generated push: false tags: python-template:latest cache-from: type=local,src=/tmp/.buildx-cache diff --git a/Dockerfile b/Dockerfile.jinja similarity index 91% rename from Dockerfile rename to Dockerfile.jinja index 9474b39..8425cc8 100644 --- a/Dockerfile +++ b/Dockerfile.jinja @@ -2,7 +2,7 @@ # Base image install all the tools needed to build the project FROM python:3.13-slim-bookworm AS base -ARG PROJECT_NAME=python-template +ARG PROJECT_NAME={{ project_name }} ARG USER=appuser ENV RUNTIME_PACKAGES=libpq-dev @@ -34,6 +34,7 @@ RUN pip install --upgrade pip \ WORKDIR /opt/app/${PROJECT_NAME} COPY --chown=${USER}:${USER} . . +RUN uv lock RUN uv sync --frozen --no-cache --no-install-project --no-default-groups @@ -89,9 +90,10 @@ RUN apt-get purge -y ${BUILD_PACKAGES} \ USER ${USER} # TODO(remer): wheel version has to match what is set in pyproject.toml -COPY --from=builder /opt/app/${PROJECT_NAME}/dist/python_template-0.1.0-py3-none-any.whl /opt/app/${PROJECT_NAME}/dist/python_template-0.1.0-py3-none-any.whl +# Note: Python wheel names convert hyphens to underscores +COPY --from=builder /opt/app/${PROJECT_NAME}/dist/*.whl /opt/app/${PROJECT_NAME}/dist/ -RUN uv run pip install --no-deps dist/python_template-0.1.0-py3-none-any.whl +RUN uv run pip install --no-deps dist/*.whl EXPOSE 8000 diff --git a/README.md b/README.md index 66988e3..ef70054 100755 --- a/README.md +++ b/README.md @@ -8,6 +8,16 @@ - Rest API built with FastAPI and SQLAlchemy - PostgreSQL database +## Clone the project using Copier + +Decide a name for your new project's directory, you will use it below. For example, `my-awesome-project`. + +Go to the directory that will be the parent of your project, and run the command with your project's name: + +`copier copy --vcs-ref main git@github.com:xmartlabs/python-template.git my-awesome-project --trust` + +Note the `--trust` option is necessary to be able to execute a post-creation script that updates your `.env` files. + ## Project setup The only things you need are [Docker](https://docs.docker.com/engine/install/), [Docker Compose](https://docs.docker.com/compose/install/), and a code editor with devcontainer support like [Visual Studio Code](https://code.visualstudio.com/download). Once you open the template with VS Code, it will recommend that you install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) if you don’t have it already. Then, a pop-up will appear to reopen the template in the devcontainer, or you can use `Ctrl / Cmd + shift + P` -> `Dev Containers: Open Folder in Container…`. Remember to add the `.env` file at the root folder; you can use `.env.example` as a reference. @@ -28,18 +38,6 @@ Alternatively, you must have: For making code changes, installing `pre-commit` is necessary (see section [Code tools: pre-commit](#pre-commit)) -### Customization - -The project's name (`python-template`) can be edited following next steps: - -1. Edit project's name in the [pyproject.toml](pyproject.toml) file -2. Set `PROJECT_NAME` env variable to be exactly the same as project's name in pyproject.toml. Ensure VSCode has this -variable loaded, otherwise the dev container might fail or not work as expected. You can open VScode with from cmd with: - -```bash -PROJECT_NAME=your-awesome-project code -``` - ## Migrations We use Alembic as database migration tool. You can run migration commands directly inside the dev container or use the provided shortcut in the `exec.sh` script. diff --git a/copier.yml b/copier.yml new file mode 100644 index 0000000..5c20100 --- /dev/null +++ b/copier.yml @@ -0,0 +1,22 @@ +project_name: + type: str + help: The name of the project + default: python-template + +project_description: + type: str + help: The description of the project + default: Xmartlabs' Python project template + +_exclude: + # Python + - __pycache__ + - .mypy_cache + - .cache + - .venv + - uv.lock + +_answers_file: .copier/.copier-answers.yml + +_tasks: + - ["{{ _copier_python }}"] diff --git a/pyproject.toml b/pyproject.toml.jinja similarity index 97% rename from pyproject.toml rename to pyproject.toml.jinja index c442c8f..c8d98bf 100644 --- a/pyproject.toml +++ b/pyproject.toml.jinja @@ -1,8 +1,8 @@ [project] # Project's name must be the same as PROJECT_NAME environment variable used elsewhere. -name = "python-template" -version = "0.1.0" -description = "Xmartlabs' Python project template" +name = "{{ project_name }}" +version = "0.0.1" +description = "{{ project_description }}" authors = [{ name = "Xmartlabs", email = "getintouch@xmartlabs.com" }] requires-python = ">=3.13.0,<4.0.0" readme = "README.md"