From 1cf227b3a8e40bae0c68b160cf05dc31a8dc6719 Mon Sep 17 00:00:00 2001 From: Jelmer Draaijer Date: Sat, 5 Oct 2024 21:20:29 +0200 Subject: [PATCH] WIP --- README.md | 4 +-- .../developing-locally-docker.rst | 6 ++-- .../developing-locally.rst | 6 ++-- tests/test_bare.sh | 6 ++-- {{cookiecutter.project_slug}}/.drone.yml | 12 +++---- .../.github/workflows/ci.yml | 24 ++++++-------- {{cookiecutter.project_slug}}/.gitlab-ci.yml | 12 +++---- .../compose/local/django/Dockerfile | 29 +++++++---------- .../compose/local/docs/Dockerfile | 27 +++++++--------- .../compose/production/django/Dockerfile | 31 +++++++++---------- .../utility/install_python_dependencies.sh | 2 +- 11 files changed, 70 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 3b42a5e1b..a3f577ffd 100644 --- a/README.md +++ b/README.md @@ -84,11 +84,11 @@ and then editing the results to include your name, email, and various configurat First, get Cookiecutter. Trust me, it's awesome: - $ pip install "cookiecutter>=1.7.0" + $ uv tool install "cookiecutter>=1.7.0" Now run it against this repo: - $ cookiecutter https://github.com/cookiecutter/cookiecutter-django + $ uvx cookiecutter https://github.com/cookiecutter/cookiecutter-django You'll be prompted for some values. Provide them, then a Django project will be created for you. diff --git a/docs/2-local-development/developing-locally-docker.rst b/docs/2-local-development/developing-locally-docker.rst index 05e188e52..580953293 100644 --- a/docs/2-local-development/developing-locally-docker.rst +++ b/docs/2-local-development/developing-locally-docker.rst @@ -154,10 +154,10 @@ This tells our computer that all future commands are specifically for the dev1 m Add 3rd party python packages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -To install a new 3rd party python package, you cannot use ``pip install ``, that would only add the package to the container. The container is ephemeral, so that new library won't be persisted if you run another container. Instead, you should modify the Docker image: -You have to modify the relevant requirement file: base, local or production by adding: :: +To install a new 3rd party python package, you cannot use ``uv add ``, that would only add the package to the container. The container is ephemeral, so that new library won't be persisted if you run another container. Instead, you should modify the Docker image: +You have to modify pyproject.toml and either add it to project.dependencies or to tool.uv.dev-dependencies by adding: :: - == + "==" To get this change picked up, you'll need to rebuild the image(s) and restart the running container: :: diff --git a/docs/2-local-development/developing-locally.rst b/docs/2-local-development/developing-locally.rst index b8484bfe0..cfb4cb4ed 100644 --- a/docs/2-local-development/developing-locally.rst +++ b/docs/2-local-development/developing-locally.rst @@ -1,7 +1,7 @@ Getting Up and Running Locally ============================== -.. index:: pip, virtualenv, PostgreSQL +.. index:: PostgreSQL Setting Up Development Environment @@ -9,7 +9,7 @@ Setting Up Development Environment Make sure to have the following on your host: -* Python 3.12 +* uv https://docs.astral.sh/uv/getting-started/installation/ * PostgreSQL_. * Redis_, if using Celery * Cookiecutter_ @@ -29,7 +29,7 @@ First things first. #. Install development requirements: :: $ cd - $ pip install -r requirements/local.txt + $ uv sync $ git init # A git repo is required for pre-commit to install $ pre-commit install diff --git a/tests/test_bare.sh b/tests/test_bare.sh index f38c9357e..8fdf6f970 100755 --- a/tests/test_bare.sh +++ b/tests/test_bare.sh @@ -18,13 +18,13 @@ cd my_awesome_project sudo utility/install_os_dependencies.sh install # Install Python deps -pip install -r requirements/local.txt +uv sync --frozen # run the project's tests -pytest +uv run pytest # Make sure the check doesn't raise any warnings -python manage.py check --fail-level WARNING +uv run python manage.py check --fail-level WARNING # Run npm build script if package.json is present if [ -f "package.json" ] diff --git a/{{cookiecutter.project_slug}}/.drone.yml b/{{cookiecutter.project_slug}}/.drone.yml index 20d6fb1bb..f8b654b12 100644 --- a/{{cookiecutter.project_slug}}/.drone.yml +++ b/{{cookiecutter.project_slug}}/.drone.yml @@ -13,7 +13,7 @@ environment: steps: - name: lint pull: if-not-exists - image: python:3.12 + image: ghcr.io/astral-sh/uv:python3.12 environment: PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit volumes: @@ -21,8 +21,8 @@ steps: path: ${PRE_COMMIT_HOME} commands: - export PRE_COMMIT_HOME=$CI_PROJECT_DIR/.cache/pre-commit - - pip install -q pre-commit - - pre-commit run --show-diff-on-failure --color=always --all-files + - uv pip install -q pre-commit pre-commit-uv + - uv run pre-commit run --show-diff-on-failure --color=always --all-files - name: test pull: if-not-exists @@ -37,10 +37,10 @@ steps: - docker-compose -f docker-compose.local.yml up -d - docker-compose -f docker-compose.local.yml run django pytest {%- else %} - image: python:3.12 + image: ghcr.io/astral-sh/uv:python3.12 commands: - - pip install -r requirements/local.txt - - pytest + - uv sync --frozen + - uv run pytest {%- endif%} volumes: diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml index 6bb555063..e8fdf35d0 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml @@ -87,26 +87,20 @@ jobs: run: docker compose -f docker-compose.local.yml down {%- else %} - - name: Set up Python - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v2 with: - python-version: '3.12' - cache: pip - cache-dependency-path: | - requirements/base.txt - requirements/local.txt - - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements/local.txt + enable-cache: "true" + - name: Install dependencies + run: uv sync - name: Check DB Migrations - run: python manage.py makemigrations --check + run: uv run python manage.py makemigrations --check - name: Run DB Migrations - run: python manage.py migrate + run: uv run python manage.py migrate - name: Test with pytest - run: pytest + run: uv run pytest {%- endif %} diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 9c7cd5367..c881e2214 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -13,16 +13,16 @@ variables: precommit: stage: lint - image: python:3.12 + image: ghcr.io/astral-sh/uv:python3.12 variables: PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit cache: paths: - ${PRE_COMMIT_HOME} before_script: - - pip install -q pre-commit + - uv pip install -q pre-commit pre-commit-uv script: - - pre-commit run --show-diff-on-failure --color=always --all-files + - uv run pre-commit run --show-diff-on-failure --color=always --all-files pytest: stage: test @@ -39,13 +39,13 @@ pytest: script: - docker compose -f docker-compose.local.yml run django pytest {%- else %} - image: python:3.12 + image: ghcr.io/astral-sh/uv:python3.12 services: - postgres:{{ cookiecutter.postgresql_version }} variables: DATABASE_URL: pgsql://$POSTGRES_USER:$POSTGRES_PASSWORD@postgres/$POSTGRES_DB before_script: - - pip install -r requirements/local.txt + - uv sync --frozen script: - - pytest + - uv run pytest {%- endif %} diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index d892c9dca..8ee051363 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -1,8 +1,6 @@ -# define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.7-slim-bookworm AS python - # Python build stage -FROM python AS python-build-stage +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS python-build-stage +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy ARG BUILD_ENVIRONMENT=local @@ -14,15 +12,17 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ libpq-dev # Requirements are installed here to ensure they will be cached. -COPY ./requirements . - -# Create Python Dependency and Sub-Dependency Wheels. -RUN pip wheel --wheel-dir /usr/src/app/wheels \ - -r ${BUILD_ENVIRONMENT}.txt +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 --frozen --no-install-project --no-dev \ +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev # Python 'run' stage -FROM python AS python-run-stage +FROM docker.io/python:3.12.7-slim-bookworm AS python-run-stage ARG BUILD_ENVIRONMENT=local ARG APP_HOME=/app @@ -58,11 +58,7 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ # All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction # copy python dependency wheels from python-build-stage -COPY --from=python-build-stage /usr/src/app/wheels /wheels/ - -# use wheels to install python dependencies -RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ - && rm -rf /wheels/ +COPY --from=builder --chown=app:app ${APP_HOME} ${APP_HOME} COPY ./compose/production/django/entrypoint /entrypoint RUN sed -i 's/\r$//g' /entrypoint @@ -86,7 +82,6 @@ RUN sed -i 's/\r$//g' /start-flower RUN chmod +x /start-flower {% endif %} -# copy application code to WORKDIR -COPY . ${APP_HOME} +ENV PATH="/app/.venv/bin:$PATH" ENTRYPOINT ["/entrypoint"] diff --git a/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile index 40caf8511..b82254407 100644 --- a/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/docs/Dockerfile @@ -1,7 +1,3 @@ -# define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.7-slim-bookworm AS python - - # Python build stage FROM python AS python-build-stage @@ -17,16 +13,17 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ && rm -rf /var/lib/apt/lists/* # Requirements are installed here to ensure they will be cached. -COPY ./requirements /requirements - -# create python dependency wheels -RUN pip wheel --no-cache-dir --wheel-dir /usr/src/app/wheels \ - -r /requirements/local.txt -r /requirements/production.txt \ - && rm -rf /requirements +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 --frozen --no-install-project --no-dev +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev # Python 'run' stage -FROM python AS python-run-stage +FROM docker.io/python:3.12.7-slim-bookworm AS python-run-stage ARG BUILD_ENVIRONMENT ENV PYTHONUNBUFFERED=1 @@ -49,14 +46,12 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ && rm -rf /var/lib/apt/lists/* # copy python dependency wheels from python-build-stage -COPY --from=python-build-stage /usr/src/app/wheels /wheels - -# use wheels to install python dependencies -RUN pip install --no-cache /wheels/* \ - && rm -rf /wheels +COPY --from=builder --chown=app:app /app /app COPY ./compose/local/docs/start /start-docs RUN sed -i 's/\r$//g' /start-docs RUN chmod +x /start-docs +ENV PATH="/app/.venv/bin:$PATH" + WORKDIR /docs diff --git a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile index a3908625c..fa3394c12 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/production/django/Dockerfile @@ -24,11 +24,9 @@ ENV DJANGO_AZURE_ACCOUNT_NAME=${DJANGO_AZURE_ACCOUNT_NAME} RUN npm run build {%- endif %} -# define an alias for the specific python version used in this file. -FROM docker.io/python:3.12.7-slim-bookworm AS python -# Python build stage -FROM python AS python-build-stage +FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS python-build-stage +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy ARG BUILD_ENVIRONMENT=production @@ -41,15 +39,17 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ # Requirements are installed here to ensure they will be cached. -COPY ./requirements . - -# Create Python Dependency and Sub-Dependency Wheels. -RUN pip wheel --wheel-dir /usr/src/app/wheels \ - -r ${BUILD_ENVIRONMENT}.txt +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 --frozen --no-install-project --no-dev +ADD . /app +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --frozen --no-dev # Python 'run' stage -FROM python AS python-run-stage +FROM docker.io/python:3.12.7-slim-bookworm AS python-run-stage ARG BUILD_ENVIRONMENT=production ARG APP_HOME=/app @@ -76,14 +76,11 @@ RUN apt-get update && apt-get install --no-install-recommends -y \ && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \ && rm -rf /var/lib/apt/lists/* -# All absolute dir copies ignore workdir instruction. All relative dir copies are wrt to the workdir instruction -# copy python dependency wheels from python-build-stage -COPY --from=python-build-stage /usr/src/app/wheels /wheels/ - -# use wheels to install python dependencies -RUN pip install --no-cache-dir --no-index --find-links=/wheels/ /wheels/* \ - && rm -rf /wheels/ +# Copy the application from the builder +COPY --from=builder --chown=app:app /app /app +# Place executables in the environment at the front of the path +ENV PATH="/app/.venv/bin:$PATH" COPY --chown=django:django ./compose/production/django/entrypoint /entrypoint RUN sed -i 's/\r$//g' /entrypoint diff --git a/{{cookiecutter.project_slug}}/utility/install_python_dependencies.sh b/{{cookiecutter.project_slug}}/utility/install_python_dependencies.sh index e09ebf6f8..d340bc454 100755 --- a/{{cookiecutter.project_slug}}/utility/install_python_dependencies.sh +++ b/{{cookiecutter.project_slug}}/utility/install_python_dependencies.sh @@ -33,7 +33,7 @@ if [ -z "$VIRTUAL_ENV" ]; then echo >&2 -e "\n" exit 1; else - pip install -r $PROJECT_DIR/requirements/local.txt + uv sync --frozen {%- if cookiecutter.use_heroku == "y" -%} pip install -r $PROJECT_DIR/requirements.txt {%- endif %}