bakefile 0.0.10__py3-none-any.whl → 0.0.11__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.
- bake/cli/bake/main.py +51 -9
- bake/cli/bake/reinvocation.py +6 -3
- bake/cli/bakefile/main.py +38 -4
- bake/cli/common/app.py +35 -33
- bake/cli/common/context.py +4 -0
- bake/cli/common/obj.py +4 -3
- bake/cli/common/params.py +10 -2
- bake/manage/lint.py +10 -7
- bake/ui/console.py +73 -9
- bake/ui/run/run.py +45 -13
- bake/ui/run/uv.py +2 -10
- bake/utils/__init__.py +8 -2
- bake/utils/settings.py +25 -0
- {bakefile-0.0.10.dist-info → bakefile-0.0.11.dist-info}/METADATA +3 -1
- {bakefile-0.0.10.dist-info → bakefile-0.0.11.dist-info}/RECORD +26 -22
- {bakefile-0.0.10.dist-info → bakefile-0.0.11.dist-info}/WHEEL +1 -1
- bakelib/__init__.py +2 -0
- bakelib/refreshable_cache/__init__.py +17 -0
- bakelib/refreshable_cache/cache.py +250 -0
- bakelib/refreshable_cache/exceptions.py +2 -0
- bakelib/space/base.py +26 -12
- bakelib/space/lib.py +161 -0
- bakelib/space/python.py +17 -0
- bakelib/space/python_lib.py +77 -0
- bakelib/space/utils.py +10 -0
- bake/cli/common/callback.py +0 -13
- bake/utils/env.py +0 -10
- {bakefile-0.0.10.dist-info → bakefile-0.0.11.dist-info}/entry_points.txt +0 -0
bakelib/space/base.py
CHANGED
|
@@ -45,6 +45,28 @@ class BaseSpace(Bakebook):
|
|
|
45
45
|
def test_all(self, ctx: Context) -> None:
|
|
46
46
|
self._no_implementation(ctx)
|
|
47
47
|
|
|
48
|
+
def _clean(
|
|
49
|
+
self,
|
|
50
|
+
ctx: Context,
|
|
51
|
+
exclude_patterns: list[str] | None,
|
|
52
|
+
default_excludes: bool,
|
|
53
|
+
default_exclude_patterns: set[str],
|
|
54
|
+
):
|
|
55
|
+
results = ctx.run("git clean -fdX -n", stream=False, dry_run=False, echo=True)
|
|
56
|
+
|
|
57
|
+
exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
|
|
58
|
+
|
|
59
|
+
if default_excludes:
|
|
60
|
+
exclude_patterns |= default_exclude_patterns
|
|
61
|
+
|
|
62
|
+
console.err.print(f"Exclude pattens: {exclude_patterns}")
|
|
63
|
+
|
|
64
|
+
remove_git_clean_candidates(
|
|
65
|
+
git_clean_dry_run_output=results.stdout,
|
|
66
|
+
exclude_patterns=exclude_patterns,
|
|
67
|
+
dry_run=ctx.dry_run,
|
|
68
|
+
)
|
|
69
|
+
|
|
48
70
|
@command(help="Clean gitignored files with optional exclusions")
|
|
49
71
|
def clean(
|
|
50
72
|
self,
|
|
@@ -62,19 +84,11 @@ class BaseSpace(Bakebook):
|
|
|
62
84
|
typer.Option(help="Apply default exclude patterns (.env, .cache)"),
|
|
63
85
|
] = True,
|
|
64
86
|
) -> None:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
exclude_patterns: set[str] = set(exclude_patterns if exclude_patterns else [])
|
|
68
|
-
|
|
69
|
-
if default_excludes:
|
|
70
|
-
exclude_patterns |= {".env", ".cache"}
|
|
71
|
-
|
|
72
|
-
console.err.print(f"Exclude pattens: {exclude_patterns}")
|
|
73
|
-
|
|
74
|
-
remove_git_clean_candidates(
|
|
75
|
-
git_clean_dry_run_output=results.stdout,
|
|
87
|
+
self._clean(
|
|
88
|
+
ctx=ctx,
|
|
76
89
|
exclude_patterns=exclude_patterns,
|
|
77
|
-
|
|
90
|
+
default_excludes=default_excludes,
|
|
91
|
+
default_exclude_patterns={".env", ".cache"},
|
|
78
92
|
)
|
|
79
93
|
|
|
80
94
|
@command(help="Clean all gitignored files")
|
bakelib/space/lib.py
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
from pydantic import SecretStr
|
|
9
|
+
from tenacity import stop_after_attempt
|
|
10
|
+
|
|
11
|
+
from bake import Context, command, console
|
|
12
|
+
from bake.ui.logger import strip_ansi
|
|
13
|
+
from bakelib.refreshable_cache import ChainedCache, KeyringCache, NullCache
|
|
14
|
+
|
|
15
|
+
from .base import BaseSpace, ToolInfo
|
|
16
|
+
from .utils import CARGO_BIN, PlatformType, get_expected_paths, setup_rustup, setup_zerv
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class PublishResult:
|
|
21
|
+
result: subprocess.CompletedProcess[str] | None
|
|
22
|
+
is_dry_run: bool
|
|
23
|
+
is_auth_failed: bool
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseLibSpace(BaseSpace):
|
|
27
|
+
bake_publish_token: SecretStr | None = None
|
|
28
|
+
_dummy_publish_token: str = "dummy-token-for-dry-run"
|
|
29
|
+
|
|
30
|
+
def setup_tools(self, ctx: Context, platform: PlatformType) -> None:
|
|
31
|
+
_ = platform
|
|
32
|
+
super().setup_tools(ctx, platform=platform)
|
|
33
|
+
setup_rustup(ctx)
|
|
34
|
+
setup_zerv(ctx)
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def _get_publish_token_from_remote(self, registry: str) -> str | None: ...
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def package_name(self, ctx: Context) -> str: ...
|
|
41
|
+
|
|
42
|
+
@abstractmethod
|
|
43
|
+
def _build_for_publish(self, ctx: Context): ...
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def _publish_with_token(
|
|
47
|
+
self, ctx: Context, token: str | None, registry: str
|
|
48
|
+
) -> PublishResult: ...
|
|
49
|
+
|
|
50
|
+
def _get_cached_publish_token(
|
|
51
|
+
self, ctx: Context, token: str | None, registry: str
|
|
52
|
+
) -> ChainedCache[str | None]:
|
|
53
|
+
token_from_local = self._get_token_from_local(token)
|
|
54
|
+
key = f"publish-token-{registry}"
|
|
55
|
+
namespace = self.package_name(ctx)
|
|
56
|
+
|
|
57
|
+
def get_publish_token() -> str | None:
|
|
58
|
+
return token_from_local or self._get_publish_token_from_remote(registry)
|
|
59
|
+
|
|
60
|
+
stop = stop_after_attempt(1) if token_from_local else None
|
|
61
|
+
|
|
62
|
+
cached_publish_token = ChainedCache(
|
|
63
|
+
backends=[KeyringCache, NullCache],
|
|
64
|
+
namespace=namespace,
|
|
65
|
+
key=key,
|
|
66
|
+
fetch_fn=get_publish_token,
|
|
67
|
+
stop=stop,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if token_from_local is not None:
|
|
71
|
+
cached_publish_token.set(token_from_local)
|
|
72
|
+
|
|
73
|
+
return cached_publish_token
|
|
74
|
+
|
|
75
|
+
def _get_token_from_local(self, token: str | None) -> str | None:
|
|
76
|
+
if token:
|
|
77
|
+
return token
|
|
78
|
+
|
|
79
|
+
if self.bake_publish_token:
|
|
80
|
+
return self.bake_publish_token.get_secret_value()
|
|
81
|
+
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@contextmanager
|
|
85
|
+
@abstractmethod
|
|
86
|
+
def _version_bump_context(self, _ctx: Context, _version: str): ...
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def _pre_publish_cleanup(self, _ctx: Context): ...
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def _version_schema(self) -> str | None:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
def _is_auth_failure(self, result: subprocess.CompletedProcess[str]) -> bool:
|
|
96
|
+
return result.returncode != 0
|
|
97
|
+
|
|
98
|
+
def _determine_version(self, ctx: Context, version: str | None) -> str:
|
|
99
|
+
return version if version else self.zerv_versioning(ctx, schema=self._version_schema)
|
|
100
|
+
|
|
101
|
+
@command(help="Build and publish the package")
|
|
102
|
+
def publish(
|
|
103
|
+
self,
|
|
104
|
+
ctx: Context,
|
|
105
|
+
registry: Annotated[str, typer.Option(help="Publish registry")] = "default",
|
|
106
|
+
token: Annotated[str | None, typer.Option(help="Publish token")] = None,
|
|
107
|
+
version: Annotated[str | None, typer.Option(help="Version to publish")] = None,
|
|
108
|
+
):
|
|
109
|
+
cached_publish_token = self._get_cached_publish_token(
|
|
110
|
+
ctx=ctx, token=token, registry=registry
|
|
111
|
+
)
|
|
112
|
+
version = self._determine_version(ctx, version)
|
|
113
|
+
|
|
114
|
+
self._pre_publish_cleanup(ctx)
|
|
115
|
+
|
|
116
|
+
with self._version_bump_context(ctx, version):
|
|
117
|
+
self._build_for_publish(ctx)
|
|
118
|
+
publish_result = self._execute_publish(
|
|
119
|
+
ctx=ctx, cached_publish_token=cached_publish_token, registry=registry
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
self._handle_publish_result(ctx, publish_result=publish_result)
|
|
123
|
+
|
|
124
|
+
def _execute_publish(
|
|
125
|
+
self, ctx: Context, cached_publish_token: ChainedCache[str | None], registry: str
|
|
126
|
+
) -> PublishResult:
|
|
127
|
+
@cached_publish_token.catch_refresh
|
|
128
|
+
def _publish() -> PublishResult:
|
|
129
|
+
token_value = cached_publish_token.get_value()
|
|
130
|
+
publish_result = self._publish_with_token(ctx=ctx, token=token_value, registry=registry)
|
|
131
|
+
|
|
132
|
+
if publish_result.result is not None and self._is_auth_failure(publish_result.result):
|
|
133
|
+
raise cached_publish_token.RefreshNeededError
|
|
134
|
+
|
|
135
|
+
return publish_result
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
return _publish()
|
|
139
|
+
except cached_publish_token.RefreshNeededError:
|
|
140
|
+
return PublishResult(result=None, is_dry_run=False, is_auth_failed=True)
|
|
141
|
+
|
|
142
|
+
def _handle_publish_result(self, ctx: Context, publish_result: PublishResult) -> None:
|
|
143
|
+
if publish_result.is_auth_failed:
|
|
144
|
+
console.error("Authentication failed. Please check your publish token.")
|
|
145
|
+
raise typer.Exit(1)
|
|
146
|
+
|
|
147
|
+
if publish_result.is_dry_run and not ctx.dry_run:
|
|
148
|
+
console.warning(
|
|
149
|
+
"This was a dry-run. To actually publish, "
|
|
150
|
+
"set the BAKE_PUBLISH_TOKEN environment variable"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
def zerv_versioning(self, ctx: Context, *, schema: str | None = None) -> str:
|
|
154
|
+
schema_flag = f"--schema {schema}" if schema else ""
|
|
155
|
+
result = ctx.run(f"zerv flow {schema_flag}", dry_run=False)
|
|
156
|
+
return strip_ansi(result.stdout.strip())
|
|
157
|
+
|
|
158
|
+
def _get_tools(self) -> dict[str, ToolInfo]:
|
|
159
|
+
tools = super()._get_tools()
|
|
160
|
+
tools["zerv"] = ToolInfo(expected_paths=get_expected_paths("zerv", {CARGO_BIN}))
|
|
161
|
+
return tools
|
bakelib/space/python.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
from bake import Context, params
|
|
5
|
+
from bake.ui.logger import strip_ansi
|
|
4
6
|
|
|
5
7
|
from .base import BaseSpace, ToolInfo
|
|
6
8
|
from .utils import VENV_BIN, get_expected_paths
|
|
@@ -14,6 +16,10 @@ def _get_python_version() -> str | None:
|
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class PythonSpace(BaseSpace):
|
|
19
|
+
def __init__(self) -> None:
|
|
20
|
+
super().__init__()
|
|
21
|
+
os.environ["UV_NO_PROGRESS"] = "true"
|
|
22
|
+
|
|
17
23
|
def _get_tools(self) -> dict[str, ToolInfo]:
|
|
18
24
|
tools = super()._get_tools()
|
|
19
25
|
tools["python"] = ToolInfo(
|
|
@@ -87,3 +93,14 @@ class PythonSpace(BaseSpace):
|
|
|
87
93
|
super().update(ctx=ctx)
|
|
88
94
|
ctx.run("uv lock --upgrade")
|
|
89
95
|
ctx.run("uv sync --all-extras --all-groups")
|
|
96
|
+
|
|
97
|
+
def _uv_version(self, ctx: Context) -> tuple[str, str]:
|
|
98
|
+
result = ctx.run("uv version", stream=False, dry_run=False, echo=False)
|
|
99
|
+
package_name, original_version = strip_ansi(result.stdout.strip()).split()
|
|
100
|
+
return package_name, original_version
|
|
101
|
+
|
|
102
|
+
def package_name(self, ctx: Context) -> str:
|
|
103
|
+
return self._uv_version(ctx)[0]
|
|
104
|
+
|
|
105
|
+
def current_version(self, ctx: Context) -> str:
|
|
106
|
+
return self._uv_version(ctx)[1]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from typing import Annotated, Literal, cast, get_args
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from bake import Context
|
|
9
|
+
|
|
10
|
+
from .lib import BaseLibSpace, PublishResult
|
|
11
|
+
from .python import PythonSpace
|
|
12
|
+
|
|
13
|
+
PublishIndex = Literal["testpypi", "pypi"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PythonLibSpace(PythonSpace, BaseLibSpace):
|
|
17
|
+
@property
|
|
18
|
+
def _version_schema(self) -> str | None:
|
|
19
|
+
return "standard-base-prerelease-post-dev"
|
|
20
|
+
|
|
21
|
+
def _registry_to_index(self, registry: str) -> PublishIndex:
|
|
22
|
+
valid_indices = get_args(PublishIndex)
|
|
23
|
+
if registry not in valid_indices:
|
|
24
|
+
raise ValueError(f"Invalid registry: {registry!r}. Expected one of {valid_indices}.")
|
|
25
|
+
return cast(PublishIndex, registry)
|
|
26
|
+
|
|
27
|
+
def _get_publish_token_from_remote(self, registry: str) -> str | None:
|
|
28
|
+
index = self._registry_to_index(registry)
|
|
29
|
+
_ = index
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def _build_for_publish(self, ctx: Context):
|
|
33
|
+
ctx.run("uv build")
|
|
34
|
+
|
|
35
|
+
def _publish_with_token(self, ctx: Context, token: str | None, registry: str) -> PublishResult:
|
|
36
|
+
index = self._registry_to_index(registry)
|
|
37
|
+
index_flag = f"--index {index} " if index == "testpypi" else ""
|
|
38
|
+
dry_run_flag = "" if token is not None else "--dry-run "
|
|
39
|
+
is_dry_run = token is None
|
|
40
|
+
|
|
41
|
+
env: dict[str, str] = {
|
|
42
|
+
"UV_PUBLISH_TOKEN": token if token is not None else self._dummy_publish_token
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
result = ctx.run(
|
|
46
|
+
f"uv publish {dry_run_flag}{index_flag}",
|
|
47
|
+
stream=True,
|
|
48
|
+
env=env,
|
|
49
|
+
check=False,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
return PublishResult(result=result, is_dry_run=is_dry_run, is_auth_failed=False)
|
|
53
|
+
|
|
54
|
+
def _is_auth_failure(self, result: subprocess.CompletedProcess[str]) -> bool:
|
|
55
|
+
auth_error_message = "403 Invalid or non-existent authentication information"
|
|
56
|
+
return result.returncode != 0 and auth_error_message in result.stderr
|
|
57
|
+
|
|
58
|
+
@contextmanager
|
|
59
|
+
def _version_bump_context(self, ctx: Context, version: str):
|
|
60
|
+
original_version = self.current_version(ctx)
|
|
61
|
+
ctx.run(f"uv version {version}")
|
|
62
|
+
try:
|
|
63
|
+
yield
|
|
64
|
+
finally:
|
|
65
|
+
ctx.run(f"uv version {original_version}")
|
|
66
|
+
|
|
67
|
+
def _pre_publish_cleanup(self, _ctx: Context):
|
|
68
|
+
shutil.rmtree("dist", ignore_errors=True)
|
|
69
|
+
|
|
70
|
+
def publish(
|
|
71
|
+
self,
|
|
72
|
+
ctx: Context,
|
|
73
|
+
index: Annotated[PublishIndex, typer.Option(help="Publish index")] = "testpypi",
|
|
74
|
+
token: Annotated[str | None, typer.Option(help="Publish token")] = None,
|
|
75
|
+
version: Annotated[str | None, typer.Option(help="Version to publish")] = None,
|
|
76
|
+
):
|
|
77
|
+
return super().publish(ctx=ctx, registry=index, token=token, version=version)
|
bakelib/space/utils.py
CHANGED
|
@@ -52,6 +52,15 @@ def setup_uv(ctx: Context) -> None:
|
|
|
52
52
|
ctx.run("uv tool update-shell")
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def setup_rustup(ctx: Context) -> None:
|
|
56
|
+
ctx.run("brew install rustup")
|
|
57
|
+
ctx.run("rustup update")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def setup_zerv(ctx: Context) -> None:
|
|
61
|
+
ctx.run("cargo install zerv")
|
|
62
|
+
|
|
63
|
+
|
|
55
64
|
def setup_bun(ctx: Context) -> None:
|
|
56
65
|
ctx.run("brew install oven-sh/bun/bun")
|
|
57
66
|
|
|
@@ -62,6 +71,7 @@ def setup_uv_tool(ctx: Context) -> None:
|
|
|
62
71
|
|
|
63
72
|
|
|
64
73
|
HOMWBREW_BIN = Path("/opt/homebrew/bin")
|
|
74
|
+
CARGO_BIN = Path.home() / ".cargo" / "bin"
|
|
65
75
|
LOCAL_BIN = Path.home() / ".local" / "bin"
|
|
66
76
|
VENV_BIN = Path.cwd() / ".venv" / "bin"
|
|
67
77
|
|
bake/cli/common/callback.py
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import typer
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def validate_file_name(file_name: str) -> str:
|
|
5
|
-
if "/" in file_name or "\\" in file_name:
|
|
6
|
-
raise typer.BadParameter(f"File name must not contain path separators: {file_name}")
|
|
7
|
-
if not file_name.endswith(".py"):
|
|
8
|
-
raise typer.BadParameter(f"File name must end with .py: {file_name}")
|
|
9
|
-
return file_name
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def validate_file_name_callback(value: str) -> str:
|
|
13
|
-
return validate_file_name(file_name=value)
|
bake/utils/env.py
DELETED
|
File without changes
|