Setuptools build integration plugin for GitVersioned.
This module implements the integration layer between GitVersioned and the
Setuptools build system. It exposes hook entry points that Setuptools calls
during distribution configuration, allowing packaging configuration to
dynamically query and apply resolved Git-based versions.
The plugin functions by registering setup keyword arguments and finalizer
hooks. It extracts the package distribution options, resolves project
context, extracts any pre-existing or environment-specified versions,
executes the Git versioning resolution, and updates the distribution
metadata version dynamically. If configured, it also injects the generated
version file into the distribution's package_data or py_modules.
Compute the package version and update the distribution metadata.
This is the primary entry point triggered during the Setuptools distribution
finalization lifecycle. It resolves the package name, extracts any established or
environment-provided version, reads configuration overrides, calculates the dynamic
version using Git metadata, and applies the version to the distribution metadata.
If a version file is generated, it is automatically injected into the distribution's
package data or module list.
.. code-block:: python
# Hook is registered as a Setuptools entry point:
# entry_point = "gitversioned.plugins.setuptools_plugin"
# func = "finalize_distribution_options"
:param distribution: The Setuptools distribution object to finalize.
:raises DistutilsSetupError: If the package name is unresolved or if version
resolution encounters an unexpected failure.
Source code in src/gitversioned/plugins/setuptools_plugin.py
| def finalize_distribution_options(distribution: Distribution) -> None:
"""
Compute the package version and update the distribution metadata.
This is the primary entry point triggered during the Setuptools distribution
finalization lifecycle. It resolves the package name, extracts any established or
environment-provided version, reads configuration overrides, calculates the dynamic
version using Git metadata, and applies the version to the distribution metadata.
If a version file is generated, it is automatically injected into the distribution's
package data or module list.
.. code-block:: python
# Hook is registered as a Setuptools entry point:
# entry_point = "gitversioned.plugins.setuptools_plugin"
# func = "finalize_distribution_options"
:param distribution: The Setuptools distribution object to finalize.
:raises DistutilsSetupError: If the package name is unresolved or if version
resolution encounters an unexpected failure.
"""
configure_logger(
enabled=True,
clear_loggers=True,
level="WARNING",
otel_formatting="disable",
enqueue=False,
format="[gitversioned:setuptools] {level} - {message}\n",
)
logger.debug("Finalizing distribution options for GitVersioned.")
project_root, source_root, package_name = _resolve_project_context(distribution)
if not package_name:
raise_distutils_setup_error("Could not determine package name.")
# Check for an established version to avoid redundant Git resolution
established_version = _extract_established_version(distribution, project_root)
resolved = os.environ.get("GITVERSIONED_RESOLVED_VERSION")
if resolved and not established_version:
established_version = resolved
config_overrides = getattr(distribution, "gitversioned_config", {})
try:
kwargs: Any = {
"package_name": package_name,
"project_root": project_root,
"src_root": source_root,
"build_is_editable": getattr(distribution, "editable", False),
}
kwargs.update(config_overrides)
settings = Settings(**kwargs)
if established_version:
logger.info(f"Using established version: {established_version}")
version_string = established_version
output_path = _find_existing_version_file(settings)
else:
repository = GitRepository(settings.project_root)
environment = BuildEnvironment(project_root=settings.project_root)
output_path, _, version, _, _ = resolve_version_output_to_stream(
settings=settings, repository=repository, environment=environment
)
version_string = str(version)
# Update distribution metadata
if hasattr(distribution, "metadata"):
distribution.metadata.version = version_string
if output_path and isinstance(output_path, Path):
_inject_output_into_distribution(
distribution=distribution,
output_path=output_path,
source_root=source_root,
package_name=package_name,
)
except Exception as error:
if is_distutils_setup_error(error):
raise
logger.exception("Unexpected failure during version resolution")
raise_distutils_setup_error(
f"Failed to resolve version: {error}", from_exception=error
)
|
Validate and store the GitVersioned configuration dictionary.
Registers the gitversioned configuration dictionary on the distribution object.
This configuration is subsequently retrieved during option finalization to customize
the version resolution process.
.. code-block:: python
# Example usage inside setup.py:
setup(
gitversioned={
"version_source_file": "version.txt",
"source_type": ["tag", "file"],
},
)
:param distribution: The Setuptools distribution object being configured.
:param attribute: The name of the setup keyword (must be "gitversioned").
:param value: The configuration settings dictionary provided in the setup call.
:raises DistutilsSetupError: If the keyword attribute is invalid or the
value is not a dict.
Source code in src/gitversioned/plugins/setuptools_plugin.py
| def setup_keywords(distribution: Distribution, attribute: str, value: Any) -> None:
"""
Validate and store the GitVersioned configuration dictionary.
Registers the `gitversioned` configuration dictionary on the distribution object.
This configuration is subsequently retrieved during option finalization to customize
the version resolution process.
.. code-block:: python
# Example usage inside setup.py:
setup(
gitversioned={
"version_source_file": "version.txt",
"source_type": ["tag", "file"],
},
)
:param distribution: The Setuptools distribution object being configured.
:param attribute: The name of the setup keyword (must be "gitversioned").
:param value: The configuration settings dictionary provided in the setup call.
:raises DistutilsSetupError: If the keyword attribute is invalid or the
value is not a dict.
"""
configure_logger(
enabled=True,
clear_loggers=True,
level="WARNING",
otel_formatting="disable",
enqueue=False,
format="[gitversioned:setuptools] {level} - {message}\n",
)
logger.debug(f"setup_keywords called with attribute='{attribute}'")
if attribute != "gitversioned":
logger.error(f"Unknown keyword argument: {attribute}")
raise_distutils_setup_error(f"Unknown keyword argument: {attribute}")
if not isinstance(value, (dict, bool)):
logger.error("gitversioned keyword argument must be a dict or bool")
raise_distutils_setup_error("gitversioned must be a dict or bool")
cast("Any", distribution).gitversioned_config = (
{} if isinstance(value, bool) else value
)
|