diff --git a/README.md b/README.md index 1a60f8ec..63347698 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,11 @@ Answer the prompts with your own desired [options](http://cookiecutter-django.re Choose from 1, 2 [1]: 1 timezone [UTC]: America/Los_Angeles windows [n]: n - use_pycharm [n]: y + Select an editor to use. The choices are: + 1 - None + 2 - PyCharm + 3 - VS Code + Choose from 1, 2, 3 [1]: 1 use_docker [n]: n Select postgresql_version: 1 - 14 diff --git a/cookiecutter.json b/cookiecutter.json index 970a5379..cc9f8c17 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -16,7 +16,7 @@ "username_type": ["username", "email"], "timezone": "UTC", "windows": "n", - "use_pycharm": "n", + "editor": ["None", "PyCharm", "VS Code"], "use_docker": "n", "postgresql_version": ["14", "13", "12", "11", "10"], "cloud_provider": ["AWS", "GCP", "Azure", "None"], diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index a1d78817..c1dcf82d 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -53,11 +53,15 @@ timezone: windows: Indicates whether the project should be configured for development on Windows. -use_pycharm: - Indicates whether the project should be configured for development with PyCharm_. +editor: + Select an editor to use. The choices are: + + 1. None + 2. PyCharm_ + 3. `VS Code`_ use_docker: - Indicates whether the project should be configured to use Docker_ and `Docker Compose`_. + Indicates whether the project should be configured to use Docker_, `Docker Compose`_ and `devcontainer`_. postgresql_version: Select a PostgreSQL_ version to use. The choices are: @@ -148,9 +152,11 @@ debug: .. _Apache Software License 2.0: http://www.apache.org/licenses/LICENSE-2.0 .. _PyCharm: https://www.jetbrains.com/pycharm/ +.. _VS Code: https://github.com/microsoft/vscode .. _Docker: https://github.com/docker/docker .. _Docker Compose: https://docs.docker.com/compose/ +.. _devcontainer: https://containers.dev/ .. _PostgreSQL: https://www.postgresql.org/docs/ diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 11f165b7..29260940 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -74,12 +74,13 @@ def remove_pycharm_files(): def remove_docker_files(): + shutil.rmtree(".devcontainer") shutil.rmtree("compose") file_names = ["local.yml", "production.yml", ".dockerignore"] for file_name in file_names: os.remove(file_name) - if "{{ cookiecutter.use_pycharm }}".lower() == "y": + if "{{ cookiecutter.editor }}".lower() == "PyCharm": file_names = ["docker_compose_up_django.xml", "docker_compose_up_docs.xml"] for file_name in file_names: os.remove(os.path.join(".idea", "runConfigurations", file_name)) @@ -427,7 +428,7 @@ def main(): if "{{ cookiecutter.username_type }}" == "username": remove_custom_user_manager_files() - if "{{ cookiecutter.use_pycharm }}".lower() == "n": + if "{{ cookiecutter.editor }}".lower() != "PyCharm": remove_pycharm_files() if "{{ cookiecutter.use_docker }}".lower() == "y": @@ -445,8 +446,8 @@ def main(): if "{{ cookiecutter.keep_local_envs_in_vcs }}".lower() == "y": print( INFO + ".env(s) are only utilized when Docker Compose and/or " - "Heroku support is enabled so keeping them does not " - "make sense given your current setup." + TERMINATOR + "Heroku support is enabled so keeping them does not make sense " + "given your current setup." + TERMINATOR ) remove_envs_and_associated_files() else: diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index bb3ce479..9e33d531 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -52,8 +52,9 @@ SUPPORTED_COMBINATIONS = [ {"open_source_license": "Not open source"}, {"windows": "y"}, {"windows": "n"}, - {"use_pycharm": "y"}, - {"use_pycharm": "n"}, + {"editor": "None"}, + {"editor": "PyCharm"}, + {"editor": "VS Code"}, {"use_docker": "y"}, {"use_docker": "n"}, {"postgresql_version": "14"}, @@ -373,14 +374,15 @@ def test_error_if_incompatible(cookies, context, invalid_context): @pytest.mark.parametrize( - ["use_pycharm", "pycharm_docs_exist"], + ["editor", "pycharm_docs_exist"], [ - ("n", False), - ("y", True), + ("None", False), + ("PyCharm", True), + ("VS Code", False), ], ) -def test_pycharm_docs_removed(cookies, context, use_pycharm, pycharm_docs_exist): - context.update({"use_pycharm": use_pycharm}) +def test_pycharm_docs_removed(cookies, context, editor, pycharm_docs_exist): + context.update({"editor": editor}) result = cookies.bake(extra_context=context) with open(f"{result.project_path}/docs/index.rst") as f: diff --git a/{{cookiecutter.project_slug}}/.devcontainer/bash_history b/{{cookiecutter.project_slug}}/.devcontainer/bash_history new file mode 100644 index 00000000..e69de29b diff --git a/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh b/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh new file mode 100644 index 00000000..bedddf64 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.devcontainer/bashrc.override.sh @@ -0,0 +1,20 @@ + +# +# .bashrc.override.sh +# + +# persistent bash history +HISTFILE=~/.bash_history +PROMPT_COMMAND="history -a; $PROMPT_COMMAND" + +# set some django env vars +source /entrypoint + +# restore default shell options +set +o errexit +set +o pipefail +set +o nounset + +# start ssh-agent +# https://code.visualstudio.com/docs/remote/troubleshooting +eval "$(ssh-agent -s)" diff --git a/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json b/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json new file mode 100644 index 00000000..c11b8dd9 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.devcontainer/devcontainer.json @@ -0,0 +1,87 @@ +// For format details, see https://containers.dev/implementors/json_reference/ +{ + "name": "{{cookiecutter.project_slug}}_dev", + "dockerComposeFile": [ + "../local.yml" + ], + "init": true, + "mounts": [ + { + "source": "./.devcontainer/bash_history", + "target": "/home/dev-user/.bash_history", + "type": "bind" + }, + { + "source": "~/.ssh", + "target": "/tmp", + "type": "bind" + }, + { + "source": "~/.ssh", + "target": "/home/dev-user/.ssh", + "type": "bind" + } + ], + // Tells devcontainer.json supporting services / tools whether they should run + // /bin/sh -c "while sleep 1000; do :; done" when starting the container instead of the container’s default command + "overrideCommand": true, + "service": "django", + // "remoteEnv": {"PATH": "/home/dev-user/.local/bin:${containerEnv:PATH}"}, + "remoteUser": "dev-user", + "workspaceFolder": "/app", + // Set *default* container specific settings.json values on container create. + "customizations": { + {%- if cookiecutter.editor == "VS Code" %} + "vscode": { + "settings": { + "editor.formatOnSave": true, + "[python]": { + "analysis.autoImportCompletions": true, + "analysis.typeCheckingMode": "basic", + "defaultInterpreterPath": "/usr/local/bin/python", + "editor.codeActionsOnSave": { + "source.organizeImports": true + }, + // Uncomment when fixed + // https://github.com/microsoft/vscode-remote-release/issues/8474 + // "editor.defaultFormatter": "ms-python.black-formatter", + "formatting.blackPath": "/usr/local/bin/black", + "formatting.provider": "black", + "languageServer": "Pylance", + // "linting.banditPath": "/usr/local/py-utils/bin/bandit", + "linting.enabled": true, + "linting.flake8Enabled": true, + "linting.flake8Path": "/usr/local/bin/flake8", + "linting.mypyEnabled": true, + "linting.mypyPath": "/usr/local/bin/mypy", + "linting.pycodestylePath": "/usr/local/bin/pycodestyle", + // "linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "linting.pylintEnabled": true, + "linting.pylintPath": "/usr/local/bin/pylint" + } + }, + // https://code.visualstudio.com/docs/remote/devcontainerjson-reference#_vs-code-specific-properties + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "davidanson.vscode-markdownlint", + "mrmlnc.vscode-duplicate", + "visualstudioexptteam.vscodeintellicode", + "visualstudioexptteam.intellicode-api-usage-examples", + // python + "ms-python.python", + "ms-python.vscode-pylance", + "ms-python.isort", + "ms-python.black-formatter", + // django + "batisteo.vscode-django" + ] + } + {%- endif %} + }, + // Uncomment the next line if you want start specific services in your Docker Compose config. + // "runServices": [], + // Uncomment the next line if you want to keep your containers running after VS Code shuts down. + // "shutdownAction": "none", + // Uncomment the next line to run commands after the container is created. + "postCreateCommand": "cat .devcontainer/bashrc.override.sh >> ~/.bashrc" +} diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 19bb2bc0..541f4084 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -161,11 +161,10 @@ typings/ !.vscode/extensions.json *.code-workspace -# Local History for Visual Studio Code -.history/ +# Local History for devcontainer +.devcontainer/bash_history - -{% if cookiecutter.use_pycharm == 'y' -%} +{% if cookiecutter.editor == 'PyCharm' -%} # Provided default Pycharm Run/Debug Configurations should be tracked by git # In case of local modifications made by Pycharm, use update-index command # for each changed file, like this: diff --git a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile index b1f459ba..3636ce1e 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile +++ b/{{cookiecutter.project_slug}}/compose/local/django/Dockerfile @@ -33,6 +33,18 @@ ENV BUILD_ENV ${BUILD_ENVIRONMENT} WORKDIR ${APP_HOME} +{% if cookiecutter.use_docker == "y" %} +# devcontainer dependencies and utils +RUN apt-get update && apt-get install --no-install-recommends -y \ + sudo git bash-completion nano ssh + +# Create devcontainer user and add it to sudoers +RUN groupadd --gid 1000 dev-user \ + && useradd --uid 1000 --gid dev-user --shell /bin/bash --create-home dev-user \ + && echo dev-user ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/dev-user \ + && chmod 0440 /etc/sudoers.d/dev-user +{% endif %} + # Install required system dependencies RUN apt-get update && apt-get install --no-install-recommends -y \ # psycopg2 dependencies @@ -49,7 +61,7 @@ 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/ + && rm -rf /wheels/ COPY ./compose/production/django/entrypoint /entrypoint RUN sed -i 's/\r$//g' /entrypoint diff --git a/{{cookiecutter.project_slug}}/docs/index.rst b/{{cookiecutter.project_slug}}/docs/index.rst index cb4cbaed..10b1c936 100644 --- a/{{cookiecutter.project_slug}}/docs/index.rst +++ b/{{cookiecutter.project_slug}}/docs/index.rst @@ -10,7 +10,7 @@ Welcome to {{ cookiecutter.project_name }}'s documentation! :maxdepth: 2 :caption: Contents: - howto{% if cookiecutter.use_pycharm == 'y' %} + howto{% if cookiecutter.editor == 'PyCharm' %} pycharm/configuration{% endif %} users