diff --git a/cookiecutter.json b/cookiecutter.json index 332c599..fd5bd0f 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -1,8 +1,7 @@ { "project_name": "My Awesome Project", "project_slug": "{{ cookiecutter.project_name.lower()|replace(' ', '_')|replace('-', '_')|replace('.', '_')|trim() }}", - "python_version": "3.10.7", - "poetry_version": "1.1.15", + "python_version": "3.11.1", "_copy_without_render": [ "*.html" ] diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 7ce4b67..0331fb2 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -2,12 +2,13 @@ import os COMMANDS_TO_RUN = [ - "poetry install", + "pip install -U pip pip-tools invoke", + "inv update-dependencies", "pre-commit install", "pre-commit autoupdate", "pre-commit run --all-files", - "git add poetry.lock", - "git commit -m 'Add poetry lock file'", + "git add ./*.txt", + "git commit -m 'Freeze dependencies'", "pycharm .", "inv test", "./src/manage.py migrate", diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index 81487f3..c34eb46 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -1,4 +1,7 @@ exclude: \.min\.(js|css)(\.map)?$|^\.idea/ +ci: + skip: [pip-compile] + repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 @@ -69,3 +72,18 @@ repos: additional_dependencies: - eslint@^7.29.0 - eslint-config-prettier@^8.3.0 + - repo: https://github.com/jazzband/pip-tools + rev: 6.11.0 + hooks: + - id: pip-compile + name: pip-compile requirements.txt + args: [-q, --allow-unsafe, --resolver=backtracking, --generate-hashes, requirements.in] + files: ^requirements\.(in|txt)$ + - id: pip-compile + name: pip-compile constraints.txt + args: [-q, --allow-unsafe, --resolver=backtracking, --strip-extras, --output-file=constraints.txt, requirements.in] + files: ^requirements\.in|constraints\.txt$ + - id: pip-compile + name: pip-compile requirements-dev.in + args: [-q, --allow-unsafe, --resolver=backtracking, --generate-hashes, requirements-dev.in] + files: ^requirements-dev\.(in|txt)$ diff --git a/{{cookiecutter.project_slug}}/Dockerfile b/{{cookiecutter.project_slug}}/Dockerfile index 8aeeb2e..994a292 100644 --- a/{{cookiecutter.project_slug}}/Dockerfile +++ b/{{cookiecutter.project_slug}}/Dockerfile @@ -1,56 +1,7 @@ -############################################## -# Build virtualenv -############################################## -FROM python:{{cookiecutter.python_version}}-bullseye AS venv - -# Prepare poetry -############################################## -# https://python-poetry.org/docs/#installation -ENV POETRY_VERSION={{cookiecutter.poetry_version}} -RUN curl -sSL https://install.python-poetry.org | python3 - - -ENV PATH /root/.local/bin:$PATH - -RUN python -m pip install --user poetry-lock-check==0.1.0 \ - cleo==0.8.1 # poetry-lock-check depends on cleo - -WORKDIR /app -COPY pyproject.toml poetry.lock ./ - -RUN python -m poetry_lock_check check-lock - -# Install python dependencies -############################################## -RUN python -m venv --copies /app/venv -# Will install dev deps as well, so that we can run tests in this image -RUN . /app/venv/bin/activate \ - && poetry install --no-interaction - -ENV PATH /app/venv/bin:$PATH - -# Collect static files & build assets -############################################## - -COPY ./src /app/src -COPY ./envs/local-envs.env /app/.env -WORKDIR /app/src - -# Required for manage.py to startup -ARG ENV_FILE=/app/.env -ARG DEBUG=true -ENV STATIC_ROOT=/app/static - -RUN mkdir -p $STATIC_ROOT - -# Build assets so that we don't need the build tools later -RUN python manage.py collectstatic --noinput --clear - - - ############################################## # write git info ############################################## -FROM alpine/git:v2.26.2 AS git +FROM alpine/git:v2.36.3 AS git WORKDIR /app COPY .git /app/.git/ @@ -82,25 +33,26 @@ RUN apt-get update -y \ # Fetch project requirements ############################################## -COPY --chown=django:django --from=venv /app/venv /app/venv/ COPY --chown=django:django --from=git /git-describe /git-commit /build-date /app/git/ -ENV PATH /app/venv/bin:$PATH - -# Fetch built assets & static files -############################################## -ENV STATIC_ROOT=/app/static -COPY --chown=django:django --from=venv $STATIC_ROOT $STATIC_ROOT # Create directory structure ############################################## WORKDIR /app -COPY pyproject.toml poetry.lock ./ +COPY --chown=django:django pyproject.toml requirements.txt ./ ADD --chown=django:django ./src ./src COPY --chown=django:django tasks.py ./tasks.py +RUN mkdir -p /app/data /app/db +RUN chown django:django /app /app/data /app/db -RUN mkdir -p /app/data -RUN chown django:django /app /app/data +ENV STATIC_ROOT=/app/static +ENV SECRET_KEY "changeme" +ENV DEBUG "false" +ENV DATABASE_URL "sqlite:////app/db/db.sqlite3" + +RUN python -m pip install --no-cache-dir -r requirements.txt +WORKDIR /app/src +RUN python manage.py collectstatic --noinput --clear EXPOSE 8000 diff --git a/{{cookiecutter.project_slug}}/README.md b/{{cookiecutter.project_slug}}/README.md index 3818e37..afbfbc8 100644 --- a/{{cookiecutter.project_slug}}/README.md +++ b/{{cookiecutter.project_slug}}/README.md @@ -1,8 +1,14 @@ # {{cookiecutter.project_slug}} ## Quick start +Clone, then ```shell +pyenv virtualenv {{cookiecutter.python_version}} {{cookiecutter.project_slug}} +pyenv local {{cookiecutter.project_slug}} +pip install pip-tools +pip-sync requirements.txt requirements-dev.txt pre-commit install --install-hooks -poetry install inv test +./src/manage.py migrate +./src/manage.py createsuperuser ``` diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index fbe54ad..805d1dd 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -1,42 +1,3 @@ -############################################################################### -# poetry -############################################################################### -[tool.poetry] -name = "{{cookiecutter.project_slug}}" -version = "0.1.0" -description = "" -authors = ["Gabriel Augendre "] - -[tool.poetry.dependencies] -python = ">=3.10.0, <4" -django = "^4.0" -django-cleanup = ">=6.0" -django-environ = ">=0.9.0" -django-htmx = ">=1.12.2" -django-linear-migrations = ">=2.2.0" -django-extensions = ">=3.1.5" -psycopg2-binary = ">=2.8" -whitenoise = ">=6.2" -requests = ">=2.28.1" -gunicorn = ">=20.1.0" - -[tool.poetry.dev-dependencies] -django-debug-toolbar = ">=3.2" -pytest = ">=6.0" -pytest-cov = ">=3.0.0" -pytest-django = ">=4.1.0" -pytest-html = ">=3.1.1" -pre-commit = ">=2.1" -model-bakery = ">=1.3.1" -freezegun = ">=1.1.0" -bpython = ">=0.22.1" -poetry-deps-scanner = ">=2.0.0" -invoke = ">=1.7.3" - -[build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" - ############################################################################### # pytest ############################################################################### diff --git a/{{cookiecutter.project_slug}}/requirements-dev.in b/{{cookiecutter.project_slug}}/requirements-dev.in new file mode 100644 index 0000000..c956029 --- /dev/null +++ b/{{cookiecutter.project_slug}}/requirements-dev.in @@ -0,0 +1,14 @@ +-c constraints.txt +django-debug-toolbar>=3.2 +pytest>=6.0 +pytest-cov>=3.0.0 +pytest-django>=4.1.0 +pytest-html>=3.1.1 +pytest-selenium>=4.0.0 +pre-commit>=2.1 +model-bakery>=1.3.1 +freezegun>=1.1.0 +bpython>=0.22.1 +invoke>=1.7.3 +hypothesis>=6.56.4 +django-browser-reload>=1.6.0 diff --git a/{{cookiecutter.project_slug}}/requirements.in b/{{cookiecutter.project_slug}}/requirements.in new file mode 100644 index 0000000..72e2479 --- /dev/null +++ b/{{cookiecutter.project_slug}}/requirements.in @@ -0,0 +1,12 @@ +django>=4.1,<5.0 +django-cleanup>=6.0 +django-environ>=0.9.0 +django-htmx>=1.12.2 +django-linear-migrations>=2.2.0 +django-extensions>=3.1.5 +psycopg2-binary>=2.8 +whitenoise>=6.2 +django-registration>=3.3 +django-anymail[mailgun]>=8.6 +requests>=2.28.1 +gunicorn>=20.1.0 diff --git a/{{cookiecutter.project_slug}}/src/common/templates/common/base.html b/{{cookiecutter.project_slug}}/src/common/templates/common/base.html index c213801..462000c 100644 --- a/{{cookiecutter.project_slug}}/src/common/templates/common/base.html +++ b/{{cookiecutter.project_slug}}/src/common/templates/common/base.html @@ -4,7 +4,7 @@ - Character Sheet + Project name {% include "common/hello-random.html" %} diff --git a/{{cookiecutter.project_slug}}/tasks.py b/{{cookiecutter.project_slug}}/tasks.py index 9eccb63..102a108 100644 --- a/{{cookiecutter.project_slug}}/tasks.py +++ b/{{cookiecutter.project_slug}}/tasks.py @@ -1,7 +1,6 @@ import time from pathlib import Path -import requests from invoke import task BASE_DIR = Path(__file__).parent.resolve(strict=True) @@ -11,6 +10,28 @@ COMPOSE_BUILD_ENV = {"COMPOSE_FILE": COMPOSE_BUILD_FILE} TEST_ENV = {"ENV_FILE": BASE_DIR / "envs" / "test-envs.env"} +@task +def update_dependencies(ctx): + common_args = "-q --allow-unsafe --resolver=backtracking --upgrade" + with ctx.cd(BASE_DIR): + ctx.run( + f"pip-compile {common_args} --generate-hashes requirements.in", + pty=True, + echo=True, + ) + ctx.run( + f"pip-compile {common_args} --strip-extras -o constraints.txt requirements.in", + pty=True, + echo=True, + ) + ctx.run( + f"pip-compile {common_args} --generate-hashes requirements-dev.in", + pty=True, + echo=True, + ) + ctx.run("pip-sync requirements.txt requirements-dev.txt", pty=True, echo=True) + + @task def makemessages(ctx): with ctx.cd(SRC_DIR): @@ -74,6 +95,8 @@ def deploy(ctx): @task def check_alive(ctx): + import requests + exception = None for _ in range(5): try: