From 5cf71fcae14c06b75a6cbfad5e9b71b6f443f10b Mon Sep 17 00:00:00 2001 From: shmyga Date: Tue, 15 Sep 2026 17:21:23 +0300 Subject: [PATCH] feat: update default presets --- project_manager/common/template.py | 12 +++++++++++- project_manager/entry/entry.py | 8 ++++---- project_manager/entry/resolver.py | 2 +- project_manager/manager.py | 10 +++++----- project_manager/preset/default/config.toml | 4 ++++ project_manager/preset/default/docker.toml | 10 ++++++++++ project_manager/preset/default/nodejs.toml | 14 ++++++++------ project_manager/preset/default/python.toml | 5 +++++ project_manager/preset/detector.py | 10 +++++++++- project_manager/preset/model.py | 1 + 10 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 project_manager/preset/default/config.toml diff --git a/project_manager/common/template.py b/project_manager/common/template.py index 8c71aef..6680eac 100644 --- a/project_manager/common/template.py +++ b/project_manager/common/template.py @@ -3,6 +3,7 @@ import os import re import tomllib from pathlib import Path +from typing import Any import dotenv from jinja2 import Environment @@ -11,13 +12,21 @@ from ..preset.model import Variables def do_bool(data: str | None) -> bool: - return not (data is None or data.lower() in ["false", "null", "none", "0", ""]) + if data is None: + return False + if not isinstance(data, str): + data = str(data) + return data.lower() not in ["false", "null", "none", "0", ""] def do_env(key: str, default: str | None = None) -> str | None: return os.environ.get(key=key.upper(), default=default) +def do_not(value: Any | None) -> bool: + return not do_bool(str(value)) + + def _build_env(basepath: Path) -> Environment: env = Environment() @@ -71,6 +80,7 @@ def _build_env(basepath: Path) -> Environment: env.filters["key"] = do_key env.filters["bool"] = do_bool env.filters["env"] = do_env + env.filters["not"] = do_not return env diff --git a/project_manager/entry/entry.py b/project_manager/entry/entry.py index 8b23f5e..f928104 100644 --- a/project_manager/entry/entry.py +++ b/project_manager/entry/entry.py @@ -85,20 +85,20 @@ class Entry: for command in task.commands: self._logger.debug(" %s", command) - def run_task(self, key: PartialTaskKey) -> bool: + def run_task(self, key: PartialTaskKey) -> int: if key.name == "show": self.show() return True - runned = False + c = 0 for task in self._tasks: if key == task.key: self._logger.info("🔵 %s %s", task.key, self) - runned = True for command in task.commands: CommandExecutor(command).run( (task.cwd and Path(task.cwd)) or self._basepath ) - return runned + c += 1 + return c def __repr__(self): path = self._basepath.relative_to(self._roopath.parent) diff --git a/project_manager/entry/resolver.py b/project_manager/entry/resolver.py index 84b0886..06d527f 100644 --- a/project_manager/entry/resolver.py +++ b/project_manager/entry/resolver.py @@ -9,7 +9,7 @@ from .entry import Entry class EntryResolver: - def __init__(self, detector: EntryDetector, ignore: PathSpec, max_depth: int = 4): + def __init__(self, detector: EntryDetector, ignore: PathSpec, max_depth: int = 2): self._detector = detector self._ignore = ignore self._max_depth = max_depth diff --git a/project_manager/manager.py b/project_manager/manager.py index cb49998..18dbf0e 100644 --- a/project_manager/manager.py +++ b/project_manager/manager.py @@ -1,6 +1,6 @@ import logging from collections import Counter -from enum import StrEnum +from enum import StrEnum, auto from pathlib import Path from typing import Iterable @@ -14,8 +14,8 @@ logger = logging.getLogger("project") class ResultType(StrEnum): - SUCCESS = "success" - FAILED = "failed" + SUCCESS = auto() + FAILED = auto() RESULT_ICONS = { @@ -40,8 +40,8 @@ class ProjectManager: counter = Counter() for entry in self._iter_entries(): try: - if entry.run_task(key): - counter[ResultType.SUCCESS] += 1 + if count := entry.run_task(key): + counter[ResultType.SUCCESS] += count except CommandError as error: self._logger.error("%s\n%s", entry, error.output) counter[ResultType.FAILED] += 1 diff --git a/project_manager/preset/default/config.toml b/project_manager/preset/default/config.toml new file mode 100644 index 0000000..9de8c58 --- /dev/null +++ b/project_manager/preset/default/config.toml @@ -0,0 +1,4 @@ +[presets.config.tasks.setup.config] +priority = 1 +conditions = ["{{ ('.env-base' | file) and ('.env' | file | not ) }}"] +commands = ["cp .env-base .env"] diff --git a/project_manager/preset/default/docker.toml b/project_manager/preset/default/docker.toml index b134a26..6ddbebf 100644 --- a/project_manager/preset/default/docker.toml +++ b/project_manager/preset/default/docker.toml @@ -34,3 +34,13 @@ commands = [ "mkdir -p {{ docker_output }}", "docker save --output {{ docker_output }}/{{ docker_name }}-{{ docker_version }}.tar {{ docker_image }}", ] + +# run +[presets.docker.tasks.run.compose] +conditions = ["{{ 'docker-compose.yaml' | file }}"] +commands = ["docker compose -f docker-compose.yaml up"] + +# develop +[presets.docker.tasks.develop.compose] +conditions = ["{{ 'docker-compose-develop.yaml' | file }}"] +commands = ["docker compose -f docker-compose-develop.yaml up --build --watch"] diff --git a/project_manager/preset/default/nodejs.toml b/project_manager/preset/default/nodejs.toml index 35200ff..cf1342d 100644 --- a/project_manager/preset/default/nodejs.toml +++ b/project_manager/preset/default/nodejs.toml @@ -1,30 +1,32 @@ [presets.nodejs] conditions = ["{{ 'package.json' | file }}"] +variables.use_nvm = "{{ (((('home' | env) + '/.nvm/nvm.sh') | file) and ('.nvmrc' | file)) | bool }}" +variables.nvm = "{{ '. ~/.nvm/nvm.sh && nvm use && ' if use_nvm | bool else '' }}" # install [presets.nodejs.tasks.setup.npm] conditions = ["{{ 'package-lock.json' | file }}"] -commands = ["npm install-clean"] +commands = ["{{ nvm }}npm install-clean"] # install legacy [presets.nodejs.tasks.setup.npm-legacy] conditions = ["{{ not ('package-lock.json' | file) }}"] -commands = ["npm install"] +commands = ["{{ nvm }}npm install"] # test [presets.nodejs.tasks.test.jest] conditions = ["{{ 'node_modules/jest' | file }}"] -commands = ["npx jest"] +commands = ["{{ nvm }}npx jest"] # build [presets.nodejs.tasks.build.npm] conditions = ["{{ 'package.json' | read | get('scripts.build') }}"] -commands = ["npm run build"] +commands = ["{{ nvm }}npm run build"] # publish [presets.nodejs.tasks.publish.npm] conditions = ["{{ 'package.json' | read | get('scripts.publish') }}"] -commands = ["npm run publish"] +commands = ["{{ nvm }}npm run publish"] # clean [presets.nodejs.tasks.clean.node_modules] @@ -34,4 +36,4 @@ commands = ["rm -rf node_modules"] # version [presets.nodejs.tasks.version.npm] conditions = ["{{ version }}"] -commands = ["npm version {{ version }} --allow-same-version"] +commands = ["{{ nvm }}npm version {{ version }} --allow-same-version"] diff --git a/project_manager/preset/default/python.toml b/project_manager/preset/default/python.toml index ed13e3d..89944bf 100644 --- a/project_manager/preset/default/python.toml +++ b/project_manager/preset/default/python.toml @@ -8,6 +8,11 @@ conditions = ["{{ python_poetry }}"] variables.python_version = "{{ 'pyproject.toml' | read | get('project.requires-python') | search('(\\d+.\\d+)') }}" commands = ["poetry env use {{ python_version }}", "poetry install"] +# playwright +[presets.python.tasks.setup.playwright] +conditions = ["{{ '.venv/bin/playwright' | file }}"] +commands = [".venv/bin/playwright install chromium --with-deps --only-shell"] + # build [presets.python.tasks.build.poetry] conditions = ["{{ python_poetry }}"] diff --git a/project_manager/preset/detector.py b/project_manager/preset/detector.py index e11c616..e425635 100644 --- a/project_manager/preset/detector.py +++ b/project_manager/preset/detector.py @@ -14,9 +14,14 @@ class PresetEntryDetector(EntryDetector): def __init__(self, preset_loader_config: PresetLoaderConfig | None = None): self._preset_loader_config = preset_loader_config + # pylint:disable=too-many-locals def detect_entry(self, basepath: Path, rootpath: Path) -> Entry: template = PathTemplate(basepath) preset = PresetLoader(basepath, self._preset_loader_config).load() + if preset.modules: + modules = [Path(item).resolve() for item in preset.modules] + if basepath not in modules: + return ENTRY_SKIP if preset.skip: return ENTRY_SKIP tasks = [] @@ -25,7 +30,10 @@ class PresetEntryDetector(EntryDetector): preset.presets.items(), key=lambda item: item[1].priority, reverse=True ) for tag, preset in presets: - preset_variables: Variables = {**global_variables, "entry_path": basepath} + preset_variables: Variables = { + **global_variables, + "entry_path": str(basepath), + } if preset.variables: if tag == "*": template.update_variables(preset.variables, global_variables) diff --git a/project_manager/preset/model.py b/project_manager/preset/model.py index 3114bf2..5af1adb 100644 --- a/project_manager/preset/model.py +++ b/project_manager/preset/model.py @@ -20,4 +20,5 @@ class EntryPreset(BaseModel): class Preset(BaseModel): skip: bool | None = None + modules: list[str] | None = None presets: dict[str, EntryPreset]