bakefile 0.0.4__py3-none-any.whl → 0.0.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. bake/__init__.py +9 -0
  2. bake/bakebook/bakebook.py +85 -0
  3. bake/bakebook/decorator.py +50 -0
  4. bake/bakebook/get.py +175 -0
  5. bake/cli/bake/__init__.py +3 -0
  6. bake/cli/bake/__main__.py +5 -0
  7. bake/cli/bake/main.py +74 -0
  8. bake/cli/bake/reinvocation.py +63 -0
  9. bake/cli/bakefile/__init__.py +3 -0
  10. bake/cli/bakefile/__main__.py +5 -0
  11. bake/cli/bakefile/add_inline.py +29 -0
  12. bake/cli/bakefile/export.py +212 -0
  13. bake/cli/bakefile/find_python.py +18 -0
  14. bake/cli/bakefile/init.py +56 -0
  15. bake/cli/bakefile/lint.py +77 -0
  16. bake/cli/bakefile/main.py +43 -0
  17. bake/cli/bakefile/uv.py +146 -0
  18. bake/cli/common/app.py +54 -0
  19. bake/cli/common/callback.py +13 -0
  20. bake/cli/common/context.py +145 -0
  21. bake/cli/common/exception_handler.py +57 -0
  22. bake/cli/common/obj.py +216 -0
  23. bake/cli/common/params.py +72 -0
  24. bake/cli/utils/__init__.py +0 -0
  25. bake/cli/utils/version.py +18 -0
  26. bake/manage/__init__.py +0 -0
  27. bake/manage/add_inline.py +71 -0
  28. bake/manage/find_python.py +210 -0
  29. bake/manage/lint.py +101 -0
  30. bake/manage/run_uv.py +88 -0
  31. bake/manage/write_bakefile.py +20 -0
  32. bake/py.typed +0 -0
  33. bake/samples/__init__.py +0 -0
  34. bake/samples/simple.py +8 -0
  35. bake/ui/__init__.py +11 -0
  36. bake/ui/console.py +58 -0
  37. bake/ui/logger/__init__.py +33 -0
  38. bake/ui/logger/capsys.py +158 -0
  39. bake/ui/logger/setup.py +53 -0
  40. bake/ui/logger/utils.py +215 -0
  41. bake/ui/params.py +5 -0
  42. bake/ui/run/__init__.py +5 -0
  43. bake/ui/run/run.py +546 -0
  44. bake/ui/run/script.py +74 -0
  45. bake/ui/run/splitter.py +257 -0
  46. bake/ui/run/uv.py +83 -0
  47. bake/ui/style.py +2 -0
  48. bake/utils/__init__.py +11 -0
  49. bake/utils/constants.py +21 -0
  50. {bakefile → bake/utils}/env.py +3 -1
  51. bake/utils/exceptions.py +17 -0
  52. {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/METADATA +16 -2
  53. bakefile-0.0.9.dist-info/RECORD +68 -0
  54. {bakefile-0.0.4.dist-info → bakefile-0.0.9.dist-info}/WHEEL +2 -2
  55. bakefile-0.0.9.dist-info/entry_points.txt +5 -0
  56. bakelib/__init__.py +23 -0
  57. bakelib/environ/__init__.py +14 -0
  58. bakelib/environ/bakebook.py +30 -0
  59. bakelib/environ/base.py +112 -0
  60. bakelib/environ/get_bakebook.py +49 -0
  61. bakelib/environ/presets.py +70 -0
  62. bakelib/space/__init__.py +0 -0
  63. bakelib/space/base.py +193 -0
  64. bakelib/space/python.py +89 -0
  65. bakelib/space/utils.py +118 -0
  66. bakefile/__init__.py +0 -13
  67. bakefile/cli/bake/__init__.py +0 -3
  68. bakefile/cli/bake/main.py +0 -127
  69. bakefile/cli/bake/resolve_bakebook.py +0 -103
  70. bakefile/cli/bake/utils.py +0 -25
  71. bakefile/cli/bakefile.py +0 -19
  72. bakefile/cli/utils/version.py +0 -9
  73. bakefile/exceptions.py +0 -9
  74. bakefile-0.0.4.dist-info/RECORD +0 -16
  75. bakefile-0.0.4.dist-info/entry_points.txt +0 -4
  76. {bakefile/cli/utils → bake/bakebook}/__init__.py +0 -0
  77. {bakefile → bake}/cli/__init__.py +0 -0
  78. /bakefile/py.typed → /bake/cli/common/__init__.py +0 -0
@@ -0,0 +1,112 @@
1
+ from typing import Any, ClassVar
2
+
3
+ from pydantic import GetCoreSchemaHandler
4
+ from pydantic_core import CoreSchema, core_schema
5
+
6
+
7
+ class BaseEnv(str):
8
+ """BaseEnvironment string with comparison and Pydantic support.
9
+
10
+ Inherits from str to provide natural string behavior while adding
11
+ comparison operators for priority-based ordering.
12
+
13
+ ENV_ORDER is a list where each element is either:
14
+ - A string (normal priority, order matters)
15
+ - A set of strings (equal priority group)
16
+
17
+ Example: ["dev", "staging", {"prod", "share"}]
18
+ - "dev" has highest priority (index 0)
19
+ - "staging" has medium priority (index 1)
20
+ - "prod" and "share" have equal lowest priority (both in set at index 2)
21
+ """
22
+
23
+ ENV_ORDER: ClassVar[list[str | set[str]]] = ["dev", "staging", "prod"]
24
+
25
+ def __init__(self, value: str):
26
+ if not self._is_valid(value):
27
+ raise ValueError(
28
+ f"Invalid {self.__class__.__name__}: '{value}'. "
29
+ f"Must be one of: {self._flattened_env_order()}"
30
+ )
31
+
32
+ @classmethod
33
+ def _is_valid(cls, value: str) -> bool:
34
+ try:
35
+ cls._get_priority_index(value)
36
+ return True
37
+ except ValueError:
38
+ return False
39
+
40
+ @classmethod
41
+ def _flattened_env_order(cls) -> list[str]:
42
+ result: list[str] = []
43
+ for item in cls.ENV_ORDER:
44
+ if isinstance(item, set):
45
+ result.extend(sorted(item))
46
+ else:
47
+ result.append(item)
48
+ return result
49
+
50
+ @classmethod
51
+ def _get_priority_index(cls, value: str) -> int:
52
+ for idx, item in enumerate(cls.ENV_ORDER):
53
+ is_in_set = isinstance(item, set) and value in item
54
+ is_equal = item == value
55
+ if is_in_set or is_equal:
56
+ return idx
57
+ raise ValueError(f"Value '{value}' not found in ENV_ORDER")
58
+
59
+ def __lt__(self, other: str) -> bool:
60
+ if type(other) is not type(self):
61
+ return NotImplemented
62
+ self_idx = self._get_priority_index(str(self))
63
+ other_idx = self._get_priority_index(str(other))
64
+ if self_idx != other_idx:
65
+ return self_idx < other_idx
66
+ # Same priority group, use alphabetical as tiebreaker
67
+ return str(self) < str(other)
68
+
69
+ def __le__(self, other: str) -> bool:
70
+ if type(other) is not type(self):
71
+ return NotImplemented
72
+ return self < other or self == other
73
+
74
+ def __gt__(self, other: str) -> bool:
75
+ if type(other) is not type(self):
76
+ return NotImplemented
77
+ return not (self < other) and self != other
78
+
79
+ def __ge__(self, other: str) -> bool:
80
+ if type(other) is not type(self):
81
+ return NotImplemented
82
+ return not (self < other)
83
+
84
+ def __eq__(self, other: object) -> bool:
85
+ if type(other) is not type(self):
86
+ return False
87
+ return str(self) == str(other)
88
+
89
+ def __ne__(self, other: object) -> bool:
90
+ return not self.__eq__(other)
91
+
92
+ def __hash__(self) -> int:
93
+ return hash(str(self))
94
+
95
+ def __repr__(self) -> str:
96
+ return f"{self.__class__.__name__}('{self!s}')"
97
+
98
+ @classmethod
99
+ def __get_pydantic_core_schema__(
100
+ cls, source_type: Any, handler: GetCoreSchemaHandler
101
+ ) -> CoreSchema:
102
+ """Pydantic v2 integration for custom type validation."""
103
+ return core_schema.no_info_after_validator_function(cls, handler(str))
104
+
105
+ @classmethod
106
+ def validate(cls, v: object) -> "BaseEnv":
107
+ """Validate and convert input to BaseEnv."""
108
+ if isinstance(v, cls):
109
+ return v
110
+ if isinstance(v, str):
111
+ return cls(v)
112
+ raise ValueError(f"Cannot convert {type(v).__name__} to {cls.__name__}")
@@ -0,0 +1,49 @@
1
+ import os
2
+ from typing import TypeVar
3
+
4
+ from dotenv import load_dotenv as _load_dotenv
5
+
6
+ from .bakebook import EnvBakebook
7
+
8
+ # TODO: When min Python >= 3.12, use PEP 695 type parameter syntax:
9
+ # def get_bakebook[E: EnvBakebook](bakebooks: list[E], ...) -> E:
10
+ E = TypeVar("E", bound=EnvBakebook)
11
+
12
+
13
+ def get_bakebook(
14
+ bakebooks: list[E],
15
+ *,
16
+ env_var_name: str = "ENV",
17
+ env_value: str | None = None,
18
+ load_dotenv: bool = True,
19
+ ) -> E:
20
+ if not bakebooks:
21
+ raise ValueError("bakebooks list cannot be empty")
22
+
23
+ # Convert to dict for O(1) lookup and duplicate detection
24
+ bakebooks_by_env: dict[str, E] = {}
25
+ for bb in bakebooks:
26
+ if not hasattr(bb, "env") or bb.env is None:
27
+ raise ValueError(f"All bakebooks must have an 'env' attribute. Found: {bb}")
28
+ env = str(bb.env)
29
+ if env in bakebooks_by_env:
30
+ raise ValueError(f"Duplicate env '{env}' found in bakebooks list")
31
+ bakebooks_by_env[env] = bb
32
+
33
+ # Get environment value
34
+ if env_value is None:
35
+ if load_dotenv:
36
+ _load_dotenv()
37
+ env_value = os.getenv(env_var_name)
38
+
39
+ # Env var not set - return lowest priority (min)
40
+ if env_value is None or env_value == "":
41
+ return bakebooks_by_env[str(min(bb.env for bb in bakebooks))]
42
+ # If env var is set, require exact match
43
+ elif env_value in bakebooks_by_env:
44
+ return bakebooks_by_env[env_value]
45
+
46
+ raise ValueError(
47
+ f"No bakebook found with env='{env_value}'. "
48
+ f"Available envs: {sorted(bakebooks_by_env.keys())}"
49
+ )
@@ -0,0 +1,70 @@
1
+ """Preset environment configurations.
2
+
3
+ This module contains pre-configured environment classes for common use cases.
4
+ Users can also create their own environments by inheriting from BaseEnv.
5
+ """
6
+
7
+ from typing import ClassVar
8
+
9
+ from bakelib.environ.base import BaseEnv
10
+
11
+
12
+ class GcpLandingZoneEnv(BaseEnv):
13
+ """GCP Landing Zone base environments.
14
+
15
+ Environment codes follow GCP Security Foundations Blueprint conventions:
16
+ https://docs.cloud.google.com/architecture/blueprints/security-foundations/summary
17
+
18
+ Environment Codes:
19
+ - d - Development
20
+ - n - Nonproduction
21
+ - p - Production
22
+ - s - Shared
23
+ - b - Bootstrap
24
+ - c - Common
25
+ - net - Network
26
+
27
+ Tiers (ordered by priority, lower index = higher priority):
28
+ - d (development) - lowest priority
29
+ - n (nonprod)
30
+ - p/s/b/c/net - highest priority, all equal (production + shared)
31
+
32
+ Example:
33
+ env = GcpLandingZoneEnv("d")
34
+ assert env < GcpLandingZoneEnv("n")
35
+ assert GcpLandingZoneEnv("n") < GcpLandingZoneEnv("p")
36
+ """
37
+
38
+ ENV_ORDER: ClassVar[list[str | set[str]]] = [
39
+ "d",
40
+ "n",
41
+ {"p", "s", "b", "c", "net"},
42
+ ]
43
+
44
+ def is_shared(self) -> bool:
45
+ return self.code in {"s", "b", "c", "net"}
46
+
47
+ @property
48
+ def name(self) -> str:
49
+ names = {
50
+ "d": "Development",
51
+ "n": "Nonproduction",
52
+ "p": "Production",
53
+ "s": "Shared",
54
+ "b": "Bootstrap",
55
+ "c": "Common",
56
+ "net": "Network",
57
+ }
58
+ return names[self.code]
59
+
60
+ @property
61
+ def code(self) -> str:
62
+ return str(self)
63
+
64
+ @property
65
+ def secondary_name(self) -> str:
66
+ return self.name if not self.is_shared() else "Shared"
67
+
68
+ @property
69
+ def secondary_code(self) -> str:
70
+ return self.code if not self.is_shared() else "s"
File without changes
bakelib/space/base.py ADDED
@@ -0,0 +1,193 @@
1
+ from pathlib import Path
2
+ from typing import Annotated, Literal
3
+
4
+ import orjson
5
+ import typer
6
+
7
+ from bake import Bakebook, Context, command
8
+ from bake.ui import console
9
+
10
+ from .utils import (
11
+ HOMWBREW_BIN,
12
+ LOCAL_BIN,
13
+ VENV_BIN,
14
+ PlatformType,
15
+ ToolInfo,
16
+ get_expected_paths,
17
+ get_platform,
18
+ remove_git_clean_candidates,
19
+ setup_brew,
20
+ setup_bun,
21
+ setup_uv,
22
+ setup_uv_tool,
23
+ )
24
+
25
+
26
+ class BaseSpace(Bakebook):
27
+ def _no_implementation(self, ctx: Context | None = None, *args, **kwargs):
28
+ _ = ctx, args, kwargs
29
+ console.error("No implementation")
30
+ raise typer.Exit(1)
31
+
32
+ @command(help="Run linters and formatters")
33
+ def lint(self, ctx: Context) -> None:
34
+ ctx.run('bunx prettier@latest --write "**/*.{js,jsx,ts,tsx,css,json,json5,yaml,yml,md\'}"')
35
+
36
+ @command(help="Run unit tests")
37
+ def test(self, ctx: Context) -> None:
38
+ self._no_implementation(ctx)
39
+
40
+ @command(help="Run integration tests")
41
+ def test_integration(self, ctx: Context) -> None:
42
+ self._no_implementation(ctx)
43
+
44
+ @command(help="Run all tests")
45
+ def test_all(self, ctx: Context) -> None:
46
+ self._no_implementation(ctx)
47
+
48
+ @command(help="Clean gitignored files with optional exclusions")
49
+ def clean(
50
+ self,
51
+ ctx: Context,
52
+ exclude_patterns: Annotated[
53
+ list[str] | None,
54
+ typer.Option(
55
+ "--exclude-patterns",
56
+ "-e",
57
+ help="Patterns to exclude",
58
+ ),
59
+ ] = None,
60
+ use_default_excludes: Annotated[
61
+ bool,
62
+ typer.Option(
63
+ "--no-default-excludes",
64
+ help="Do not apply default exclude patterns",
65
+ is_flag=True,
66
+ ),
67
+ ] = False,
68
+ ) -> None:
69
+ results = ctx.run("git clean -fdX -n", stream=False, dry_run=False, echo=True)
70
+
71
+ exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
72
+
73
+ if not use_default_excludes:
74
+ exclude_patterns |= {".env", ".cache"}
75
+
76
+ console.err.print(f"Exclude pattens: {exclude_patterns}")
77
+
78
+ remove_git_clean_candidates(
79
+ git_clean_dry_run_output=results.stdout,
80
+ exclude_patterns=exclude_patterns,
81
+ dry_run=ctx.dry_run,
82
+ )
83
+
84
+ @command(help="Clean all gitignored files")
85
+ def clean_all(self, ctx: Context) -> None:
86
+ ctx.run("git clean -fdX")
87
+
88
+ def setup_tool_managers(self, ctx: Context, platform: PlatformType) -> None:
89
+ _ = platform
90
+ setup_brew(ctx)
91
+
92
+ def setup_tools(self, ctx: Context, platform: PlatformType) -> None:
93
+ _ = platform
94
+ setup_bun(ctx)
95
+ setup_uv(ctx)
96
+ setup_uv_tool(ctx)
97
+
98
+ def setup_project(self, ctx: Context) -> None:
99
+ ctx.run("uv run pre-commit install")
100
+
101
+ @command(help="Setup development environment")
102
+ def setup_dev(self, ctx: Context) -> None:
103
+ platform = get_platform()
104
+ console.echo(f"Detected platform: {platform}")
105
+
106
+ if platform != "macos":
107
+ console.warning(f"Platform '{platform}' is not supported. Running in dry-run mode.")
108
+ overridden_dry_run = True
109
+ else:
110
+ overridden_dry_run = ctx.dry_run
111
+
112
+ with ctx.override_dry_run(overridden_dry_run):
113
+ self.clean(ctx=ctx)
114
+ self.setup_tool_managers(ctx=ctx, platform=platform)
115
+ self.setup_tools(ctx=ctx, platform=platform)
116
+ self.setup_project(ctx=ctx)
117
+
118
+ def _assert_which_path(
119
+ self,
120
+ ctx: Context,
121
+ tool_name: str,
122
+ tool_info: ToolInfo,
123
+ ) -> bool:
124
+ result = ctx.run(f"which {tool_name}", stream=False)
125
+ if ctx.dry_run:
126
+ return True
127
+ actual_path = Path(result.stdout.strip())
128
+
129
+ if actual_path in set(tool_info.expected_paths):
130
+ console.success(f"{tool_name}: {actual_path}")
131
+ return True
132
+
133
+ console.warning(f"{tool_name}: unexpected location (got {actual_path})")
134
+ return False
135
+
136
+ def _get_tools(self) -> dict[str, ToolInfo]:
137
+ return {
138
+ # homebrew only
139
+ "bun": ToolInfo(expected_paths=get_expected_paths("bun", {HOMWBREW_BIN})),
140
+ # homebrew or venv
141
+ "uv": ToolInfo(expected_paths=get_expected_paths("uv", {HOMWBREW_BIN, VENV_BIN})),
142
+ # local or venv
143
+ "bakefile": ToolInfo(
144
+ expected_paths=get_expected_paths("bakefile", {LOCAL_BIN, VENV_BIN})
145
+ ),
146
+ "pre-commit": ToolInfo(
147
+ expected_paths=get_expected_paths("pre-commit", {LOCAL_BIN, VENV_BIN})
148
+ ),
149
+ }
150
+
151
+ @command(help="List development tools")
152
+ def tools(
153
+ self,
154
+ ctx: Context,
155
+ format: Annotated[
156
+ Literal["json", "names"],
157
+ typer.Option("--format", "-f", help="Output format"),
158
+ ] = "json",
159
+ ) -> None:
160
+ _ = ctx
161
+ tools = self._get_tools()
162
+ if format == "json":
163
+ output: dict[str, dict[str, str | None]] = {k: v.model_dump() for k, v in tools.items()}
164
+ console.echo(orjson.dumps(output, option=orjson.OPT_INDENT_2).decode())
165
+ else:
166
+ console.echo("\n".join(sorted(tools.keys())))
167
+
168
+ @command(help="Assert development environment setup")
169
+ def assert_setup_dev(
170
+ self,
171
+ ctx: Context,
172
+ skip_test: Annotated[
173
+ bool,
174
+ typer.Option(
175
+ "--skip-test",
176
+ "-s",
177
+ help="Skip running tests",
178
+ is_flag=True,
179
+ ),
180
+ ] = False,
181
+ ) -> None:
182
+ tools = self._get_tools()
183
+ for tool_name, tool_info in tools.items():
184
+ self._assert_which_path(ctx, tool_name, tool_info)
185
+
186
+ self.lint(ctx)
187
+ if not skip_test:
188
+ self.test(ctx)
189
+
190
+ @command(help="Upgrade all dependencies")
191
+ def update(self, ctx: Context) -> None:
192
+ ctx.run("uv python upgrade")
193
+ ctx.run("uv tool upgrade --all")
@@ -0,0 +1,89 @@
1
+ from pathlib import Path
2
+
3
+ from bake import Context, params
4
+
5
+ from .base import BaseSpace, ToolInfo
6
+ from .utils import VENV_BIN, get_expected_paths
7
+
8
+
9
+ def _get_python_version() -> str | None:
10
+ path = Path(".python-version")
11
+ if not path.exists():
12
+ return None
13
+ return path.read_text().strip()
14
+
15
+
16
+ class PythonSpace(BaseSpace):
17
+ def _get_tools(self) -> dict[str, ToolInfo]:
18
+ tools = super()._get_tools()
19
+ tools["python"] = ToolInfo(
20
+ version=_get_python_version(),
21
+ expected_paths=list(get_expected_paths("python", {VENV_BIN})),
22
+ )
23
+ return tools
24
+
25
+ def lint(self, ctx: Context) -> None:
26
+ super().lint(ctx=ctx)
27
+
28
+ ctx.run(
29
+ "uv run toml-sort --sort-inline-arrays --in-place "
30
+ "--sort-first=project,dependency-groups pyproject.toml"
31
+ )
32
+ ctx.run("uv run ruff format --exit-non-zero-on-format .")
33
+ ctx.run("uv run ruff check --fix --exit-non-zero-on-fix .")
34
+ ctx.run("uv run ty check --error-on-warning --no-progress .")
35
+ ctx.run("uv run deptry .")
36
+
37
+ def _test(
38
+ self,
39
+ ctx: Context,
40
+ *,
41
+ tests_paths: str | list[str],
42
+ verbose: bool = False,
43
+ coverage_report: bool = True,
44
+ ) -> None:
45
+ paths = tests_paths if isinstance(tests_paths, str) else " ".join(tests_paths)
46
+
47
+ cmd = f"uv run pytest {paths}"
48
+
49
+ if coverage_report:
50
+ cmd += " --cov=src --cov-report=html --cov-report=term-missing --cov-report=xml"
51
+
52
+ if verbose:
53
+ cmd += " -s -v"
54
+
55
+ ctx.run(cmd)
56
+
57
+ def test_integration(
58
+ self,
59
+ ctx: Context,
60
+ verbose: params.verbose_bool = False,
61
+ ) -> None:
62
+ integration_tests_path = "tests/integration/"
63
+ if Path(integration_tests_path).exists():
64
+ tests_path = integration_tests_path
65
+ self._test(ctx, tests_paths=tests_path, verbose=verbose)
66
+ else:
67
+ self._no_implementation(ctx)
68
+
69
+ def test(self, ctx: Context) -> None:
70
+ unit_tests_path = "tests/unit/"
71
+ tests_path = unit_tests_path if Path(unit_tests_path).exists() else "tests/"
72
+ self._test(ctx, tests_paths=tests_path)
73
+
74
+ def test_all(self, ctx: Context) -> None:
75
+ unit_tests_path = "tests/unit/"
76
+ if Path(unit_tests_path).exists():
77
+ tests_path = "tests/"
78
+ self._test(ctx, tests_paths=tests_path)
79
+ else:
80
+ self._no_implementation(ctx)
81
+
82
+ def setup_project(self, ctx: Context) -> None:
83
+ super().setup_project(ctx=ctx)
84
+ ctx.run("uv sync --all-extras --all-groups --frozen")
85
+
86
+ def update(self, ctx: Context) -> None:
87
+ super().update(ctx=ctx)
88
+ ctx.run("uv lock --upgrade")
89
+ ctx.run("uv sync --all-extras --all-groups")
bakelib/space/utils.py ADDED
@@ -0,0 +1,118 @@
1
+ import shutil
2
+ import sys
3
+ from enum import Enum
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ import pathspec
8
+ from pathspec.patterns.gitignore.basic import GitIgnoreBasicPattern
9
+ from pydantic import BaseModel, Field
10
+
11
+ from bake import Context
12
+ from bake.ui import console
13
+
14
+
15
+ def setup_brew(ctx: Context) -> None:
16
+ ctx.run("brew update")
17
+ ctx.run("brew upgrade")
18
+ ctx.run("brew cleanup")
19
+ ctx.run("brew list")
20
+ ctx.run("brew leaves")
21
+
22
+
23
+ class ToolInfo(BaseModel):
24
+ version: str | None = None
25
+ expected_paths: list[Path] = Field(default_factory=list, exclude=True)
26
+
27
+
28
+ class Platform(Enum):
29
+ MACOS = "macos"
30
+ LINUX = "linux"
31
+ WINDOWS = "windows"
32
+ OTHER = "other"
33
+
34
+
35
+ PlatformType = Literal["macos", "linux", "windows", "other"]
36
+
37
+
38
+ def get_platform() -> PlatformType:
39
+ if sys.platform == "darwin":
40
+ return Platform.MACOS.value
41
+ elif sys.platform == "linux":
42
+ return Platform.LINUX.value
43
+ elif sys.platform == "win32":
44
+ return Platform.WINDOWS.value
45
+ return Platform.OTHER.value
46
+
47
+
48
+ def setup_uv(ctx: Context) -> None:
49
+ ctx.run("brew install uv")
50
+ ctx.run("uv python upgrade")
51
+ ctx.run("uv tool upgrade --all")
52
+ ctx.run("uv tool update-shell")
53
+
54
+
55
+ def setup_bun(ctx: Context) -> None:
56
+ ctx.run("brew install oven-sh/bun/bun")
57
+
58
+
59
+ def setup_uv_tool(ctx: Context) -> None:
60
+ ctx.run("uv tool install bakefile")
61
+ ctx.run("uv tool install pre-commit")
62
+
63
+
64
+ HOMWBREW_BIN = Path("/opt/homebrew/bin")
65
+ LOCAL_BIN = Path.home() / ".local" / "bin"
66
+ VENV_BIN = Path.cwd() / ".venv" / "bin"
67
+
68
+
69
+ def get_expected_paths(tool: str, locations: set[Path]) -> list[Path]:
70
+ return [loc / tool for loc in locations]
71
+
72
+
73
+ def _skip_msg(path: Path, suffix: str, dry_run: bool) -> None:
74
+ verb = "Would skip" if dry_run else "Skipping"
75
+ console.echo(f"[yellow]~[/yellow] {verb} {suffix}{path}")
76
+
77
+
78
+ def _remove_msg(path: Path, dry_run: bool) -> None:
79
+ verb = "Would remove" if dry_run else "Removing"
80
+ console.echo(f"[red]-[/red] [dim]{verb}[/dim] {path}")
81
+
82
+
83
+ def _should_remove_path(path: Path, dry_run: bool) -> None:
84
+ _remove_msg(path, dry_run)
85
+ if dry_run:
86
+ return
87
+
88
+ if path.is_dir():
89
+ shutil.rmtree(path)
90
+ else:
91
+ path.unlink(missing_ok=True)
92
+
93
+
94
+ def remove_git_clean_candidates(
95
+ git_clean_dry_run_output: str, exclude_patterns: set[str], dry_run: bool
96
+ ) -> None:
97
+ spec = pathspec.PathSpec.from_lines(
98
+ GitIgnoreBasicPattern,
99
+ exclude_patterns,
100
+ )
101
+
102
+ for line in git_clean_dry_run_output.splitlines():
103
+ line = line.strip()
104
+ if not line.startswith("Would remove "):
105
+ continue
106
+
107
+ rel_path = line.removeprefix("Would remove ").strip()
108
+ path = Path(rel_path)
109
+
110
+ if spec.match_file(rel_path):
111
+ _skip_msg(path, "", dry_run)
112
+ continue
113
+
114
+ if path.is_dir() and (path / ".git").exists():
115
+ _skip_msg(path, "git repository ", dry_run)
116
+ continue
117
+
118
+ _should_remove_path(path, dry_run)
bakefile/__init__.py DELETED
@@ -1,13 +0,0 @@
1
- from importlib.metadata import PackageNotFoundError, version
2
-
3
- __all__ = ["__version__"]
4
-
5
-
6
- def _get_version() -> str:
7
- try:
8
- return version("bakefile")
9
- except PackageNotFoundError:
10
- return "0.0.0"
11
-
12
-
13
- __version__ = _get_version()
@@ -1,3 +0,0 @@
1
- from bakefile.cli.bake.main import main
2
-
3
- __all__ = ["main"]