pep723-to-wheel 0.1.0__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.
- pep723_to_wheel/__init__.py +5 -0
- pep723_to_wheel/cli.py +45 -0
- pep723_to_wheel/core.py +352 -0
- pep723_to_wheel-0.1.0.dist-info/METADATA +73 -0
- pep723_to_wheel-0.1.0.dist-info/RECORD +8 -0
- pep723_to_wheel-0.1.0.dist-info/WHEEL +4 -0
- pep723_to_wheel-0.1.0.dist-info/entry_points.txt +2 -0
- pep723_to_wheel-0.1.0.dist-info/licenses/LICENSE +21 -0
pep723_to_wheel/cli.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Typer-based CLI entry points."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from pep723_to_wheel.core import build_script_to_wheel, import_wheel_to_script
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(add_completion=False, no_args_is_help=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("build")
|
|
15
|
+
def build_command(
|
|
16
|
+
script_path: Path = typer.Argument(..., help="Path to the PEP 723 script."),
|
|
17
|
+
output_dir: Path | None = typer.Option(
|
|
18
|
+
None, "--output-dir", "-o", help="Directory for the built wheel."
|
|
19
|
+
),
|
|
20
|
+
version: str | None = typer.Option(
|
|
21
|
+
None,
|
|
22
|
+
"--version",
|
|
23
|
+
"-v",
|
|
24
|
+
help="Wheel version (defaults to calendar versioning).",
|
|
25
|
+
),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Build a wheel from a PEP 723 script."""
|
|
28
|
+
|
|
29
|
+
result = build_script_to_wheel(script_path, output_dir, version)
|
|
30
|
+
typer.echo(str(result.wheel_path))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.command("import")
|
|
34
|
+
def import_command(
|
|
35
|
+
wheel_or_package: str = typer.Argument(
|
|
36
|
+
..., help="Wheel path or package name to import."
|
|
37
|
+
),
|
|
38
|
+
output_path: Path = typer.Option(
|
|
39
|
+
..., "--output", "-o", help="Path to write the reconstructed script."
|
|
40
|
+
),
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Reconstruct a script from a wheel or package name."""
|
|
43
|
+
|
|
44
|
+
result = import_wheel_to_script(wheel_or_package, output_path)
|
|
45
|
+
typer.echo(str(result.script_path))
|
pep723_to_wheel/core.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
"""Core implementation for the pep723-to-wheel CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
import re
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import tempfile
|
|
12
|
+
import tomllib
|
|
13
|
+
import zipfile
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
16
|
+
|
|
17
|
+
PEP723_START = "# /// script"
|
|
18
|
+
PEP723_END = "# ///"
|
|
19
|
+
UTC = timezone.utc
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class BuildResult:
|
|
24
|
+
"""Result metadata for a build operation."""
|
|
25
|
+
|
|
26
|
+
wheel_path: Path
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class ImportResult:
|
|
31
|
+
"""Result metadata for an import operation."""
|
|
32
|
+
|
|
33
|
+
script_path: Path
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Pep723Header(BaseModel):
|
|
37
|
+
"""PEP 723 script metadata."""
|
|
38
|
+
|
|
39
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
40
|
+
|
|
41
|
+
requires_python: str | None = Field(default=None, alias="requires-python")
|
|
42
|
+
dependencies: list[str] = Field(default_factory=list)
|
|
43
|
+
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_script(cls, script_path: Path) -> "Pep723Header":
|
|
46
|
+
"""Extract and parse a PEP 723 header from a script file."""
|
|
47
|
+
|
|
48
|
+
text = script_path.read_text(encoding="utf-8")
|
|
49
|
+
block = _extract_pep723_block(text)
|
|
50
|
+
data = _parse_pep723_kv(block)
|
|
51
|
+
return cls.model_validate(data)
|
|
52
|
+
|
|
53
|
+
def render_block(self) -> str:
|
|
54
|
+
"""Render the PEP 723 header block."""
|
|
55
|
+
|
|
56
|
+
body_lines: list[str] = []
|
|
57
|
+
if self.requires_python:
|
|
58
|
+
body_lines.append(f'requires-python = "{self.requires_python}"')
|
|
59
|
+
if self.dependencies:
|
|
60
|
+
deps_formatted = ", ".join(f'"{dep}"' for dep in self.dependencies)
|
|
61
|
+
body_lines.append(f"dependencies = [{deps_formatted}]")
|
|
62
|
+
return "\n".join(
|
|
63
|
+
[
|
|
64
|
+
PEP723_START,
|
|
65
|
+
*[f"# {line}" for line in body_lines],
|
|
66
|
+
PEP723_END,
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _extract_pep723_block(text: str) -> str:
|
|
72
|
+
lines = text.splitlines()
|
|
73
|
+
inside_block = False
|
|
74
|
+
block_lines: list[str] = []
|
|
75
|
+
for line in lines:
|
|
76
|
+
if line.strip() == PEP723_START:
|
|
77
|
+
inside_block = True
|
|
78
|
+
continue
|
|
79
|
+
if line.strip() == PEP723_END and inside_block:
|
|
80
|
+
break
|
|
81
|
+
if inside_block:
|
|
82
|
+
block_lines.append(line)
|
|
83
|
+
return "\n".join(block_lines)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _parse_pep723_kv(block: str) -> dict:
|
|
87
|
+
if not block.strip():
|
|
88
|
+
return {}
|
|
89
|
+
cleaned = "\n".join(
|
|
90
|
+
line.lstrip("#").lstrip() for line in block.splitlines()
|
|
91
|
+
)
|
|
92
|
+
return tomllib.loads(cleaned)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _normalize_project_name(name: str) -> str:
|
|
96
|
+
normalized = re.sub(r"[^a-zA-Z0-9]+", "-", name).strip("-").lower()
|
|
97
|
+
return normalized or "pep723-script"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _normalize_module_name(name: str) -> str:
|
|
101
|
+
module_name = re.sub(r"[^a-zA-Z0-9_]+", "_", name).strip("_").lower()
|
|
102
|
+
if not module_name or module_name[0].isdigit():
|
|
103
|
+
module_name = f"pkg_{module_name}"
|
|
104
|
+
return module_name
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _format_dependencies_block(dependencies: list[str]) -> list[str]:
|
|
108
|
+
lines = ["dependencies = ["]
|
|
109
|
+
if dependencies:
|
|
110
|
+
deps_formatted = ",\n ".join(f"\"{dep}\"" for dep in dependencies)
|
|
111
|
+
lines.append(f" {deps_formatted}")
|
|
112
|
+
lines.append("]")
|
|
113
|
+
return lines
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _extract_requires_dist(metadata_text: str) -> list[str]:
|
|
117
|
+
requirements: list[str] = []
|
|
118
|
+
for line in metadata_text.splitlines():
|
|
119
|
+
if line.startswith("Requires-Dist:"):
|
|
120
|
+
requirements.append(line.replace("Requires-Dist:", "", 1).strip())
|
|
121
|
+
return requirements
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _extract_requires_python(metadata_text: str) -> str | None:
|
|
125
|
+
for line in metadata_text.splitlines():
|
|
126
|
+
if line.startswith("Requires-Python:"):
|
|
127
|
+
return line.replace("Requires-Python:", "", 1).strip()
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _calendar_version(script_path: Path) -> str:
|
|
132
|
+
mtime = script_path.stat().st_mtime
|
|
133
|
+
timestamp = datetime.fromtimestamp(mtime, tz=UTC)
|
|
134
|
+
return f"{timestamp.year}.{timestamp.month:02d}.{int(mtime)}"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _build_temp_project(
|
|
138
|
+
script_path: Path,
|
|
139
|
+
output_dir: Path,
|
|
140
|
+
version: str,
|
|
141
|
+
) -> Path:
|
|
142
|
+
pep723 = Pep723Header.from_script(script_path)
|
|
143
|
+
dependencies = pep723.dependencies
|
|
144
|
+
requires_python = pep723.requires_python
|
|
145
|
+
|
|
146
|
+
project_name = _normalize_project_name(script_path.stem)
|
|
147
|
+
module_name = _normalize_module_name(project_name)
|
|
148
|
+
|
|
149
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
150
|
+
temp_path = Path(temp_dir)
|
|
151
|
+
package_dir = temp_path / "src" / module_name
|
|
152
|
+
package_dir.mkdir(parents=True)
|
|
153
|
+
|
|
154
|
+
(package_dir / "__init__.py").write_text(
|
|
155
|
+
f'"""Package for {project_name}."""\n',
|
|
156
|
+
encoding="utf-8",
|
|
157
|
+
)
|
|
158
|
+
(package_dir / "script.py").write_text(
|
|
159
|
+
script_path.read_text(encoding="utf-8"),
|
|
160
|
+
encoding="utf-8",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
pyproject = temp_path / "pyproject.toml"
|
|
164
|
+
toml_lines = [
|
|
165
|
+
"[project]",
|
|
166
|
+
f'name = "{project_name}"',
|
|
167
|
+
f'version = "{version}"',
|
|
168
|
+
'description = "PEP 723 script bundle"',
|
|
169
|
+
]
|
|
170
|
+
if requires_python:
|
|
171
|
+
toml_lines.append(f'requires-python = "{requires_python}"')
|
|
172
|
+
toml_lines.extend(_format_dependencies_block(dependencies))
|
|
173
|
+
toml_lines.extend(
|
|
174
|
+
[
|
|
175
|
+
"",
|
|
176
|
+
"[build-system]",
|
|
177
|
+
'requires = ["hatchling>=1.27.0"]',
|
|
178
|
+
'build-backend = "hatchling.build"',
|
|
179
|
+
"",
|
|
180
|
+
"[tool.hatch.build.targets.wheel]",
|
|
181
|
+
f'packages = ["src/{module_name}"]',
|
|
182
|
+
"",
|
|
183
|
+
]
|
|
184
|
+
)
|
|
185
|
+
pyproject.write_text(
|
|
186
|
+
"\n".join(toml_lines),
|
|
187
|
+
encoding="utf-8",
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
temp_output_dir = temp_path / "dist"
|
|
191
|
+
temp_output_dir.mkdir(parents=True, exist_ok=True)
|
|
192
|
+
subprocess.run(
|
|
193
|
+
["uv", "build", "--wheel", "--out-dir", str(temp_output_dir)],
|
|
194
|
+
check=True,
|
|
195
|
+
cwd=temp_path,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
wheels = list(temp_output_dir.glob("*.whl"))
|
|
199
|
+
if not wheels:
|
|
200
|
+
raise FileNotFoundError(f"No wheel produced in {temp_output_dir}")
|
|
201
|
+
if len(wheels) > 1:
|
|
202
|
+
raise RuntimeError(
|
|
203
|
+
f"Expected exactly one wheel in {temp_output_dir}, found {len(wheels)}: "
|
|
204
|
+
f"{', '.join(str(w) for w in wheels)}"
|
|
205
|
+
)
|
|
206
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
207
|
+
built_wheel = wheels[0]
|
|
208
|
+
dest_wheel = output_dir / built_wheel.name
|
|
209
|
+
shutil.copy2(built_wheel, dest_wheel)
|
|
210
|
+
return dest_wheel
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _find_import_name(wheel: zipfile.ZipFile, package_name: str) -> str | None:
|
|
214
|
+
normalized_package = package_name.replace("-", "_")
|
|
215
|
+
candidates: set[str] = set()
|
|
216
|
+
for name in wheel.namelist():
|
|
217
|
+
if name.endswith("/"):
|
|
218
|
+
continue
|
|
219
|
+
parts = name.split("/")
|
|
220
|
+
if len(parts) == 1 and name.endswith(".py"):
|
|
221
|
+
candidates.add(Path(name).stem)
|
|
222
|
+
continue
|
|
223
|
+
if len(parts) >= 2 and parts[-1] == "__init__.py":
|
|
224
|
+
top_level = parts[0]
|
|
225
|
+
if top_level.endswith(".dist-info") or top_level.endswith(".data"):
|
|
226
|
+
continue
|
|
227
|
+
if top_level.startswith("__"):
|
|
228
|
+
continue
|
|
229
|
+
candidates.add(top_level)
|
|
230
|
+
if normalized_package in candidates:
|
|
231
|
+
return normalized_package
|
|
232
|
+
if len(candidates) == 1:
|
|
233
|
+
return next(iter(candidates))
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def build_script_to_wheel(
|
|
238
|
+
script_path: Path,
|
|
239
|
+
output_dir: Path | None = None,
|
|
240
|
+
version: str | None = None,
|
|
241
|
+
) -> BuildResult:
|
|
242
|
+
"""Build a wheel from a PEP 723 script.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
script_path: Path to the PEP 723 script.
|
|
246
|
+
output_dir: Directory to write the wheel into.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
BuildResult containing the wheel location.
|
|
250
|
+
"""
|
|
251
|
+
|
|
252
|
+
if not script_path.exists():
|
|
253
|
+
raise FileNotFoundError(script_path)
|
|
254
|
+
target_dir = output_dir or script_path.parent / "dist"
|
|
255
|
+
resolved_version = version or _calendar_version(script_path)
|
|
256
|
+
wheel_path = _build_temp_project(script_path, target_dir, resolved_version)
|
|
257
|
+
return BuildResult(wheel_path=wheel_path)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _download_wheel(package: str, dest: Path) -> Path:
|
|
261
|
+
subprocess.run(
|
|
262
|
+
[
|
|
263
|
+
"uv",
|
|
264
|
+
"pip",
|
|
265
|
+
"download",
|
|
266
|
+
"--only-binary",
|
|
267
|
+
":all:",
|
|
268
|
+
"--dest",
|
|
269
|
+
str(dest),
|
|
270
|
+
package,
|
|
271
|
+
],
|
|
272
|
+
check=True,
|
|
273
|
+
)
|
|
274
|
+
wheels = sorted(dest.glob("*.whl"))
|
|
275
|
+
if not wheels:
|
|
276
|
+
raise FileNotFoundError(f"No wheel downloaded for {package}")
|
|
277
|
+
return wheels[-1]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _read_script_from_wheel(wheel_path: Path) -> str | None:
|
|
281
|
+
with zipfile.ZipFile(wheel_path) as wheel:
|
|
282
|
+
for name in wheel.namelist():
|
|
283
|
+
if name.endswith("/script.py"):
|
|
284
|
+
return wheel.read(name).decode("utf-8")
|
|
285
|
+
return None
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _build_script_from_metadata(wheel_path: Path) -> str:
|
|
289
|
+
with zipfile.ZipFile(wheel_path) as wheel:
|
|
290
|
+
metadata_name = next(
|
|
291
|
+
(name for name in wheel.namelist() if name.endswith(".dist-info/METADATA")),
|
|
292
|
+
None,
|
|
293
|
+
)
|
|
294
|
+
if metadata_name is None:
|
|
295
|
+
raise ValueError("Wheel metadata not found.")
|
|
296
|
+
metadata_text = wheel.read(metadata_name).decode("utf-8")
|
|
297
|
+
name_line = next(
|
|
298
|
+
(line for line in metadata_text.splitlines() if line.startswith("Name: ")),
|
|
299
|
+
None,
|
|
300
|
+
)
|
|
301
|
+
if name_line is None:
|
|
302
|
+
raise ValueError("Wheel metadata missing Name field.")
|
|
303
|
+
package_name = name_line.replace("Name: ", "", 1).strip()
|
|
304
|
+
import_name = _find_import_name(wheel, package_name)
|
|
305
|
+
requires = _extract_requires_dist(metadata_text)
|
|
306
|
+
requires_python = _extract_requires_python(metadata_text)
|
|
307
|
+
all_packages = [package_name]
|
|
308
|
+
all_packages.extend(dep for dep in requires if dep not in all_packages)
|
|
309
|
+
header = Pep723Header(
|
|
310
|
+
requires_python=requires_python,
|
|
311
|
+
dependencies=all_packages,
|
|
312
|
+
)
|
|
313
|
+
lines = [header.render_block()]
|
|
314
|
+
if import_name:
|
|
315
|
+
lines.append(f"import {import_name}")
|
|
316
|
+
lines.append("")
|
|
317
|
+
return "\n".join(lines)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _script_text_from_wheel(wheel_path: Path) -> str:
|
|
321
|
+
script_text = _read_script_from_wheel(wheel_path)
|
|
322
|
+
if script_text is None:
|
|
323
|
+
script_text = _build_script_from_metadata(wheel_path)
|
|
324
|
+
return script_text
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def import_wheel_to_script(
|
|
328
|
+
wheel_or_package: str,
|
|
329
|
+
output_path: Path,
|
|
330
|
+
) -> ImportResult:
|
|
331
|
+
"""Reconstruct a script from a wheel or package name.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
wheel_or_package: Wheel filename/path or a package name to install with uv.
|
|
335
|
+
output_path: Destination path for the reconstructed script.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
ImportResult containing the script location.
|
|
339
|
+
"""
|
|
340
|
+
|
|
341
|
+
wheel_path = Path(wheel_or_package)
|
|
342
|
+
if wheel_path.exists():
|
|
343
|
+
script_text = _script_text_from_wheel(wheel_path)
|
|
344
|
+
else:
|
|
345
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
346
|
+
temp_path = Path(temp_dir)
|
|
347
|
+
wheel_to_read = _download_wheel(wheel_or_package, temp_path)
|
|
348
|
+
script_text = _script_text_from_wheel(wheel_to_read)
|
|
349
|
+
|
|
350
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
351
|
+
output_path.write_text(script_text, encoding="utf-8")
|
|
352
|
+
return ImportResult(script_path=output_path)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pep723-to-wheel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PoC for pep-723 script to wheel and back
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: pydantic>=2.5
|
|
8
|
+
Requires-Dist: tomli-w>=1.1.0
|
|
9
|
+
Requires-Dist: typer>=0.21.1
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# pep723-to-wheel
|
|
13
|
+
|
|
14
|
+
[](https://github.com/jooh/pep723-to-wheel/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/jooh/pep723-to-wheel)
|
|
16
|
+
|
|
17
|
+
A small utility for converting [PEP 723](https://peps.python.org/pep-0723/) inline dependency scripts into wheels and reconstructing scripts from wheels. Especially useful for taking [reproducible Marimo notebooks](https://marimo.io/blog/sandboxed-notebooks) to production environments.
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
Build a wheel from a script that has a PEP 723 inline block:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pep723-to-wheel build path/to/script.py --output-dir dist
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Set an explicit wheel version (defaults to calendar versioning using the script mtime as the patch segment):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pep723-to-wheel build path/to/script.py --version 2024.12.25
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Reconstruct a script from a wheel or package name:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pep723-to-wheel import path/to/package.whl --output reconstructed.py
|
|
37
|
+
pep723-to-wheel import requests --output reconstructed.py
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Library
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from pep723_to_wheel import build_script_to_wheel, import_wheel_to_script
|
|
45
|
+
|
|
46
|
+
result = build_script_to_wheel(Path("script.py"))
|
|
47
|
+
print(result.wheel_path)
|
|
48
|
+
|
|
49
|
+
import_result = import_wheel_to_script("requests", Path("reconstructed.py"))
|
|
50
|
+
print(import_result.script_path)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
make test
|
|
57
|
+
make typecheck
|
|
58
|
+
make ruff
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Release process
|
|
62
|
+
|
|
63
|
+
Releases are automated on pushes to `main` by the CD workflow in `.github/workflows/cd.yml`.
|
|
64
|
+
|
|
65
|
+
1. The workflow determines the latest `v*` Git tag and runs `.github/workflows/cd_version.py` to
|
|
66
|
+
resolve the next version. If the latest tag matches the current major/minor, it bumps the patch
|
|
67
|
+
to one higher than the max of the current patch and the tag patch.
|
|
68
|
+
2. If `pyproject.toml` changes, the workflow commits the version bump back to `main`.
|
|
69
|
+
3. It tags the release as `v<version>`, builds the wheel with `uv build --wheel`, publishes to
|
|
70
|
+
PyPI, and creates a GitHub release with the wheel attached.
|
|
71
|
+
|
|
72
|
+
To trigger a release, merge or push changes to `main` and ensure `PYPI_API_TOKEN` is configured in
|
|
73
|
+
the repository secrets.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
pep723_to_wheel/__init__.py,sha256=UZEM6bNBmh5CrdVeyf-5Eka70a98SD6BEmJoGaAg6ws,208
|
|
2
|
+
pep723_to_wheel/cli.py,sha256=rBwdcrrMo0zJednHoGPXMdO595gpTjJcgp0YLXlV_ks,1289
|
|
3
|
+
pep723_to_wheel/core.py,sha256=rXfjhafw-y0OvV8gBuQAVc8ejHt3Q5cU7zOTFwigI9w,11130
|
|
4
|
+
pep723_to_wheel-0.1.0.dist-info/METADATA,sha256=LC-9-VxtTtnB0BIJde3P837vzocTmgSbLE5C89HJhpM,2530
|
|
5
|
+
pep723_to_wheel-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
6
|
+
pep723_to_wheel-0.1.0.dist-info/entry_points.txt,sha256=bi6cqb9WEwR2xT249k8wYQv9lkhwzdqpvezJwFp1zmo,60
|
|
7
|
+
pep723_to_wheel-0.1.0.dist-info/licenses/LICENSE,sha256=R1voKzqfBhEnGOTKHENGjQjn2qkeSm-TLziX2Vat2V8,1069
|
|
8
|
+
pep723_to_wheel-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Johan Carlin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|