diff --git a/project_manager/__init__.py b/project_manager/__init__.py index e69de29..e060b37 100644 --- a/project_manager/__init__.py +++ b/project_manager/__init__.py @@ -0,0 +1 @@ +from .logging import * diff --git a/project_manager/manager.py b/project_manager/manager.py index e63af96..0db2138 100644 --- a/project_manager/manager.py +++ b/project_manager/manager.py @@ -13,14 +13,13 @@ logger = logging.getLogger("project") class ProjectManager: - DEFAULT_PRESET = Path(__file__).parent / "default_preset.toml" - DEFAULT_IGNORE = ["node_modules", ".venv", ".git"] + DEFAULT_IGNORE = ["node_modules", ".venv", ".git", "tests"] _logger = logger def __init__(self, basepath: Path): self._basepaht = basepath self._resolver = EntryResolver( - PresetEntryDetector(self.DEFAULT_PRESET), + PresetEntryDetector(), self.DEFAULT_IGNORE, ) diff --git a/project_manager/pipe/core.py b/project_manager/pipe/core.py index 94a87cd..8454cf5 100644 --- a/project_manager/pipe/core.py +++ b/project_manager/pipe/core.py @@ -67,6 +67,8 @@ class PipeRunner: result = None for pipe in pipes: result = pipe.apply(result, self._basepath) + if result is None: + break return result def resolve_value(self, pipeline: str) -> Any: diff --git a/project_manager/preset/default/python.toml b/project_manager/preset/default/python.toml index 57f04c2..d926d62 100644 --- a/project_manager/preset/default/python.toml +++ b/project_manager/preset/default/python.toml @@ -6,7 +6,11 @@ conditions = [ "file(pyproject.toml) | get(build-system.build-backend) | equal(poetry.core.masonry.api)", ] variables.python_version = "file(pyproject.toml) | get(tool.poetry.dependencies.python) | search((\\d.\\d))" -commands = ["poetry env use {python_version}", "poetry install"] +commands = [ + "poetry env use system", + "poetry env use {python_version}", + "poetry install", +] [presets.python.tasks.build.poetry] conditions = [ @@ -17,7 +21,7 @@ commands = ["poetry build"] [presets.python.tasks.publish.poetry] conditions = [ "file(pyproject.toml) | get(build-system.build-backend) | equal(poetry.core.masonry.api)", - "file(poetry.toml)", + "file(poetry.toml) | get(repositories)", ] variables.python_repository = "file(poetry.toml) | get(repositories) | key(0)" commands = ["poetry publish -r {python_repository}"] diff --git a/project_manager/preset/entry.py b/project_manager/preset/entry.py index ff58275..56f4004 100644 --- a/project_manager/preset/entry.py +++ b/project_manager/preset/entry.py @@ -5,7 +5,7 @@ from typing import Optional from ..entry import Entry, EntryDetector from ..pipe.core import PipeParser, PipeRunner -from .resolve import PresetResolver +from .resolve import PresetResolver, PresetResolverConfig __all__ = ("PresetEntryDetector",) @@ -13,11 +13,12 @@ __all__ = ("PresetEntryDetector",) class PresetEntryDetector(EntryDetector): _PIPE_PARSER = PipeParser() - def __init__(self, filename: Path): + def __init__(self, preset_resolver_config: Optional[PresetResolverConfig] = None): self._pipeparser = PipeParser() + self._preset_resolver_config = preset_resolver_config def detect_entry(self, basepath: Path) -> Optional["Entry"]: - preset = PresetResolver(basepath).resolve_preset() + preset = PresetResolver(basepath, self._preset_resolver_config).resolve_preset() piperunner = PipeRunner(basepath) tasks = defaultdict(list) for preset_name, preset in preset.presets.items(): @@ -26,14 +27,16 @@ class PresetEntryDetector(EntryDetector): for task_name, task_pack in preset.tasks.items(): for task_item_name, task in task_pack.items(): if piperunner.check_conditions(task.conditions): - variables = ( - { - k: piperunner.resolve_value(v) - for k, v in task.variables.items() - } - if task.variables - else {} - ) + variables = { + "entry_path": basepath, + } + if task.variables: + variables.update( + { + k: piperunner.resolve_value(v) + for k, v in task.variables.items() + } + ) tasks[task_name] += [ command.format(**variables) for command in task.commands ] diff --git a/project_manager/preset/resolve.py b/project_manager/preset/resolve.py index 9018742..e94cfd9 100644 --- a/project_manager/preset/resolve.py +++ b/project_manager/preset/resolve.py @@ -1,35 +1,49 @@ -__all__ = ("PresetResolver",) - - +from dataclasses import dataclass from pathlib import Path -from typing import Dict, Iterable +from typing import Dict, Iterable, Optional import tomli from ..util import merge_dicts from .model import Preset +__all__ = ( + "PresetResolverConfig", + "PresetResolver", +) + + +@dataclass +class PresetResolverConfig: + deafult: bool = True + user: bool = True + project: bool = True + class PresetResolver: _source_cache: Dict[Path, dict] = {} - def __init__(self, basepath: Path): + def __init__(self, basepath: Path, config: Optional[PresetResolverConfig] = None): self._basepath = basepath + self._config = config or PresetResolverConfig() def iter_sources(self) -> Iterable[Path]: - for filepath in (Path(__file__).parent / "default").iterdir(): - yield filepath - user_preset = Path.home() / ".preset.toml" - if user_preset.exists(): - yield user_preset - projectpath = self._basepath - while projectpath.exists(): - project_preset = projectpath / ".preset.toml" - if project_preset.exists(): - yield project_preset - if projectpath.parent == projectpath: - break - projectpath = projectpath.parent + if self._config.deafult: + for filepath in (Path(__file__).parent / "default").iterdir(): + yield filepath + if self._config.user: + user_preset = Path.home() / ".preset.toml" + if user_preset.exists(): + yield user_preset + if self._config.project: + projectpath = self._basepath + while projectpath.exists(): + project_preset = projectpath / ".preset.toml" + if project_preset.exists(): + yield project_preset + if projectpath.parent == projectpath: + break + projectpath = projectpath.parent def resolve_preset(self) -> Preset: result_preset_data = {} diff --git a/pyproject.toml b/pyproject.toml index c5c57d2..5f97b18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "project-manager" -version = "0.1.1" +version = "0.1.2" description = "" authors = ["shmyga "] readme = "README.md" diff --git a/tests/nodejs/package.json b/tests/nodejs/package.json new file mode 100644 index 0000000..edc260b --- /dev/null +++ b/tests/nodejs/package.json @@ -0,0 +1,11 @@ +{ + "name": "example", + "version": "0.1.0", + "description": "", + "scripts": { + "build": "echo build" + }, + "devDependencies": { + "jest": "^29.2.2" + } +} diff --git a/tests/python/poetry.toml b/tests/python/poetry.toml new file mode 100644 index 0000000..ab1033b --- /dev/null +++ b/tests/python/poetry.toml @@ -0,0 +1,2 @@ +[virtualenvs] +in-project = true diff --git a/tests/python/pyproject.toml b/tests/python/pyproject.toml new file mode 100644 index 0000000..f99b240 --- /dev/null +++ b/tests/python/pyproject.toml @@ -0,0 +1,17 @@ +[tool.poetry] +name = "example" +version = "0.1.0" +description = "" +authors = [] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry.group.dev.dependencies] +black = "^23.1.0" +pytest = "^7.2.2" +isort = "^5.12.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/test_entry.py b/tests/test_entry.py index b7be142..a1434c5 100644 --- a/tests/test_entry.py +++ b/tests/test_entry.py @@ -1,2 +1,48 @@ -def test_entry(): - pass +import shutil +from pathlib import Path +from typing import Optional + +import pytest + +from project_manager.entry import Entry, EntryDetector +from project_manager.preset.entry import PresetEntryDetector +from project_manager.preset.resolve import PresetResolverConfig + + +@pytest.fixture +def preset_resolver_config(): + return PresetResolverConfig( + user=False, + project=False, + ) + + +@pytest.fixture +def entry_detector(preset_resolver_config: PresetResolverConfig): + return PresetEntryDetector(preset_resolver_config) + + +@pytest.fixture +def entry(entry_detector: EntryDetector, entry_path: Path, tmp_path: Path): + destination = shutil.copytree(entry_path, tmp_path / entry_path.name) + return entry_detector.detect_entry(destination) + + +@pytest.mark.parametrize("entry_path", [Path(__file__).parent / "python"]) +def test_python_entry(entry: Optional[Entry], entry_detector: EntryDetector): + assert entry is not None + assert {"setup", "build"} == entry._tasks.keys() + entry.run_task("setup") + entry = entry_detector.detect_entry(entry._basepath) + assert entry is not None + assert {"setup", "build", "lint", "format", "test", "clean"} == entry._tasks.keys() + + +@pytest.mark.parametrize("entry_path", [Path(__file__).parent / "nodejs"]) +def test_nodejs_entry(entry: Optional[Entry], entry_detector: EntryDetector): + assert entry is not None + assert {"setup", "build"} == entry._tasks.keys() + entry.run_task("setup") + entry = entry_detector.detect_entry(entry._basepath) + assert entry is not None + assert {"setup", "build", "test", "clean"} == entry._tasks.keys()