crate: improve determining project name/version from directory name
If the heuristics fail because the project uses a weird naming or versioning scheme or if the directory name does not match the "{project}-{version}" pattern. This is mostly the case for "workspace" proejcts. In the case the heuristics fail, the version can be overriden on the command line.
This commit is contained in:
parent
323dbea4b6
commit
228efa58fd
4 changed files with 91 additions and 8 deletions
|
@ -8,7 +8,7 @@ from cargo2rpm.semver import Version
|
|||
from rust2rpm import log
|
||||
from rust2rpm.cli import get_parser
|
||||
from rust2rpm.conf import load_config
|
||||
from rust2rpm.crate import InvalidProjectError, process_project
|
||||
from rust2rpm.crate import InvalidProjectError, InvalidVersionError, process_project
|
||||
from rust2rpm.cratesio import NoVersionsError
|
||||
from rust2rpm.distgit import get_package_info
|
||||
from rust2rpm.generator import spec_render_crate, spec_render_project, spec_render_workspace
|
||||
|
@ -56,6 +56,12 @@ def main():
|
|||
except InvalidProjectError:
|
||||
log.error(f"Invalid argument: {args.crate!r} is not a local file or directory")
|
||||
sys.exit(1)
|
||||
except InvalidVersionError:
|
||||
log.error(
|
||||
f"Could not determine project name and version automatically. "
|
||||
"Please specify the project version on the command line."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if metadata.is_workspace():
|
||||
base_name = project
|
||||
|
|
|
@ -58,6 +58,10 @@ class InvalidProjectError(ValueError):
|
|||
pass
|
||||
|
||||
|
||||
class InvalidVersionError(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def local_toml_file(toml_path: str) -> tuple[str, list[str], list[str]]:
|
||||
assert os.path.isfile(toml_path)
|
||||
assert os.path.basename(toml_path) == "Cargo.toml"
|
||||
|
@ -149,14 +153,52 @@ def project_is_path(path: str) -> bool:
|
|||
return "/" in path or path in {".", ".."}
|
||||
|
||||
|
||||
def guess_local_project_version_from_path(project: str) -> tuple[str, str]:
|
||||
def guess_local_project_version_from_dir(dir_name: str) -> tuple[str, str]:
|
||||
"""
|
||||
Use a simple heuristic to determine the project name and version from the
|
||||
name of the directory that contains the Cargo.toml file.
|
||||
|
||||
Raises an InvalidVersionError if the automatically determined version is
|
||||
not valid according to SemVer.
|
||||
"""
|
||||
|
||||
project = dir_name.rstrip("0123456789.").removesuffix("-")
|
||||
version = dir_name.removeprefix(f"{project}-")
|
||||
|
||||
try:
|
||||
Version.parse(version)
|
||||
except ValueError as exc:
|
||||
raise InvalidVersionError(exc.args)
|
||||
|
||||
return project, version
|
||||
|
||||
|
||||
def guess_local_project_version_from_path(project: str, version: Optional[str]) -> tuple[str, str]:
|
||||
"""
|
||||
Use a simple heuristic to determine the project name and version from the
|
||||
"project" argument supplied on the command line.
|
||||
|
||||
If the argument points at a file (i.e. a Cargo.toml file), the heuristics
|
||||
use the name of the file's parent directory. If the argument points at a
|
||||
directory, the name of the directory itself is used.
|
||||
|
||||
Raises an InvalidVersionError if the heuristics for automatically
|
||||
determining the project name and version fail, or if the automatically
|
||||
determined version is not valid according to SemVer. In this case,
|
||||
supplying the optional "version" argument on the command line can override
|
||||
the version string.
|
||||
"""
|
||||
|
||||
if os.path.isdir(project):
|
||||
dir_name = os.path.split(os.path.abspath(project))[1]
|
||||
else:
|
||||
dir_name = os.path.split(os.path.dirname(os.path.abspath(project)))[1]
|
||||
project = dir_name.rstrip("0123456789.").removesuffix("-")
|
||||
version = dir_name.removeprefix(f"{project}-")
|
||||
return project, version
|
||||
|
||||
if version:
|
||||
project = dir_name.removesuffix(f"-{version}")
|
||||
return project, version
|
||||
else:
|
||||
return guess_local_project_version_from_dir(dir_name)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
@ -170,6 +212,7 @@ def toml_temp_copy(toml_path: str):
|
|||
|
||||
def process_project_local(
|
||||
project: str,
|
||||
version: Optional[str],
|
||||
patch: bool,
|
||||
patch_foreign: bool,
|
||||
vendor: bool,
|
||||
|
@ -188,7 +231,7 @@ def process_project_local(
|
|||
|
||||
# fall back to the directory name for determining the name / version
|
||||
# of the project heuristically
|
||||
name, version = guess_local_project_version_from_path(project)
|
||||
name, version = guess_local_project_version_from_path(project, version)
|
||||
|
||||
log.warn(f"Falling back to {name!r} as the name of the project (based on the name of the containing folder).")
|
||||
diffs: tuple[Optional[list[str]], Optional[list[str]]] = (None, None)
|
||||
|
@ -276,7 +319,7 @@ def process_project(
|
|||
log.warn("The '--store-crate' flag has no effect for unpacked sources.")
|
||||
|
||||
name, version, diffs, metadata, doc_files, license_files, vendor_tarball = process_project_local(
|
||||
project, patch, patch_foreign, vendor
|
||||
project, version, patch, patch_foreign, vendor
|
||||
)
|
||||
return name, version, diffs, metadata, doc_files, license_files, True, vendor_tarball
|
||||
|
||||
|
|
|
@ -533,7 +533,12 @@ def spec_render_workspace(
|
|||
|
||||
is_cdylib = metadata.is_cdylib()
|
||||
|
||||
rpm_version = Version.parse(upstream_version).to_rpm()
|
||||
try:
|
||||
rpm_version = Version.parse(upstream_version).to_rpm()
|
||||
except ValueError:
|
||||
log.warn(f"Version {upstream_version!r} is not valid according to SemVer.")
|
||||
rpm_version = upstream_version
|
||||
|
||||
rpm_description = main_package.get_description()
|
||||
rpm_summary = main_package.get_summary()
|
||||
|
||||
|
|
29
rust2rpm/tests/test_crate.py
Normal file
29
rust2rpm/tests/test_crate.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
import pytest
|
||||
|
||||
from rust2rpm.crate import guess_local_project_version_from_dir
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,project,version",
|
||||
[
|
||||
("project-1.2.3", "project", "1.2.3"),
|
||||
("test-project-1.2.3", "test-project", "1.2.3"),
|
||||
],
|
||||
ids=repr,
|
||||
)
|
||||
def test_guess_local_project_version_from_dir(path: str, project: str, version: str):
|
||||
p, v = guess_local_project_version_from_dir(path)
|
||||
assert (p, v) == (project, version)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"path,error",
|
||||
[
|
||||
("helix-23.10-source", "Invalid version"),
|
||||
],
|
||||
ids=repr,
|
||||
)
|
||||
def test_guess_local_project_version_from_dir_fail(path: str, error: str):
|
||||
with pytest.raises(ValueError) as exc:
|
||||
guess_local_project_version_from_dir(path)
|
||||
assert error in str(exc.value)
|
Loading…
Reference in a new issue