From cb2ff89cfc57622d6e07a50d4345693e24762b79 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 04:37:56 +0000 Subject: [PATCH 01/24] Add some stuff --- plugin_creator/__init__.py | 2 + plugin_creator/cli.py | 92 ++++++++++++++++++++++++++++++++++++ plugin_creator/helpers.py | 30 ++++++++++++ plugin_creator/validators.py | 37 +++++++++++++++ pyproject.toml | 3 ++ setup.cfg | 9 ++++ setup.py | 37 +++++++++++++++ 7 files changed, 210 insertions(+) create mode 100644 plugin_creator/__init__.py create mode 100644 plugin_creator/cli.py create mode 100644 plugin_creator/helpers.py create mode 100644 plugin_creator/validators.py create mode 100644 pyproject.toml create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/plugin_creator/__init__.py b/plugin_creator/__init__.py new file mode 100644 index 0000000..a51b95b --- /dev/null +++ b/plugin_creator/__init__.py @@ -0,0 +1,2 @@ + +PLUGIN_CREATOR_VERSION = "0.1.0" diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py new file mode 100644 index 0000000..59cc6b5 --- /dev/null +++ b/plugin_creator/cli.py @@ -0,0 +1,92 @@ + +import license +import questionary +from cookiecutter.main import cookiecutter + +import validators +from helpers import error, info, success, warning + + +def gather_info() -> dict: + """Gather project information from the user.""" + + context = {} + + # Basic project information + context['plugin_title'] = questionary.text( + "Enter plugin name", + default="Custom InvenTree Plugin", + validate=validators.ProjectNameValidator, + ).ask().strip() + + context['plugin_description'] = questionary.text( + "Enter plugin description", + default="A custom InvenTree plugin", + validate=validators.NotEmptyValidator, + ).ask().strip() + + context['plugin_name'] = context['plugin_title'].replace(" ", "") + + # Convert the project name to a package name + # e.g. 'Custom Plugin' -> 'custom_plugin' + context['plugin_slug'] = context['plugin_title'].replace(" ", "-").lower() + context['package_name'] = context['plugin_slug'].replace("-", "_") + + success(f"Generating plugin '{context['plugin_title']}' - {context['plugin_description']}") + info(f" - Package Name: {context['package_name']}") + + info("Enter author information:") + + context['author_name'] = questionary.text( + "Author name", + default="John Doe", + validate=validators.NotEmptyValidator, + ).ask().strip() + + context["author_email"] = questionary.text( + "Author email", + default="email@domain.org", + ).ask().strip() + + context["project_url"] = questionary.text( + "Project URL", + default="", + ).ask().strip() + + # Extract license information + available_licences = [l for l in license.iter()] + license_keys = [l.id for l in available_licences] + + context['license_key'] = questionary.select( + "Select a license", + default="MIT", + choices=license_keys, + ).ask() + + context['license_text'] = license.find( + context['license_key'] + ).render( + name=context['author_name'], + email=context['author_email'], + ) + + return context + +def main(): + """Run plugin scaffolding.""" + + info("InvenTree Plugin Creator Tool") + + context = gather_info() + + # Run cookiecutter template + cookiecutter( + 'plugin-cookiecutter/', + no_input=True, + output_dir=context['plugin_name'], + extra_context=context, + overwrite_if_exists=True + ) + +if __name__ == "__main__": + main() diff --git a/plugin_creator/helpers.py b/plugin_creator/helpers.py new file mode 100644 index 0000000..a7f105d --- /dev/null +++ b/plugin_creator/helpers.py @@ -0,0 +1,30 @@ +import questionary +import sys + + +def pretty_print(*args, color='green'): + """Print a message with a color.""" + questionary.print(" ".join(args), style=f'fg:{color}') + + +def error(*args, exit=True): + """Print an error message.""" + pretty_print(*args, color='red') + + if exit: + sys.exit(1) + + +def warning(*args): + """Print a warning message.""" + pretty_print(*args, color='yellow') + + +def success(*args): + """Print a success message.""" + pretty_print(*args, color='green') + + +def info(*args): + """Print an info message.""" + pretty_print(*args, color='blue') diff --git a/plugin_creator/validators.py b/plugin_creator/validators.py new file mode 100644 index 0000000..cf5dd11 --- /dev/null +++ b/plugin_creator/validators.py @@ -0,0 +1,37 @@ +import keyword +import re + +from questionary import Validator, ValidationError + + +class NotEmptyValidator(Validator): + """Ensure that the input is not empty.""" + + def validate(self, document): + """Ensure that the input is not empty.""" + if not document.text: + raise ValidationError(message="Input must not be empty") + + +class ProjectNameValidator(NotEmptyValidator): + """Validate the project name.""" + + def validate(self, document): + """Ensure that the project name is valid.""" + + super().validate(document) + + project_name = document.text + + if len(project_name) > 50: + raise ValidationError(message="Must be less than 50 characters long") + + # Must not be a reserved keyword + if keyword.iskeyword(project_name): + raise ValidationError(message="Must not be a reserved Python keyword") + + pattern = r"^[a-zA-Z_][a-zA-Z0-9 ]*$" + + if not re.match(pattern, project_name): + raise ValidationError(message=f"Must match pattern: '{pattern}'") + \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..7fd26b9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..09ea220 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,9 @@ +[flake8] +ignore = + # - W293 - blank lines contain whitespace + W293, + # - E501 - line too long (82 characters) + E501 + N802 +exclude = .git,__pycache__,dist,build,test.py +max-complexity = 20 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..b9b450d --- /dev/null +++ b/setup.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +import setuptools + +from plugin_creator import PLUGIN_CREATOR_VERSION + +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + +setuptools.setup( + name="inventree-plugin-creator", + version=PLUGIN_CREATOR_VERSION, + author="Oliver Walters", + author_email="oliver.henry.walters@gmail.com", + description="InvenTree plugin creator", + long_description=long_description, + long_description_content_type='text/markdown', + keywords="inventree plugin scaffold", + url="https://github.com/inventree/plugin-creator", + license="MIT", + packages=setuptools.find_packages(), + include_package_data=True, + install_requires=[ + 'cookiecutter', + 'questionary', + ], + setup_requires=[ + "wheel", + "twine", + ], + python_requires=">=3.9", + entry_points={ + "console_scripts": [ + "create-inventree-plugin = plugin_creator.cli:main" + ] + } +) \ No newline at end of file From 10f5810ab2693c1f2731a68408f325850aabe4e0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 04:39:44 +0000 Subject: [PATCH 02/24] Add workflow files --- .github/workflows/build.yaml | 27 ++++++++++++++++++++++ .github/workflows/pep.yaml | 28 +++++++++++++++++++++++ .github/workflows/pypi.yaml | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 .github/workflows/build.yaml create mode 100644 .github/workflows/pep.yaml create mode 100644 .github/workflows/pypi.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..be8b9a1 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,27 @@ +# Build the package, make sure it compiles OK + +name: Build Package + +on: + - push + - pull_request + +jobs: + + build: + name: Build Package + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install Python Dependencies + run: | + pip install --upgrade wheel setuptools twine build + - name: Build Binary + run: | + python -m build diff --git a/.github/workflows/pep.yaml b/.github/workflows/pep.yaml new file mode 100644 index 0000000..c28c344 --- /dev/null +++ b/.github/workflows/pep.yaml @@ -0,0 +1,28 @@ +name: Style Checks + +on: ["push", "pull_request"] + +jobs: + style: + runs-on: ubuntu-latest + + strategy: + max-parallel: 4 + matrix: + python-version: [3.9] + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install Deps + run: | + pip install invoke + pip install flake8 + pip install pep8-naming + - name: Style Checks + run: | + flake8 . diff --git a/.github/workflows/pypi.yaml b/.github/workflows/pypi.yaml new file mode 100644 index 0000000..45e4df5 --- /dev/null +++ b/.github/workflows/pypi.yaml @@ -0,0 +1,44 @@ +# Publish to PyPi package index + +name: PIP Publish + +on: + release: + types: [published] + +jobs: + + publish: + name: Publish to PyPi + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install Node.js + uses: actions/setup-node@v2 + with: + node-version: "18" + - name: Install Python Dependencies + run: | + pip install --upgrade wheel setuptools twine build + - name: Build Frontend + run: | + cd frontend + npm install + npm run build --emptyOutDir + - name: Build Binary + run: | + python -m build + - name: Publish + run: | + python -m twine upload dist/* + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + TWINE_REPOSITORY: pypi + \ No newline at end of file From 08c0c2f417db0c1411eda1f2bf88e3796b483df5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 04:49:17 +0000 Subject: [PATCH 03/24] Install / build / etc --- plugin_creator/__init__.py | 2 -- plugin_creator/cli.py | 21 +++++++++++++++++---- setup.py | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/plugin_creator/__init__.py b/plugin_creator/__init__.py index a51b95b..e69de29 100644 --- a/plugin_creator/__init__.py +++ b/plugin_creator/__init__.py @@ -1,2 +0,0 @@ - -PLUGIN_CREATOR_VERSION = "0.1.0" diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index 59cc6b5..fed848c 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -1,10 +1,14 @@ +import argparse +import os + import license import questionary from cookiecutter.main import cookiecutter -import validators -from helpers import error, info, success, warning + +from .import validators +from .helpers import error, info, success, warning def gather_info() -> dict: @@ -75,15 +79,24 @@ def gather_info() -> dict: def main(): """Run plugin scaffolding.""" + parser = argparse.ArgumentParser(description="InvenTree Plugin Creator Tool") + parser.add_argument("--default", action="store_true", help="Use default values for all prompts (non-interactive mode)") + parser.add_argument("--template", action="store", help="Specify input template file") + parser.add_argument('--output', action='store', help='Specify output directory', default='.') + + args = parser.parse_args() + info("InvenTree Plugin Creator Tool") context = gather_info() + output_dir = os.path.join(args.output, context['plugin_name']) + # Run cookiecutter template cookiecutter( - 'plugin-cookiecutter/', + args.template, no_input=True, - output_dir=context['plugin_name'], + output_dir=output_dir, extra_context=context, overwrite_if_exists=True ) diff --git a/setup.py b/setup.py index b9b450d..7642c36 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ import setuptools -from plugin_creator import PLUGIN_CREATOR_VERSION +PLUGIN_CREATOR_VERSION = "0.1.0" with open('README.md', encoding='utf-8') as f: long_description = f.read() From fe13a7a5cfc717e32bec79aed0ddceca84ed6bec Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 04:51:57 +0000 Subject: [PATCH 04/24] Support "default" values --- plugin_creator/cli.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index fed848c..1255693 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -88,9 +88,13 @@ def main(): info("InvenTree Plugin Creator Tool") - context = gather_info() - - output_dir = os.path.join(args.output, context['plugin_name']) + if args.default: + info("Using default values for all prompts...") + context = {} + output_dir = os.path.join(args.output, "MyCustomPlugin") + else: + context = gather_info() + output_dir = os.path.join(args.output, context['plugin_name']) # Run cookiecutter template cookiecutter( @@ -101,5 +105,7 @@ def main(): overwrite_if_exists=True ) + success(f"Plugin created -> '{output_dir}'") + if __name__ == "__main__": main() From 59300c5ad6e35607a1b2536eca3229acbbf2d0b2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 05:08:28 +0000 Subject: [PATCH 05/24] Generate mixins --- plugin_creator/cli.py | 15 ++++++++++++++- plugin_creator/mixins.py | 26 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 plugin_creator/mixins.py diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index 1255693..fdb94a4 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -7,8 +7,9 @@ from cookiecutter.main import cookiecutter -from .import validators from .helpers import error, info, success, warning +from . import mixins +from . import validators def gather_info() -> dict: @@ -16,6 +17,8 @@ def gather_info() -> dict: context = {} + info("Enter project information:") + # Basic project information context['plugin_title'] = questionary.text( "Enter plugin name", @@ -74,6 +77,16 @@ def gather_info() -> dict: email=context['author_email'], ) + # Plugin structure information + info("Enter plugin structure information:") + + context['plugin_mixins'] = { + 'mixin_list': questionary.checkbox( + "Select plugin mixins", + choices=mixins.available_mixins(), + ).ask() + } + return context def main(): diff --git a/plugin_creator/mixins.py b/plugin_creator/mixins.py new file mode 100644 index 0000000..2efafdf --- /dev/null +++ b/plugin_creator/mixins.py @@ -0,0 +1,26 @@ +"""InvenTree plugin mixin selection.""" + + +def available_mixins() -> list: + """Return a list of available plugin mixin classes.""" + return [ + 'APICallMixin', + 'ActionMixin', + 'AppMixin', + 'BarcodeMixin', + # 'BulkNotificationMethod', + 'CurrencyExchangeMixin', + 'EventMixin', + 'IconPackMixin', + 'LabelPrintingMixin', + 'LocateMixin', + # 'NavigationMixin', + 'ReportMixin', + 'ScheduleMixin', + 'SettingsMixin', + # 'SingleNotificationMethod', + 'SupplierBarcodeMixin', + 'UrlsMixin', + 'UserInterfaceMixin', + 'ValidationMixin', + ] \ No newline at end of file From 3e7c37a424e4310de2d9d14401d1a683fafef5e3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 05:28:14 +0000 Subject: [PATCH 06/24] PEP fixes --- plugin_creator/cli.py | 8 +++++--- plugin_creator/mixins.py | 2 +- setup.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index fdb94a4..698f513 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -7,7 +7,7 @@ from cookiecutter.main import cookiecutter -from .helpers import error, info, success, warning +from .helpers import info, success from . import mixins from . import validators @@ -61,8 +61,8 @@ def gather_info() -> dict: ).ask().strip() # Extract license information - available_licences = [l for l in license.iter()] - license_keys = [l.id for l in available_licences] + available_licences = [lic for lic in license.iter()] + license_keys = [lic.id for lic in available_licences] context['license_key'] = questionary.select( "Select a license", @@ -89,6 +89,7 @@ def gather_info() -> dict: return context + def main(): """Run plugin scaffolding.""" @@ -120,5 +121,6 @@ def main(): success(f"Plugin created -> '{output_dir}'") + if __name__ == "__main__": main() diff --git a/plugin_creator/mixins.py b/plugin_creator/mixins.py index 2efafdf..94b14f2 100644 --- a/plugin_creator/mixins.py +++ b/plugin_creator/mixins.py @@ -23,4 +23,4 @@ def available_mixins() -> list: 'UrlsMixin', 'UserInterfaceMixin', 'ValidationMixin', - ] \ No newline at end of file + ] diff --git a/setup.py b/setup.py index 7642c36..b545828 100644 --- a/setup.py +++ b/setup.py @@ -34,4 +34,4 @@ "create-inventree-plugin = plugin_creator.cli:main" ] } -) \ No newline at end of file +) From 7258f77968eb789ea0b4d3df9bfe85a74c0403bf Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 05:35:01 +0000 Subject: [PATCH 07/24] Update README.md --- README.md | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 18bcc2c..20dab64 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,33 @@ -# plugin-creator -Command line tool for scaffolding a new InvenTree plugin +# InvenTree Plugin Creator + +Command line tool for scaffolding a new InvenTree plugin. + +## Description + +This is a command line tool which allows for rapid scaffolding of a new InvenTree plugin. + +It uses the [cookiecutter project](https://cookiecutter.readthedocs.io/en/stable/) to generate a new project, based on a [custom template](https://github.com/inventree/plugin-cookiecutter). + +## Installation + +To install the plugin creator, run: + +```bash +pip install -U inventree-plugin-creator +``` + +## Usage + +To create a new plugin, run: + +```bash +create-inventree-plugin +``` + +This will prompt you for required information about the plugin you wish to create. + +To view the available options, run: + +```bash +create-inventree-plugin --help +``` From 7f164522ce59d3cc0784095c3fbeaf6beacfe830 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:22:11 +0000 Subject: [PATCH 08/24] Include template giles --- MANIFEST.in | 1 + plugin_creator/cli.py | 8 +- plugin_creator/template/cookiecutter.json | 16 ++ .../template/hooks/post_gen_project.py | 1 + .../template/hooks/pre_gen_project.py | 11 ++ plugin_creator/template/hooks/pre_prompt.py | 1 + .../{{ cookiecutter.plugin_name }}/.gitignore | 162 ++++++++++++++++++ .../{{ cookiecutter.plugin_name }}/LICENSE | 1 + .../MANIFEST.in | 0 .../{{ cookiecutter.plugin_name }}/README.md | 25 +++ .../pyproject.toml | 3 + .../{{ cookiecutter.plugin_name }}/setup.cfg | 9 + .../{{ cookiecutter.plugin_name }}/setup.py | 39 +++++ .../__init__.py | 3 + .../{{ cookiecutter.package_name }}/plugin.py | 33 ++++ 15 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 MANIFEST.in create mode 100644 plugin_creator/template/cookiecutter.json create mode 100644 plugin_creator/template/hooks/post_gen_project.py create mode 100644 plugin_creator/template/hooks/pre_gen_project.py create mode 100644 plugin_creator/template/hooks/pre_prompt.py create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/.gitignore create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/LICENSE create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/MANIFEST.in create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/README.md create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/pyproject.toml create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.cfg create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py create mode 100644 plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..e9cd345 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-include plugin_creator/template * \ No newline at end of file diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index 698f513..b575f27 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -95,7 +95,6 @@ def main(): parser = argparse.ArgumentParser(description="InvenTree Plugin Creator Tool") parser.add_argument("--default", action="store_true", help="Use default values for all prompts (non-interactive mode)") - parser.add_argument("--template", action="store", help="Specify input template file") parser.add_argument('--output', action='store', help='Specify output directory', default='.') args = parser.parse_args() @@ -110,9 +109,14 @@ def main(): context = gather_info() output_dir = os.path.join(args.output, context['plugin_name']) + src_path = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "template" + ) + # Run cookiecutter template cookiecutter( - args.template, + src_path, no_input=True, output_dir=output_dir, extra_context=context, diff --git a/plugin_creator/template/cookiecutter.json b/plugin_creator/template/cookiecutter.json new file mode 100644 index 0000000..963ae0f --- /dev/null +++ b/plugin_creator/template/cookiecutter.json @@ -0,0 +1,16 @@ +{ + "plugin_title": "My Custom Plugin", + "plugin_name": "MyCustomPlugin", + "plugin_slug": "my-custom-plugin", + "package_name": "my_custom_plugin", + "plugin_version": "0.1.0", + "plugin_mixins": { + "mixin_list": [] + }, + "plugin_description": "A short description of the project", + "project_url": "https://github.com/username/my-custom-plugin", + "author_name": "John Doe", + "author_email": "email@admin.org", + "license_key": "MIT", + "license_text": "..." +} \ No newline at end of file diff --git a/plugin_creator/template/hooks/post_gen_project.py b/plugin_creator/template/hooks/post_gen_project.py new file mode 100644 index 0000000..c43d698 --- /dev/null +++ b/plugin_creator/template/hooks/post_gen_project.py @@ -0,0 +1 @@ +# Custom hook which runs after the project has been generated \ No newline at end of file diff --git a/plugin_creator/template/hooks/pre_gen_project.py b/plugin_creator/template/hooks/pre_gen_project.py new file mode 100644 index 0000000..1c4dd17 --- /dev/null +++ b/plugin_creator/template/hooks/pre_gen_project.py @@ -0,0 +1,11 @@ +# Custom hook which runs before the project is generated + +from collections import OrderedDict + +data = {{ cookiecutter }} + +# Debug if needed +if False: + print("Cookiecutter data:") + for k, v in data.items(): + print(f" - {k}: {v}") \ No newline at end of file diff --git a/plugin_creator/template/hooks/pre_prompt.py b/plugin_creator/template/hooks/pre_prompt.py new file mode 100644 index 0000000..bae80d7 --- /dev/null +++ b/plugin_creator/template/hooks/pre_prompt.py @@ -0,0 +1 @@ +# custom hook which runs before user input is gathered \ No newline at end of file diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/.gitignore b/plugin_creator/template/{{ cookiecutter.plugin_name }}/.gitignore new file mode 100644 index 0000000..82f9275 --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/.gitignore @@ -0,0 +1,162 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/LICENSE b/plugin_creator/template/{{ cookiecutter.plugin_name }}/LICENSE new file mode 100644 index 0000000..94a4066 --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/LICENSE @@ -0,0 +1 @@ +{{ cookiecutter.license_text }} \ No newline at end of file diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/MANIFEST.in b/plugin_creator/template/{{ cookiecutter.plugin_name }}/MANIFEST.in new file mode 100644 index 0000000..e69de29 diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/README.md b/plugin_creator/template/{{ cookiecutter.plugin_name }}/README.md new file mode 100644 index 0000000..143a7ad --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/README.md @@ -0,0 +1,25 @@ +# {{ cookiecutter.plugin_name }} + +{{ cookiecutter.plugin_description }} + +## Installation + +### InvenTree Plugin Manager + +... todo ... + +### Command Line + +To install manually via the command line, run the following command: + +```bash +pip install {{ cookiecutter.plugin_slug }} +``` + +## Configuration + +... todo ... + +## Usage + +... todo ... diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/pyproject.toml b/plugin_creator/template/{{ cookiecutter.plugin_name }}/pyproject.toml new file mode 100644 index 0000000..7fd26b9 --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.cfg b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.cfg new file mode 100644 index 0000000..09ea220 --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.cfg @@ -0,0 +1,9 @@ +[flake8] +ignore = + # - W293 - blank lines contain whitespace + W293, + # - E501 - line too long (82 characters) + E501 + N802 +exclude = .git,__pycache__,dist,build,test.py +max-complexity = 20 diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py new file mode 100644 index 0000000..c24ce3e --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import setuptools + +from src.{{ cookiecutter.package_name }} import PLUGIN_VERSION + +with open('README.md', encoding='utf-8') as f: + long_description = f.read() + +setuptools.setup( + name="{{ cookiecutter.package_name }}", + version=PLUGIN_VERSION, + author="{{ cookiecutter.author_name }}", + {%- if cookiecutter.author_email %} + author_email="{{ cookiecutter.author_email }}", + {% endif -%} + description="{{ cookiecutter.plugin_description }}", + long_description=long_description, + long_description_content_type='text/markdown', + {%- if cookiecutter.project_url %} + url="{{ cookiecutter.project_url }}", + {% endif -%} + license="{{ cookiecutter.license_key }}", + packages=setuptools.find_packages(), + include_package_data=True, + install_requires=[ + # Enter your dependencies here + ], + setup_requires=[ + "wheel", + "twine", + ], + python_requires=">=3.9", + entry_points={ + "inventree_plugins": [ + "{{ cookiecutter.plugin_name }} = {{ cookiecutter.package_name }}.plugin:{{ cookiecutter.plugin_name }}" + ] + }, +) diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py new file mode 100644 index 0000000..b36e501 --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +PLUGIN_VERSION = {{ cookiecutter.plugin_version }} diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py new file mode 100644 index 0000000..cdc8cfc --- /dev/null +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py @@ -0,0 +1,33 @@ +"""{{ cookiecutter.plugin_description }}""" + +from plugin import InvenTreePlugin +{% if cookiecutter.plugin_mixins.mixin_list %} +from plugin.mixins import {{ cookiecutter.plugin_mixins.mixin_list | map('trim') | join(', ') }} +{% endif %} +from . import PLUGIN_VERSION +{% if cookiecutter.plugin_mixins.mixin_list %} +class {{ cookiecutter.plugin_name }}({{ cookiecutter.plugin_mixins.mixin_list | map('trim') | join(', ') }}, InvenTreePlugin): +{% else %} +class {{ cookiecutter.plugin_name }}(InvenTreePlugin): + {% endif -%} + """{{ cookiecutter.plugin_description }}""" + + # Plugin metadata + TITLE = "{{ cookiecutter.plugin_title }}" + NAME = "{{ cookiecutter.plugin_name }}" + SLUG = "{{ cookiecutter.plugin_slug }}" + + AUTHOR = "{{ cookiecutter.author_name }}" + DESCRIPTION = "{{ cookiecutter.plugin_description }}" + VERSION = PLUGIN_VERSION + + {%- if cookiecutter.plugin_mixins.mixin_list %} + + {%- if "SettingsMixin" in cookiecutter.plugin_mixins.mixin_list %} + # Plugin settings + SETTINGS = { + # Define your plugin settings here... + } + {% endif -%} + + {% endif -%} \ No newline at end of file From ac09f756e4e0a1c837af9a4af0586f46e1713c5c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:26:15 +0000 Subject: [PATCH 09/24] Test plugin builds correctly --- .github/workflows/build.yaml | 13 +++++++++++-- plugin_creator/cli.py | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index be8b9a1..58a4a09 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,6 +22,15 @@ jobs: - name: Install Python Dependencies run: | pip install --upgrade wheel setuptools twine build - - name: Build Binary + - name: Build Package run: | - python -m build + pip install . + - name: Generate plugin + run: | + create-inventree-plugin --default --output my_plugin + test -d my_plugin + test -d my_plugin/MyCustomPlugin + test -d my_plugin/MyCustomPlugin/src + test -f my_plugin/MyCustomPlugin/src/__init__.py + test -f my_plugin/MyCustomPlugin/src/plugin.py + test -f my_plugin/MyCustomPlugin/README.md diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index b575f27..6add869 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -104,16 +104,16 @@ def main(): if args.default: info("Using default values for all prompts...") context = {} - output_dir = os.path.join(args.output, "MyCustomPlugin") else: context = gather_info() - output_dir = os.path.join(args.output, context['plugin_name']) src_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), "template" ) + output_dir = os.path.abspath(args.output) + # Run cookiecutter template cookiecutter( src_path, From eadad0b6fe7048f67b3982a041409ccf86837ca2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:33:36 +0000 Subject: [PATCH 10/24] Load default values from json file --- README.md | 2 +- plugin_creator/cli.py | 41 +++++++++++++++++++++++----------------- plugin_creator/mixins.py | 12 ++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 20dab64..9787550 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Command line tool for scaffolding a new InvenTree plugin. This is a command line tool which allows for rapid scaffolding of a new InvenTree plugin. -It uses the [cookiecutter project](https://cookiecutter.readthedocs.io/en/stable/) to generate a new project, based on a [custom template](https://github.com/inventree/plugin-cookiecutter). +It uses the [cookiecutter project](https://cookiecutter.readthedocs.io/en/stable/) to generate a new project, based on a custom project template. ## Installation diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index 6add869..bb3dfbb 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -1,5 +1,6 @@ import argparse +import json import os import license @@ -12,23 +13,33 @@ from . import validators -def gather_info() -> dict: - """Gather project information from the user.""" +def default_values() -> dict: + """Read default values out from the cookiecutter.json file.""" + fn = os.path.join( + os.path.dirname(__file__), + "template", + "cookiecutter.json" + ) + + with open(fn, "r") as f: + return json.load(f) - context = {} + +def gather_info(context: dict) -> dict: + """Gather project information from the user.""" info("Enter project information:") # Basic project information context['plugin_title'] = questionary.text( "Enter plugin name", - default="Custom InvenTree Plugin", + default=context['plugin_title'], validate=validators.ProjectNameValidator, ).ask().strip() context['plugin_description'] = questionary.text( "Enter plugin description", - default="A custom InvenTree plugin", + default=context['plugin_description'], validate=validators.NotEmptyValidator, ).ask().strip() @@ -46,18 +57,18 @@ def gather_info() -> dict: context['author_name'] = questionary.text( "Author name", - default="John Doe", + default=context['author_name'], validate=validators.NotEmptyValidator, ).ask().strip() context["author_email"] = questionary.text( "Author email", - default="email@domain.org", + default=context["author_email"], ).ask().strip() context["project_url"] = questionary.text( "Project URL", - default="", + default=context['project_url'], ).ask().strip() # Extract license information @@ -81,10 +92,7 @@ def gather_info() -> dict: info("Enter plugin structure information:") context['plugin_mixins'] = { - 'mixin_list': questionary.checkbox( - "Select plugin mixins", - choices=mixins.available_mixins(), - ).ask() + 'mixin_list': mixins.get_mixins() } return context @@ -101,11 +109,10 @@ def main(): info("InvenTree Plugin Creator Tool") - if args.default: - info("Using default values for all prompts...") - context = {} - else: - context = gather_info() + context = default_values() + + if not args.default: + context = gather_info(context) src_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), diff --git a/plugin_creator/mixins.py b/plugin_creator/mixins.py index 94b14f2..16509cb 100644 --- a/plugin_creator/mixins.py +++ b/plugin_creator/mixins.py @@ -1,5 +1,7 @@ """InvenTree plugin mixin selection.""" +import questionary + def available_mixins() -> list: """Return a list of available plugin mixin classes.""" @@ -24,3 +26,13 @@ def available_mixins() -> list: 'UserInterfaceMixin', 'ValidationMixin', ] + + +def get_mixins() -> list: + """Ask user to select plugin mixins.""" + + return questionary.checkbox( + "Select plugin mixins", + choices=available_mixins(), + default="SettingsMixin" + ).ask() \ No newline at end of file From af852fb51e9c130a7406d1e83c6be8328560cde5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:34:11 +0000 Subject: [PATCH 11/24] Generated plugin should build and install --- .github/workflows/build.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 58a4a09..6bf1448 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -34,3 +34,7 @@ jobs: test -f my_plugin/MyCustomPlugin/src/__init__.py test -f my_plugin/MyCustomPlugin/src/plugin.py test -f my_plugin/MyCustomPlugin/README.md + - name: Build and Install Plugin + run: | + cd my_plugin + pip install . From 2b82e3e9fb541797ba86a69c89ace06d63edce75 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:45:03 +0000 Subject: [PATCH 12/24] Fix for generated files --- .github/workflows/build.yaml | 2 ++ .github/workflows/pep.yaml | 4 +--- plugin_creator/template/cookiecutter.json | 4 +++- .../src/{{ cookiecutter.package_name }}/__init__.py | 2 +- .../src/{{ cookiecutter.package_name }}/plugin.py | 9 ++++----- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6bf1448..55a9729 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -22,6 +22,7 @@ jobs: - name: Install Python Dependencies run: | pip install --upgrade wheel setuptools twine build + pip install flake8 pep8-naming - name: Build Package run: | pip install . @@ -37,4 +38,5 @@ jobs: - name: Build and Install Plugin run: | cd my_plugin + flake8 . pip install . diff --git a/.github/workflows/pep.yaml b/.github/workflows/pep.yaml index c28c344..4ca2770 100644 --- a/.github/workflows/pep.yaml +++ b/.github/workflows/pep.yaml @@ -20,9 +20,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install Deps run: | - pip install invoke - pip install flake8 - pip install pep8-naming + pip install flake8 pep8-naming - name: Style Checks run: | flake8 . diff --git a/plugin_creator/template/cookiecutter.json b/plugin_creator/template/cookiecutter.json index 963ae0f..907c0f4 100644 --- a/plugin_creator/template/cookiecutter.json +++ b/plugin_creator/template/cookiecutter.json @@ -5,7 +5,9 @@ "package_name": "my_custom_plugin", "plugin_version": "0.1.0", "plugin_mixins": { - "mixin_list": [] + "mixin_list": [ + "SettingsMixin" + ] }, "plugin_description": "A short description of the project", "project_url": "https://github.com/username/my-custom-plugin", diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py index b36e501..bde33f5 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -PLUGIN_VERSION = {{ cookiecutter.plugin_version }} +PLUGIN_VERSION = "{{ cookiecutter.plugin_version }}" diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py index cdc8cfc..88530f4 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py @@ -9,8 +9,8 @@ class {{ cookiecutter.plugin_name }}({{ cookiecutter.plugin_mixins.mixin_list | map('trim') | join(', ') }}, InvenTreePlugin): {% else %} class {{ cookiecutter.plugin_name }}(InvenTreePlugin): - {% endif -%} - """{{ cookiecutter.plugin_description }}""" + {% endif %} + """{{ cookiecutter.plugin_name }} - custom InvenTree plugin.""" # Plugin metadata TITLE = "{{ cookiecutter.plugin_title }}" @@ -22,9 +22,8 @@ class {{ cookiecutter.plugin_name }}(InvenTreePlugin): VERSION = PLUGIN_VERSION {%- if cookiecutter.plugin_mixins.mixin_list %} - - {%- if "SettingsMixin" in cookiecutter.plugin_mixins.mixin_list %} - # Plugin settings + {% if "SettingsMixin" in cookiecutter.plugin_mixins.mixin_list %} + # Plugin settings (from SettingsMixin) SETTINGS = { # Define your plugin settings here... } From 185790c583c7783283fb4462dd843ace0f86b817 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:54:38 +0000 Subject: [PATCH 13/24] Template file tweaks --- plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py | 2 +- .../{src => }/{{ cookiecutter.package_name }}/__init__.py | 0 .../{src => }/{{ cookiecutter.package_name }}/plugin.py | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename plugin_creator/template/{{ cookiecutter.plugin_name }}/{src => }/{{ cookiecutter.package_name }}/__init__.py (100%) rename plugin_creator/template/{{ cookiecutter.plugin_name }}/{src => }/{{ cookiecutter.package_name }}/plugin.py (100%) diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py index c24ce3e..880b0ea 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -2,7 +2,7 @@ import setuptools -from src.{{ cookiecutter.package_name }} import PLUGIN_VERSION +from {{ cookiecutter.package_name }} import PLUGIN_VERSION with open('README.md', encoding='utf-8') as f: long_description = f.read() diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/__init__.py similarity index 100% rename from plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/__init__.py rename to plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/__init__.py diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py similarity index 100% rename from plugin_creator/template/{{ cookiecutter.plugin_name }}/src/{{ cookiecutter.package_name }}/plugin.py rename to plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py From 4bb3c793ba5e7cfff1026ede229629ec83191b64 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:55:03 +0000 Subject: [PATCH 14/24] Fix package requirements --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index b545828..dab371b 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ include_package_data=True, install_requires=[ 'cookiecutter', + 'license', 'questionary', ], setup_requires=[ From b00a53dc81441f05893af30420096b1eb68546b2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 08:59:32 +0000 Subject: [PATCH 15/24] Fix for setup.py --- .../{{ cookiecutter.plugin_name }}/setup.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py index 880b0ea..826810f 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -1,8 +1,30 @@ # -*- coding: utf-8 -*- +import importlib +import importlib.util +import os import setuptools -from {{ cookiecutter.package_name }} import PLUGIN_VERSION + +def get_plugin_version() -> str: + """Read the plugin version from the source code.""" + module_path = os.path.join( + os.path.dirname(__file__), + "{{ cookiecutter.package_name }}", + "__init__.py" + ) + + spec = importlib.util.spec_from_file_location( + "{{ cookiecutter.package_name }}", + module_path + ) + + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + return module.PLUGIN_VERSION + +PLUGIN_VERSION = get_plugin_version() with open('README.md', encoding='utf-8') as f: long_description = f.read() @@ -24,7 +46,7 @@ packages=setuptools.find_packages(), include_package_data=True, install_requires=[ - # Enter your dependencies here + # Enter your plugin library dependencies here ], setup_requires=[ "wheel", From 250c4d4df5f03b20395a1d39df878fdb39942112 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:05:21 +0000 Subject: [PATCH 16/24] Enhanced templating --- plugin_creator/__init__.py | 3 +++ .../{{ cookiecutter.plugin_name }}/setup.py | 27 +++++-------------- setup.py | 13 +++++++-- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/plugin_creator/__init__.py b/plugin_creator/__init__.py index e69de29..e5dde8a 100644 --- a/plugin_creator/__init__.py +++ b/plugin_creator/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +PLUGIN_CREATOR_VERSION = "1.0.0" diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py index 826810f..8c9442c 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -5,33 +5,18 @@ import os import setuptools - -def get_plugin_version() -> str: - """Read the plugin version from the source code.""" - module_path = os.path.join( - os.path.dirname(__file__), - "{{ cookiecutter.package_name }}", - "__init__.py" - ) - - spec = importlib.util.spec_from_file_location( - "{{ cookiecutter.package_name }}", - module_path - ) - - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - return module.PLUGIN_VERSION - -PLUGIN_VERSION = get_plugin_version() +"""Read the plugin version from the source code.""" +module_path = os.path.join(os.path.dirname(__file__), "{{ cookiecutter.package_name }}", "__init__.py") +spec = importlib.util.spec_from_file_location("{{ cookiecutter.package_name }}", module_path) +{{ cookiecutter.package_name }} = importlib.util.module_from_spec(spec) +spec.loader.exec_module({{ cookiecutter.package_name }}) with open('README.md', encoding='utf-8') as f: long_description = f.read() setuptools.setup( name="{{ cookiecutter.package_name }}", - version=PLUGIN_VERSION, + version={{ cookiecutter.package_name }}.PLUGIN_VERSION, author="{{ cookiecutter.author_name }}", {%- if cookiecutter.author_email %} author_email="{{ cookiecutter.author_email }}", diff --git a/setup.py b/setup.py index dab371b..ae5e582 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,24 @@ # -*- coding: utf-8 -*- +import importlib +import importlib.util +import os import setuptools -PLUGIN_CREATOR_VERSION = "0.1.0" +# Read version number from source code +module_path = os.path.join(os.path.dirname(__file__), "plugin_creator", "__init__.py") +spec = importlib.util.spec_from_file_location("plugin_creator", module_path) +plugin_loader = importlib.util.module_from_spec(spec) +spec.loader.exec_module(plugin_loader) + +PLUGIN_CREATOR_VERSION = plugin_loader.PLUGIN_CREATOR_VERSION with open('README.md', encoding='utf-8') as f: long_description = f.read() setuptools.setup( name="inventree-plugin-creator", - version=PLUGIN_CREATOR_VERSION, + version=plugin_loader.PLUGIN_CREATOR_VERSION, author="Oliver Walters", author_email="oliver.henry.walters@gmail.com", description="InvenTree plugin creator", From 1804b6c1bc6cd98ac8cfaefc66517c33f585bec0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:09:17 +0000 Subject: [PATCH 17/24] Include creator version info --- plugin_creator/cli.py | 6 ++++-- plugin_creator/template/cookiecutter.json | 4 +++- .../template/{{ cookiecutter.plugin_name }}/setup.py | 6 ++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index bb3dfbb..3ad6bb6 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -7,11 +7,11 @@ import questionary from cookiecutter.main import cookiecutter - -from .helpers import info, success +from . import PLUGIN_CREATOR_VERSION from . import mixins from . import validators +from .helpers import info, success def default_values() -> dict: """Read default values out from the cookiecutter.json file.""" @@ -111,6 +111,8 @@ def main(): context = default_values() + context["plugin_creator_version"] = PLUGIN_CREATOR_VERSION + if not args.default: context = gather_info(context) diff --git a/plugin_creator/template/cookiecutter.json b/plugin_creator/template/cookiecutter.json index 907c0f4..6a55013 100644 --- a/plugin_creator/template/cookiecutter.json +++ b/plugin_creator/template/cookiecutter.json @@ -14,5 +14,7 @@ "author_name": "John Doe", "author_email": "email@admin.org", "license_key": "MIT", - "license_text": "..." + "license_text": "...", + "plugin_creator_version": "", + "plugin_creator_url": "https://github.com/inventree/plugin_creator" } \ No newline at end of file diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py index 8c9442c..d1daa2b 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -5,6 +5,12 @@ import os import setuptools +"""This file is used to package the {{ cookiecutter.plugin_name }} plugin. + +- It was generated by the InvenTree Plugin Creator tool - version {{ cookiecutter.plugin_creator_version }} +- Ref: {{ cookiecutter.plugin_creator_url }} +""" + """Read the plugin version from the source code.""" module_path = os.path.join(os.path.dirname(__file__), "{{ cookiecutter.package_name }}", "__init__.py") spec = importlib.util.spec_from_file_location("{{ cookiecutter.package_name }}", module_path) From c8900d025c631fa258b146e2a6b359aaf2770658 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:11:25 +0000 Subject: [PATCH 18/24] Ignore template dir for flake --- plugin_creator/cli.py | 1 + plugin_creator/mixins.py | 2 +- setup.cfg | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plugin_creator/cli.py b/plugin_creator/cli.py index 3ad6bb6..6df602a 100644 --- a/plugin_creator/cli.py +++ b/plugin_creator/cli.py @@ -13,6 +13,7 @@ from .helpers import info, success + def default_values() -> dict: """Read default values out from the cookiecutter.json file.""" fn = os.path.join( diff --git a/plugin_creator/mixins.py b/plugin_creator/mixins.py index 16509cb..13107b6 100644 --- a/plugin_creator/mixins.py +++ b/plugin_creator/mixins.py @@ -35,4 +35,4 @@ def get_mixins() -> list: "Select plugin mixins", choices=available_mixins(), default="SettingsMixin" - ).ask() \ No newline at end of file + ).ask() diff --git a/setup.cfg b/setup.cfg index 09ea220..31dcf56 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,5 +5,11 @@ ignore = # - E501 - line too long (82 characters) E501 N802 -exclude = .git,__pycache__,dist,build,test.py +exclude = + .git, + __pycache__, + plugin_creator/template, + dist, + build, + test.py max-complexity = 20 From 409bc28dc59357502e840a6c9f57d47b67624441 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:12:19 +0000 Subject: [PATCH 19/24] Fix newline --- plugin_creator/validators.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin_creator/validators.py b/plugin_creator/validators.py index cf5dd11..b97119e 100644 --- a/plugin_creator/validators.py +++ b/plugin_creator/validators.py @@ -34,4 +34,3 @@ def validate(self, document): if not re.match(pattern, project_name): raise ValidationError(message=f"Must match pattern: '{pattern}'") - \ No newline at end of file From 1128400928f5f2704b6285127879ebbb3eb3e362 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:16:14 +0000 Subject: [PATCH 20/24] Additional info --- .../{{ cookiecutter.package_name }}/plugin.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py index 88530f4..8526a31 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py @@ -16,11 +16,16 @@ class {{ cookiecutter.plugin_name }}(InvenTreePlugin): TITLE = "{{ cookiecutter.plugin_title }}" NAME = "{{ cookiecutter.plugin_name }}" SLUG = "{{ cookiecutter.plugin_slug }}" - - AUTHOR = "{{ cookiecutter.author_name }}" DESCRIPTION = "{{ cookiecutter.plugin_description }}" VERSION = PLUGIN_VERSION + # Additional project information + AUTHOR = "{{ cookiecutter.author_name }}" + {% if cookiecutter.project_url -%} + WEBSITE = "{{ cookiecutter.project_url }}" + {%- endif %} + LICENSE = "{{ cookiecutter.license_key }}" + {%- if cookiecutter.plugin_mixins.mixin_list %} {% if "SettingsMixin" in cookiecutter.plugin_mixins.mixin_list %} # Plugin settings (from SettingsMixin) From 41f74eba18e2e51b8cb78944135ea4d73a114ef8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 09:18:39 +0000 Subject: [PATCH 21/24] Update build script --- .github/workflows/build.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 55a9729..5c622d8 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -28,15 +28,15 @@ jobs: pip install . - name: Generate plugin run: | - create-inventree-plugin --default --output my_plugin - test -d my_plugin - test -d my_plugin/MyCustomPlugin - test -d my_plugin/MyCustomPlugin/src - test -f my_plugin/MyCustomPlugin/src/__init__.py - test -f my_plugin/MyCustomPlugin/src/plugin.py - test -f my_plugin/MyCustomPlugin/README.md + create-inventree-plugin --default --output output + test -d output || echo "Output directory not found" + test -d output/MyCustomPlugin || echo "Plugin directory not found" + test -d output/MyCustomPlugin/src || echo "Source directory not found" + test -f output/MyCustomPlugin/src/__init__.py || echo "__init__.py not found" + test -f output/MyCustomPlugin/src/plugin.py || echo "plugin.py not found" + test -f output/MyCustomPlugin/README.md || echo "README.md not found" - name: Build and Install Plugin run: | - cd my_plugin + cd output flake8 . pip install . From 632425f9cbb8cc789644b37c693ec5c98196f0f6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 11:19:45 +0000 Subject: [PATCH 22/24] Cleanup templated files --- .../template/{{ cookiecutter.plugin_name }}/setup.py | 2 +- .../{{ cookiecutter.package_name }}/plugin.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py index d1daa2b..8ac7428 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/setup.py @@ -15,7 +15,7 @@ module_path = os.path.join(os.path.dirname(__file__), "{{ cookiecutter.package_name }}", "__init__.py") spec = importlib.util.spec_from_file_location("{{ cookiecutter.package_name }}", module_path) {{ cookiecutter.package_name }} = importlib.util.module_from_spec(spec) -spec.loader.exec_module({{ cookiecutter.package_name }}) +spec.loader.exec_module({{ cookiecutter.package_name }}) with open('README.md', encoding='utf-8') as f: long_description = f.read() diff --git a/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py index 8526a31..d69fcab 100644 --- a/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py +++ b/plugin_creator/template/{{ cookiecutter.plugin_name }}/{{ cookiecutter.package_name }}/plugin.py @@ -5,6 +5,7 @@ from plugin.mixins import {{ cookiecutter.plugin_mixins.mixin_list | map('trim') | join(', ') }} {% endif %} from . import PLUGIN_VERSION + {% if cookiecutter.plugin_mixins.mixin_list %} class {{ cookiecutter.plugin_name }}({{ cookiecutter.plugin_mixins.mixin_list | map('trim') | join(', ') }}, InvenTreePlugin): {% else %} @@ -32,6 +33,5 @@ class {{ cookiecutter.plugin_name }}(InvenTreePlugin): SETTINGS = { # Define your plugin settings here... } - {% endif -%} - - {% endif -%} \ No newline at end of file + {%- endif -%} + {%- endif %} From 89d7a533678a5b3f29f5003121f0e201fa388a7a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 11:21:51 +0000 Subject: [PATCH 23/24] Hide unsupported mixin classes --- plugin_creator/mixins.py | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/plugin_creator/mixins.py b/plugin_creator/mixins.py index 13107b6..ded4611 100644 --- a/plugin_creator/mixins.py +++ b/plugin_creator/mixins.py @@ -5,26 +5,28 @@ def available_mixins() -> list: """Return a list of available plugin mixin classes.""" + + # TODO: Support the commented out mixins + return [ - 'APICallMixin', - 'ActionMixin', - 'AppMixin', - 'BarcodeMixin', + # 'APICallMixin', + # 'ActionMixin', + # 'AppMixin', + # 'BarcodeMixin', # 'BulkNotificationMethod', - 'CurrencyExchangeMixin', - 'EventMixin', - 'IconPackMixin', - 'LabelPrintingMixin', - 'LocateMixin', + # 'CurrencyExchangeMixin', + # 'EventMixin', + # 'IconPackMixin', + # 'LabelPrintingMixin', + # 'LocateMixin', # 'NavigationMixin', - 'ReportMixin', - 'ScheduleMixin', + # 'ReportMixin', + # 'ScheduleMixin', 'SettingsMixin', - # 'SingleNotificationMethod', - 'SupplierBarcodeMixin', - 'UrlsMixin', - 'UserInterfaceMixin', - 'ValidationMixin', + # 'SupplierBarcodeMixin', + # 'UrlsMixin', + # 'UserInterfaceMixin', + # 'ValidationMixin', ] From 51fd414935341626d09e9b153b9c5705d104e84a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 30 Jan 2025 11:24:09 +0000 Subject: [PATCH 24/24] Fix build.yaml --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 5c622d8..598dacb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -37,6 +37,6 @@ jobs: test -f output/MyCustomPlugin/README.md || echo "README.md not found" - name: Build and Install Plugin run: | - cd output + cd output/MyCustomPlugin flake8 . pip install .