release-gitter/pseudo_builder.py
Ian Fijolek f9c462b94a Improve debug logging
This also includes one fix that I discovered while improving the
logging. Even if a git url was provided, release_gitter was lookig for a
local package declaration (Cargo.toml) to identify the version.

With this change, the url parsing and the local repo logic are split
allowing for more detailed logging as well as avoiding this potential
bug.
2024-11-18 11:36:40 -08:00

187 lines
5.8 KiB
Python

"""
This builder functions as a pseudo builder that instead downloads and installs a binary file using
release-gitter based on a pyproject.toml file. It's a total hack...
"""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
from shutil import copy
from shutil import copytree
import toml
from wheel.wheelfile import WheelFile
import release_gitter as rg
from release_gitter import removeprefix
@dataclass
class Config:
name: str
format: str
git_url: str
hostname: str
owner: str
repo: str
version: str | None = None
pre_release: bool = False
version_git_tag: bool = False
version_git_no_fetch: bool = False
map_system: dict[str, str] | None = None
map_arch: dict[str, str] | None = None
exec: str | None = None
extract_all: bool = False
extract_files: list[str] | None = None
include_extra_files: list[str] | None = None
def download(config: Config, wheel_scripts: Path) -> list[Path]:
"""Download and extract files to the wheel_scripts directory"""
return rg.download_release(
rg.GitRemoteInfo(config.hostname, config.owner, config.repo),
wheel_scripts,
config.format,
version=config.version,
system_mapping=config.map_system,
arch_mapping=config.map_arch,
extract_files=config.extract_files,
pre_release=config.pre_release,
exec=config.exec,
)
def read_metadata() -> Config:
"""Read configuration from pyproject.toml"""
pyproject = toml.load("pyproject.toml").get("tool", {}).get("release-gitter")
if not pyproject:
raise ValueError("Must have configuration in [tool.release-gitter]")
git_url = pyproject.pop("git-url", None)
remote_info = rg.parse_git_url(git_url)
config = Config(
name=pyproject.pop("name", remote_info.repo),
format=pyproject.pop("format"),
git_url=git_url,
hostname=pyproject.pop("hostname", remote_info.hostname),
owner=pyproject.pop("owner", remote_info.owner),
repo=pyproject.pop("repo", remote_info.repo),
)
for key, value in pyproject.items():
setattr(config, str(key).replace("-", "_"), value)
if config.version is None:
config.version = rg.read_version(
config.version_git_tag,
not config.version_git_no_fetch,
)
if config.extract_all:
config.extract_files = []
return config
class _PseudoBuildBackend:
# Should allow passing args as `--build-option`
_gitter_args = None
def prepare_metadata_for_build_wheel(
self, metadata_directory, config_settings=None
):
# Create a .dist-info directory containing wheel metadata inside metadata_directory. Eg {metadata_directory}/{package}-{version}.dist-info/
print("Prepare meta", metadata_directory, config_settings)
metadata = read_metadata()
version = removeprefix(metadata.version, "v") if metadata.version else "0.0.0"
# Returns distinfo dir?
dist_info = Path(metadata_directory) / f"{metadata.name}-{version}.dist-info"
dist_info.mkdir()
# Write metadata
pkg_info = dist_info / "METADATA"
pkg_info.write_text(
"\n".join(
[
"Metadata-Version: 2.1",
f"Name: {metadata.name}",
f"Version: {version}",
]
)
)
# Write wheel info
wheel_info = dist_info / "WHEEL"
wheel_info.write_text(
"\n".join(
[
"Wheel-Version: 1.0",
"Root-Is-Purelib: true",
"Tag: py2-none-any",
"Tag: py3-none-any",
]
)
)
return str(dist_info)
def build_sdist(self, sdist_directory, config_settings=None):
# Builds a .tar.gz and places it in specified sdist_directory
# That should contain a toplevel drectory of `name-version` containing source files and the pyproject.toml
# HACK: This isn't needed or used
p = Path(sdist_directory + ".dist-info")
return p
def build_wheel(
self, wheel_directory, config_settings=None, metadata_directory=None
):
if metadata_directory is None:
raise ValueError("Cannot build wheel without metadata_directory")
metadata_directory = Path(metadata_directory)
metadata = read_metadata()
version = removeprefix(metadata.version, "v") if metadata.version else "0.0.0"
wheel_directory = Path(wheel_directory)
wheel_directory.mkdir(exist_ok=True)
wheel_scripts = wheel_directory / f"{metadata.name}-{version}.data/scripts"
wheel_scripts.mkdir(parents=True, exist_ok=True)
copytree(metadata_directory, wheel_directory / metadata_directory.name)
metadata = read_metadata()
download(metadata, wheel_scripts)
for file_name in metadata.include_extra_files or []:
file = Path(file_name)
if Path.cwd() in file.absolute().parents:
copy(file_name, wheel_scripts / file)
else:
raise ValueError(
f"Cannot include any path that is not within the current directory: {file_name}"
)
print(f"ls {wheel_directory}: {list(wheel_directory.rglob('*'))}")
wheel_filename = f"{metadata.name}-{version}-py2.py3-none-any.whl"
with WheelFile(wheel_directory / wheel_filename, "w") as wf:
print("Repacking wheel as {}...".format(wheel_filename), end="")
# sys.stdout.flush()
wf.write_files(str(wheel_directory))
return wheel_filename
_BACKEND = _PseudoBuildBackend()
prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
build_sdist = _BACKEND.build_sdist
build_wheel = _BACKEND.build_wheel