From a55a4c65b2764072f71695463b495f42e8791e8f Mon Sep 17 00:00:00 2001 From: Bastien Gautier Date: Fri, 27 Oct 2023 00:41:47 +0200 Subject: [PATCH] New python dev container (#2) New version for app-python & azure-function-python --- .devcontainer/check-post-install.sh | 26 --- .devcontainer/devcontainer.json | 36 ---- .devcontainer/install-deps.sh | 11 +- .devcontainer/install.sh | 132 ++++----------- .devcontainer/install/01-project-review.sh | 71 ++++++++ .../install/02-fix-vscode-settings-file.sh | 25 +++ ...03-generate-file-vscode-extensions-json.sh | 53 ++++++ .../05-configure-virtual-environment.sh | 21 +++ .../07-initialize-new-packages-app-python.sh | 72 ++++++++ ...lize-new-packages-azure-function-python.sh | 68 ++++++++ .devcontainer/install/10-configure-git.sh | 81 +++++++++ ...-update-pip-identify-dependency-manager.sh | 28 ++++ ...20-configure-additional-package-indexes.sh | 53 ++++++ .../27-install-requirements-app-python.sh | 98 +++++++++++ ...tall-requirements-azure-function-python.sh | 158 ++++++++++++++++++ .../install/28-install-requirements-dev.sh | 101 +++++++++++ .../install/29-install-requirements-test.sh | 71 ++++++++ .../30-configure-project-with-poetry.sh | 30 ++++ .../35-check-dependency-manager-version.sh | 23 +++ .../install/40-check-pyright-version.sh | 33 ++++ .../install/45-install-pre-commit.sh | 118 +++++++++++++ .../install/55-configure-vscode-tools.sh | 36 ++++ .../60-generate-file-vscode-settings-json.sh | 28 ++++ .../install/70-generate-file-vimrc.sh | 31 ++++ .../install/75-generate-file-bashrc.sh | 54 ++++++ .../76-generate-file-vscode-launch-json.sh | 54 ++++++ .../77-generate-file-vscode-tasks-json.sh | 53 ++++++ .../80-initialize-unit-testing-structure.sh | 62 +++++++ .../install/85-extension-installation.sh | 89 ++++++++++ .../install/86-fix-mypy-extension.sh | 91 ++++++++++ .../install/90-clean-installation-file.sh | 67 ++++++++ ...__65-generate-file-vscode-launch-json.sh__ | 23 +++ .devcontainer/project.env | 0 .../{ => templates/app-python}/Dockerfile | 10 +- .../app-python/config/extensions.json | 1 + .../templates/app-python/config/launch.json | 8 + .../app-python/config/requirements.txt | 0 .../templates/app-python/config/tasks.json | 1 + .../templates/app-python/devcontainer.json | 73 ++++++++ .../app-python/new_package/__init__.py | 0 .../app-python/new_package/__main__.py | 6 + .../app-python/new_package/application.py | 25 +++ .../templates/app-python/new_package/py.typed | 0 .../templates/app-python/unittest/__init__.py | 0 .../app-python/unittest/test_application.py | 20 +++ .../azure-function-python/Dockerfile | 28 ++++ .../config/extensions.json | 4 + .../azure-function-python/config/launch.json | 10 ++ .../config/requirements.txt | 1 + .../azure-function-python/config/tasks.json | 12 ++ .../azure-function-python/devcontainer.json | 80 +++++++++ .../new_package/.funcignore | 11 ++ .../new_package/__init__.py | 0 .../new_package/function_app.py | 13 ++ .../new_package/host.json | 15 ++ .../new_package/local.settings.json | 8 + .../templates/default/config/extensions.json | 7 + .../templates/default/config/launch.json | 10 ++ .../default/config/requirements-dev.txt | 7 + .../default/config/requirements-test.txt | 2 + .../templates/default/config/requirements.txt | 0 .../templates/default/config/tasks.json | 86 ++++++++++ .github/workflows/control.yaml | 27 ++- .gitignore | 5 + .vscode/extensions.json | 14 -- .vscode/launch.json | 15 -- .vscode/settings.json | 72 -------- .vscode/tasks.json | 27 --- README.md | 32 +--- docs/README.md | 3 + .../00-configure-new-project.md | 18 ++ ...01-duplicate-configuration-example-file.md | 20 +++ ...-configure-packaging-dependency-manager.md | 48 ++++++ .../03-configure-a-formatter.md | 62 +++++++ .../04-configure-unit-tests.md | 48 ++++++ .../05-configure-code-coverage.md | 34 ++++ .../06-configure-pre-commit.md | 34 ++++ .../07-information-linters.md | 16 ++ .../99-configure-end.md | 4 + docs/install-local-environment.md | 72 ++++++++ my_project/main.py | 28 ---- pyrightconfig.json | 4 + requirements-dev.txt | 6 - requirements.txt | 1 - setup.cfg | 19 +-- tools/color.sh | 10 ++ tools/execute-build-package.sh | 22 +++ tools/execute-configuration-check.sh | 32 ++++ tools/execute-linters.sh | 87 +++++----- tools/execute-purge-python-cache.sh | 26 +++ tools/execute-reset-project.sh | 41 +++++ tools/execute-tests.sh | 94 +++++++++++ tools/install-extensions.sh | 36 ++++ tools/pip-upgrade-all-python-package.sh | 18 -- tools/upgrade-all-python-package.sh | 95 +++++++++++ 95 files changed, 2953 insertions(+), 452 deletions(-) delete mode 100755 .devcontainer/check-post-install.sh delete mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/install/01-project-review.sh create mode 100755 .devcontainer/install/02-fix-vscode-settings-file.sh create mode 100755 .devcontainer/install/03-generate-file-vscode-extensions-json.sh create mode 100755 .devcontainer/install/05-configure-virtual-environment.sh create mode 100755 .devcontainer/install/07-initialize-new-packages-app-python.sh create mode 100755 .devcontainer/install/07-initialize-new-packages-azure-function-python.sh create mode 100755 .devcontainer/install/10-configure-git.sh create mode 100755 .devcontainer/install/15-update-pip-identify-dependency-manager.sh create mode 100755 .devcontainer/install/20-configure-additional-package-indexes.sh create mode 100755 .devcontainer/install/27-install-requirements-app-python.sh create mode 100755 .devcontainer/install/27-install-requirements-azure-function-python.sh create mode 100755 .devcontainer/install/28-install-requirements-dev.sh create mode 100755 .devcontainer/install/29-install-requirements-test.sh create mode 100755 .devcontainer/install/30-configure-project-with-poetry.sh create mode 100755 .devcontainer/install/35-check-dependency-manager-version.sh create mode 100755 .devcontainer/install/40-check-pyright-version.sh create mode 100755 .devcontainer/install/45-install-pre-commit.sh create mode 100755 .devcontainer/install/55-configure-vscode-tools.sh create mode 100755 .devcontainer/install/60-generate-file-vscode-settings-json.sh create mode 100755 .devcontainer/install/70-generate-file-vimrc.sh create mode 100755 .devcontainer/install/75-generate-file-bashrc.sh create mode 100755 .devcontainer/install/76-generate-file-vscode-launch-json.sh create mode 100755 .devcontainer/install/77-generate-file-vscode-tasks-json.sh create mode 100755 .devcontainer/install/80-initialize-unit-testing-structure.sh create mode 100755 .devcontainer/install/85-extension-installation.sh create mode 100755 .devcontainer/install/86-fix-mypy-extension.sh create mode 100644 .devcontainer/install/90-clean-installation-file.sh create mode 100755 .devcontainer/install/__65-generate-file-vscode-launch-json.sh__ create mode 100644 .devcontainer/project.env rename .devcontainer/{ => templates/app-python}/Dockerfile (72%) create mode 100644 .devcontainer/templates/app-python/config/extensions.json create mode 100644 .devcontainer/templates/app-python/config/launch.json create mode 100644 .devcontainer/templates/app-python/config/requirements.txt create mode 100644 .devcontainer/templates/app-python/config/tasks.json create mode 100644 .devcontainer/templates/app-python/devcontainer.json create mode 100644 .devcontainer/templates/app-python/new_package/__init__.py create mode 100644 .devcontainer/templates/app-python/new_package/__main__.py create mode 100644 .devcontainer/templates/app-python/new_package/application.py create mode 100644 .devcontainer/templates/app-python/new_package/py.typed create mode 100644 .devcontainer/templates/app-python/unittest/__init__.py create mode 100644 .devcontainer/templates/app-python/unittest/test_application.py create mode 100644 .devcontainer/templates/azure-function-python/Dockerfile create mode 100644 .devcontainer/templates/azure-function-python/config/extensions.json create mode 100644 .devcontainer/templates/azure-function-python/config/launch.json create mode 100644 .devcontainer/templates/azure-function-python/config/requirements.txt create mode 100644 .devcontainer/templates/azure-function-python/config/tasks.json create mode 100644 .devcontainer/templates/azure-function-python/devcontainer.json create mode 100644 .devcontainer/templates/azure-function-python/new_package/.funcignore create mode 100644 .devcontainer/templates/azure-function-python/new_package/__init__.py create mode 100644 .devcontainer/templates/azure-function-python/new_package/function_app.py create mode 100644 .devcontainer/templates/azure-function-python/new_package/host.json create mode 100644 .devcontainer/templates/azure-function-python/new_package/local.settings.json create mode 100644 .devcontainer/templates/default/config/extensions.json create mode 100644 .devcontainer/templates/default/config/launch.json create mode 100644 .devcontainer/templates/default/config/requirements-dev.txt create mode 100644 .devcontainer/templates/default/config/requirements-test.txt create mode 100644 .devcontainer/templates/default/config/requirements.txt create mode 100644 .devcontainer/templates/default/config/tasks.json delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/settings.json delete mode 100644 .vscode/tasks.json create mode 100644 docs/README.md create mode 100644 docs/dev-container-configuration/00-configure-new-project.md create mode 100644 docs/dev-container-configuration/01-duplicate-configuration-example-file.md create mode 100644 docs/dev-container-configuration/02-configure-packaging-dependency-manager.md create mode 100644 docs/dev-container-configuration/03-configure-a-formatter.md create mode 100644 docs/dev-container-configuration/04-configure-unit-tests.md create mode 100644 docs/dev-container-configuration/05-configure-code-coverage.md create mode 100644 docs/dev-container-configuration/06-configure-pre-commit.md create mode 100644 docs/dev-container-configuration/07-information-linters.md create mode 100644 docs/dev-container-configuration/99-configure-end.md create mode 100644 docs/install-local-environment.md delete mode 100644 my_project/main.py create mode 100644 pyrightconfig.json delete mode 100644 requirements-dev.txt delete mode 100644 requirements.txt create mode 100755 tools/color.sh create mode 100755 tools/execute-build-package.sh create mode 100755 tools/execute-configuration-check.sh create mode 100755 tools/execute-purge-python-cache.sh create mode 100755 tools/execute-reset-project.sh create mode 100755 tools/execute-tests.sh create mode 100755 tools/install-extensions.sh delete mode 100644 tools/pip-upgrade-all-python-package.sh create mode 100755 tools/upgrade-all-python-package.sh diff --git a/.devcontainer/check-post-install.sh b/.devcontainer/check-post-install.sh deleted file mode 100755 index 4d3bb6d..0000000 --- a/.devcontainer/check-post-install.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash - -RED="\e[31m" -GREEN="\e[32m" -BLUE="\e[34m" -ENDCOLOR="\e[0m" - -echo -e "\n${BLUE}#####################################" -echo -e "${BLUE}#### CHECK-POST-INSTALL.SH ####${ENDCOLOR}" -echo -e "${BLUE}#####################################" - -source /workspaces/app/.venv/bin/activate - -if which pip >/dev/null; then - echo -e "\n${GREEN}> Display PIP info/version.${ENDCOLOR}\n" - pip --version -fi - -if which poetry >/dev/null; then - echo -e "\n${GREEN}> Display POETRY info/version.${ENDCOLOR}\n" - poetry --version -fi - -# ADD [here] your other verification todo. - -echo -e "" diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 814a9a7..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,36 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/python-3 -{ - "name": "Python 3", - "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/app,type=bind", - "workspaceFolder": "/workspaces/app", - "build": { - "dockerfile": "Dockerfile", - "context": "..", - "args": { - // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 - // Append -bullseye or -buster to pin to an OS version. - // Use -bullseye variants on local on arm64/Apple Silicon. - "VARIANT": "3.10-bullseye", - // Options - "NODE_VERSION": "lts/*" - } - }, - // Configure tool-specific properties. - "customizations": { - // Configure properties specific to VS Code. - "vscode": { - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "ms-python.python", - "ms-python.vscode-pylance" - ] - } - }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "sudo chmod +x /workspaces/app/.devcontainer/install.sh && /workspaces/app/.devcontainer/install.sh", - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. - "remoteUser": "vscode" -} diff --git a/.devcontainer/install-deps.sh b/.devcontainer/install-deps.sh index 50caa50..e090544 100644 --- a/.devcontainer/install-deps.sh +++ b/.devcontainer/install-deps.sh @@ -1,13 +1,4 @@ #!/bin/bash sudo apt-get update -sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install vim - -# Install the Microsoft ODBC driver for SQL Server (Linux) -# https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server - -# curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - -# curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list -# sudo apt-get update -# sudo ACCEPT_EULA=Y apt-get install -y msodbcsql18 -# Notice: Add pyodbc in requirements.txt or pyproject.toml file. +sudo DEBIAN_FRONTEND=noninteractive apt-get -yq install vim dos2unix diff --git a/.devcontainer/install.sh b/.devcontainer/install.sh index 598bf80..6d59f33 100755 --- a/.devcontainer/install.sh +++ b/.devcontainer/install.sh @@ -1,121 +1,51 @@ #!/bin/bash -RED="\e[31m" -GREEN="\e[32m" -BLUE="\e[34m" -YELLOW="\e[33m" -ENDCOLOR="\e[0m" +. "$WORKSPACE_PATH/tools/color.sh" -echo -e "\n${BLUE}################################" -echo -e "${BLUE}#### INSTALL.SH ####${ENDCOLOR}" -echo -e "${BLUE}################################" +cd "$WORKSPACE_PATH/.devcontainer/install" -echo -e "\n${GREEN}> Configure virtual environment.${ENDCOLOR}\n" +CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +echo -e "\n${BLUE}You are about to initiate a project '${YELLOW}$CONTAINER_TYPE${BLUE}'.${ENDCOLOR}" -sudo chgrp vscode /workspaces/app/.venv -sudo chown vscode /workspaces/app/.venv - -git config --global --add safe.directory /workspaces/app -git config --global core.eol lf -git config --global core.autocrlf false - -python3 -m venv /workspaces/app/.venv -PATH="/workspaces/app/.venv/bin:$PATH" - -echo -e "Done.\n" - -echo -e "${GREEN}> Update PIP tool.${ENDCOLOR}\n" -pip install --upgrade pip - -echo -e "\n${GREEN}> Identify the packaging and dependency manager to install.${ENDCOLOR}\n" - -PIP_MANAGER=false -POETRY_MANAGER=false - -NEW_POETRY_INSTALL=false - -FILE=/workspaces/app/requirements.txt - -if [ -f "$FILE" ]; -then - echo -e "PIP configuration file was found (requirements.txt).\n" - PIP_MANAGER=true -fi - -FILE=/workspaces/app/pyproject.toml - -if [ -f "$FILE" ]; -then - echo -e "POETRY configuration file was found (pyproject.toml).${ENDCOLOR}" - POETRY_MANAGER=true -fi - -if [ "$POETRY_MANAGER" = true ] && [ "$PIP_MANAGER" = true ]; -then - echo -e "${RED}> ERROR: You cannot define two packaging and dependency manager in the same time.${ENDCOLOR}\n" +if [ ]; then + echo -e "\nπŸ’₯ ${RED}Installation was aborted. Specified container type '$CONTAINER_TYPE' is not supported.${ENDCOLOR}πŸ’₯\n" exit 1 fi -if [ "$POETRY_MANAGER" = false ] && [ "$PIP_MANAGER" = false ]; -then - - echo -e "${YELLOW}No packaging and dependency manager was found.${ENDCOLOR}" - echo -e "${YELLOW}Type 'PIP' or 'POETRY' if you want to install a packaging and dependency manager !${ENDCOLOR}" - echo -e "${YELLOW}Another option will install no packaging and dependency manager.${ENDCOLOR}" - echo -e "${YELLOW}Your selection :${ENDCOLOR}" +install_files=() - read MANAGER - echo -e "The following packaging and dependency manager will be installed : $MANAGER\n" - - if [ "${MANAGER^^}" = "POETRY" ] - then - POETRY_MANAGER=true - NEW_POETRY_INSTALL=true +while IFS= read -r -d $'\0' current_file ; do + if ! grep -q "# ignored: $CONTAINER_TYPE" "$current_file"; then + install_files+=("$current_file") fi +done < <(find ./ -type f -name "*.sh" -print0) - if [ "${MANAGER^^}" = "PIP" ] - then - PIP_MANAGER=true - touch /workspaces/app/requirements.txt - touch /workspaces/app/requirements-dev.txt - fi +install_files=($(printf "%s\n" "${install_files[@]}" | sort)) -fi - -source /workspaces/app/.venv/bin/activate +echo -e "${BLUE}We have found ${YELLOW}${#install_files[@]}${BLUE} installation files.${ENDCOLOR}" -if [ "$PIP_MANAGER" = true ]; -then +for install_file in "${install_files[@]}"; do + if [ -f "$install_file" ]; then - echo -e "${GREEN}> Install dependencies with PIP.${ENDCOLOR}\n" + echo -e "\n\e[104m Execute: $install_file \e[49m" - # pip install keyring artifacts-keyring + if [ ! -x "$install_file" ]; then + chmod +x "$install_file" + fi - # cat <> /workspaces/app/.venv/pip.conf - # [global] - # extra-index-url=https://pkgs.dev.azure.com/... - # EOF + ./"$install_file" - pip install -r /workspaces/app/requirements-dev.txt - pip install -r /workspaces/app/requirements.txt + if [ "$?" -ge 1 ]; then + echo -e "\nπŸ’₯ ${RED}Installation was aborted. Check the errors displayed above.${ENDCOLOR}πŸ’₯\n" + exit 1 + else + echo -e "${YELLOW}... Press any key to continue ..." + read -s -p " " -n 1 -r + echo -e "${ENDCOLOR}" + fi -fi - -if [ "$POETRY_MANAGER" = true ]; -then - - echo -e "${GREEN}> Install POETRY tool and install dependencies.${ENDCOLOR}\n" - curl -sSL https://install.python-poetry.org | python3 - - poetry completions bash >> ~/.bash_completion - - if [ "$POETRY_MANAGER" = true ]; - then - poetry init fi +done - poetry install - -fi - -chmod +x /workspaces/app/.devcontainer/check-post-install.sh -/workspaces/app/.devcontainer/check-post-install.sh +echo -e "πŸŽ‰ ${YELLOW}Installation is finished!${ENDCOLOR}" +echo -e "πŸŽ‰ ${YELLOW}You can close all terminal windows and reload the project!${ENDCOLOR}\n" diff --git a/.devcontainer/install/01-project-review.sh b/.devcontainer/install/01-project-review.sh new file mode 100755 index 0000000..ab686ea --- /dev/null +++ b/.devcontainer/install/01-project-review.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Project review #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Project review.${ENDCOLOR}\n" + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +PRE_COMMIT_ENABLED=$(jq -r '.customizations.vscode.settings."git.preCommitEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +PYTEST_ENABLED=$(jq -r '.customizations.vscode.settings."python.testing.pytestEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +UNITTEST_ENABLED=$(jq -r '.customizations.vscode.settings."python.testing.unittestEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +COVERAGE_ENABLED=$(jq -r '.customizations.vscode.settings."python.testing.coverageEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); +FORMATTER=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +PIP_EXTRA_INDEX_URL=$(jq -r '.customizations.vscode.settings."python.pip.extraIndexUrl"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +echo -e "The dependency manager used for the project is ${YELLOW}$DEPENDENCY_MANAGER${ENDCOLOR}." +echo -e "The formatter used for the project is ${YELLOW}$FORMATTER${ENDCOLOR}.\n" + +if [ "$PIP_EXTRA_INDEX_URL" = "" ] || [ "$PIP_EXTRA_INDEX_URL" = "null" ]; then + echo -e "No additional package indexes is configured.\n" +else + echo -e "An additional package indexes is specified (value: ${YELLOW}$PIP_EXTRA_INDEX_URL${ENDCOLOR}).\n" +fi + +if [ $PRE_COMMIT_ENABLED = "true" ]; then + echo -e "βœ”οΈγ…€Pre-commit" +else + echo -e "βœ–οΈγ…€Pre-commit" +fi + +if [ $PYTEST_ENABLED = "true" ]; then + echo -e "βœ”οΈγ…€Pytest" +else + echo -e "βœ–οΈγ…€Pytest" +fi + +if [ $UNITTEST_ENABLED = "true" ]; then + echo -e "βœ”οΈγ…€Unittest" +else + echo -e "βœ–οΈγ…€Unittest" +fi + +if [ $COVERAGE_ENABLED = "true" ]; then + echo -e "βœ”οΈγ…€Code coverage" +else + echo -e "βœ–οΈγ…€Code coverage" +fi + +if [ "$DEPENDENCY_MANAGER" != "pip" ] && [ "$DEPENDENCY_MANAGER" != "poetry" ]; then + echo -e "\n${RED}No correct packaging and dependency manager is configured.${ENDCOLOR}" + echo -e "${RED}Only 'pip' and 'poetry' managers are supported.${ENDCOLOR}" + STOP=true +fi + +if [ "$FORMATTER" != "eeyore.yapf" ] && [ "$FORMATTER" != "ms-python.black-formatter" ]; then + echo -e "\n${RED}No correct formatter is configured.${ENDCOLOR}" + echo -e "${RED}Only 'eeyore.yapf' and 'ms-python.black-formatter' formaters are supported.${ENDCOLOR}" + STOP=true +fi + +if [ "$STOP" == "true" ]; then + exit 1 +fi + +echo -e "" diff --git a/.devcontainer/install/02-fix-vscode-settings-file.sh b/.devcontainer/install/02-fix-vscode-settings-file.sh new file mode 100755 index 0000000..d0aace5 --- /dev/null +++ b/.devcontainer/install/02-fix-vscode-settings-file.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Fix VSCode Settings File #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Fix VSCode Settings File.${ENDCOLOR}\n" + +jq 'del(.["python.formatting.autopep8Path", + "python.formatting.blackPath", + "python.linting.flake8Path", + "python.linting.flake8Enabled", + "python.linting.mypyPath", + "python.linting.mypyEnabled", + "python.linting.pylintPath", + "python.linting.pylintEnabled" +])' $HOME/.vscode-server/data/Machine/settings.json > /tmp/.vscode-server-settings.json + +mv /tmp/.vscode-server-settings.json $HOME/.vscode-server/data/Machine/settings.json + +echo -e "File '$HOME/.vscode-server/data/Machine/settings.json' has been modified to fix slow issue with linters.\n" diff --git a/.devcontainer/install/03-generate-file-vscode-extensions-json.sh b/.devcontainer/install/03-generate-file-vscode-extensions-json.sh new file mode 100755 index 0000000..252c2d5 --- /dev/null +++ b/.devcontainer/install/03-generate-file-vscode-extensions-json.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate .vscode/extensions.json file #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +mkdir -p $SOURCE_PATH +mkdir "$WORKSPACE_PATH/.vscode" + +if [ -f "$WORKSPACE_PATH/.vscode/extensions.json" ]; then + echo -e "\n${YELLOW}Nothing to do because file is already existing.\n${ENDCOLOR}" +else + + echo -e "\n${GREEN}> Generate file '.vscode/extensions.json'.${ENDCOLOR}\n" + + # Create initial file. + merged_content=$(echo '{"recommendations": []}' | jq '.') + + # Add extensions specifically for the container type. + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/extensions.json" ]; then + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/extensions.json) + merged_content=$(echo "$merged_content" | jq ".recommendations += $content") + fi + + # Add extensions specifically for the default formatter. + defaultFormatter=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ "$defaultFormatter" == "ms-python.autopep8" ] || [ "$defaultFormatter" == "ms-python.black-formatter" ] || [ "$defaultFormatter" == "eeyore.yapf" ]; then + content="[\"$defaultFormatter\"]" + merged_content=$(echo "$merged_content" | jq ".recommendations += $content") + fi + + # Add other extensions. + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/extensions.json" ]; then + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/default/config/extensions.json) + merged_content=$(echo "$merged_content" | jq ".recommendations += $content") + fi + + # Pretty-print the content and create the file. + formatted_content=$(echo $merged_content | jq -c '.') + echo $formatted_content | python -m json.tool --indent=2 > "$WORKSPACE_PATH/.vscode/extensions.json" + + echo -e "Done.\n" + +fi diff --git a/.devcontainer/install/05-configure-virtual-environment.sh b/.devcontainer/install/05-configure-virtual-environment.sh new file mode 100755 index 0000000..6e90c7f --- /dev/null +++ b/.devcontainer/install/05-configure-virtual-environment.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Configure virtual environment #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Configure virtual environment.${ENDCOLOR}\n" + +sudo chgrp vscode $WORKSPACE_PATH/.venv +sudo chown vscode $WORKSPACE_PATH/.venv + +python3 -m venv $WORKSPACE_PATH/.venv +PATH="$WORKSPACE_PATH/.venv/bin:$PATH" + +mkdir -p $WORKSPACE_PATH/.venv/lib + +echo -e "Done\n" diff --git a/.devcontainer/install/07-initialize-new-packages-app-python.sh b/.devcontainer/install/07-initialize-new-packages-app-python.sh new file mode 100755 index 0000000..76fb8dc --- /dev/null +++ b/.devcontainer/install/07-initialize-new-packages-app-python.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# ignored: azure-function-python +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Initialize new packages (app python) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +mkdir -p $SOURCE_PATH + +if [ "$(ls -A "$SOURCE_PATH" | wc -l)" -ge 1 ]; then + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +else + + quantity=0 + + while true; do + + if [ "$quantity" -eq 0 ]; then + echo -e "\n${YELLOW}Do you want to create a package? (y/n)${ENDCOLOR}" + read -p "> " choice + else + echo -e "\n${YELLOW}Do you want to create another package? (y/n)${ENDCOLOR}" + read -p "> " choice + fi + + case "$choice" in + [Yy]*) + + echo -e "\n${YELLOW}How do you name the package?${ENDCOLOR}" + read -p "> " package_name + + if [ -z "$package_name" ]; then + echo -e "\n${RED}The package name cannot be empty. Please enter a valid name.${ENDCOLOR}" + elif [ -d "$SOURCE_PATH/$package_name" ]; then + echo -e "\n${RED}This package has already been created.${ENDCOLOR}" + else + + mkdir -p "$SOURCE_PATH/$package_name" + cp -r "$WORKSPACE_PATH/.devcontainer/templates/app-python/new_package/"* "$SOURCE_PATH/$package_name" + + files=(`ls $SOURCE_PATH/$package_name/*.py`) + + for file in "${files[@]}"; do + sed -i "s/{package_name}/$package_name/" "$file" + done + + quantity=$((quantity + 1)) + + fi + ;; + [Nn]*) break ;; + *) echo -e "\n${RED}Please answer with 'y' (yes) or 'n' (no).${ENDCOLOR}" ;; + + esac + + done + + if [ "$quantity" -ge 1 ]; then + echo -e "\n${BLUE}You have created $quantity package(s).${ENDCOLOR}" + fi + +fi + +echo -e "" diff --git a/.devcontainer/install/07-initialize-new-packages-azure-function-python.sh b/.devcontainer/install/07-initialize-new-packages-azure-function-python.sh new file mode 100755 index 0000000..edcfeda --- /dev/null +++ b/.devcontainer/install/07-initialize-new-packages-azure-function-python.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# ignored: app-python +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Initialize new packages (azure function) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +if [ "$(ls -A "$SOURCE_PATH" | wc -l)" -ge 1 ]; then + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +else + + quantity=0 + + while true; do + + if [ "$quantity" -eq 0 ]; then + echo -e "\n${YELLOW}Do you want to create a package? (y/n)${ENDCOLOR}" + read -p "> " choice + else + echo -e "\n${YELLOW}Do you want to create another package? (y/n)${ENDCOLOR}" + read -p "> " choice + fi + + case "$choice" in + [Yy]*) + + mkdir -p $SOURCE_PATH + + echo -e "\n${YELLOW}How do you name the package?${ENDCOLOR}" + read -p "> " package_name + + if [ -z "$package_name" ]; then + echo -e "\n${RED}The package name cannot be empty. Please enter a valid name.${ENDCOLOR}" + elif [ -d "$SOURCE_PATH/$package_name" ]; then + echo -e "\n${RED}This package has already been created.${ENDCOLOR}" + else + + mkdir -p "$SOURCE_PATH/$package_name" + + cp -r "$WORKSPACE_PATH/.devcontainer/templates/azure-function-python/new_package/"* "$SOURCE_PATH/$package_name" + ln -s "$WORKSPACE_PATH/.venv" "$SOURCE_PATH/$package_name/.venv" + + quantity=$((quantity + 1)) + + fi + ;; + [Nn]*) break ;; + *) echo -e "\n${RED}Please answer with 'y' (yes) or 'n' (no).${ENDCOLOR}" ;; + + esac + + done + + if [ "$quantity" -ge 1 ]; then + echo -e "\n${BLUE}You have created $quantity package(s).${ENDCOLOR}" + fi + +fi + +echo -e "" diff --git a/.devcontainer/install/10-configure-git.sh b/.devcontainer/install/10-configure-git.sh new file mode 100755 index 0000000..f9cbfac --- /dev/null +++ b/.devcontainer/install/10-configure-git.sh @@ -0,0 +1,81 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Configure Git #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Configure Git.${ENDCOLOR}\n" + +if [ ! -f "$WORKSPACE_PATH/.devcontainer/user.env" ]; then + touch "$WORKSPACE_PATH/.devcontainer/user.env" +fi + +source "$WORKSPACE_PATH/.devcontainer/user.env" + +if [ "$GIT_USERNAME" = "" ]; then + + while true; do + + echo -e "${YELLOW}What is your name (format: Firstname Lastname)?${ENDCOLOR}" + read -p "> " git_name + + if [ -z "$git_name" ]; then + echo -e "\n${RED}Your name cannot be empty. Please enter a valid name.${ENDCOLOR}\n" + else + echo "GIT_USERNAME=\"$git_name\"" >> "$WORKSPACE_PATH/.devcontainer/user.env" + break + fi + + done + +fi + +if [ "$GIT_EMAIL" = "" ]; then + + while true; do + + echo -e "\n${YELLOW}What is your email?${ENDCOLOR}" + read -p "> " git_email + echo "" + + if [ -z "$git_email" ]; then + echo -e "${RED}Your email cannot be empty. Please enter a valid email.${ENDCOLOR}" + else + echo "GIT_EMAIL=\"$git_email\"" >> "$WORKSPACE_PATH/.devcontainer/user.env" + break + fi + + done + +fi +set -o allexport +source "$WORKSPACE_PATH/.devcontainer/user.env" +set +o allexport + +git config --global --add safe.directory "$WORKSPACE_PATH" +git config --global core.eol lf +git config --global core.autocrlf false +git config --global pull.rebase true + +git config --global user.name "$GIT_USERNAME" +git config --global user.email "$GIT_EMAIL" + +echo -e "Done" + +echo -e "\n${GREEN}> Update '.bashrc' file.${ENDCOLOR}\n" + +script_name=$(basename "$0" | tr '[:lower:]' '[:upper:]') + +sed -i "/# \[START\]:GENERATED_FROM_$script_name/,/# \[END\]:GENERATED_FROM_$script_name/d" ~/.bashrc + +cat <>~/.bashrc +# [START]:GENERATED_FROM_$script_name +source "\$WORKSPACE_PATH/.devcontainer/user.env" +# [END]:GENERATED_FROM_$script_name +EOF + +echo -e "Done\n" diff --git a/.devcontainer/install/15-update-pip-identify-dependency-manager.sh b/.devcontainer/install/15-update-pip-identify-dependency-manager.sh new file mode 100755 index 0000000..230c272 --- /dev/null +++ b/.devcontainer/install/15-update-pip-identify-dependency-manager.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Update pip / Identify dependency manager #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +echo -e "\n${GREEN}> Update pip tool.${ENDCOLOR}\n" +pip install --upgrade pip + +echo -e "\n${GREEN}> Identify the packaging and dependency manager to install.${ENDCOLOR}\n" + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" != "" ]; then + echo -e "The dependency manager used for the project is ${YELLOW}$DEPENDENCY_MANAGER${ENDCOLOR}.\n" +fi + +if [ "$DEPENDENCY_MANAGER" != "pip" ] && [ "$DEPENDENCY_MANAGER" != "poetry" ]; then + echo -e "${RED}No correct packaging and dependency manager was configured.${ENDCOLOR}" + echo -e "${RED}Only pip and poetry manager are supported.${ENDCOLOR}\n" + exit 1 +fi diff --git a/.devcontainer/install/20-configure-additional-package-indexes.sh b/.devcontainer/install/20-configure-additional-package-indexes.sh new file mode 100755 index 0000000..2ddd46b --- /dev/null +++ b/.devcontainer/install/20-configure-additional-package-indexes.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Configure additional package indexes #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +echo -e "\n${GREEN}> Configure additional package indexes.${ENDCOLOR}\n" + +PIP_EXTRA_INDEX_URL=$(jq -r '.customizations.vscode.settings."python.pip.extraIndexUrl"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$PIP_EXTRA_INDEX_URL" != "" ] && [ "$PIP_EXTRA_INDEX_URL" != "null" ]; then + + echo -e "An additional package indexes is specified (value: ${YELLOW}$PIP_EXTRA_INDEX_URL${ENDCOLOR}).\n" + + DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + echo -e "${GREEN}> Update file '.venv/pip.conf'.${ENDCOLOR}\n" + + if [ ! -f "$WORKSPACE_PATH/.venv/pip.conf" ]; then + +cat <"$WORKSPACE_PATH/.venv/pip.conf" +[global] +EOF + + fi + + sed -i "/^\[global\]$/,/^\[/ {/extra-index-url/d}" "$WORKSPACE_PATH/.venv/pip.conf" + sed -i "/^\[global\]$/a extra-index-url=$PIP_EXTRA_INDEX_URL" "$WORKSPACE_PATH/.venv/pip.conf" + + echo -e "Done" + + echo -e "\n${GREEN}> Install pakages needed for the authentification.${ENDCOLOR}\n" + pip install keyring artifacts-keyring + + echo -e "" + + else + echo -e "${RED}Only implemented for the projects using 'pip' as dependency manager.${ENDCOLOR}\n" + fi + + + +else + echo -e "${YELLOW}Nothing to do.${ENDCOLOR}\n" +fi diff --git a/.devcontainer/install/27-install-requirements-app-python.sh b/.devcontainer/install/27-install-requirements-app-python.sh new file mode 100755 index 0000000..d597d46 --- /dev/null +++ b/.devcontainer/install/27-install-requirements-app-python.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# ignored: azure-function-python +# ignored: poetry-dependency-manager + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install requirements.txt (app-python version) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + # INSTALL PACKAGE FROM REQUIREMENTS.TXT + + if [ ! -f "$WORKSPACE_PATH/requirements.txt" ]; then + echo -e "\n${GREEN}> Initialize pip Manager (requirements.txt).${ENDCOLOR}\n" + + echo "" > /tmp/tmp_requirements.txt + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements.txt" >> /tmp/tmp_requirements.txt + fi + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements.txt" >> /tmp/tmp_requirements.txt + fi + + sed -i '/^$/d' /tmp/tmp_requirements.txt + sort -u /tmp/tmp_requirements.txt > $WORKSPACE_PATH/requirements.txt + + rm -f /tmp/tmp_requirements.txt + + echo -e "Pip configuration file was created (requirements.txt)." + + quantity=0 + + echo -e "\n${GREEN}> Install package in the project (requirements.txt).${ENDCOLOR}\n" + + while true; do + + if [ "$quantity" -eq 0 ]; then + echo -e "${BLUE}You can directly edit the 'requirements.txt' file to add packages faster and answer 'no' to the next question.${ENDCOLOR}" + echo -e "\n${YELLOW}Do you want to install a package (pandas, numpy, marshmallow, streamlit, etc.)? (y/n)${ENDCOLOR}" + read -p "> " choice + else + echo -e "\n${YELLOW}Do you want to install another package (pandas, numpy, marshmallow, streamlit, etc.)? (y/n)${ENDCOLOR}" + read -p "> " choice + fi + + case "$choice" in + [Yy]*) + + echo -e "\n${YELLOW}What is the package name?${ENDCOLOR}" + read -p "> " package_name + + echo -e "" + pip install "$package_name" + + if pip show "$package_name" >/dev/null 2>&1; then + echo -e "\n${BLUE}The package '$package_name' is now installed.${ENDCOLOR}" + echo $package_name >> $WORKSPACE_PATH/requirements.txt + else + echo -e "\n${RED}The package '$package_name' cannot be installed.${ENDCOLOR}" + fi + + quantity=$((quantity + 1)) + + ;; + [Nn]*) echo -n ""; break ;; + *) echo -e "\n${RED}Please answer with 'y' (yes) or 'n' (no).${ENDCOLOR}\n" ;; + + esac + + done + + fi + + echo -e "\n${GREEN}> Install dependencies with pip (requirements.txt).${ENDCOLOR}\n" + + if [ -z "$(cat $WORKSPACE_PATH/requirements.txt)" ]; then + echo -e "No package to install" + else + pip install -r $WORKSPACE_PATH/requirements.txt + echo -e "\nDone\n" + fi + +else + echo -e "\n${YELLOW}Nothing to do because PIP is not used.${ENDCOLOR}" +fi diff --git a/.devcontainer/install/27-install-requirements-azure-function-python.sh b/.devcontainer/install/27-install-requirements-azure-function-python.sh new file mode 100755 index 0000000..9b9a8ec --- /dev/null +++ b/.devcontainer/install/27-install-requirements-azure-function-python.sh @@ -0,0 +1,158 @@ +#!/bin/bash + +# ignored: app-python +# ignored: poetry-dependency-manager + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install requirements.txt (app-python version) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + # INSTALL PACKAGE FROM REQUIREMENTS.TXT + + no_existing_file=true + + projects=`find "$SOURCE_PATH" -mindepth 1 -maxdepth 1 -type d -name "*"` + requirements_files=`find "$SOURCE_PATH" -mindepth 2 -maxdepth 2 -type f -name 'requirements.txt'` + + if [ -n "$requirements_files" ]; then + + echo -e "\n${GREEN}> Initialize pip Manager (requirements.txt).${ENDCOLOR}" + echo -e "\n${BLUE}At least one 'requirements.txt' file was found.${ENDCOLOR}\n" + IFS=$'\n' + for file in $requirements_files; do + echo -e "- $file" + done + echo -e "\n${BLUE}These files will be used and not modified. No other file will be created.${ENDCOLOR}" + no_existing_file=false + + if [ "$(echo "$requirements_files" | wc -l)" -ne "$(echo "$projects" | wc -l)" ]; then + echo -e "\n${RED}The number of 'requirements.txt' files should match the number of Azure Function projects.${ENDCOLOR}\n" + fi + + fi + + if [ "$no_existing_file" == true ]; then + + echo -e "\n${GREEN}> Initialize pip Manager (requirements.txt).${ENDCOLOR}\n" + + IFS=$'\n' + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + for project in $projects; do + + echo "" > /tmp/tmp_requirements.txt + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements.txt" >> /tmp/tmp_requirements.txt + fi + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements.txt" >> /tmp/tmp_requirements.txt + fi + + sed -i '/^$/d' /tmp/tmp_requirements.txt + sort -u /tmp/tmp_requirements.txt > $project/requirements.txt + + rm -f /tmp/tmp_requirements.txt + + done + + echo -e "Pip configuration file was created (requirements.txt)." + + quantity=0 + + echo -e "\n${GREEN}> Install package in the project (requirements.txt).${ENDCOLOR}\n" + + requirements_files=`find "$SOURCE_PATH" -mindepth 2 -maxdepth 2 -type f -name 'requirements.txt'` + + for file in $requirements_files; do + cat "$file" >> /tmp/tmp_requirements.txt + done + + sort -u /tmp/tmp_requirements.txt > /tmp/requirements.txt + + if [ ! -z "$(cat /tmp/requirements.txt)" ]; then + echo -e "${BLUE}> The following packages will be automatically installed.${ENDCOLOR}\n" + cat /tmp/requirements.txt + fi + + rm -f /tmp/requirements.txt /tmp/tmp_requirements.txt + + while true; do + + if [ "$quantity" -eq 0 ]; then + echo -e "\n${BLUE}You can directly edit the 'requirements.txt' files to add packages faster and answer 'no' to the next question.${ENDCOLOR}" + echo -e "\n${YELLOW}Do you want to install a package (pandas, numpy, snowflake-connector-python, streamlit, etc.)? (y/n)${ENDCOLOR}" + read -p "> " choice + else + echo -e "\n${YELLOW}Do you want to install another package (pandas, numpy, marshmallow, streamlit, etc.)? (y/n)${ENDCOLOR}" + read -p "> " choice + fi + + case "$choice" in + [Yy]*) + + echo -e "\n${YELLOW}What is the package name?${ENDCOLOR}" + read -p "> " package_name + + echo -e "" + pip install "$package_name" + + if pip show "$package_name" >/dev/null 2>&1; then + echo -e "\n${BLUE}The package '$package_name' is now installed.${ENDCOLOR}" + + for project in $projects; do + echo $package_name >> $project/requirements.txt + done + + else + echo -e "\n${RED}The package '$package_name' cannot be installed.${ENDCOLOR}" + fi + + quantity=$((quantity + 1)) + + ;; + [Nn]*) echo -n ""; break ;; + *) echo -e "\n${RED}Please answer with 'y' (yes) or 'n' (no).${ENDCOLOR}\n" ;; + + esac + + done + + fi + + echo -e "\n${GREEN}> Install dependencies with pip (requirements.txt).${ENDCOLOR}\n" + + echo "" > /tmp/requirements.txt + echo "" > /tmp/tmp_requirements.txt + + requirements_files=`find "$SOURCE_PATH" -mindepth 2 -maxdepth 2 -type f -name 'requirements.txt'` + + for file in $requirements_files; do + cat "$file" >> /tmp/tmp_requirements.txt + done + + sort -u /tmp/tmp_requirements.txt > /tmp/requirements.txt + + if [ -z "$(cat /tmp/requirements.txt)" ]; then + echo -e "No package to install\n" + else + pip install -r /tmp/requirements.txt + echo -e "\nDone\n" + fi + + rm -f /tmp/requirements.txt /tmp/tmp_requirements.txt + +else + echo -e "\n${YELLOW}Nothing to do because PIP is not used.${ENDCOLOR}" +fi diff --git a/.devcontainer/install/28-install-requirements-dev.sh b/.devcontainer/install/28-install-requirements-dev.sh new file mode 100755 index 0000000..a6188c0 --- /dev/null +++ b/.devcontainer/install/28-install-requirements-dev.sh @@ -0,0 +1,101 @@ +#!/bin/bash + +# ignored: poetry-dependency-manager + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install requirements.txt (dev) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + # PREPARE PRE-COMMIT PACKAGE IF NEEDED. + + echo -e "\n${GREEN}> Pre-commit status.${ENDCOLOR}\n" + + PRE_COMMIT_ENABLED=$(jq -r '.customizations.vscode.settings."git.preCommitEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ $PRE_COMMIT_ENABLED = "true" ]; then + echo -e "Pre-commit will be activated for the project." + else + echo -e "Pre-commit won't be activated for the project." + fi + + # PREPARE FORMATTER PACKAGE IF NEEDED. + + echo -e "\n${GREEN}> Identity the Python formatter to use from devcontainer.json.${ENDCOLOR}\n" + + defaultFormatter=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + formatter_package="" + + if [ "$defaultFormatter" = "ms-python.black-formatter" ]; then + formatter_package="black" + fi + + if [ "$defaultFormatter" = "eeyore.yapf" ]; then + formatter_package="yapf" + fi + + if [ "$defaultFormatter" = "ms-python.autopep8" ]; then + formatter_package="autopep8" + fi + + if [ "$defaultFormatter" = "null" ]; then + echo -e "${RED}No Python formatter specified (not recommended).${ENDCOLOR}" + else + echo -e "Python formatter used in the project : '$defaultFormatter'" + fi + + if [ ! -f "$WORKSPACE_PATH/requirements-dev.txt" ]; then + echo -e "\n${GREEN}> Initialize pip Manager (requirements-dev.txt).${ENDCOLOR}\n" + + precommit_package="" + + if [ "$PRE_COMMIT_ENABLED" = "true" ]; then + precommit_package="pre-commit" + fi + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + echo "" > /tmp/tmp_requirements.txt + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements-dev.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements-dev.txt" >> /tmp/tmp_requirements.txt + fi + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements-dev.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements-dev.txt" >> /tmp/tmp_requirements.txt + fi + + sed -i "s/{precommit_package}/$precommit_package/" /tmp/tmp_requirements.txt + sed -i "s/{formatter_package}/$formatter_package/" /tmp/tmp_requirements.txt + + sed -i '/^$/d' /tmp/tmp_requirements.txt + sort -u /tmp/tmp_requirements.txt > $WORKSPACE_PATH/requirements-dev.txt + + rm -f /tmp/tmp_requirements.txt + + echo -e "Pip configuration file was created (requirements-dev.txt)." + + fi + + echo -e "\n${GREEN}> Install dependencies with pip (requirements-dev.txt).${ENDCOLOR}\n" + + if [ -z "$(cat $WORKSPACE_PATH/requirements-dev.txt)" ]; then + echo -e "No package to install\n" + else + pip install -r $WORKSPACE_PATH/requirements-dev.txt + echo -e "\nDone\n" + fi + +else + echo -e "\n${YELLOW}Nothing to do because PIP is not used.${ENDCOLOR}" +fi diff --git a/.devcontainer/install/29-install-requirements-test.sh b/.devcontainer/install/29-install-requirements-test.sh new file mode 100755 index 0000000..44e2613 --- /dev/null +++ b/.devcontainer/install/29-install-requirements-test.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# ignored: poetry-dependency-manager + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install requirements.txt (test) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + if [ ! -f "$WORKSPACE_PATH/requirements-test.txt" ]; then + echo -e "\n${GREEN}> Initialize pip Manager (requirements-test.txt).${ENDCOLOR}\n" + + unittest_package="" + coverage_package="" + + active=$(jq -r '.customizations.vscode.settings."python.testing.pytestEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ "$active" = "true" ]; then + unittest_package="pytest-xdist" + fi + + active=$(jq -r '.customizations.vscode.settings."python.testing.coverageEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ "$active" = "true" ]; then + coverage_package="coverage" + fi + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + echo "" > /tmp/tmp_requirements.txt + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements-test.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/default/config/requirements-test.txt" >> /tmp/tmp_requirements.txt + fi + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements-test.txt" ]; then + cat "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/requirements-test.txt" >> /tmp/tmp_requirements.txt + fi + + sed -i "s/{coverage_package}/$coverage_package/" /tmp/tmp_requirements.txt + sed -i "s/{unittest_package}/$unittest_package/" /tmp/tmp_requirements.txt + + sed -i '/^$/d' /tmp/tmp_requirements.txt + sort -u /tmp/tmp_requirements.txt > $WORKSPACE_PATH/requirements-test.txt + + rm -f /tmp/tmp_requirements.txt + + echo -e "Pip configuration file was created (requirements-test.txt)." + fi + + echo -e "\n${GREEN}> Install dependencies with pip (requirements-test.txt).${ENDCOLOR}\n" + + if [ -z "$(cat $WORKSPACE_PATH/requirements-test.txt)" ]; then + echo -e "No package to install\n" + else + pip install -r $WORKSPACE_PATH/requirements-test.txt + echo -e "\nDone\n" + fi + +else + echo -e "\n${YELLOW}Nothing to do because PIP is not used.${ENDCOLOR}" +fi diff --git a/.devcontainer/install/30-configure-project-with-poetry.sh b/.devcontainer/install/30-configure-project-with-poetry.sh new file mode 100755 index 0000000..98b2402 --- /dev/null +++ b/.devcontainer/install/30-configure-project-with-poetry.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# ignored: azure-function-python +# ignored: pip-dependency-manager + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Configure project with Poetry #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +if [ "$DEPENDENCY_MANAGER" = "poetry" ]; then + + source $WORKSPACE_PATH/.venv/bin/activate + + echo -e "${GREEN}> Install POETRY tool and install dependencies.${ENDCOLOR}\n" + curl -sSL https://install.python-poetry.org | python3 - + poetry completions bash >> ~/.bash_completion + + if [ ! -f "pyproject.toml" ]; then + poetry init + fi + + poetry install + +else + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}\n" +fi diff --git a/.devcontainer/install/35-check-dependency-manager-version.sh b/.devcontainer/install/35-check-dependency-manager-version.sh new file mode 100755 index 0000000..26ff1e9 --- /dev/null +++ b/.devcontainer/install/35-check-dependency-manager-version.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Check dependency manager version #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +if which pip >/dev/null; then + echo -e "\n${GREEN}> Display pip version.${ENDCOLOR}\n" + pip --version +fi + +if which poetry >/dev/null; then + echo -e "\n${GREEN}> Display POETRY version.${ENDCOLOR}\n" + poetry --version +fi + +echo -e "" diff --git a/.devcontainer/install/40-check-pyright-version.sh b/.devcontainer/install/40-check-pyright-version.sh new file mode 100755 index 0000000..b054484 --- /dev/null +++ b/.devcontainer/install/40-check-pyright-version.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Initialize & Check pyright version #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +if which pyright >/dev/null; then + echo -e "\n${GREEN}> Initialize & Check pyright version.${ENDCOLOR}\n" + + if [ -d "$HOME/.cache/pyright-python/nodeenv" ]; then + quantity=$(find "$HOME/.cache/pyright-python/nodeenv" -maxdepth 1 -type f | wc -l) + + if [ "$quantity" -eq 0 ]; then + rm -rf /home/vscode/.cache/pyright-python/nodeenv + fi + fi + + timeout --preserve-status 120s pyright --version + + if [ "$?" == 143 ]; then + rm -rf "$HOME/.cache/pyright-python/nodeenv" + echo -e "\n\n${RED}Initialization has failed. Version cannot be found.${RED}" + fi + +fi + +echo -e "" diff --git a/.devcontainer/install/45-install-pre-commit.sh b/.devcontainer/install/45-install-pre-commit.sh new file mode 100755 index 0000000..63a68af --- /dev/null +++ b/.devcontainer/install/45-install-pre-commit.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install pre-commit #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +source $WORKSPACE_PATH/.venv/bin/activate + +cd $WORKSPACE_PATH + +PRE_COMMIT_ENABLED=$(jq -r '.customizations.vscode.settings."git.preCommitEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$PRE_COMMIT_ENABLED" = "true" ]; then + + if [ ! -f "$WORKSPACE_PATH/.pre-commit-config.yaml" ]; then + + echo -e "\n${GREEN}> Create pre-commit file.${ENDCOLOR}\n" + + defaultFormatter=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' .devcontainer/devcontainer.json); + + formater_precommit="" + + if [ "$defaultFormatter" = "eeyore.yapf" ]; then + formater_precommit=$(cat <>$WORKSPACE_PATH/.pre-commit-config.yaml +--- +repos: + - repo: local + hooks: + - id: yamllint + name: yamllint + entry: /workspaces/app/.venv/bin/yamllint + language: system + types: [python] + files: ^.*\.(yml|yaml)$ + + - repo: local + hooks: + - id: pyright + name: pyright + entry: /workspaces/app/.venv/bin/pyright + language: system + types: [python] + files: ^.*\.py$ + + - repo: local + hooks: + - id: pylint + name: pylint + entry: /workspaces/app/.venv/bin/pylint + language: system + types: [python] + files: ^.*\.py$ + + - repo: local + hooks: + - id: flake8 + name: flake8 + entry: /workspaces/app/.venv/bin/flake8 + language: system + types: [python] + files: ^.*\.py$ + + - repo: local + hooks: + - id: mypy + name: mypy + entry: /workspaces/app/.venv/bin/mypy + language: system + types: [python] + files: ^.*\.py$ + + - repo: local + hooks: + - id: yapf + name: yapf + entry: /workspaces/app/.venv/bin/yapf + language: system + types: [python] + files: ^.*\.py$ + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-json + - id: no-commit-to-branch + args: [--branch, master, --branch, main] + +EOF + + echo -e "Done" + + fi + + echo -e "\n${GREEN}> Install pre-commit.${ENDCOLOR}\n" + pre-commit install --install-hooks + +else + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +fi + +echo -e "" diff --git a/.devcontainer/install/55-configure-vscode-tools.sh b/.devcontainer/install/55-configure-vscode-tools.sh new file mode 100755 index 0000000..16eafe2 --- /dev/null +++ b/.devcontainer/install/55-configure-vscode-tools.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Configure VSCode tools #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +UTILS_DIRECTORY="/usr/local/py-utils/bin" + +echo -e "\n${GREEN}> Configure binaries used by VSCode (linters, formatters, etc.).${ENDCOLOR}\n" + +mkdir -p "$HOME/.local/" + +if [ -e "$HOME/.local/lib" ]; +then + rm -rf "$HOME/.local/lib" +fi + +ln -s "$WORKSPACE_PATH/.venv/lib" "$HOME/.local/lib" + +binaries=$(ls "$UTILS_DIRECTORY") + +for binary in $binaries; do + + if [ -x "$WORKSPACE_PATH/.venv/bin/$binary" ]; then + echo "Create symbolic link for '$binary'." + rm "$UTILS_DIRECTORY/$binary" + ln -s "$WORKSPACE_PATH/.venv/bin/$binary" "$UTILS_DIRECTORY/$binary" + fi + +done + +echo -e "\nDone\n" diff --git a/.devcontainer/install/60-generate-file-vscode-settings-json.sh b/.devcontainer/install/60-generate-file-vscode-settings-json.sh new file mode 100755 index 0000000..859acb5 --- /dev/null +++ b/.devcontainer/install/60-generate-file-vscode-settings-json.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate file .vscode/settings.json #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +if [ -f "$WORKSPACE_PATH/.vscode/settings.json" ]; then + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +else + + echo -e "\n${GREEN}> Generate file '.vscode/settings.json'.${ENDCOLOR}\n" + +cat <$WORKSPACE_PATH/.vscode/settings.json +{ +} +EOF + + echo -e "Done" + +fi + +echo -e "" diff --git a/.devcontainer/install/70-generate-file-vimrc.sh b/.devcontainer/install/70-generate-file-vimrc.sh new file mode 100755 index 0000000..daa7f1a --- /dev/null +++ b/.devcontainer/install/70-generate-file-vimrc.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate file .vimrc #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Generate file '.vimrc'.${ENDCOLOR}\n" + +cat <~/.vimrc +filetype on " DΓ©tection du type de fichier +syntax on " Coloration syntaxique +set background=dark " Adapte les couleurs pour un fond noir (idΓ©eal dans PuTTY) +set linebreak " Coupe les lignes au dernier mot et pas au caractΓ¨re (requier Wrap lines, actif par dΓ©faut) +set visualbell " Utilisation du clignotement Γ  la place du "beep" +set showmatch " Surligne le mots recherchΓ©s dans le texte +set hlsearch " Surligne tous les rΓ©sultats de la recherche +set autoindent " Auto-indentation des nouvelles lignes +set expandtab " Remplace les "Tabulation" par des espaces +set shiftwidth=4 " (auto) Indentation de 2 espaces +set smartindent " Active "smart-indent" (amΓ©liore l'auto-indentation, notamment en collant du texte dΓ©jΓ  indentΓ©) +set smarttab " Active "smart-tabs" (amΓ©liore l'auto-indentation, Gestion des espaces en dΓ©but de ligne pour l'auto-indentation) +set softtabstop=4 " Tabulation = 2 espaces +set mouse-=a +set backspace=indent,eol,start +EOF + +echo -e "Done\n" diff --git a/.devcontainer/install/75-generate-file-bashrc.sh b/.devcontainer/install/75-generate-file-bashrc.sh new file mode 100755 index 0000000..a568bd1 --- /dev/null +++ b/.devcontainer/install/75-generate-file-bashrc.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate file .bashrc #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Generate file '.bashrc'.${ENDCOLOR}\n" + +script_name=$(basename "$0" | tr '[:lower:]' '[:upper:]') + +sed -i "/# \[START\]:GENERATED_FROM_$script_name/,/# \[END\]:GENERATED_FROM_$script_name/d" ~/.bashrc + +cat <>~/.bashrc +# [START]:GENERATED_FROM_$script_name + +source /usr/lib/git-core/git-sh-prompt +source /usr/share/bash-completion/completions/git + +export GIT_PS1_SHOWDIRTYSTATE=1 GIT_PS1_SHOWSTASHSTATE=1 GIT_PS1_SHOWUNTRACKEDFILES=1 +export GIT_PS1_SHOWUPSTREAM=verbose GIT_PS1_DESCRIBE_STYLE=branch GIT_PS1_SHOWCOLORHINTS=1 +export GIT_PS1_HIDE_IF_PWD_IGNORED=1 +PS1='\[\e[33;52m\][\t] \[\033[01;34m\]\w\[\e[1;32m\]\$(__git_ps1 " (%s)")\[\e[0;37m\] \$\[\033[00m\] ' + +RED="\e[31m" +ENDCOLOR="\e[0m" + +defaultFormatter=\$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' \$WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "\$defaultFormatter" == "ms-python.autopep8" ] || [ "\$defaultFormatter" == "ms-python.black-formatter" ] || [ "\$defaultFormatter" == "eeyore.yapf" ]; then + + quantity=(\`code --list-extensions --show-versions 2>/dev/null | grep "\$defaultFormatter" | wc -l\`) + + if [ "\$quantity" == "0" ]; then + echo -e "\n\${RED}The extension '\$defaultFormatter' related to the formatter seems not to be installed." + echo -e "Please check again or install the extensions via the task named 'Tools: Install extensions'.\n" + echo -e "Failure to adhere to a common code formatting tool in a Python project can lead to style inconsistencies, merge conflicts, and reduced team productivity.\${ENDCOLOR}\n" + fi + +else + echo -e "\n\${RED}The formatter '\$defaultFormatter' mentionned in the configuration is not recognized.\${ENDCOLOR}" + echo -e "\${RED}Please check your configuration!\${ENDCOLOR}\n" +fi + +export PATH="$WORKSPACE_PATH/tools:\$PATH" +source /workspaces/app/.venv/bin/activate + +# [END]:GENERATED_FROM_$script_name +EOF + +echo -e "Done\n" diff --git a/.devcontainer/install/76-generate-file-vscode-launch-json.sh b/.devcontainer/install/76-generate-file-vscode-launch-json.sh new file mode 100755 index 0000000..05aa1f0 --- /dev/null +++ b/.devcontainer/install/76-generate-file-vscode-launch-json.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate .vscode/launch.json file #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +mkdir -p $SOURCE_PATH + +if [ -f "$WORKSPACE_PATH/.vscode/launch.json" ]; then + echo -e "\n${YELLOW}Nothing to do because file is already existing.\n${ENDCOLOR}" +else + + echo -e "\n${GREEN}> Generate file '.vscode/launch.json'.${ENDCOLOR}\n" + + # Create initial file. + merged_content=$(echo '{"version": "0.2.0", "configurations": []}' | jq '.') + + # Add configuration specifically for the container type. + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/launch.json" ]; then + + projects=(`ls $SOURCE_PATH/`) + + for project in "${projects[@]}"; do + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/launch.json) + path=$(echo "$SOURCE_PATH/$project" | sed 's/\//\\\//g') + content=$(echo "$content" | sed "s/{project_path}/$path/") + content=$(echo "$content" | sed "s/{project_name}/$project/") + merged_content=$(echo "$merged_content" | jq ".configurations += $content") + done + + fi + + # Add other configurations. + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/launch.json" ]; then + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/default/config/launch.json) + merged_content=$(echo "$merged_content" | jq ".configurations += $content") + fi + + # Pretty-print the content and create the file. + formatted_content=$(echo $merged_content | jq '(.configurations | unique) as $unique_configurations | .configurations = $unique_configurations' | jq '.configurations |= sort_by(.label)' | jq -c '.') + echo $formatted_content | python -m json.tool --indent=2 > "$WORKSPACE_PATH/.vscode/launch.json" + + echo -e "Done.\n" + +fi diff --git a/.devcontainer/install/77-generate-file-vscode-tasks-json.sh b/.devcontainer/install/77-generate-file-vscode-tasks-json.sh new file mode 100755 index 0000000..6aeec66 --- /dev/null +++ b/.devcontainer/install/77-generate-file-vscode-tasks-json.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Generate .vscode/tasks.json file #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +mkdir -p $SOURCE_PATH + +if [ -f "$WORKSPACE_PATH/.vscode/tasks.json" ]; then + echo -e "\n${YELLOW}Nothing to do because file is already existing.\n${ENDCOLOR}" +else + + echo -e "\n${GREEN}> Generate file '.vscode/tasks.json'.${ENDCOLOR}\n" + + # Create initial file. + merged_content=$(echo '{"version":"2.0.0", "tasks":[]}' | jq '.') + + # Add configuration specifically for the container type. + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/tasks.json" ]; then + + projects=(`ls $SOURCE_PATH/`) + + for project in "${projects[@]}"; do + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/${CONTAINER_TYPE}/config/tasks.json) + path=$(echo "$SOURCE_PATH/$project" | sed 's/\//\\\//g') + content=$(echo "$content" | sed "s/{project_path}/$path/") + content=$(echo "$content" | sed "s/{project_name}/$project/") + merged_content=$(echo "$merged_content" | jq ".tasks += $content") + done + + fi + + # Add other configurations. + if [ -f "$WORKSPACE_PATH/.devcontainer/templates/default/config/tasks.json" ]; then + content=$(cat $WORKSPACE_PATH/.devcontainer/templates/default/config/tasks.json) + merged_content=$(echo "$merged_content" | jq ".tasks += $content") + fi + + # Pretty-print the content and create the file. + formatted_content=$(echo $merged_content | jq '(.tasks | unique) as $unique_tasks | .tasks = $unique_tasks' | jq '.tasks |= sort_by(.label)' | jq -c '.') + echo $formatted_content | python -m json.tool --indent=2 > "$WORKSPACE_PATH/.vscode/tasks.json" + + echo -e "Done.\n" + +fi diff --git a/.devcontainer/install/80-initialize-unit-testing-structure.sh b/.devcontainer/install/80-initialize-unit-testing-structure.sh new file mode 100755 index 0000000..cca3e33 --- /dev/null +++ b/.devcontainer/install/80-initialize-unit-testing-structure.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# ignored: azure-function-python +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Initialize Unit Testing structure #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +mkdir -p $SOURCE_PATH + +pytest_enabled=$(jq -r '.customizations.vscode.settings."python.testing.pytestEnabled"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$pytest_enabled" != "true" ] || [ "$(ls -A "$SOURCE_PATH" | wc -l)" -eq 0 ]; then + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +else + + echo -e "\n${GREEN}> Unit Testing directory structure will be created.${ENDCOLOR}" + + mkdir -p "$UNIT_TESTING_PATH" + touch "$UNIT_TESTING_PATH/__init__.py" + + package_directories=$(ls "$SOURCE_PATH" | sort) + + quantity=0 + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + for package_name in $package_directories; do + + if [ ! -d "$UNIT_TESTING_PATH/tests_$package_name" ]; + then + + mkdir -p "$UNIT_TESTING_PATH/tests_$package_name" + touch "$UNIT_TESTING_PATH/tests_$package_name/__init__.py" + + cp -r "$WORKSPACE_PATH/.devcontainer/templates/$CONTAINER_TYPE/unittest/"* "$UNIT_TESTING_PATH/tests_$package_name/" + + files=(`ls "$UNIT_TESTING_PATH/tests_$package_name/"*.py`) + + for file in "${files[@]}"; do + sed -i "s/{package_name}/$package_name/" "$file" + done + + echo -e "\nUnit Testing directory for 'tests_$package_name' created" + quantity=$((quantity + 1)) + fi + + done + + + if [ "$quantity" -eq 0 ]; then + echo -e "\nNo Unit Testing directory has been created" + fi + +fi + +echo -e "" diff --git a/.devcontainer/install/85-extension-installation.sh b/.devcontainer/install/85-extension-installation.sh new file mode 100755 index 0000000..ce07a59 --- /dev/null +++ b/.devcontainer/install/85-extension-installation.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### VS Code extention installation #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +mkdir -p $SOURCE_PATH + +defaultFormatter=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$defaultFormatter" == "ms-python.autopep8" ] || [ "$defaultFormatter" == "ms-python.black-formatter" ] || [ "$defaultFormatter" == "eeyore.yapf" ]; then + + echo -e "\n${GREEN}> The project is configured to use a formatter.${ENDCOLOR}" + + case "$defaultFormatter" in + "ms-python.autopep8") + defaultFormatter_name="Autopep8" + defaultFormatter_url="https://marketplace.visualstudio.com/items?itemName=ms-python.autopep8" + ;; + "ms-python.black-formatter") + defaultFormatter_name="Black" + defaultFormatter_url="https://marketplace.visualstudio.com/items?itemName=ms-python.black-formatter" + ;; + "eeyore.yapf") + defaultFormatter_name="Yapf" + defaultFormatter_url="https://marketplace.visualstudio.com/items?itemName=eeyore.yapf" + ;; + *) + # CΓ³digo a ejecutar si no se encuentra coincidencia con ningΓΊn PATRΓ“N + ;; + esac + + if [ "$defaultFormatter_name" != "" ]; then + + echo -e "${YELLOW}\nYou have to install the following extension to be compliant with the configuration.${ENDCOLOR}\n" + echo -e "${YELLOW}Formatter name:${ENDCOLOR} '$defaultFormatter_name'" + echo -e "${YELLOW}VS Marketplace Link:${ENDCOLOR} '$defaultFormatter_url'" + + echo -e "\n${BLUE}The extension will be installed automatically.${ENDCOLOR}\n" + code --install-extension "$defaultFormatter" + + quantity=(`code --list-extensions --show-versions 2>/dev/null | grep "$defaultFormatter" | wc -l`) + + if [ "$quantity" == "0" ]; then + + echo -e "\n${RED}The extension has not been automatically installed.${ENDCOLOR}" + echo -e "\n${BLUE}To install the extension manually, you can in the sidebar on the left, in the 'extension' menu (ctrl+shift+x) search for the extension from its extension id which is:${ENDCOLOR} '$defaultFormatter'\n" + + echo -e "${YELLOW}... When the extension is installed, press any key to continue ..." + read -s -p " " -n 1 -r + echo -e "${ENDCOLOR}" + + message=(`code --list-extensions --show-versions 2>&1 >/dev/null`) + + else + echo -e "" + fi + + if [ "$message" != "" ]; then + echo -e "Not able to check if the extension related to the formatter is correctly installed." + echo -e "Check will be done later." + else + + quantity=(`code --list-extensions --show-versions 2>/dev/null | grep "$defaultFormatter_id" | wc -l`) + + if [ "$quantity" == "0" ]; then + echo -e "${RED}The extension seems not to be installed. Please check again!${ENDCOLOR}" + echo -e "${RED}Failure to adhere to a common code formatting tool in a Python project can lead to style inconsistencies, merge conflicts, and reduced team productivity.${ENDCOLOR}\n" + echo -e "${RED}Despite this anomaly, the container installation processus will continue.${ENDCOLOR}" + else + echo -e "Done. Extension is installed." + fi + + fi + + else + echo -e "${RED}\nThe formatter mentionned in the configuration is not recognized. Please check your configuration!${ENDCOLOR}" + echo -e "${RED}Please check your configuration!${ENDCOLOR}" + fi + +else + echo -e "\n${YELLOW}Nothing to do.${ENDCOLOR}" +fi + +echo -e "" diff --git a/.devcontainer/install/86-fix-mypy-extension.sh b/.devcontainer/install/86-fix-mypy-extension.sh new file mode 100755 index 0000000..a50d5c2 --- /dev/null +++ b/.devcontainer/install/86-fix-mypy-extension.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Fix Mypy extension #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Fix Mypy extension.${ENDCOLOR}\n" + +ORIGINAL_FILE="$HOME/.vscode-server/extensions/ms-python.mypy-type-checker-2023.4.0/bundled/tool/lsp_server.py" + +if [ -f "$ORIGINAL_FILE" ]; then + + cp "$ORIGINAL_FILE" /tmp/lsp_server.py + + # MODIFICATION #1. + +cat <>/tmp/python_script.py + +new_string = 'r"^(?P(?P..[^:]*):(?P\d+)(?::(?P\d+))?(?::(?P\d+)(?::(?P\d+))?)?): (?P\w+): (?P.*?)(?: \[(?P[\w-]+)\])?$"' +old_string = 'r"^(?P(?P..[^:]*):(?P\d+):(?P\d+)(?::(?P\d+):(?P\d+))?): (?P\w+): (?P.*?)(?: \[(?P[\w-]+)\])?$"' + +with open("/tmp/lsp_server.py", "r") as file: + content = file.read() + +content = content.replace(old_string, new_string) + +with open("/tmp/lsp_server.py", "w") as file: file.write(content) +EOF + + python3 "/tmp/python_script.py" + + # MODIFICATION #2. + +cat <>/tmp/python_script.py +new_string = """ + try: # modified + start_char = int(data["char"]) # modified + except: # modified + start_char = 1 # modified +""" + +old_string = ' start_char = int(data["char"])' + +with open("/tmp/lsp_server.py", "r") as file: + content = file.read() + +content = content.replace(old_string, new_string) + +with open("/tmp/lsp_server.py", "w") as file: + file.write(content) +EOF + + python3 "/tmp/python_script.py" + + # MODIFICATION #3. + +cat <>/tmp/python_script.py +new_string = """ + try: # modified + end_char = data["end_char"] # modified + except: # modified + end_char = None # modified +""" + +old_string = ' end_char = data["end_char"]' + +with open("/tmp/lsp_server.py", "r") as file: + content = file.read() + +content = content.replace(old_string, new_string) + +with open("/tmp/lsp_server.py", "w") as file: + file.write(content) +EOF + + python3 "/tmp/python_script.py" + + rm "/tmp/python_script.py" + + cp "$ORIGINAL_FILE" "$ORIGINAL_FILE".bkp + mv "/tmp/lsp_server.py" "$ORIGINAL_FILE" + + echo -e "Extension file has been modified to fix issue.\n" + +else + echo -e "\n${YELLOW}> Nothing to do ... Maybe a new extension version was releases (2023.4.0).${YELLOW}\n" +fi diff --git a/.devcontainer/install/90-clean-installation-file.sh b/.devcontainer/install/90-clean-installation-file.sh new file mode 100644 index 0000000..afb85a9 --- /dev/null +++ b/.devcontainer/install/90-clean-installation-file.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# can-be-removed-after-installation + +. "$WORKSPACE_PATH/tools/color.sh" + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Clean the installation files #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Clean the installation files.${ENDCOLOR}\n" + +echo -e "${BLUE}This is the last script. You can now clean the installation files which are used only for a new project.${ENDCOLOR}" + +while true; do + + echo -e "\n${YELLOW}Do you want to remove all installation files used only for a new project? (y/n)${ENDCOLOR}" + read -p "> " choice + + case "$choice" in + [Yy]*) + rm -rf "$WORKSPACE_PATH/.devcontainer/templates" + rm -rf "$WORKSPACE_PATH/docs/dev-container-configuration" + + install_files=() + + echo -e "" + + while IFS= read -r -d $'\0' current_file ; do + if grep -q "# can-be-removed-after-installation" "$current_file"; then + echo -e "- Deleted: "$current_file + rm $current_file + continue + fi + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if grep -q "# ignored: $CONTAINER_TYPE" "$current_file"; then + echo -e "- Deleted: "$current_file + rm $current_file + continue + fi + + DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + if grep -q "# ignored: $DEPENDENCY_MANAGER-dependency-manager" "$current_file"; then + echo -e "- Deleted: "$current_file + rm $current_file + continue + fi + + done < <(find ./ -type f -name "*.sh" -print0) + + echo -e "\nDone. The installation files are deleted.\n" + + break + ;; + [Nn]*) + echo -e "\nNo file was deleted.\n" + break + ;; + *) echo -e "\n${RED}Please answer with 'y' (yes) or 'n' (no).${ENDCOLOR}" ;; + esac + +done diff --git a/.devcontainer/install/__65-generate-file-vscode-launch-json.sh__ b/.devcontainer/install/__65-generate-file-vscode-launch-json.sh__ new file mode 100755 index 0000000..31edc69 --- /dev/null +++ b/.devcontainer/install/__65-generate-file-vscode-launch-json.sh__ @@ -0,0 +1,23 @@ +# can-be-removed-after-installation + + if [ -f "$WORKSPACE_PATH/src/$package_directory/run_streamlit.py" ]; then + +new_config=$( +cat < /tmp/temporary_launch.json + mv /tmp/temporary_launch.json "$WORKSPACE_PATH/.vscode/launch.json" + + fi diff --git a/.devcontainer/project.env b/.devcontainer/project.env new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/Dockerfile b/.devcontainer/templates/app-python/Dockerfile similarity index 72% rename from .devcontainer/Dockerfile rename to .devcontainer/templates/app-python/Dockerfile index fe906dd..fbbf92b 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/templates/app-python/Dockerfile @@ -1,5 +1,4 @@ -# See here for image contents: -ARG VARIANT="3.10-bullseye" +ARG VARIANT="3.11-bullseye" FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} WORKDIR /workspaces/app @@ -21,4 +20,9 @@ VOLUME $WORKSPACE_FOLDER/.venv # Fix issue with pylint and other linters: -ENV PYTHONPATH=$WORKSPACE_FOLDER +ENV PYTHONPATH=$WORKSPACE_FOLDER/src +ENV SOURCE_PATH=$WORKSPACE_FOLDER/src + +ENV UNIT_TESTING_PATH=$WORKSPACE_FOLDER/tests + +ENV WORKSPACE_PATH=$WORKSPACE_FOLDER diff --git a/.devcontainer/templates/app-python/config/extensions.json b/.devcontainer/templates/app-python/config/extensions.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/.devcontainer/templates/app-python/config/extensions.json @@ -0,0 +1 @@ +[] diff --git a/.devcontainer/templates/app-python/config/launch.json b/.devcontainer/templates/app-python/config/launch.json new file mode 100644 index 0000000..176fcc1 --- /dev/null +++ b/.devcontainer/templates/app-python/config/launch.json @@ -0,0 +1,8 @@ +[ + { + "name": "Execute module '{project_name}'", + "type": "python", + "request": "launch", + "module": "{project_name}" + } +] diff --git a/.devcontainer/templates/app-python/config/requirements.txt b/.devcontainer/templates/app-python/config/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/app-python/config/tasks.json b/.devcontainer/templates/app-python/config/tasks.json new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/.devcontainer/templates/app-python/config/tasks.json @@ -0,0 +1 @@ +[] diff --git a/.devcontainer/templates/app-python/devcontainer.json b/.devcontainer/templates/app-python/devcontainer.json new file mode 100644 index 0000000..a071ffc --- /dev/null +++ b/.devcontainer/templates/app-python/devcontainer.json @@ -0,0 +1,73 @@ +{ + "name": "Python 3", + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/app,type=bind", + "workspaceFolder": "/workspaces/app", + "mounts": [ + "type=bind,source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,readonly" + ], + "runArgs": [ + "--env-file", + ".devcontainer/project.env" + ], + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "VARIANT": "3.11-bullseye", + "NODE_VERSION": "lts/*" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "sanaajani.taskrunnercode", + "ms-python.vscode-pylance", + "ms-python.pylint", + "ms-python.mypy-type-checker", + "ms-python.flake8" + ], + "settings": { + "container.type": "app-python", + "python.terminal.activateEnvironment": false, + "python.defaultInterpreterPath": "/workspaces/app/.venv/bin/python", + "python.createEnvironment.contentButton": "hide", + "python.analysis.typeCheckingMode": "strict", + "python.analysis.importFormat": "absolute", + "python.analysis.logLevel": "Trace", + "python.languageServer": "Pylance", + "python.testing.pytestArgs": [ + "tests" + ], + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, + "files.eol": "\n", + "files.exclude": { + "**/__pycache__": true, + "**/.mypy_cache": true, + "**/.venv": true, + "**/.pytest_cache": true, + "**/.python_packages": true + }, + "editor.formatOnSaveMode": "modifications", + "editor.formatOnSave": true, + "mypy-type-checker.args": [], + "_python.dependencyManager": "pip", + "__python.dependencyManager": "poetry", + "python.dependencyManager": "", + "python.pip.extraIndexUrl": null, + "python.testing.coverageEnabled": false, + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": false, + "_editor.defaultFormatter": "ms-python.autopep8", + "__editor.defaultFormatter": "ms-python.black-formatter", + "___editor.defaultFormatter": "eeyore.yapf", + "editor.defaultFormatter": "", + "git.preCommitEnabled": false + } + } + }, + "postCreateCommand": "sudo chmod +x $WORKSPACE_PATH/.devcontainer/install.sh && $WORKSPACE_PATH/.devcontainer/install.sh", + "remoteUser": "vscode" +} diff --git a/.devcontainer/templates/app-python/new_package/__init__.py b/.devcontainer/templates/app-python/new_package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/app-python/new_package/__main__.py b/.devcontainer/templates/app-python/new_package/__main__.py new file mode 100644 index 0000000..80420d1 --- /dev/null +++ b/.devcontainer/templates/app-python/new_package/__main__.py @@ -0,0 +1,6 @@ +""" File: src/example_one_one_one/__main__.py """ + +import {package_name}.application as app + +if __name__ == "__main__": + app.run() diff --git a/.devcontainer/templates/app-python/new_package/application.py b/.devcontainer/templates/app-python/new_package/application.py new file mode 100644 index 0000000..cedf6a5 --- /dev/null +++ b/.devcontainer/templates/app-python/new_package/application.py @@ -0,0 +1,25 @@ +""" File: src/{package_name}/application.py """ + + +def run() -> None: # pylint: disable=unused-variable + """ + The `run` function calls the `hello` function and prints the returned message. + """ + message: str = hello() + print(message) + + +def hello(name: str = "John Doe") -> str: + """ + The function `hello` takes an optional `name` parameter and returns a greeting message with the + provided name or a default name if none is provided. + + Args: + name (str): The `name` parameter is a string that represents a person's name. + It has a default value of "John Doe" if no value is provided when calling the `hello` function. + + Returns: + the string "Hello {name} from example_one_one_one! How are you today!" + """ + + return f"Hello {name} from {package_name}! How are you today?" diff --git a/.devcontainer/templates/app-python/new_package/py.typed b/.devcontainer/templates/app-python/new_package/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/app-python/unittest/__init__.py b/.devcontainer/templates/app-python/unittest/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/app-python/unittest/test_application.py b/.devcontainer/templates/app-python/unittest/test_application.py new file mode 100644 index 0000000..083b3b6 --- /dev/null +++ b/.devcontainer/templates/app-python/unittest/test_application.py @@ -0,0 +1,20 @@ +""" File : tests/{package_name}/test_application.py """ + +from typing import Any + +import {package_name}.application as app + + +def test_hello() -> None: # pylint: disable=unused-variable + """...""" + + assert app.hello() == "Hello John Doe from {package_name}! How are you today?" + assert app.hello("Jane Doe") == "Hello Jane Doe from {package_name}! How are you today?" + + +def test_run(capsys: Any) -> None: # pylint: disable=unused-variable + """...""" + + app.run() + capured = capsys.readouterr() + assert capured.out == "Hello John Doe from {package_name}! How are you today?\n" diff --git a/.devcontainer/templates/azure-function-python/Dockerfile b/.devcontainer/templates/azure-function-python/Dockerfile new file mode 100644 index 0000000..cbbd17d --- /dev/null +++ b/.devcontainer/templates/azure-function-python/Dockerfile @@ -0,0 +1,28 @@ +ARG VARIANT="3.11" +FROM mcr.microsoft.com/azure-functions/python:4-python${VARIANT}-core-tools + +WORKDIR /workspaces/app +ARG WORKSPACE_FOLDER="/workspaces/app" + +# Install program dependencies: + +RUN mkdir /tmp/config_container + +COPY /.devcontainer/install-deps.sh /tmp/config_container/install-deps.sh +RUN chmod +x /tmp/config_container/install-deps.sh +RUN /tmp/config_container/install-deps.sh + +RUN rm -rvf /tmp/config_container + +# Create virtual environment: + +VOLUME $WORKSPACE_FOLDER/.venv + +# Fix issue with pylint and other linters: + +ENV PYTHONPATH=$WORKSPACE_FOLDER/src +ENV SOURCE_PATH=$WORKSPACE_FOLDER/src + +ENV UNIT_TESTING_PATH=$WORKSPACE_FOLDER/tests + +ENV WORKSPACE_PATH=$WORKSPACE_FOLDER diff --git a/.devcontainer/templates/azure-function-python/config/extensions.json b/.devcontainer/templates/azure-function-python/config/extensions.json new file mode 100644 index 0000000..0f6626b --- /dev/null +++ b/.devcontainer/templates/azure-function-python/config/extensions.json @@ -0,0 +1,4 @@ +[ + "ms-azuretools.vscode-azurefunctions", + "ms-python.python" +] diff --git a/.devcontainer/templates/azure-function-python/config/launch.json b/.devcontainer/templates/azure-function-python/config/launch.json new file mode 100644 index 0000000..122791d --- /dev/null +++ b/.devcontainer/templates/azure-function-python/config/launch.json @@ -0,0 +1,10 @@ +[ + { + "name": "Attach to Python Functions: {project_name}", + "type": "python", + "request": "attach", + "port": 9091, + "preLaunchTask": "Azure Function - Start: {project_name}", + "justMyCode": true + } +] diff --git a/.devcontainer/templates/azure-function-python/config/requirements.txt b/.devcontainer/templates/azure-function-python/config/requirements.txt new file mode 100644 index 0000000..75db2c4 --- /dev/null +++ b/.devcontainer/templates/azure-function-python/config/requirements.txt @@ -0,0 +1 @@ +azure-functions diff --git a/.devcontainer/templates/azure-function-python/config/tasks.json b/.devcontainer/templates/azure-function-python/config/tasks.json new file mode 100644 index 0000000..429abd1 --- /dev/null +++ b/.devcontainer/templates/azure-function-python/config/tasks.json @@ -0,0 +1,12 @@ +[ + { + "label":"Azure Function - Start: {project_name}", + "type":"func", + "command":"host start --script-root {project_path}", + "problemMatcher":"$func-python-watch", + "isBackground":true, + "options": { + "cwd": "{project_path}" + } + } +] diff --git a/.devcontainer/templates/azure-function-python/devcontainer.json b/.devcontainer/templates/azure-function-python/devcontainer.json new file mode 100644 index 0000000..c16b47a --- /dev/null +++ b/.devcontainer/templates/azure-function-python/devcontainer.json @@ -0,0 +1,80 @@ +{ + "name": "Azure Functions & Python 3", + "forwardPorts": [ 7071 ], + "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/app,type=bind", + "workspaceFolder": "/workspaces/app", + "mounts": [ + "type=bind,source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,readonly" + ], + "runArgs": [ + "--env-file", + ".devcontainer/project.env" + ], + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "VARIANT": "3.11", + "NODE_VERSION": "lts/*" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-azuretools.vscode-azurefunctions", + "sanaajani.taskrunnercode", + "ms-python.vscode-pylance", + "ms-python.pylint", + "ms-python.mypy-type-checker", + "ms-python.flake8" + ], + "settings": { + "container.type": "azure-function-python", + "python.terminal.activateEnvironment": false, + "python.defaultInterpreterPath": "/workspaces/app/.venv/bin/python", + "python.createEnvironment.contentButton": "hide", + "python.analysis.typeCheckingMode": "strict", + "python.analysis.importFormat": "absolute", + "python.analysis.logLevel": "Trace", + "python.languageServer": "Pylance", + "python.testing.pytestArgs": [ + "tests" + ], + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, + "files.eol": "\n", + "files.exclude": { + "**/__pycache__": true, + "**/.mypy_cache": true, + "**/.venv": true, + "**/.pytest_cache": true, + "**/.python_packages": true + }, + "editor.formatOnSaveMode": "modifications", + "editor.formatOnSave": true, + "mypy-type-checker.args": [], + "python.dependencyManager": "pip", + "python.pip.extraIndexUrl": null, + "python.testing.coverageEnabled": false, + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": false, + "_editor.defaultFormatter": "ms-python.autopep8", + "__editor.defaultFormatter": "ms-python.black-formatter", + "___editor.defaultFormatter": "eeyore.yapf", + "editor.defaultFormatter": "", + "git.preCommitEnabled": false, + "azureFunctions.scmDoBuildDuringDeployment": true, + "azureFunctions.pythonVenv": ".venv", + "azureFunctions.projectLanguage": "Python", + "azureFunctions.projectRuntime": "~4", + "azureFunctions.projectLanguageModel": 2, + "debug.internalConsoleOptions": "neverOpen", + "task.autoDetect": "off" + } + } + }, + "postCreateCommand": "sudo chmod +x $WORKSPACE_PATH/.devcontainer/install.sh && $WORKSPACE_PATH/.devcontainer/install.sh", + "remoteUser": "vscode" +} diff --git a/.devcontainer/templates/azure-function-python/new_package/.funcignore b/.devcontainer/templates/azure-function-python/new_package/.funcignore new file mode 100644 index 0000000..989cb9f --- /dev/null +++ b/.devcontainer/templates/azure-function-python/new_package/.funcignore @@ -0,0 +1,11 @@ +.git* +.vscode +.funcignore +.mypy_cache +.pytest_cache +__pycache__ +__azurite_db*__.json +__blobstorage__ +__queuestorage__ +local.settings.json +test diff --git a/.devcontainer/templates/azure-function-python/new_package/__init__.py b/.devcontainer/templates/azure-function-python/new_package/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/azure-function-python/new_package/function_app.py b/.devcontainer/templates/azure-function-python/new_package/function_app.py new file mode 100644 index 0000000..69c7644 --- /dev/null +++ b/.devcontainer/templates/azure-function-python/new_package/function_app.py @@ -0,0 +1,13 @@ +"""...""" + +import logging +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION) + + +@app.route(route="http_trigger", auth_level=func.AuthLevel.FUNCTION) +def http_trigger(req: func.HttpRequest) -> func.HttpResponse: + """...""" + logging.info("Python HTTP trigger function processed a request.") + return func.HttpResponse(f"Hello. This HTTP triggered function executed successfully ({req.method.upper()})") diff --git a/.devcontainer/templates/azure-function-python/new_package/host.json b/.devcontainer/templates/azure-function-python/new_package/host.json new file mode 100644 index 0000000..fd4bee7 --- /dev/null +++ b/.devcontainer/templates/azure-function-python/new_package/host.json @@ -0,0 +1,15 @@ +{ + "version": "2.0", + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[3.*, 4.0.0)" + } +} \ No newline at end of file diff --git a/.devcontainer/templates/azure-function-python/new_package/local.settings.json b/.devcontainer/templates/azure-function-python/new_package/local.settings.json new file mode 100644 index 0000000..41fdfd4 --- /dev/null +++ b/.devcontainer/templates/azure-function-python/new_package/local.settings.json @@ -0,0 +1,8 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "python", + "AzureWebJobsFeatureFlags": "EnableWorkerIndexing", + "AzureWebJobsStorage": "" + } +} diff --git a/.devcontainer/templates/default/config/extensions.json b/.devcontainer/templates/default/config/extensions.json new file mode 100644 index 0000000..cfa3848 --- /dev/null +++ b/.devcontainer/templates/default/config/extensions.json @@ -0,0 +1,7 @@ +[ + "njpwerner.autodocstring", + "aaron-bond.better-comments", + "mohsen1.prettify-json", + "vscode-icons-team.vscode-icons", + "gruntfuggly.todo-tree" +] diff --git a/.devcontainer/templates/default/config/launch.json b/.devcontainer/templates/default/config/launch.json new file mode 100644 index 0000000..d891f63 --- /dev/null +++ b/.devcontainer/templates/default/config/launch.json @@ -0,0 +1,10 @@ +[ + { + "name": "Python: File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": true + } +] diff --git a/.devcontainer/templates/default/config/requirements-dev.txt b/.devcontainer/templates/default/config/requirements-dev.txt new file mode 100644 index 0000000..a5afb0e --- /dev/null +++ b/.devcontainer/templates/default/config/requirements-dev.txt @@ -0,0 +1,7 @@ +yamllint +pyright +pylint +flake8 +mypy +{formatter_package} +{precommit_package} diff --git a/.devcontainer/templates/default/config/requirements-test.txt b/.devcontainer/templates/default/config/requirements-test.txt new file mode 100644 index 0000000..cbabed0 --- /dev/null +++ b/.devcontainer/templates/default/config/requirements-test.txt @@ -0,0 +1,2 @@ +{unittest_package} +{coverage_package} diff --git a/.devcontainer/templates/default/config/requirements.txt b/.devcontainer/templates/default/config/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/.devcontainer/templates/default/config/tasks.json b/.devcontainer/templates/default/config/tasks.json new file mode 100644 index 0000000..b691fb0 --- /dev/null +++ b/.devcontainer/templates/default/config/tasks.json @@ -0,0 +1,86 @@ +[ + { + "label":"CI/CD: Build Package", + "type":"shell", + "command":"${workspaceFolder}/tools/execute-build-package.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"build", + "isDefault":true + } + }, + { + "label":"CI/CD: Configuration Files Check", + "type":"shell", + "command":"${workspaceFolder}/tools/execute-configuration-check.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"test", + "isDefault":true + } + }, + { + "label":"CI/CD: Execute Linters", + "type":"shell", + "command":"${workspaceFolder}/tools/execute-linters.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"test", + "isDefault":true + } + }, + { + "label":"CI/CD: Execute Tests", + "type":"shell", + "command":"${workspaceFolder}/tools/execute-tests.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"test", + "isDefault":true + } + }, + { + "label":"Tools: Purge Cache", + "type":"shell", + "command":"${workspaceFolder}/tools/execute-purge-python-cache.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"none", + "isDefault":true + } + }, + { + "label":"Tools: Install extensions", + "type":"shell", + "command":"${workspaceFolder}/tools/install-extensions.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"none", + "isDefault":true + } + }, + { + "label":"Tools: Update all Python Packages", + "type":"shell", + "command":"${workspaceFolder}/tools/upgrade-all-python-package.sh", + "problemMatcher":[ + + ], + "group":{ + "kind":"none", + "isDefault":true + } + } +] diff --git a/.github/workflows/control.yaml b/.github/workflows/control.yaml index 1dd9ffd..31464ca 100644 --- a/.github/workflows/control.yaml +++ b/.github/workflows/control.yaml @@ -15,7 +15,6 @@ on: # yamllint disable-line rule:truthy env: PYTHON_VERSION: "3.10" ARTIFACT_NAME: "mypkg" - PACKAGE_NAME: "mypkg" jobs: check: @@ -41,8 +40,8 @@ jobs: - name: Initialize environment variables run: | CURRENT_PWD=$(pwd) - PYTHONPATH="$CURRENT_PWD/src" - echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV + SOURCE_PATH="$CURRENT_PWD/src" + echo "SOURCE_PATH=$SOURCE_PATH" >> $GITHUB_ENV WORKINGPATH="$CURRENT_PWD" echo "WORKINGPATH=$WORKINGPATH" >> $GITHUB_ENV @@ -50,42 +49,42 @@ jobs: id: pyright run: | . $WORKINGPATH/.venv/bin/activate - pyright $PYTHONPATH -p $WORKINGPATH/tools/pyrightconfig.json + pyright $SOURCE_PATH -p $WORKINGPATH/tools/pyrightconfig.json - name: Pylint id: pylint if: ${{ always() }} run: | . $WORKINGPATH/.venv/bin/activate - pylint $PYTHONPATH/${{ env.PACKAGE_NAME }} --score=false + pylint $SOURCE_PATH --score=false - name: Flake8 id: flake8 if: ${{ always() }} run: | . $WORKINGPATH/.venv/bin/activate - flake8 $PYTHONPATH/${{ env.PACKAGE_NAME }} + flake8 $SOURCE_PATH - name: Mypy id: mypy if: ${{ always() }} run: | . $WORKINGPATH/.venv/bin/activate - mypy $PYTHONPATH/${{ env.PACKAGE_NAME }} + mypy $SOURCE_PATH - name: Pylama id: pylama if: ${{ always() }} run: | . $WORKINGPATH/.venv/bin/activate - pylama $PYTHONPATH/${{ env.PACKAGE_NAME }} + pylama $SOURCE_PATH - name: Yapf id: yapf if: ${{ always() }} run: | . $WORKINGPATH/.venv/bin/activate - yapf --diff $PYTHONPATH/${{ env.PACKAGE_NAME }} --recursive + yapf --diff $SOURCE_PATH --recursive - name: Linter Results if: | @@ -130,8 +129,8 @@ jobs: - name: Initialize environment variables run: | CURRENT_PWD=$(pwd) - PYTHONPATH="$CURRENT_PWD/src" - echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV + SOURCE_PATH="$CURRENT_PWD/src" + echo "SOURCE_PATH=$SOURCE_PATH" >> $GITHUB_ENV WORKINGPATH="$CURRENT_PWD" echo "WORKINGPATH=$WORKINGPATH" >> $GITHUB_ENV @@ -171,8 +170,8 @@ jobs: - name: Initialize environment variables run: | CURRENT_PWD=$(pwd) - PYTHONPATH="$CURRENT_PWD/src" - echo "PYTHONPATH=$PYTHONPATH" >> $GITHUB_ENV + SOURCE_PATH="$CURRENT_PWD/src" + echo "SOURCE_PATH=$SOURCE_PATH" >> $GITHUB_ENV WORKINGPATH="$CURRENT_PWD" echo "WORKINGPATH=$WORKINGPATH" >> $GITHUB_ENV @@ -183,7 +182,7 @@ jobs: - name: "YAML Lint" id: yaml_lint run: | - yamllint $PYTHONPATH/${{ env.PACKAGE_NAME }} + yamllint $SOURCE_PATH - name: Configuration Results if: (success() || failure()) && (steps.yaml_lint.outcome == 'failure') diff --git a/.gitignore b/.gitignore index 6adbc44..fdd7e0c 100644 --- a/.gitignore +++ b/.gitignore @@ -142,3 +142,8 @@ __MACOSX/ # Temporary directory tmp temp + +# Ignore developer personal files +.devcontainer/user.env +.vscode/settings.json +.pre-commit-config.yaml diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index d8f72f2..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "recommendations": [ - "njpwerner.autodocstring", - "aaron-bond.better-comments", - "streetsidesoftware.code-spell-checker", - "discretegames.f5anything", - "mohsen1.prettify-json", - "vscode-icons-team.vscode-icons", - "foxundermoon.shell-format", - "sanaajani.taskrunnercode", - "gruntfuggly.todo-tree", - "redhat.vscode-yaml" - ] -} diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 9e95440..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python : Current file", - "type": "python", - "request": "launch", - "program": "${file}", - "console": "integratedTerminal" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index bf31a65..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "debug.internalConsoleOptions": "neverOpen", - "python.terminal.activateEnvironment": true, - "python.defaultInterpreterPath": "/workspaces/app/.venv/bin/python", - "python.linting.enabled": true, - // "python.linting.pylamaEnabled": true, - // "python.linting.pylamaPath": "/workspaces/app/.venv/bin/pylama", - "python.linting.mypyEnabled": true, - "python.linting.mypyPath": "/workspaces/app/.venv/bin/mypy", - "python.linting.mypyArgs": [], - "python.linting.pylintEnabled": true, - "python.linting.pylintPath": "/workspaces/app/.venv/bin/pylint", - "python.linting.flake8Enabled": true, - "python.linting.flake8Path": "/workspaces/app/.venv/bin/flake8", - "python.formatting.provider": "yapf", - "python.formatting.yapfPath": "/workspaces/app/.venv/bin/yapf", - // "python.linting.flake8Enabled": false, - // "python.linting.flake8Path": "/workspaces/app/.venv/bin/flake8", - // "python.formatting.provider": "black", - // "python.formatting.blackPath": "/workspaces/app/.venv/bin/black", - // "editor.formatOnSaveMode": "file", - "editor.formatOnSave": true, - "files.trimTrailingWhitespace": true, - "files.trimFinalNewlines": true, - "files.insertFinalNewline": true, - "files.eol": "\n", - "files.exclude": { - "**/__pycache__": true, - "**/.mypy_cache": true, - "**/.venv": true, - "**/.pytest_cache": true - }, - "python.analysis.typeCheckingMode": "strict", - "python.analysis.importFormat": "absolute", - "python.analysis.diagnosticMode": "workspace", - "python.analysis.logLevel": "Trace", - "python.analysis.diagnosticSeverityOverrides": { - "reportUnusedVariable": "information" - }, - "python.languageServer": "Pylance", - "python.analysis.exclude": [ - "**/.venv" - ], - "docwriter.style": "Google", - "python.testing.pytestArgs": [ - "tests" - ], - "python.testing.unittestEnabled": false, - "python.testing.pytestEnabled": true, - "python.createEnvironment.contentButton": "hide", - "python.linting.maxNumberOfProblems": 50, - "cSpell.words": [ - "dataframe", - "dataframes", - "dropna", - "isin", - "iterrows", - "yapf" - ], - "files.autoSaveDelay": 120000, - "cSpell.maxDuplicateProblems": 2, - "cSpell.maxNumberOfProblems": 25, - "cSpell.ignorePaths": [ - "package-lock.json", - "node_modules", - "vscode-extension", - ".git/objects", - ".vscode", - ".vscode-insiders", - ".devcontainer" - ] -} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index da33924..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - // See https://go.microsoft.com/fwlink/?LinkId=733558 - // for the documentation about the tasks.json format - "version": "2.0.0", - "tasks": [ - { - "label": "All Python Packages: Install / Update", - "type": "shell", - "command": "${workspaceFolder}/tools/upgrade-all-python-package.sh", - "problemMatcher": [], - "group": { - "kind": "none", - "isDefault": true - } - }, - { - "label": "CI/CD: Execute", - "type": "shell", - "command": "${workspaceFolder}/tools/execute-linters.sh", - "problemMatcher": [], - "group": { - "kind": "test", - "isDefault": true - } - } - ] -} diff --git a/README.md b/README.md index b3062c7..b285f21 100644 --- a/README.md +++ b/README.md @@ -6,34 +6,4 @@ # devcontainer-python - -## Linters & tools - -> Linters are programs that advise about code quality by displaying warnings and errors. - -By default, the following linters and tools are installed and configured: - -- [Flake8](https://github.com/pycqa/flake8/) -- [Mypy](https://mypy.readthedocs.io/en/stable/) -- [Pylama](https://klen.github.io/pylama/) -- [Pylint](https://pylint.pycqa.org/en/latest/) -- [Yapf](https://github.com/google/yapf) - -You could find my own configuration in the setup.cfg file.
-Don't hesitate to share with me your own configuration. - -## VSCode extensions - -The following extensions are recommanded: - -- [AutoDocstring - Python Docstring Generator](https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring) -- [Prettify JSON](https://marketplace.visualstudio.com/items?itemName=mohsen1.prettify-json) -- [Pylance](https://marketplace.visualstudio.com/items?itemName=ms-python.vscode-pylance) -- [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) -- [Python Type Hint](https://marketplace.visualstudio.com/items?itemName=njqdev.vscode-python-typehint) -- [Todo Tree](https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree) -- [VSCode-icons](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) - -The file .vcode/settings.json was updated with my own configuration. - -Enjoy! +After cloning the repository, please check the documentation [to create a new project](./docs/dev-container-configuration/00-configure-new-project.md). diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..0dd05f4 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Your Project Name + +[How install your local development environment?](./install-local-environment.md) diff --git a/docs/dev-container-configuration/00-configure-new-project.md b/docs/dev-container-configuration/00-configure-new-project.md new file mode 100644 index 0000000..f091ab6 --- /dev/null +++ b/docs/dev-container-configuration/00-configure-new-project.md @@ -0,0 +1,18 @@ +# Create your project based on dev-containter template. + +Thank you for cloning this repository. + +Before starting your development, you have to configure your project. + +You have to configure successively the different parts of your project : + +- Configure a packaging and dependency manager +- Configure a formatter +- Configure Unit Testing +- Etc. + +Don't worry! We will explain to you step by step what you need to do. + +When you are ready to continue, you can begin setting up your project. + +###### **β–Ά Next step: [Duplicate configuration example file](./01-duplicate-configuration-example-file.md)** diff --git a/docs/dev-container-configuration/01-duplicate-configuration-example-file.md b/docs/dev-container-configuration/01-duplicate-configuration-example-file.md new file mode 100644 index 0000000..926c2ed --- /dev/null +++ b/docs/dev-container-configuration/01-duplicate-configuration-example-file.md @@ -0,0 +1,20 @@ +###### **◀️ Previous step: [Introduction](./00-configure-new-project.md)** + +# Duplicate configuration example file + +Before you begin, you need to rename the file named *'devcontainer.json-example'* located in the *'.devcontainer/'* directory to *'devcontainer.json'*. + +The new tree should look like : + +``` +.devcontainer/ +β”œβ”€β”€ Dockerfile +β”œβ”€β”€ devcontainer.json +β”œβ”€β”€ install-deps.sh +β”œβ”€β”€ ... +└── install.sh +```` + +Now we will mainly modify this file. + +###### **β–Ά Next step: [Configure a packaging and dependency manager](./02-configure-packaging-dependency-manager.md)** diff --git a/docs/dev-container-configuration/02-configure-packaging-dependency-manager.md b/docs/dev-container-configuration/02-configure-packaging-dependency-manager.md new file mode 100644 index 0000000..239085f --- /dev/null +++ b/docs/dev-container-configuration/02-configure-packaging-dependency-manager.md @@ -0,0 +1,48 @@ +###### **◀️ Previous step: [Duplicate configuration example file](./01-duplicate-configuration-example-file.md)** + +# Configure a packaging and dependency manager (mandatory) + +> A packaging and dependency manager is a tool that facilitates the installation, management, and distribution of Python packages and their dependencies. It makes it easier to obtain and keep external elements that a Python project requires, ensuring they work well together. + +To get started you need to set up a packaging and dependency manager. + +## Choose a packaging and dependency manager + +You can choose between two packaging and dependency managers. + +**Name:** Pip
+**Website Link:** https://pip.pypa.io/en/stable/ + +**Name:** Poetry
+**Website Link:** https://python-poetry.org/ + +## Modify the configuration + +When your choice is made, you must modify the configuration file *'devcontainer.json'* located in the directory named *'.devcontainer/'*. + +You must modify the value of the attribute named 'python.dependencyManager'. + +```txt +customizations +└── vscode + └── settings + └── settings + └── python.dependencyManager <<< +``` + +The possible values are 'pip' or 'poetry'. + +The result should be : + +``` + "python.dependencyManager": "pip", +``` + +... or ... + +``` + "python.dependencyManager": "poetry", +``` + + +###### **β–Ά Next step: [Configure a formater](./03-configure-a-formatter.md)** diff --git a/docs/dev-container-configuration/03-configure-a-formatter.md b/docs/dev-container-configuration/03-configure-a-formatter.md new file mode 100644 index 0000000..2838809 --- /dev/null +++ b/docs/dev-container-configuration/03-configure-a-formatter.md @@ -0,0 +1,62 @@ +###### **◀️ Previous step: [Configure a packaging and dependency manager](./02-configure-packaging-dependency-manager.md)** + +# Configure a formatter (mandatory) + +> A code formatter is a tool that automates code formatting according to predefined style rules. It ensures consistent code presentation, making it easier to read, maintain, and collaborate within a development team. + +To get started you need to set up a formatter. + +## Choose a formatter + +You can read this [article](https://blog.frank-mich.com/python-code-formatters-comparison-black-autopep8-and-yapf/) to make your choice! + +**Name:** Autopep8
+**Website Link:** https://github.com/hhatto/autopep8 + +**Name:** Black
+**Website Link:** https://black.readthedocs.io/en/stable/ + +**Name:** Yapf
+**Website Link:** https://github.com/google/yapf + +## Modify the configuration + +When your choice is made, you must modify the configuration file *'devcontainer.json'* located in the directory named *'.devcontainer/'*. + +You must modify the value of the attribute named *'editor.defaultFormatter'*. + +```txt +customizations +└── vscode + └── settings + └── settings + └── editor.defaultFormatter <<< +``` + +The possible values are : + +- **ms-python.autopep8** for *'Autopep8'* +- **ms-python.black-formatter** for *'Black'* +- **eeyore.yapf** for *'Yapf'* + +To use **autopep8**, the result should be : + +```json + "editor.defaultFormatter": "ms-python.autopep8", +``` + +To use **Black**, the result should be : + +```json + "editor.defaultFormatter": "ms-python.black-formatter", +``` + +To use **Yapf**, the result should be : + +```json + "editor.defaultFormatter": "eeyore.yapf", +``` + +The Visual Studio Code extension related to the formatter will be installed later. + +###### **β–Ά Next step: [Configure unit tests](./04-configure-unit-tests.md)** diff --git a/docs/dev-container-configuration/04-configure-unit-tests.md b/docs/dev-container-configuration/04-configure-unit-tests.md new file mode 100644 index 0000000..7910dd7 --- /dev/null +++ b/docs/dev-container-configuration/04-configure-unit-tests.md @@ -0,0 +1,48 @@ +###### **◀️ Previous step: [Configure a formater](./03-configure-a-formatter.md)** + +# Configure unit tests (recommended) + +> Unit tests are automated checks performed on individual code components to ensure their proper functioning. They help ensure software quality and reliability by isolating and testing each unit independently. + +You can skip this step if you don't want to set up unit tests. + +## Choose an unit test library + +**Name:** Pytest
+**Website Link:** https://docs.pytest.org/en/ + +**Name:** Unittest
+**Website Link:** https://docs.python.org/3/library/unittest.html + +You can decide to use the both libraries. + +## Modify the configuration + +When your choice is made, you must modify the configuration file *'devcontainer.json'* located in the directory named *'.devcontainer/'*. + +You must modify the value of one or two attributes named *'python.testing.pytestEnabled'* and *'python.testing.unittestEnabled'* + +```txt +customizations +└── vscode + └── settings + └── settings + β”œβ”€β”€ python.testing.pytestEnabled <<< + └── python.testing.unittestEnabled <<< +``` + +The possible values are 'true' and 'false'. + +To activate pytest, the result should be : + +```json +"python.testing.pytestEnabled": true, +``` + +To activate unittest, the result should be : + +```json +"python.testing.unittestEnabled": true, +``` + +###### **β–Ά Next step: [Configure code coverage](./05-configure-code-coverage.md)** diff --git a/docs/dev-container-configuration/05-configure-code-coverage.md b/docs/dev-container-configuration/05-configure-code-coverage.md new file mode 100644 index 0000000..fff1bd2 --- /dev/null +++ b/docs/dev-container-configuration/05-configure-code-coverage.md @@ -0,0 +1,34 @@ +###### **◀️ Previous step: [Configure unit tests](./04-configure-unit-tests.md)** + +# Configure Code Coverage (recommended) + +> Code coverage is a metric that measures the percentage of code lines or branches executed by a set of tests in a software application. It helps assess how thoroughly a codebase has been tested, identifying areas that may need more testing to improve software reliability. + +You can skip this step if you don't want to set up coverage. + +## Information about coverage + +**Name:** Coverage
+**Website Link:** https://coverage.readthedocs.io/en + +## Modify the configuration + +To set up coverage, you must modify the configuration file *'devcontainer.json'* located in the directory named *'.devcontainer/'*. + +You must modify the value of the attribute named *'python.testing.coverageEnabled'*. + +```txt +customizations +└── vscode + └── settings + └── settings + └── python.testing.coverageEnabled <<< +``` + +The possible values are 'true' and 'false'. To activate coverage, the result should be : + +```json +"python.testing.coverageEnabled": true, +``` + +###### **β–Ά Next step: [Configure pre-commit](./06-configure-pre-commit.md)** diff --git a/docs/dev-container-configuration/06-configure-pre-commit.md b/docs/dev-container-configuration/06-configure-pre-commit.md new file mode 100644 index 0000000..41616a7 --- /dev/null +++ b/docs/dev-container-configuration/06-configure-pre-commit.md @@ -0,0 +1,34 @@ +###### **◀️ Previous step: [Configure code coverage](./05-configure-code-coverage.md)** + +# Configure pre-commit (optional) + +> A pre-commit is a script or a series of automated actions executed before a set of code changes is committed into a version control system like Git. It helps verify code compliance, apply style standards, and detect potential errors before they are integrated into the main repository. + +You can skip this step if you don't want to set up pre-commit. + +## Information about pre-commit + +**Name:** Pre-commit
+**Website Link:** https://pre-commit.com/ + +## Modify the configuration + +To set up pre-commit, you must modify the configuration file *'devcontainer.json'* located in the directory named *'.devcontainer/'*. + +You must modify the value of the attribute named *'git.preCommitEnabled'*. + +```txt +customizations +└── vscode + └── settings + └── settings + └── git.preCommitEnabled <<< +``` + +The possible values are 'true' and 'false'. To activate pre-commit, the result should be : + +```json +"git.preCommitEnabled": true, +``` + +###### **β–Ά Next step: [Information linters](./07-information-linters.md)** diff --git a/docs/dev-container-configuration/07-information-linters.md b/docs/dev-container-configuration/07-information-linters.md new file mode 100644 index 0000000..c0e6b1c --- /dev/null +++ b/docs/dev-container-configuration/07-information-linters.md @@ -0,0 +1,16 @@ +###### **◀️ Previous step: [Configure pre-commit](./06-configure-pre-commit.md)** + +# Information linters + +> A linter is a tool that analyzes your code for potential errors, stylistic issues, and adherence to coding standards. It helps maintain code quality and consistency by providing feedback and suggestions for improvement during the development process. + +By default, four (4) linters are installed and configured when initializing the project, you will have the possibility to deactivate them later. + +The linters installed by default are : + +- Flake8 +- Mypy +- Pylance / Pyright +- Pylint + +###### **β–Ά Next step: [Finalize the configuration](./99-configure-end.md)** diff --git a/docs/dev-container-configuration/99-configure-end.md b/docs/dev-container-configuration/99-configure-end.md new file mode 100644 index 0000000..e52a063 --- /dev/null +++ b/docs/dev-container-configuration/99-configure-end.md @@ -0,0 +1,4 @@ +###### **◀️ Previous step: [Information linters](./07-information-linters.md)** + +Congratulation, your project is almost ready to use.
+Now, you have to configure your local environment as explicated [here](../install-local-environment.md). diff --git a/docs/install-local-environment.md b/docs/install-local-environment.md new file mode 100644 index 0000000..4af1868 --- /dev/null +++ b/docs/install-local-environment.md @@ -0,0 +1,72 @@ +# How install your local development environment? + +## Clone the project + +**If you have have already cloned the project, skip this step.** + +Clone the repository on your local machine.
+I recommend to clone the Git repository using the SSH protocol with a SSH key. + +1/ Go to the [project home page](../). +2/ Click on the "code" button. +3/ Select "SSH" and copy the url. +4/ Open a terminal and paste this url. + +The url should be like : + +``` +git@github.com:user/repo.git +``` + +## Information about SSH key + +Your local .ssh directory will be available inside the container.
+You will be able to use your SSH key with the applications running inside the container. + +The directory is mounted in read-only and it is located : + +```bash +/home/vscode/.ssh/ +``` +You can locate your local .ssh directory depending on your host operating system. + +| Host operating system | Host directory | Container directory | +| :--- | :--- | :--- | +| macOS | $HOME/.ssh | /home/vscode/.ssh | +| Linux | $HOME/.ssh | /home/vscode/.ssh | +| Windows | %userprofile%/.ssh | /home/vscode/.ssh | + + +## Build container + +Now you are ready to build the container. Some personal information will be asked during the installation processus. + +The installation will be completely finished when the following message will be displayed in the terminal **"You can close all terminal windows and reload the project".** + +The steps to build the container are : + +- Go to the "View" menu in the top bar. +- Select "Command Palette..." (or use the keyboard shortcut Ctrl+Shift+P). +- Type "Dev Containers: Rebuild Container" in the command palette search bar and select this option. + +This step may take some time, depending on the complexity of your container and your internet connection speed. 😴 + +## Reload the project + +After building the container, I recommend to reload your project. + +- Go to the "View" menu in the top bar. +- Select "Command Palette..." (or use the keyboard shortcut Ctrl+Shift+P). +- Type "Developer: Reload Window" in the command palette search bar and select this option. + +This will refresh the Visual Studio Code window with the changes made during the container rebuild. + +## Running your application + +Now that your container is ready and the project is reloaded, you can run your application. + +- Go to the "Run and Debug" view in the lateral bar (or use the keyboard shortcut Ctrl+Shift+D). +- Choose the desired configuration from the list located at the top of the panel. +- Start Debugging. + +... diff --git a/my_project/main.py b/my_project/main.py deleted file mode 100644 index cd537cf..0000000 --- a/my_project/main.py +++ /dev/null @@ -1,28 +0,0 @@ -""" Program to check the linters availability and their correct configuration """ - -# Program has been written to generate six errors... - -# Mypy: Missing type parameters for generic type "dict" [type-arg] -# Pylance: Expected type arguments for generic class "dict" -# > Use Dict[str, str] instead of dict to resolve the issue. - -# Mypy: Skipping analyzing "pandas": module is installed, but ... or py.typed marker [import] -# Mypy: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports -# > Check setup.cfg to resolve the issue. - -# Pylint: Line too long (110/100) -# Pylama: Line too long (110 > 100 characters) [pycodestyle] -# Flake8: Line too long (110 > 100 characters) [pycodestyle] -# > Change the configuration or the code to resolve the issue. - -import pandas as pd - -my_unused_variable: dict = { - "foo": - "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" -} - -del my_unused_variable - -df = pd.DataFrame() -del df diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..52860fb --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,4 @@ +{ + "typeCheckingMode": "strict", + "reportMissingTypeStubs": false +} diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index f3bb88b..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -pylama -pylint -mypy -yapf -flake8 -# black diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 1411a4a..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -pandas \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 70dbe55..bc911b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,14 +1,9 @@ [mypy] strict = true +ignore_missing_imports = false -# [mypy-pandas.*] -# ignore_missing_imports = true - -[pylama:pycodestyle] -# For reference: https://github.com/klen/pylama - -# Maximum number of characters on a single line. -max_line_length=100 +[mypy-streamlit.*] +ignore_missing_imports = true [yapf] # For reference: https://github.com/google/yapf#knobs @@ -29,7 +24,7 @@ blank_line_before_nested_class_or_def=True coalesce_brackets=False # The column limit (or max line-length), same as pycodestyle and pylint -column_limit=100 +column_limit=150 # Put closing brackets on a separate line, dedented, if the bracketed expression can't fit in a single line. # Applies to all kinds of brackets, including function definitions and calls. @@ -124,7 +119,7 @@ variable-naming-style=snake_case [pylint.FORMAT] # Maximum number of characters on a single line. -max-line-length=100 +max-line-length=150 # This flag controls whether inconsistent-quotes generates a warning when the character # used as a quote delimiter is used inconsistently within a module. @@ -157,7 +152,7 @@ score=yes [pylint.'MESSAGES CONTROL'] # Disable the message, report, category or checker with the given id(s). -disable = too-few-public-methods +disable = too-few-public-methods,unused-variable,fixme # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=no @@ -169,6 +164,6 @@ confidence= [flake8] # extend-ignore = E203 -max-line-length = 100 +max-line-length = 150 exclude = .git,__pycache__,docs/source/conf.py,old,build,dist max-complexity = 10 diff --git a/tools/color.sh b/tools/color.sh new file mode 100755 index 0000000..14613c9 --- /dev/null +++ b/tools/color.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +export RED="\e[31m" +export GREEN="\e[32m" +export BLUE="\e[34m" +export YELLOW="\e[33m" + +export BOLD="\e[1m" + +export ENDCOLOR="\e[0m" diff --git a/tools/execute-build-package.sh b/tools/execute-build-package.sh new file mode 100755 index 0000000..dc0f1a8 --- /dev/null +++ b/tools/execute-build-package.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Build package #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${RED}Not yet implemented${ENDCOLOR}\n" + +# echo -e "\n${YELLOW}> Install build library.${ENDCOLOR}" +# python3 -m pip install --upgrade build + +# echo -e "\n${YELLOW}> Build project $PACKAGE_NAME.${ENDCOLOR}" +# python3 -m build diff --git a/tools/execute-configuration-check.sh b/tools/execute-configuration-check.sh new file mode 100755 index 0000000..a621090 --- /dev/null +++ b/tools/execute-configuration-check.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +SETUP_CFG_FILE=$WORKSPACE_PATH/setup.cfg + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Check configuration files (YAML) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +VIRTUAL_ENVIRONMENT_DIRECTORY="/workspaces/app/.venv/bin" + +# TOOL : YAML Lint + +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/yamllint" ]; then + + echo -e "\n${YELLOW}> YAML Lint.${ENDCOLOR}" + + $VIRTUAL_ENVIRONMENT_DIRECTORY/yamllint $SOURCE_PATH + + if [ "$?" -eq 0 ]; then + echo -e "\n${GREEN}${BOLD}Success: no issues found${ENDCOLOR}\n" + fi + +fi diff --git a/tools/execute-linters.sh b/tools/execute-linters.sh index 87c2d96..ddc3e22 100755 --- a/tools/execute-linters.sh +++ b/tools/execute-linters.sh @@ -3,62 +3,73 @@ source /workspaces/app/.venv/bin/activate sleep 0.5 -RED="\e[31m" -GREEN="\e[32m" -BLUE="\e[34m" -YELLOW="\e[33m" -BOLD="\e[1m" +. "$WORKSPACE_PATH/tools/color.sh" -ENDCOLOR="\e[0m" +clear -echo -e "\n${YELLOW}> YAML Lint.${ENDCOLOR}" -yamllint /workspaces/app/snow_revoke_privileges/ +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Check package files (Python) #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" +VIRTUAL_ENVIRONMENT_DIRECTORY="/workspaces/app/.venv/bin" + +# TOOL : pyright + +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/pyright" ]; then + echo -e "\n${YELLOW}> Pyright / Pylance.${ENDCOLOR}\n" + timeout --preserve-status 60s pyright $SOURCE_PATH fi -echo -e "\n${YELLOW}> Pyright / Pylance.${ENDCOLOR}" -result=$(pyright /workspaces/app/snow_revoke_privileges/) +# TOOL : pylint + +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/pylint" ]; then + echo -e "\n${YELLOW}> Pylint.${ENDCOLOR}\n" + pylint $SOURCE_PATH --score=false --jobs=4 -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: $result${ENDCOLOR}" -else - echo -e $result + if [ "$?" -eq 0 ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + fi fi -echo -e "\n${YELLOW}> Pylint.${ENDCOLOR}" -pylint /workspaces/app/snow_revoke_privileges/ --score=false --jobs=10 +# TOOL : flake8 -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/flake8" ]; then + echo -e "\n${YELLOW}> Flake8.${ENDCOLOR}\n" + flake8 $SOURCE_PATH + + if [ "$?" -eq 0 ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + fi fi -echo -e "\n${YELLOW}> Flake8.${ENDCOLOR}" -flake8 /workspaces/app/snow_revoke_privileges/ +# TOOL : mypy -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/mypy" ]; then + echo -e "\n${YELLOW}> Mypy.${ENDCOLOR}\n" + mypy $SOURCE_PATH fi -echo -e "\n${YELLOW}> Mypy.${ENDCOLOR}" -mypy /workspaces/app/snow_revoke_privileges/ +# TOOL : yapf -echo -e "\n${YELLOW}> Pylama.${ENDCOLOR}" -pylama /workspaces/app/snow_revoke_privileges/ +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/yapf" ]; then + echo -e "\n${YELLOW}> Yapf.${ENDCOLOR}\n" + result=$(yapf --diff $SOURCE_PATH --recursive | grep "(reformatted)" | grep "+++") -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + if [ "$result" = "" ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + else + result_modified=$(echo "$result" | sed 's/+++/would reformat/g; s/(reformatted)//g') + echo -e "${BOLD}$result_modified${ENDCOLOR}" + fi fi -echo -e "\n${YELLOW}> Yapf.${ENDCOLOR}" -yapf --diff /workspaces/app/snow_revoke_privileges/ --recursive +# TOOL : black -if [ "$?" -eq 0 ]; then - echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" +if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/black" ]; then + echo -e "\n${YELLOW}> Black.${ENDCOLOR}\n" + black --check $SOURCE_PATH fi -echo -e "\n${YELLOW}> Pytest.${ENDCOLOR}\n" -pytest ./tests/test_*.py -v -s -n auto --no-header --no-summary - -echo -e "\n${BLUE}All verifications are done.${ENDCOLOR}\n" +echo -e "\n${BLUE}All verifications are Done${ENDCOLOR}\n" diff --git a/tools/execute-purge-python-cache.sh b/tools/execute-purge-python-cache.sh new file mode 100755 index 0000000..c5461e5 --- /dev/null +++ b/tools/execute-purge-python-cache.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +source /workspaces/app/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Purge Python file cache #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${YELLOW}> Remove __pycache__ files.${ENDCOLOR}" +find $WORKSPACE_PATH -type d -iregex ".*__pycache__.*" -exec rm -rf {} + +echo -e "\nDone" + +echo -e "\n${YELLOW}> Remove mypy_cache files.${ENDCOLOR}" +find $WORKSPACE_PATH -type d -iregex ".*\.mypy_cache" -exec rm -rf {} + +echo -e "\nDone" + +echo -e "\n${YELLOW}> Remove pytest_cache files.${ENDCOLOR}" +find $WORKSPACE_PATH -type d -iregex ".*\.pytest_cache" -exec rm -rf {} + +echo -e "\nDone\n" diff --git a/tools/execute-reset-project.sh b/tools/execute-reset-project.sh new file mode 100755 index 0000000..069295d --- /dev/null +++ b/tools/execute-reset-project.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Reset project #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Reset project files.${ENDCOLOR}\n" + +if which pre-commit > /dev/null 2>&1; then + pre-commit uninstall +fi + +rm -rf .git/hooks + +rm -vf "$WORKSPACE_PATH/.devcontainer/user.env" + +rm -vf "$WORKSPACE_PATH/.vscode/launch.json" +rm -vf "$WORKSPACE_PATH/.vscode/settings.json" +rm -vf "$WORKSPACE_PATH/.vscode/extensions.json" +rm -vf "$WORKSPACE_PATH/.vscode/tasks.json" + +rm -vf "$WORKSPACE_PATH/requirements-dev.txt" +rm -vf "$WORKSPACE_PATH/requirements-test.txt" +rm -vf "$WORKSPACE_PATH/requirements.txt" + +rm -vf "$WORKSPACE_PATH/.coverage" +rm -vf "$WORKSPACE_PATH/.pre-commit-config.yaml" + +rm -rvf "$WORKSPACE_PATH/src/" +rm -rvf "$WORKSPACE_PATH/tests/" + +echo -e "Done\n" diff --git a/tools/execute-tests.sh b/tools/execute-tests.sh new file mode 100755 index 0000000..2d9caec --- /dev/null +++ b/tools/execute-tests.sh @@ -0,0 +1,94 @@ +#!/bin/bash + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Check & execute unit tests #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +VIRTUAL_ENVIRONMENT_DIRECTORY="/workspaces/app/.venv/bin" + +if [ -d "$WORKSPACE_PATH/tests" ]; then + + # TOOL : pyright + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/pyright" ]; then + echo -e "\n${YELLOW}> Pyright / Pylance.${ENDCOLOR}\n" + pyright $WORKSPACE_PATH/tests + fi + + # TOOL : pylint + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/pylint" ]; then + echo -e "\n${YELLOW}> Pylint.${ENDCOLOR}\n" + pylint $WORKSPACE_PATH/tests --score=false --jobs=10 + + if [ "$?" -eq 0 ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + fi + fi + + # TOOL : flake8 + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/flake8" ]; then + echo -e "\n${YELLOW}> Flake8.${ENDCOLOR}\n" + flake8 $WORKSPACE_PATH/tests + + if [ "$?" -eq 0 ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + fi + fi + + # TOOL : mypy + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/mypy" ]; then + echo -e "\n${YELLOW}> Mypy.${ENDCOLOR}\n" + mypy --follow-imports=skip $WORKSPACE_PATH/tests + fi + + # TOOL : yapf + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/yapf" ]; then + echo -e "\n${YELLOW}> Yapf.${ENDCOLOR}\n" + result=$(yapf --diff $WORKSPACE_PATH/tests --recursive | grep "(reformatted)" | grep "+++") + + if [ "$result" = "" ]; then + echo -e "${GREEN}${BOLD}Success: no issues found${ENDCOLOR}" + else + result_modified=$(echo "$result" | sed 's/+++/would reformat/g; s/(reformatted)//g') + echo -e "${BOLD}$result_modified${ENDCOLOR}" + fi + fi + + # TOOL : black + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/black\n" ]; then + echo -e "\n${YELLOW}> Black.${ENDCOLOR}" + $VIRTUAL_ENVIRONMENT_DIRECTORY/black --check $WORKSPACE_PATH/tests + fi + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/pytest" ]; then + echo -e "\n${YELLOW}> Pytest.${ENDCOLOR}\n" + pytest $WORKSPACE_PATH/tests/*/test_*.py -v -s -n auto + fi + + if [ -x "$VIRTUAL_ENVIRONMENT_DIRECTORY/coverage" ]; then + echo -e "\n${YELLOW}> Coverage Analysis.${ENDCOLOR}\n" + coverage run -m pytest $WORKSPACE_PATH/tests/*/test_*.py -q + + echo -e "\n${YELLOW}> Coverage Report.${ENDCOLOR}\n" + coverage report -m + fi + + echo -e "\n${BLUE}All verifications are Done${ENDCOLOR}\n" + +else + echo -e "\n${YELLOW}No directory '$WORKSPACE_PATH/tests' found.${YELLOW}\n" +fi diff --git a/tools/install-extensions.sh b/tools/install-extensions.sh new file mode 100755 index 0000000..fb8cac4 --- /dev/null +++ b/tools/install-extensions.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Install extensions #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${YELLOW}> Extension related to the formatter${ENDCOLOR}\n" + +defaultFormatter=$(jq -r '.customizations.vscode.settings."editor.defaultFormatter"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$defaultFormatter" == "ms-python.autopep8" ] || [ "$defaultFormatter" == "ms-python.black-formatter" ] || [ "$defaultFormatter" == "eeyore.yapf" ]; then + echo -e "- $defaultFormatter:\n" + code --install-extension "$defaultFormatter" +else + echo -e "${RED}The formatter '$defaultFormatter' mentionned in the configuration is not recognized.${ENDCOLOR}" + echo -e "${RED}Please check your configuration!${ENDCOLOR}\n" +fi + +echo -e "\n${YELLOW}> Extensions defined in .devcontainer/devcontainer.json${ENDCOLOR}\n" + +extensions=$(jq -r '.customizations.vscode.extensions[]' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +for extension in $extensions; do + echo -e "- $extension:\n" + code --install-extension "$extension" + echo -e "" +done diff --git a/tools/pip-upgrade-all-python-package.sh b/tools/pip-upgrade-all-python-package.sh deleted file mode 100644 index 4ba7886..0000000 --- a/tools/pip-upgrade-all-python-package.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -source /workspaces/app/.venv/bin/activate -sleep 0.5 - -RED="\e[31m" -GREEN="\e[32m" -BLUE="\e[34m" -YELLOW="\e[33m" -BOLD="\e[1m" - -ENDCOLOR="\e[0m" - -echo -e "\n${YELLOW}> Use: requirements-dev.txt${ENDCOLOR}\n" -pip install -r /workspaces/app/requirements-dev.txt --upgrade - -echo -e "\n${YELLOW}> Use: requirements.txt${ENDCOLOR}\n" -pip install -r /workspaces/app/requirements.txt --upgrade diff --git a/tools/upgrade-all-python-package.sh b/tools/upgrade-all-python-package.sh new file mode 100755 index 0000000..25eaf25 --- /dev/null +++ b/tools/upgrade-all-python-package.sh @@ -0,0 +1,95 @@ +#!/bin/bash + +source $WORKSPACE_PATH/.venv/bin/activate +sleep 0.5 + +. "$WORKSPACE_PATH/tools/color.sh" + +clear + +echo -e "\n${BLUE}#############################################################${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}##### Upgrade Python packages #####${ENDCOLOR}" +echo -e "${BLUE}##### #####${ENDCOLOR}" +echo -e "${BLUE}#############################################################${ENDCOLOR}" + +echo -e "\n${GREEN}> Identify the packaging and dependency manager to install.${ENDCOLOR}\n" + +DEPENDENCY_MANAGER=$(jq -r '.customizations.vscode.settings."python.dependencyManager"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + +if [ "$DEPENDENCY_MANAGER" != "" ]; then + echo -e "The dependency manager used for the project is ${YELLOW}$DEPENDENCY_MANAGER${ENDCOLOR}.\n" +fi + +if [ "$DEPENDENCY_MANAGER" != "pip" ] && [ "$DEPENDENCY_MANAGER" != "poetry" ]; then + echo -e "${RED}No correct packaging and dependency manager was configured.${ENDCOLOR}" + echo -e "${RED}Only pip and poetry manager are supported.${ENDCOLOR}\n" + exit 1 +fi + +if [ "$DEPENDENCY_MANAGER" = "pip" ]; then + + CONTAINER_TYPE=$(jq -r '.customizations.vscode.settings."container.type"' $WORKSPACE_PATH/.devcontainer/devcontainer.json); + + echo "" > /tmp/requirements.txt + echo "" > /tmp/tmp_requirements.txt + + if [ "$CONTAINER_TYPE" == "azure-function-python" ]; then + + requirements_files=`find "$SOURCE_PATH" -mindepth 2 -maxdepth 2 -type f -name 'requirements.txt'` + projects=`find "$SOURCE_PATH" -mindepth 1 -maxdepth 1 -type d -name "*"` + + if [ ! -n "$requirements_files" ]; then + echo -e "${RED}No 'requirements.txt' files was found.${ENDCOLOR}\n" + else + + IFS=$'\n' + for file in $requirements_files; do + cat "$file" >> /tmp/tmp_requirements.txt + done + + sort -u /tmp/tmp_requirements.txt > /tmp/requirements.txt + + echo -e "${BLUE}The following files will be used.${ENDCOLOR}\n" + + for file in $requirements_files; do + echo -e "- $file" + done + + if [ "$(echo "$requirements_files" | wc -l)" -ne "$(echo "$projects" | wc -l)" ]; then + echo -e "${RED}The number of 'requirements.txt' files should match the number of Azure Function projects.${ENDCOLOR}\n" + fi + + echo -e "" + + fi + + else + cat /workspaces/app/requirements.txt > /tmp/requirements.txt + fi + + # INSTALL 'requirements.txt' FILES + + echo -e "${GREEN}> Update dependencies with PIP (requirements.txt).${ENDCOLOR}\n" + pip install -r /tmp/requirements.txt --upgrade + echo -e "Done\n" + + rm -f /tmp/requirements.txt /tmp/tmp_requirements.txt + + # INSTALL 'requirements-dev.txt' FILES + + echo -e "${GREEN}> Update dependencies with PIP (requirements-dev.txt).${ENDCOLOR}\n" + pip install -r /workspaces/app/requirements-dev.txt --upgrade + echo -e "Done\n" + + # INSTALL 'requirements-test.txt' FILES + + echo -e "${GREEN}> Update dependencies with PIP (requirements-test.txt).${ENDCOLOR}\n" + pip install -r /workspaces/app/requirements-test.txt --upgrade + echo -e "Done\n" + +fi + +if [ "$DEPENDENCY_MANAGER" = "poetry" ]; then + poetry update +fi