From bd30e5a9fca6ce04347e3ae69de59c2c4332abd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89rico=20Andrei?= Date: Fri, 17 May 2024 12:54:03 +0200 Subject: [PATCH] Fix project generation --- backend_addon/Makefile | 34 ++++++--- backend_addon/cookiecutter.json | 1 + backend_addon/hooks/post_gen_project.py | 76 +++++++++---------- backend_addon/tests/conftest.py | 10 +++ backend_addon/tests/test_cutter.py | 32 ++++++++ .../.github/workflows/meta.yml | 14 +++- .../.meta.toml | 23 ++++-- .../MANIFEST.in | 2 + .../{{ cookiecutter.__folder_name }}/Makefile | 16 ++-- .../pyproject.toml | 21 +++-- .../scripts/create_site.py | 11 +-- .../{{ cookiecutter.__folder_name }}/setup.py | 5 +- .../profiles/default/metadata.xml | 5 +- project/cookiecutter.json | 3 + project/hooks/post_gen_project.py | 63 +++++++++++---- project/tests/conftest.py | 9 +++ project/tests/test_cutter.py | 16 ++++ project/tests/test_project_backend.py | 34 ++++++++- project/tests/test_project_cache.py | 31 ++++++++ .../backend/Dockerfile | 2 +- .../backend/Dockerfile.acceptance | 2 +- .../backend/instance.yaml | 6 -- requirements.txt | 2 +- sub/cache/hooks/post_gen_project.py | 49 ++++++++++++ 24 files changed, 359 insertions(+), 108 deletions(-) rename {project/{{ cookiecutter.__folder_name }}/backend => backend_addon/{{ cookiecutter.__folder_name }}}/scripts/create_site.py (81%) delete mode 100644 project/{{ cookiecutter.__folder_name }}/backend/instance.yaml create mode 100644 sub/cache/hooks/post_gen_project.py diff --git a/backend_addon/Makefile b/backend_addon/Makefile index f1124d7..cdfece5 100644 --- a/backend_addon/Makefile +++ b/backend_addon/Makefile @@ -12,6 +12,11 @@ YELLOW=`tput setaf 3` .PHONY: all all: build +TEMPLATE = $(shell basename $(CURRENT_DIR)) +ADDON_FOLDER_NAME = collective.addon +BASE_FOLDER = ../ +BIN_FOLDER = ${BASE_FOLDER}/bin + # Add the following 'help' target to your Makefile # And add help text after each target name starting with '\#\#' @@ -21,24 +26,29 @@ help: ## This help message .PHONY: clean clean: ## Clean - rm -rf collective.addon + rm -rf volto-addon -../bin/cookieplone: ## cookieplone installation - $(MAKE) -C ".." bin/cookieplone +$(BIN_FOLDER)/cookieplone: ## cookieplone installation + $(MAKE) -C $(BASE_FOLDER) bin/cookieplone .PHONY: format -format: ../bin/cookieplone ## Format code +format: $(BIN_FOLDER)/cookieplone ## Format code @echo "$(GREEN)==> Formatting codebase $(RESET)" - ../bin/black hooks tests - ../bin/isort hooks tests + $(BIN_FOLDER)/black hooks tests + $(BIN_FOLDER)/isort hooks tests .PHONY: generate -generate: ../bin/cookieplone ## Create a sample package +generate: $(BIN_FOLDER)/cookieplone ## Create a sample package @echo "$(GREEN)==> Creating new test package$(RESET)" - rm -rf collective.addon - ../bin/cookieplone . --no-input + rm -rf $(ADDON_FOLDER_NAME) + COOKIEPLONE_REPOSITORY=$(BASE_FOLDER) $(BIN_FOLDER)/cookieplone $(TEMPLATE) --no_input .PHONY: test -test: ../bin/cookieplone ## Create a sample package and tests it - @echo "$(GREEN)==> Creating new test package$(RESET)" - ../bin/python -m pytest tests +test: $(BIN_FOLDER)/cookieplone ## Create a sample package and tests it + @echo "$(GREEN)==> Test template$(RESET)" + $(BIN_FOLDER)/python -m pytest tests + +.PHONY: test-pdb +test-pdb: $(BIN_FOLDER)/cookieplone ## Stop on the first failed test + @echo "$(GREEN)==> Test template, stop on first error$(RESET)" + $(BIN_FOLDER)/python -m pytest tests -x --pdb diff --git a/backend_addon/cookiecutter.json b/backend_addon/cookiecutter.json index 8485539..42ee090 100644 --- a/backend_addon/cookiecutter.json +++ b/backend_addon/cookiecutter.json @@ -13,6 +13,7 @@ "__package_namespace": "{{ cookiecutter.python_package_name | package_namespace }}", "__folder_name": "{{ cookiecutter.python_package_name }}", "__python_package_name_upper": "{{ cookiecutter.python_package_name | pascal_case }}", + "__profile_language": "en", "__version_package": "1.0.0a0", "__profile_version": "1000", "__gha_enable": true, diff --git a/backend_addon/hooks/post_gen_project.py b/backend_addon/hooks/post_gen_project.py index 1c1d11f..0950614 100644 --- a/backend_addon/hooks/post_gen_project.py +++ b/backend_addon/hooks/post_gen_project.py @@ -1,65 +1,63 @@ """Post generation hook.""" -import subprocess -import sys +from copy import deepcopy from collections import OrderedDict from pathlib import Path -from cookieplone.utils import console, files +from cookieplone.utils import console, files, git + context: OrderedDict = {{cookiecutter}} + # PATH OF CONTENT TO BE REMOVED -TO_REMOVE_PATHS = { +FEATURES_TO_REMOVE = { "feature_headless": [ "serializers", ] } +def handle_feature_headless(context: OrderedDict, output_dir: Path): + package_namespace = context.get("__package_namespace") + package_name = context.get("__package_name") + output_dir = output_dir / "src" / package_namespace / package_name + files.remove_files(output_dir, FEATURES_TO_REMOVE["feature_headless"]) -def run_cmd(command: str, shell: bool, cwd: str) -> bool: - proc = subprocess.run(command, shell=shell, cwd=cwd, capture_output=True) - if proc.returncode: - # Write errors to the main process stderr - console.error(f"Error while running {command}") - return False if proc.returncode else True - - -def remove_files(category: str): - to_remove = TO_REMOVE_PATHS.get(category, []) - package_namespace = "{{ cookiecutter.__package_namespace }}" - package_name = "{{ cookiecutter.__package_name }}" - base_path = Path("src") / package_namespace / package_name - # Remove all files - files.remove_files(base_path, to_remove) - -def initialize_git(): - """Apply black and isort to the generated codebase.""" - console.info("Git repository") - steps = [ - ["Initialize", ["git", "init", "."], False, "."], - ["Add files", ["git", "add", "."], False, "."], - ] - for step in steps: - msg, command, shell, cwd = step - console.info(f" - {msg}") - result = run_cmd(command, shell=shell, cwd=cwd) - if not result: - sys.exit(1) +def handle_git_initialization(context: OrderedDict, output_dir: Path): + """Initialize a GIT repository for the project codebase.""" + git.initialize_repository(output_dir) def main(): """Final fixes.""" - keep_headless = int(context.get("feature_headless")) - if not keep_headless: - remove_files("feature_headless") - if int(context.get("__backend_addon_git_initialize")): - initialize_git() + output_dir = Path().cwd() + remove_headless = not int(context.get("feature_headless")) + initialize_git = bool(int(context.get("__backend_addon_git_initialize"))) + # Cleanup / Git + actions = [ + [ + handle_feature_headless, + "Remove files used in headless setup", + remove_headless, + ], + [ + handle_git_initialization, + "Initialize Git repository", + initialize_git, + ], + ] + for func, title, enabled in actions: + if not int(enabled): + continue + new_context = deepcopy(context) + console.print(f" -> {title}") + func(new_context, output_dir) + msg = """ [bold blue]{{ cookiecutter.title }}[/bold blue] - Now, enter the repositorym run the code formatter with: + Now, enter the repository run the code formatter with: make format diff --git a/backend_addon/tests/conftest.py b/backend_addon/tests/conftest.py index 94f1e13..12a2f29 100644 --- a/backend_addon/tests/conftest.py +++ b/backend_addon/tests/conftest.py @@ -25,6 +25,7 @@ "pyproject.toml", "README.md", "requirements.txt", + "scripts/create_site.py", "setup.py", "tox.ini", ] @@ -88,6 +89,7 @@ def context(cookieplone_root) -> dict: "author": "Plone Collective", "email": "collective@plone.org", "feature_headless": "1", + "__backend_addon_git_initialize": "1", "__cookieplone_repository_path": f"{cookieplone_root}", } @@ -100,6 +102,14 @@ def context_no_headless(context) -> dict: new_context["feature_headless"] = "0" return new_context +@pytest.fixture(scope="session") +def context_no_git(context) -> dict: + """Cookiecutter context without Git repository.""" + new_context = deepcopy(context) + new_context["python_package_name"] = "collective.addonnogit" + new_context["__backend_addon_git_initialize"] = "0" + return new_context + @pytest.fixture(scope="session") def bad_context() -> dict: diff --git a/backend_addon/tests/test_cutter.py b/backend_addon/tests/test_cutter.py index b9ba1be..246f8fc 100644 --- a/backend_addon/tests/test_cutter.py +++ b/backend_addon/tests/test_cutter.py @@ -2,6 +2,7 @@ import pytest +from pathlib import Path from .conftest import PKG_SRC_FEATURE_HEADLESS, PKG_SRC_FILES, ROOT_FILES @@ -56,3 +57,34 @@ def test_pkg_src_feature_files_generated(cutter_result, file_path: str): path = src_path / file_path assert path.exists() assert path.is_file() + + +@pytest.mark.parametrize( + "file_path,schema_name", + [ + [".github/workflows/meta.yml", "github-workflow"], + [".pre-commit-config.yaml", "pre-commit-config"], + ["pyproject.toml", "pyproject"], + ], +) +def test_json_schema( + cutter_result, schema_validate_file, file_path: str, schema_name: str +): + path = cutter_result.project_path / file_path + assert schema_validate_file(path, schema_name) + + +def test_git_initialization(cutter_result): + from cookieplone.utils import git + + path = cutter_result.project_path + repo = git.repo_from_path(path) + assert Path(repo.working_dir) == path + + +def test_git_initialization_not_set(cookies, context_no_git): + from cookieplone.utils import git + + cutter_result = cookies.bake(extra_context=context_no_git) + path = cutter_result.project_path + assert git.check_path_is_repository(path) is False diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/.github/workflows/meta.yml b/backend_addon/{{ cookiecutter.__folder_name }}/.github/workflows/meta.yml index 39a164d..96a81a4 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/.github/workflows/meta.yml +++ b/backend_addon/{{ cookiecutter.__folder_name }}/.github/workflows/meta.yml @@ -1,5 +1,5 @@ # Generated from: -# https://github.com/plone/meta/tree/master/config/default +# https://github.com/plone/meta/tree/main/config/default # See the inline comments on how to expand/tweak this configuration file name: Meta on: @@ -28,14 +28,15 @@ jobs: uses: plone/meta/.github/workflows/qa.yml@main test: uses: plone/meta/.github/workflows/test.yml@main + with: + + py-versions: '["3.12", "3.11", "3.10"]' coverage: uses: plone/meta/.github/workflows/coverage.yml@main dependencies: uses: plone/meta/.github/workflows/dependencies.yml@main release_ready: uses: plone/meta/.github/workflows/release_ready.yml@main - circular: - uses: plone/meta/.github/workflows/circular.yml@main ## # To modify the list of default jobs being created add in .meta.toml: @@ -57,6 +58,13 @@ jobs: # os_dependencies = "git libxml2 libxslt" ## +## +# To test against a specific matrix of python versions +# when running tests jobs, add in .meta.toml: +# [github] +# py_versions = "['3.12', '3.11']" +## + ## # Specify additional jobs in .meta.toml: diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/.meta.toml b/backend_addon/{{ cookiecutter.__folder_name }}/.meta.toml index 8cb7797..133695b 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/.meta.toml +++ b/backend_addon/{{ cookiecutter.__folder_name }}/.meta.toml @@ -1,19 +1,30 @@ # Generated from: -# https://github.com/plone/meta/tree/master/config/default +# https://github.com/plone/meta/tree/main/config/default # See the inline comments on how to expand/tweak this configuration file [meta] template = "default" -commit-id = "25d2fa7f" +commit-id = "71d0218b" [pyproject] codespell_skip = "*.min.js" codespell_ignores = "vew" -dependencies_ignores = "['plone.volto', 'zestreleaser.towncrier', 'zest.releaser', 'pytest', 'pytest-cov', 'pytest-plone']" dependencies_mappings = [ - "Plone = ['Products.CMFPlone', 'Products.CMFCore', 'Products.GenericSetup']", - ] + "pytest-plone = ['pytest', 'plone.testing', 'plone.app.testing']", +] +dependencies_ignores = "['plone.app.iterate', 'plone.app.upgrade', 'plone.volto', 'zestreleaser.towncrier', 'zest.releaser', 'pytest-cov']" [tox] -use_mxdev = true test_runner = "pytest" test_path = "/tests" +use_mxdev = true +test_deps_additional = "" + +[github] +py_versions = "[\"3.12\", \"3.11\", \"3.10\"]" +jobs = [ + "qa", + "test", + "coverage", + "dependencies", + "release_ready", + ] diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/MANIFEST.in b/backend_addon/{{ cookiecutter.__folder_name }}/MANIFEST.in index 40ac789..3123abd 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/MANIFEST.in +++ b/backend_addon/{{ cookiecutter.__folder_name }}/MANIFEST.in @@ -2,6 +2,8 @@ graft src/{{ cookiecutter.__package_namespace }} graft docs graft news graft tests +graft scripts +include *.acceptance include .coveragerc include .dockerignore include .editorconfig diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/Makefile b/backend_addon/{{ cookiecutter.__folder_name }}/Makefile index 1d7c8a3..aeea158 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/Makefile +++ b/backend_addon/{{ cookiecutter.__folder_name }}/Makefile @@ -51,11 +51,13 @@ bin/pip bin/tox bin/mxdev: bin/pip install -U "pip" "wheel" "cookiecutter" "mxdev" "tox" "pre-commit" if [ -d $(GIT_FOLDER) ]; then bin/pre-commit install; else echo "$(RED) Not installing pre-commit$(RESET)";fi -.PHONY: config -config: bin/pip ## Create instance configuration +instance/etc/zope.ini: bin/pip ## Create instance configuration @echo "$(GREEN)==> Create instance configuration$(RESET)" bin/cookiecutter -f --no-input --config-file instance.yaml gh:plone/cookiecutter-zope-instance +.PHONY: config +config: instance/etc/zope.ini + .PHONY: build-dev build-dev: config ## pip install Plone packages @echo "$(GREEN)==> Setup Build$(RESET)" @@ -65,11 +67,9 @@ build-dev: config ## pip install Plone packages .PHONY: install install: build-dev ## Install Plone 6.0 - .PHONY: build build: build-dev ## Install Plone 6.0 - .PHONY: clean clean: ## Remove old virtualenv and creates a new one @echo "$(RED)==> Cleaning environment and build$(RESET)" @@ -80,9 +80,13 @@ start: ## Start a Plone instance on localhost:8080 PYTHONWARNINGS=ignore ./bin/runwsgi instance/etc/zope.ini .PHONY: console -console: ## Start a zope console +console: instance/etc/zope.ini ## Start a zope console PYTHONWARNINGS=ignore ./bin/zconsole debug instance/etc/zope.conf +.PHONY: create-site +create-site: instance/etc/zope.ini ## Create a new site from scratch + PYTHONWARNINGS=ignore ./bin/zconsole run instance/etc/zope.conf ./scripts/create_site.py + .PHONY: format format: bin/tox ## Format the codebase according to our standards @echo "$(GREEN)==> Format codebase$(RESET)" @@ -100,7 +104,7 @@ bin/i18ndude: bin/pip .PHONY: i18n i18n: bin/i18ndude ## Update locales @echo "$(GREEN)==> Updating locales$(RESET)" - bin/update_dist_locale + bin/update_locale # Tests .PHONY: test diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/pyproject.toml b/backend_addon/{{ cookiecutter.__folder_name }}/pyproject.toml index e9b79b5..c982aa9 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/pyproject.toml +++ b/backend_addon/{{ cookiecutter.__folder_name }}/pyproject.toml @@ -1,6 +1,9 @@ # Generated from: -# https://github.com/plone/meta/tree/master/config/default +# https://github.com/plone/meta/tree/main/config/default # See the inline comments on how to expand/tweak this configuration file +[build-system] +requires = ["setuptools>=68.2"] + [tool.towncrier] directory = "news/" filename = "CHANGES.md" @@ -118,8 +121,8 @@ Zope = [ 'Products.CMFCore', 'Products.CMFDynamicViewFTI', ] python-dateutil = ['dateutil'] -ignore-packages = ['plone.volto', 'zestreleaser.towncrier', 'zest.releaser', 'pytest', 'pytest-cov', 'pytest-plone'] -Plone = ['Products.CMFPlone', 'Products.CMFCore', 'Products.GenericSetup'] +pytest-plone = ['pytest', 'plone.testing', 'plone.app.testing'] +ignore-packages = ['plone.app.iterate', 'plone.app.upgrade', 'plone.volto', 'zestreleaser.towncrier', 'zest.releaser', 'pytest-cov'] ## # Add extra configuration options in .meta.toml: @@ -129,19 +132,20 @@ Plone = ['Products.CMFPlone', 'Products.CMFCore', 'Products.GenericSetup'] # "gitpython = ['git']", # "pygithub = ['github']", # ] -# """ ## [tool.check-manifest] ignore = [ ".editorconfig", + ".flake8", ".meta.toml", ".pre-commit-config.yaml", - "tox.ini", - ".flake8", + "dependabot.yml", "mx.ini", + "tox.ini", ] + ## # Add extra configuration options in .meta.toml: # [pyproject] @@ -149,6 +153,11 @@ ignore = [ # "*.map.js", # "*.pyc", # """ +# check_manifest_extra_lines = """ +# ignore-bad-ideas = [ +# "some/test/file/PKG-INFO", +# ] +# """ ## diff --git a/project/{{ cookiecutter.__folder_name }}/backend/scripts/create_site.py b/backend_addon/{{ cookiecutter.__folder_name }}/scripts/create_site.py similarity index 81% rename from project/{{ cookiecutter.__folder_name }}/backend/scripts/create_site.py rename to backend_addon/{{ cookiecutter.__folder_name }}/scripts/create_site.py index 00a4e15..e4e19f0 100644 --- a/project/{{ cookiecutter.__folder_name }}/backend/scripts/create_site.py +++ b/backend_addon/{{ cookiecutter.__folder_name }}/scripts/create_site.py @@ -1,5 +1,5 @@ from AccessControl.SecurityManagement import newSecurityManager -from {{ cookiecutter.python_package_name }}.interfaces import I{{ cookiecutter.__python_package_name_upper }}Layer +from {{ cookiecutter.python_package_name }}.interfaces import IBrowserLayer from Products.CMFPlone.factory import _DEFAULT_PROFILE from Products.CMFPlone.factory import addPloneSite from Testing.makerequest import makerequest @@ -27,13 +27,11 @@ def asbool(s): DELETE_EXISTING = asbool(os.getenv("DELETE_EXISTING")) -app = makerequest(app) # noQA +app = makerequest(globals()["app"]) request = app.REQUEST -ifaces = [ - I{{ cookiecutter.__python_package_name_upper }}Layer, -] + list(directlyProvidedBy(request)) +ifaces = [IBrowserLayer] + list(directlyProvidedBy(request)) directlyProvides(request, *ifaces) @@ -47,11 +45,10 @@ def asbool(s): "profile_id": _DEFAULT_PROFILE, "extension_ids": [ "{{ cookiecutter.python_package_name }}:default", - "{{ cookiecutter.python_package_name }}:initial", ], "setup_content": False, "default_language": "{{ cookiecutter.__profile_language }}", - "portal_timezone": "America/Sao_Paulo", + "portal_timezone": "UTC", } if site_id in app.objectIds() and DELETE_EXISTING: diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/setup.py b/backend_addon/{{ cookiecutter.__folder_name }}/setup.py index 15eafc3..6d81f6c 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/setup.py +++ b/backend_addon/{{ cookiecutter.__folder_name }}/setup.py @@ -54,6 +54,7 @@ "plone.api", {%- if cookiecutter.feature_headless == '1' %} "plone.restapi", + "plone.volto", {%- endif %} ], extras_require={ @@ -64,13 +65,13 @@ "plone.restapi[test]", "pytest", "pytest-cov", - "pytest-plone>=0.2.0", + "pytest-plone>=0.5.0", ], }, entry_points=""" [z3c.autoinclude.plugin] target = plone [console_scripts] - update_dist_locale = {{ cookiecutter.python_package_name }}.locales.update:update_locale + update_locale = {{ cookiecutter.python_package_name }}.locales.update:update_locale """, ) diff --git a/backend_addon/{{ cookiecutter.__folder_name }}/src/{{ cookiecutter.__package_namespace }}/{{ cookiecutter.__package_name }}/profiles/default/metadata.xml b/backend_addon/{{ cookiecutter.__folder_name }}/src/{{ cookiecutter.__package_namespace }}/{{ cookiecutter.__package_name }}/profiles/default/metadata.xml index 9f3dc35..8d8f114 100644 --- a/backend_addon/{{ cookiecutter.__folder_name }}/src/{{ cookiecutter.__package_namespace }}/{{ cookiecutter.__package_name }}/profiles/default/metadata.xml +++ b/backend_addon/{{ cookiecutter.__folder_name }}/src/{{ cookiecutter.__package_namespace }}/{{ cookiecutter.__package_name }}/profiles/default/metadata.xml @@ -2,6 +2,9 @@ {{ cookiecutter.__profile_version }} - + {%- if cookiecutter.feature_headless == '1' %} + profile-plone.restapi:default + profile-plone.volto:default + {%- endif %} diff --git a/project/cookiecutter.json b/project/cookiecutter.json index 2fe704a..03af39f 100644 --- a/project/cookiecutter.json +++ b/project/cookiecutter.json @@ -69,6 +69,9 @@ "__devops_varnish_version": "7.4", "__devops_db_version": "14", "__devops_db_password": "{{ random_ascii_string(12) }}", + "__backend_addon_git_initialize": "0", + "__frontend_addon_git_initialize": "0", + "__project_git_initialize": "1", "__prompts__": { "title": "Project Title", "project_slug": "Project Slug (Used for repository id)", diff --git a/project/hooks/post_gen_project.py b/project/hooks/post_gen_project.py index a4e1900..86badaa 100644 --- a/project/hooks/post_gen_project.py +++ b/project/hooks/post_gen_project.py @@ -5,12 +5,16 @@ from pathlib import Path from cookieplone import generator -from cookieplone.utils import console, files +from cookieplone.utils import console, files, git context: OrderedDict = {{cookiecutter}} -BACKEND_ADDON_REMOVE = [".github", ".git"] +BACKEND_ADDON_REMOVE = [ + ".github", + ".git", + ".meta.toml", +] FRONTEND_ADDON_REMOVE = [".github"] @@ -35,24 +39,27 @@ } -def prepare_devops(context: OrderedDict, output_dir: Path): - """Clean up devops.""" - keep_ansible = int(context.get("devops_ansible")) - keep_gha_manual_deploy = int(context.get("devops_gha_deploy")) - to_remove = [] - if not keep_ansible: - to_remove.extend(DEVOPS_TO_REMOVE["ansible"]) - if not keep_gha_manual_deploy: - to_remove.extend(DEVOPS_TO_REMOVE["gha"]) - files.remove_files(output_dir, to_remove) +def handle_devops_ansible(context: OrderedDict, output_dir: Path): + """Clean up ansible.""" + files.remove_files(output_dir, DEVOPS_TO_REMOVE["ansible"]) + + +def handle_devops_gha_deploy(context: OrderedDict, output_dir: Path): + """Clean up ansible.""" + files.remove_files(output_dir, DEVOPS_TO_REMOVE["gha"]) + + +def handle_git_initialization(context: OrderedDict, output_dir: Path): + """Initialize a GIT repository for the project codebase.""" + git.initialize_repository(output_dir) def generate_backend_addon(context, output_dir): """Run Plone Addon generator.""" output_dir = output_dir folder_name = "backend" - # Do not initialize the repository - context["__backend_addon_git_initialize"] = "0" + # Headless + context["feature_headless"] = "1" generator.generate_subtemplate( "backend_addon", output_dir, folder_name, context, BACKEND_ADDON_REMOVE ) @@ -87,6 +94,32 @@ def generate_sub_project_settings(context: OrderedDict, output_dir: Path): def main(): """Final fixes.""" output_dir = Path().cwd() + + # Cleanup / Git + actions = [ + [ + handle_devops_ansible, + "Remove Ansible files", + not int(context.get("devops_ansible")), + ], + [ + handle_devops_gha_deploy, + "Remove GHA deployment files", + not int(context.get("devops_gha_deploy")), + ], + [ + handle_git_initialization, + "Remove GHA deployment files", + bool(int(context.get("__project_git_initialize"))), + ], + ] + for func, title, enabled in actions: + if not int(enabled): + continue + new_context = deepcopy(context) + console.print(f" -> {title}") + func(new_context, output_dir) + subtemplates = context.get("__cookieplone_subtemplates", []) funcs = {k: v for k, v in globals().items() if k.startswith("generate_")} for template_id, title, enabled in subtemplates: @@ -102,8 +135,6 @@ def main(): console.print(f" -> {title}") func(new_context, output_dir) - # Run devops - prepare_devops(context, output_dir) msg = """ [bold blue]{{ cookiecutter.title }}[/bold blue] diff --git a/project/tests/conftest.py b/project/tests/conftest.py index 766f0ed..fbd3647 100644 --- a/project/tests/conftest.py +++ b/project/tests/conftest.py @@ -33,6 +33,7 @@ def context(cookieplone_root) -> dict: "frontend_addon_name": "volto-ploneorgbr", "language_code": "en", "github_organization": "plonegovbr", + "__project_git_initialize": "1", "container_registry": "github", "__cookieplone_repository_path": f"{cookieplone_root}", } @@ -46,6 +47,14 @@ def context_devops_cache(context) -> dict: return new_context +@pytest.fixture(scope="session") +def context_no_git(context) -> dict: + """Cookiecutter context.""" + new_context = deepcopy(context) + new_context["__project_git_initialize"] = "0" + return new_context + + @pytest.fixture(scope="session") def context_devops_no_cache(context) -> dict: """Cookiecutter context.""" diff --git a/project/tests/test_cutter.py b/project/tests/test_cutter.py index 8ed7acf..526c8d9 100644 --- a/project/tests/test_cutter.py +++ b/project/tests/test_cutter.py @@ -48,3 +48,19 @@ def test_root_folders(cutter_result, folder_name: str): """Test folders were created.""" folder = cutter_result.project_path / folder_name assert folder.is_dir() + + +def test_git_initialization(cutter_result): + from cookieplone.utils import git + + path = cutter_result.project_path + repo = git.repo_from_path(path) + assert Path(repo.working_dir) == path + + +def test_git_initialization_not_set(cookies, context_no_git): + from cookieplone.utils import git + + cutter_result = cookies.bake(extra_context=context_no_git) + path = cutter_result.project_path + assert git.check_path_is_repository(path) is False diff --git a/project/tests/test_project_backend.py b/project/tests/test_project_backend.py index 9595640..8063e46 100644 --- a/project/tests/test_project_backend.py +++ b/project/tests/test_project_backend.py @@ -1,5 +1,7 @@ """Test Generator: /backend.""" +from pathlib import Path + import pytest BACKEND_FILES = [ @@ -26,7 +28,7 @@ def test_backend_top_level_files(cutter_result, filename: str): BACKEND_PACKAGE_FILES_PYTEST = [ - "src/ploneorgbr/setup.py", + "setup.py", "src/plonegov/ploneorgbr/configure.zcml", "src/plonegov/ploneorgbr/dependencies.zcml", "src/plonegov/ploneorgbr/permissions.zcml", @@ -49,6 +51,7 @@ def test_backend_package_files_pytest(cutter_result, filename: str): FILES_TO_BE_REMOVED = [ ".github", ".git", + ".meta.toml", ] @@ -59,3 +62,32 @@ def test_backend_package_files_removed(cutter_result, filename: str): path = backend_folder / filename assert path.exists() is False assert path.parent.exists() + + +BACKEND_HEADLESS_FILE_CHECKS = [ + ["setup.py", "plone.volto"], + ["setup.py", "plone.restapi"], + ["src/plonegov/ploneorgbr/dependencies.zcml", "plone.volto"], + ["src/plonegov/ploneorgbr/dependencies.zcml", "plone.restapi"], + ["src/plonegov/ploneorgbr/profiles/default/metadata.xml", "plone.restapi:default"], + ["src/plonegov/ploneorgbr/profiles/default/metadata.xml", "plone.volto:default"], +] + + +@pytest.mark.parametrize("filename,content", BACKEND_HEADLESS_FILE_CHECKS) +def test_backend_headless_support(cutter_result, filename: str, content: str): + """Test backend files contain headless support.""" + backend_folder = cutter_result.project_path / "backend" + path = backend_folder / filename + assert path.is_file() + assert content in path.read_text() + + +def test_git_repo_is_the_project(cutter_result): + from cookieplone.utils import git + + path = cutter_result.project_path + backend_path = path / "backend" + repo = git.repo_from_path(path) + assert Path(repo.working_dir) != backend_path + assert Path(repo.working_dir) == path diff --git a/project/tests/test_project_cache.py b/project/tests/test_project_cache.py index cc5b982..59d63da 100644 --- a/project/tests/test_project_cache.py +++ b/project/tests/test_project_cache.py @@ -31,3 +31,34 @@ def test_project_no_cache(cutter_result_devops_no_cache, filepath: str): folder = cutter_result_devops_no_cache.project_path path = folder / filepath assert path.exists() is False + + +CACHE_CONFIGURATION = [ + [ + "backend/src/plonegov/ploneorgbr/profiles/default/metadata.xml", + "plone.app.caching:default", + ], + [ + "backend/src/plonegov/ploneorgbr/profiles/default/metadata.xml", + "plone.app.caching:with-caching-proxy", + ], + ["backend/src/plonegov/ploneorgbr/dependencies.zcml", "plone.app.caching"], +] + + +@pytest.mark.parametrize("filepath,content", CACHE_CONFIGURATION) +def test_project_no_cache_no_config( + cutter_result_devops_no_cache, filepath: str, content: str +): + """Test Cache-related configurations are not present.""" + folder = cutter_result_devops_no_cache.project_path + file_content = (folder / filepath).read_text() + assert content not in file_content + + +@pytest.mark.parametrize("filepath,content", CACHE_CONFIGURATION) +def test_project_no_cache_config(cutter_result, filepath: str, content: str): + """Test Cache-related configurations are not present.""" + folder = cutter_result.project_path + file_content = (folder / filepath).read_text() + assert content in file_content diff --git a/project/{{ cookiecutter.__folder_name }}/backend/Dockerfile b/project/{{ cookiecutter.__folder_name }}/backend/Dockerfile index 4b996a6..f89f61f 100644 --- a/project/{{ cookiecutter.__folder_name }}/backend/Dockerfile +++ b/project/{{ cookiecutter.__folder_name }}/backend/Dockerfile @@ -12,7 +12,7 @@ RUN <=2.0.0 diff --git a/project/{{ cookiecutter.__folder_name }}/backend/instance.yaml b/project/{{ cookiecutter.__folder_name }}/backend/instance.yaml deleted file mode 100644 index 3dc4038..0000000 --- a/project/{{ cookiecutter.__folder_name }}/backend/instance.yaml +++ /dev/null @@ -1,6 +0,0 @@ -default_context: - initial_user_name: 'admin' - initial_user_password: 'admin' - zcml_package_includes: '{{ cookiecutter.python_package_name }}' - - db_storage: direct diff --git a/requirements.txt b/requirements.txt index 27e8647..31b9499 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,6 @@ isort pytest pytest-cookies pytest-jsonschema >= 1.0.0a2 -cookieplone>=0.5.6 +cookieplone>=0.6.1 GitPython wheel diff --git a/sub/cache/hooks/post_gen_project.py b/sub/cache/hooks/post_gen_project.py new file mode 100644 index 0000000..f7d8660 --- /dev/null +++ b/sub/cache/hooks/post_gen_project.py @@ -0,0 +1,49 @@ +"""Post generation hook.""" + +from collections import OrderedDict +from pathlib import Path +from typing import Callable + +from cookieplone.utils import console, plone + +context: OrderedDict = {{cookiecutter}} + + +def update_file(filepath: Path, func: Callable, content: str) -> Path: + src_data = filepath.read_text() + filepath.write_text(func(content, src_data)) + return filepath + + +def set_configurations(package_root: Path, context: OrderedDict): + """Adjust dependencies.zcml and profiles/default/metadata.xml.""" + info = [ + ( + "profiles/default/metadata.xml", + plone.add_dependency_profile_to_metadata, + "plone.app.caching:default", + ), + ( + "profiles/default/metadata.xml", + plone.add_dependency_profile_to_metadata, + "plone.app.caching:with-caching-proxy", + ), + ("dependencies.zcml", plone.add_dependency_to_zcml, "plone.app.caching"), + ] + for path, func, content in info: + filepath: Path = package_root / path + if filepath.exists(): + filepath = update_file(filepath, func, content) + + +def main(): + """Final fixes.""" + output_dir = Path().cwd() + namespace = context.get("__package_namespace") + package_name = context.get("__package_name") + package_root = output_dir / "backend/src" / namespace / package_name + set_configurations(package_root, context) + + +if __name__ == "__main__": + main()