From ca1a5ce12df29e09bb0309c6f7d801c88fb81ab9 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 18:57:24 +0100 Subject: [PATCH 01/11] feat: copy config files to new app --- frappe/utils/boilerplate.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 80edc67a0f..7fcd4bfcb6 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -8,6 +8,7 @@ import os import pathlib import re import textwrap +from pathlib import Path import click import git @@ -116,6 +117,13 @@ def get_license_text(license_name: str) -> str: return license_name +def copy_from_frappe(rel_path: str, new_app_path: str): + """Copy files from frappe app to new app.""" + src = Path(frappe.get_app_path("frappe", "..")) / rel_path + target = Path(new_app_path) / rel_path + Path(target).write_text(Path(src).read_text()) + + def _create_app_boilerplate(dest, hooks, no_git=False): frappe.create_folder( os.path.join(dest, hooks.app_name, hooks.app_name, frappe.scrub(hooks.app_title)), @@ -172,6 +180,10 @@ def _create_app_boilerplate(dest, hooks, no_git=False): app_directory = os.path.join(dest, hooks.app_name) + copy_from_frappe(".editorconfig", app_directory) + copy_from_frappe(".eslintrc", app_directory) + copy_from_frappe(".flake8", app_directory) + if hooks.create_github_workflow: _create_github_workflow_files(dest, hooks) From c522b938d729120b560818d64e4270935a9f230c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:11:42 +0100 Subject: [PATCH 02/11] feat: create pre-commit config for new app --- frappe/utils/boilerplate.py | 85 +++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 7fcd4bfcb6..4f8943ca12 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -152,6 +152,9 @@ def _create_app_boilerplate(dest, hooks, no_git=False): with open(os.path.join(dest, hooks.app_name, "pyproject.toml"), "w") as f: f.write(frappe.as_unicode(pyproject_template.format(**hooks))) + with open(os.path.join(dest, hooks.app_name, ".pre-commit-config.yaml"), "w") as f: + f.write(frappe.as_unicode(precommit_template.format(**hooks))) + with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: f.write( frappe.as_unicode( @@ -690,3 +693,85 @@ patches_template = """[pre_model_sync] [post_model_sync] # Patches added in this section will be executed after doctypes are migrated""" + + +precommit_template = """exclude: 'node_modules|.git' +default_stages: [commit] +fail_fast: false + + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + files: "{app_name}.*" + exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" + - id: check-yaml + - id: check-merge-conflict + - id: check-ast + - id: check-json + - id: check-toml + - id: check-yaml + - id: debug-statements + + - repo: https://github.com/asottile/pyupgrade + rev: v3.9.0 + hooks: + - id: pyupgrade + args: ['--py310-plus'] + + - repo: https://github.com/frappe/black + rev: 951ccf4d5bb0d692b457a5ebc4215d755618eb68 + hooks: + - id: black + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier + types_or: [javascript, vue, scss] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + {app_name}/public/dist/.*| + .*node_modules.*| + .*boilerplate.*| + {app_name}/templates/includes/.*| + {app_name}/public/js/lib/.* + )$ + + + - repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.44.0 + hooks: + - id: eslint + types_or: [javascript] + args: ['--quiet'] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + {app_name}/public/dist/.*| + cypress/.*| + .*node_modules.*| + .*boilerplate.*| + {app_name}/templates/includes/.*| + {app_name}/public/js/lib/.* + )$ + + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + additional_dependencies: ['flake8-bugbear',] + +ci: + autoupdate_schedule: weekly + skip: [] + submodules: false +""" From 0ffa95c73ed6e117d22590f7ad69bfb7aaf69338 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:39:42 +0100 Subject: [PATCH 03/11] refactor: README template --- frappe/utils/boilerplate.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 4f8943ca12..2cfa31c588 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -156,13 +156,8 @@ def _create_app_boilerplate(dest, hooks, no_git=False): f.write(frappe.as_unicode(precommit_template.format(**hooks))) with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: - f.write( - frappe.as_unicode( - "## {}\n\n{}\n\n#### License\n\n{}".format( - hooks.app_title, hooks.app_description, hooks.app_license - ) - ) - ) + f.write(frappe.as_unicode(readme_template.format(**hooks))) + license_body = get_license_text(license_name=hooks.app_license) with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f: f.write(frappe.as_unicode(license_body)) @@ -775,3 +770,12 @@ ci: skip: [] submodules: false """ + +readme_template = """### {app_title} + +{app_description} + +### License + +{app_license} +""" From 670d66210b943f75fcba512bd6814d16bb3c776a Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:44:24 +0100 Subject: [PATCH 04/11] refactor: _create_app_boilerplate Loop over dict items instead of copying the same code for each file. --- frappe/utils/boilerplate.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 2cfa31c588..577c70b89f 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -146,24 +146,20 @@ def _create_app_boilerplate(dest, hooks, no_git=False): with open(os.path.join(dest, hooks.app_name, hooks.app_name, "public", ".gitkeep"), "w") as f: f.write("") - with open(os.path.join(dest, hooks.app_name, hooks.app_name, "__init__.py"), "w") as f: - f.write(frappe.as_unicode(init_template)) - - with open(os.path.join(dest, hooks.app_name, "pyproject.toml"), "w") as f: - f.write(frappe.as_unicode(pyproject_template.format(**hooks))) - - with open(os.path.join(dest, hooks.app_name, ".pre-commit-config.yaml"), "w") as f: - f.write(frappe.as_unicode(precommit_template.format(**hooks))) - - with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: - f.write(frappe.as_unicode(readme_template.format(**hooks))) - license_body = get_license_text(license_name=hooks.app_license) - with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f: - f.write(frappe.as_unicode(license_body)) - with open(os.path.join(dest, hooks.app_name, hooks.app_name, "modules.txt"), "w") as f: - f.write(frappe.as_unicode(hooks.app_title)) + TEMPLATE_MAP = { + "__init__.py": init_template, + "pyproject.toml": pyproject_template, + ".pre-commit-config.yaml": precommit_template, + "README.md": readme_template, + "license.txt": license_body, + "modules.txt": hooks.app_title, + } + + for filename, template in TEMPLATE_MAP.items(): + with open(os.path.join(dest, hooks.app_name, filename), "w") as f: + f.write(frappe.as_unicode(template.format(**hooks))) # These values could contain quotes and can break string declarations # So escaping them before setting variables in setup.py and hooks.py From 3efad0912308e0acb0b6ff8d444eb8c6ec46bb44 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:49:29 +0100 Subject: [PATCH 05/11] feat: add info about pre-commit to README --- frappe/utils/boilerplate.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 577c70b89f..0ce075b189 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -771,6 +771,24 @@ readme_template = """### {app_title} {app_description} +### Contributing + +This app uses `pre-commit` for code formatting and linting. Please [install pre-commit](https://pre-commit.com/#installation) and enable it for this repository: + +```bash +cd apps/{app_name} +pre-commit install +``` + +Pre-commit is configured to use the following tools for checking and formatting your code: + +- black +- isort +- flake8 +- eslint +- prettier +- pyupgrade + ### License {app_license} From f2a936c2e1d0c627ef6190ff63d251cf80424502 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:04:07 +0100 Subject: [PATCH 06/11] Revert "refactor: _create_app_boilerplate" This reverts commit 670d66210b943f75fcba512bd6814d16bb3c776a. --- frappe/utils/boilerplate.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 0ce075b189..a36d64d74e 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -146,20 +146,24 @@ def _create_app_boilerplate(dest, hooks, no_git=False): with open(os.path.join(dest, hooks.app_name, hooks.app_name, "public", ".gitkeep"), "w") as f: f.write("") + with open(os.path.join(dest, hooks.app_name, hooks.app_name, "__init__.py"), "w") as f: + f.write(frappe.as_unicode(init_template)) + + with open(os.path.join(dest, hooks.app_name, "pyproject.toml"), "w") as f: + f.write(frappe.as_unicode(pyproject_template.format(**hooks))) + + with open(os.path.join(dest, hooks.app_name, ".pre-commit-config.yaml"), "w") as f: + f.write(frappe.as_unicode(precommit_template.format(**hooks))) + + with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: + f.write(frappe.as_unicode(readme_template.format(**hooks))) + license_body = get_license_text(license_name=hooks.app_license) + with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f: + f.write(frappe.as_unicode(license_body)) - TEMPLATE_MAP = { - "__init__.py": init_template, - "pyproject.toml": pyproject_template, - ".pre-commit-config.yaml": precommit_template, - "README.md": readme_template, - "license.txt": license_body, - "modules.txt": hooks.app_title, - } - - for filename, template in TEMPLATE_MAP.items(): - with open(os.path.join(dest, hooks.app_name, filename), "w") as f: - f.write(frappe.as_unicode(template.format(**hooks))) + with open(os.path.join(dest, hooks.app_name, hooks.app_name, "modules.txt"), "w") as f: + f.write(frappe.as_unicode(hooks.app_title)) # These values could contain quotes and can break string declarations # So escaping them before setting variables in setup.py and hooks.py From f1f85f63a31c4d2ef595eea095e4e180018ed654 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 18 Dec 2023 20:11:56 +0100 Subject: [PATCH 07/11] feat: ask for branch name Default to frappe's branch name (version-15 -> version-15) to clearly communicate compatibility. --- frappe/utils/boilerplate.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index a36d64d74e..4c9df88005 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -15,6 +15,7 @@ import git import requests import frappe +from frappe.utils.change_log import get_app_branch APP_TITLE_PATTERN = re.compile(r"^(?![\W])[^\d_\s][\w -]+$", flags=re.UNICODE) @@ -57,6 +58,7 @@ def _get_user_inputs(app_name): "default": False, "type": bool, }, + "branch_name": {"prompt": "Branch Name", "default": get_app_branch("frappe")}, } for property, config in new_app_config.items(): @@ -190,7 +192,7 @@ def _create_app_boilerplate(dest, hooks, no_git=False): f.write(frappe.as_unicode(gitignore_template.format(app_name=hooks.app_name))) # initialize git repository - app_repo = git.Repo.init(app_directory, initial_branch="develop") + app_repo = git.Repo.init(app_directory, initial_branch=hooks.branch_name) app_repo.git.add(A=True) app_repo.index.commit("feat: Initialize App") @@ -775,6 +777,16 @@ readme_template = """### {app_title} {app_description} +### Installation + +You can install this app using the [bench](https://github.com/frappe/bench) CLI: + +```bash +cd $PATH_TO_YOUR_BENCH +bench get-app $URL_OF_THIS_REPO --branch {branch_name} +bench install-app {app_name} +``` + ### Contributing This app uses `pre-commit` for code formatting and linting. Please [install pre-commit](https://pre-commit.com/#installation) and enable it for this repository: From 9845777170bfe4f9ab6fe20785d342ba56659c8e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 26 Dec 2023 16:36:49 +0100 Subject: [PATCH 08/11] test: create app - add `branch_name` to `default_hooks`; use `default_hooks` in tests - convert `default_user_input` to list, add `branch_name` --- frappe/tests/test_boilerplate.py | 62 ++++++++++++-------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py index c46a6584c4..beba381b7a 100644 --- a/frappe/tests/test_boilerplate.py +++ b/frappe/tests/test_boilerplate.py @@ -33,22 +33,20 @@ class TestBoilerPlate(unittest.TestCase): "app_publisher": "Test Publisher", "app_email": "example@example.org", "app_license": "mit", + "branch_name": "develop", "create_github_workflow": False, } ) - cls.default_user_input = frappe._dict( - { - "title": "Test App", - "description": "This app's description contains 'single quotes' and \"double quotes\".", - "publisher": "Test Publisher", - "email": "example@example.org", - "icon": "", # empty -> default - "color": "", - "app_license": "mit", - "github_workflow": "n", - } - ) + cls.default_user_input = [ + "", # title (accept default) + "This app's description contains 'single quotes' and \"double quotes\".", # + "Test Publisher", # publisher + "example@example.org", # email + "", # license (accept default) + "", # create github workflow (accept default) + "develop", # branch name + ] cls.bench_path = frappe.utils.get_bench_path() cls.apps_dir = os.path.join(cls.bench_path, "apps") @@ -86,7 +84,7 @@ class TestBoilerPlate(unittest.TestCase): @staticmethod def get_user_input_stream(inputs): user_inputs = [] - for value in inputs.values(): + for value in inputs: if isinstance(value, list): user_inputs.extend(value) else: @@ -99,14 +97,13 @@ class TestBoilerPlate(unittest.TestCase): self.assertDictEqual(hooks, self.default_hooks) def test_invalid_inputs(self): - invalid_inputs = copy.copy(self.default_user_input).update( - { - "title": ["1nvalid Title", "valid title"], - "email": ["notavalidemail", "what@is@this.email", "example@example.org"], - } - ) + invalid_inputs = copy.copy(self.default_user_input) + invalid_inputs[0] = ["1nvalid Title", "valid title"] + invalid_inputs[3] = ["notavalidemail", "what@is@this.email", "example@example.org"] + with patch("sys.stdin", self.get_user_input_stream(invalid_inputs)): hooks = _get_user_inputs(self.default_hooks.app_name) + self.assertEqual(hooks.app_title, "valid title") self.assertEqual(hooks.app_email, "example@example.org") @@ -118,17 +115,9 @@ class TestBoilerPlate(unittest.TestCase): ) def test_create_app(self): app_name = "test_app" - - hooks = frappe._dict( - { - "app_name": app_name, - "app_title": "Test App", - "app_description": "This app's description contains 'single quotes' and \"double quotes\".", - "app_publisher": "Test Publisher", - "app_email": "example@example.org", - "app_license": "mit", - } - ) + hooks = self.default_hooks.copy() + hooks.app_name = app_name + del hooks["create_github_workflow"] self.create_app(hooks) new_app_dir = os.path.join(self.bench_path, self.apps_dir, app_name) @@ -152,17 +141,10 @@ class TestBoilerPlate(unittest.TestCase): ) def test_create_app_without_git_init(self): app_name = "test_app_no_git" + hooks = self.default_hooks.copy() + hooks.app_name = app_name + del hooks["create_github_workflow"] - hooks = frappe._dict( - { - "app_name": app_name, - "app_title": "Test App", - "app_description": "This app's description contains 'single quotes' and \"double quotes\".", - "app_publisher": "Test Publisher", - "app_email": "example@example.org", - "app_license": "mit", - } - ) self.create_app(hooks, no_git=True) new_app_dir = os.path.join(self.apps_dir, app_name) From 95d0fb57cd0bc2eb0b4dbce6669db69d0b9ebc07 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 14 Jan 2024 17:36:47 +0100 Subject: [PATCH 09/11] feat: linter workflow for new apps --- .github/workflows/linters.yml | 1 + frappe/utils/boilerplate.py | 88 +++++++++++++++++++++++++++++++++-- 2 files changed, 85 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 8b35471d0c..5c7f33e87d 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -1,3 +1,4 @@ +# When updating this file, please also update the linter_workflow_template in frappe/utils/boilerplate.py name: Linters on: diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 88d96f41c7..8f0c6431c3 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -157,9 +157,6 @@ def _create_app_boilerplate(dest, hooks, no_git=False): with open(os.path.join(dest, hooks.app_name, ".pre-commit-config.yaml"), "w") as f: f.write(frappe.as_unicode(precommit_template.format(**hooks))) - with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: - f.write(frappe.as_unicode(readme_template.format(**hooks))) - license_body = get_license_text(license_name=hooks.app_license) with open(os.path.join(dest, hooks.app_name, "license.txt"), "w") as f: f.write(frappe.as_unicode(license_body)) @@ -186,6 +183,12 @@ def _create_app_boilerplate(dest, hooks, no_git=False): if hooks.create_github_workflow: _create_github_workflow_files(dest, hooks) + hooks.readme_ci_section = readme_ci_section + else: + hooks.readme_ci_section = "" + + with open(os.path.join(dest, hooks.app_name, "README.md"), "w") as f: + f.write(frappe.as_unicode(readme_template.format(**hooks))) if not no_git: with open(os.path.join(dest, hooks.app_name, ".gitignore"), "w") as f: @@ -207,6 +210,10 @@ def _create_github_workflow_files(dest, hooks): with open(ci_workflow, "w") as f: f.write(github_workflow_template.format(**hooks)) + linter_workflow = workflows_path / "linter.yml" + with open(linter_workflow, "w") as f: + f.write(linter_workflow_template) + PATCH_TEMPLATE = textwrap.dedent( ''' @@ -771,6 +778,69 @@ ci: submodules: false """ +linter_workflow_template = """ +name: Linters + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + linter: + name: 'Frappe Linter' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + cache: pip + - uses: pre-commit/action@v3.0.0 + + - name: Download Semgrep rules + run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules + + - name: Run Semgrep rules + run: | + pip install semgrep + semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness + + deps-vulnerable-check: + name: 'Vulnerable Dependency Check' + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + + - uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Install and run pip-audit + run: | + pip install pip-audit + cd ${GITHUB_WORKSPACE} + pip-audit --desc on . +""" + readme_template = """### {app_title} {app_description} @@ -802,8 +872,18 @@ Pre-commit is configured to use the following tools for checking and formatting - eslint - prettier - pyupgrade - +{readme_ci_section} ### License {app_license} """ + +readme_ci_section = """ +### CI + +This app can use GitHub Actions for CI. The following workflows are configured: + +- CI: Installs this app and runs unit tests on every push to `develop` branch. +- Linters: Runs [Frappe Semgrep Rules](https://github.com/frappe/semgrep-rules) and [pip-audit](https://pypi.org/project/pip-audit/) on every pull request. + +""" From 1d4c61aa86abac7d6a4ac77da556060e17552317 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 29 Jan 2024 15:18:08 +0100 Subject: [PATCH 10/11] test: adjust param for `get_user_input_stream` --- frappe/tests/test_boilerplate.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frappe/tests/test_boilerplate.py b/frappe/tests/test_boilerplate.py index beba381b7a..4f41e178a2 100644 --- a/frappe/tests/test_boilerplate.py +++ b/frappe/tests/test_boilerplate.py @@ -180,13 +180,13 @@ class TestBoilerPlate(unittest.TestCase): os.access(frappe.get_app_path("frappe"), os.W_OK), "Only run if frappe app paths is writable" ) def test_new_patch_util(self): - user_inputs = { - "app_name": "frappe", - "doctype": "User", - "docstring": "Delete all users", - "file_name": "", # Accept default - "patch_folder_confirmation": "Y", - } + user_inputs = [ + "frappe", # app name + "User", # doctype + "Delete all users", # docstring + "", # file_name: accept default + "Y", # confirm patch folder + ] patches_txt = pathlib.Path(pathlib.Path(frappe.get_app_path("frappe", "patches.txt"))) original_patches = patches_txt.read_text() From d67066dfaeddc14e358ceae5e04e809cec5538ed Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 18 Feb 2024 16:42:41 +0100 Subject: [PATCH 11/11] fix: switch to ruff --- frappe/utils/boilerplate.py | 66 ++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/frappe/utils/boilerplate.py b/frappe/utils/boilerplate.py index 733902e60a..f7bee66008 100644 --- a/frappe/utils/boilerplate.py +++ b/frappe/utils/boilerplate.py @@ -177,7 +177,6 @@ def _create_app_boilerplate(dest, hooks, no_git=False): copy_from_frappe(".editorconfig", app_directory) copy_from_frappe(".eslintrc", app_directory) - copy_from_frappe(".flake8", app_directory) if hooks.create_github_workflow: _create_github_workflow_files(dest, hooks) @@ -343,6 +342,41 @@ build-backend = "flit_core.buildapi" # These dependencies are only installed when developer mode is enabled [tool.bench.dev-dependencies] # package_name = "~=1.1.0" + +[tool.ruff] +line-length = 110 +target-version = "py310" + +[tool.ruff.lint] +select = [ + "F", + "E", + "W", + "I", + "UP", + "B", +] +ignore = [ + "B017", # assertRaises(Exception) - should be more specific + "B018", # useless expression, not assigned to anything + "B023", # function doesn't bind loop variable - will have last iteration's value + "B904", # raise inside except without from + "E101", # indentation contains mixed spaces and tabs + "E402", # module level import not at top of file + "E501", # line too long + "E741", # ambiguous variable name + "F401", # "unused" imports + "F403", # can't detect undefined names from * import + "F405", # can't detect undefined names from * import + "F722", # syntax error in forward type annotation + "F821", # undefined name + "W191", # indentation contains tabs +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "tab" +docstring-code-format = true """ hooks_template = """app_name = "{app_name}" @@ -715,16 +749,15 @@ repos: - id: check-yaml - id: debug-statements - - repo: https://github.com/asottile/pyupgrade - rev: v3.9.0 + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.2.0 hooks: - - id: pyupgrade - args: ['--py310-plus'] + - id: ruff + name: "Run ruff linter and apply fixes" + args: ["--fix"] - - repo: https://github.com/frappe/black - rev: 951ccf4d5bb0d692b457a5ebc4215d755618eb68 - hooks: - - id: black + - id: ruff-format + name: "Format Python code" - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.7.1 @@ -759,17 +792,6 @@ repos: {app_name}/public/js/lib/.* )$ - - repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - - - repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 - hooks: - - id: flake8 - additional_dependencies: ['flake8-bugbear',] - ci: autoupdate_schedule: weekly skip: [] @@ -864,9 +886,7 @@ pre-commit install Pre-commit is configured to use the following tools for checking and formatting your code: -- black -- isort -- flake8 +- ruff - eslint - prettier - pyupgrade