Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/options/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Config options
dirty_template
version_file
count_commits
prefer_tag
version_callback
sort_by
branch_formatter
Expand Down
26 changes: 26 additions & 0 deletions docs/options/prefer_tag.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.. _prefer-tag-option:

``prefer_tag``
~~~~~~~~~~~~~~

Used together with the :ref:`version-file-option` option.

By default, when :ref:`version-file-option` is set, any tags in the repo are
ignored (see :issue:`155`). With this option enabled, the latest Git tag takes precedence over the version file content.

.. note::

This option is used only with :ref:`version-file-option`,
and only takes effect when the repo actually contains at least one tag;
otherwise the version file content is used as usual.

When the tag and the version file content are different,
a warning is logged, so stale version files are easier to notice.

Type
^^^^
``bool``

Default value
^^^^^^^^^^^^^
``False``
8 changes: 8 additions & 0 deletions docs/schemas/file/version_file.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,16 @@ of this file (``1.0.0``) will be returned instead default version number.

**Please take into account that any tags in the repo are ignored if this option is being used.**

.. note::

If you want the latest Git tag to take precedence over the version file
(for example, when a release tag like ``v2.1.0rc1`` is pushed but the
version file still contains ``2.1.0``), set the
:ref:`prefer-tag-option` option to ``true``.

See also
"""""""""
- :ref:`version-callback`
- :ref:`version-file-option` option
- :ref:`prefer-tag-option` option
- :ref:`runtime-version`
1 change: 1 addition & 0 deletions setuptools_git_versioning/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"version_callback": None,
"version_file": None,
"count_commits_from_version_file": False,
"prefer_tag": False,
"tag_formatter": None,
"branch_formatter": None,
"tag_filter": None,
Expand Down
71 changes: 51 additions & 20 deletions setuptools_git_versioning/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def version_from_git( # noqa: PLR0915, PLR0912, PLR0913, C901
version_callback: str | Callable[[], str] | None = None,
version_file: str | os.PathLike | None = None,
count_commits_from_version_file: bool = False,
prefer_tag: bool = False,
tag_formatter: Callable[[str], str] | str | None = None,
branch_formatter: Callable[[str], str] | str | None = None,
tag_filter: Callable[[str], str | None] | str | None = None,
Expand Down Expand Up @@ -156,33 +157,63 @@ def version_from_git( # noqa: PLR0915, PLR0912, PLR0913, C901
log.log(INFO, "HEAD is tagged: %r", on_tag)

if version_file:
log.log(INFO, "Checking for 'version_file'")

version_file_path = project_root.joinpath(version_file)
if not version_file_path.exists():
if prefer_tag and tag is not None:
Comment thread
lxcxjxhx marked this conversation as resolved.
log.log(
INFO,
"version_file '%s' does not exist, return starting_version %r",
version_file_path,
starting_version,
"Both 'version_file' and a Git tag (%r) are present, and 'prefer_tag' is enabled: using the tag",
tag,
)
tag = None
if tag_sha is None:
tag_sha = get_sha(tag, root=root)
log.log(DEBUG, "Tag SHA-256: %r", tag_sha)
on_tag = head_sha is not None and tag_sha is not None and head_sha == tag_sha
file_tag = None
version_file_path = project_root.joinpath(version_file)
if version_file_path.exists():
file_tag = version_file_path.read_text().strip() or None
if file_tag and file_tag.lstrip("v") != tag.lstrip("v"):
log.warning(
"Git tag %r and 'version_file' content %r are different: using the tag. "
"Update the version file to silence this warning",
tag,
file_tag,
)
ccount = count_since(tag_sha, root=root) if tag_sha else None
log.log(INFO, "Commits count between HEAD and last tag: %r", ccount)

if tag_formatter is not None:
tag_format_callback = create_tag_formatter(tag_formatter, package_name=package_name, root=root)
tag = tag_format_callback(tag)
log.log(DEBUG, "Tag after formatting: %r", tag)
else:
log.log(INFO, "Reading version_file '%s' content", version_file)
tag = version_file_path.read_text().strip() or None

if not tag:
log.log(INFO, "File %r is empty", version_file)
log.log(INFO, "Checking for 'version_file'")

version_file_path = project_root.joinpath(version_file)
if not version_file_path.exists():
log.log(
INFO,
"version_file '%s' does not exist, return starting_version %r",
version_file_path,
starting_version,
)
tag = None
else:
log.log(DEBUG, "File content: %r", tag)
if not count_commits_from_version_file:
return sanitize_version(tag)
log.log(INFO, "Reading version_file '%s' content", version_file)
tag = version_file_path.read_text().strip() or None

if not tag:
log.log(INFO, "File %r is empty", version_file)
else:
log.log(DEBUG, "File content: %r", tag)

if not count_commits_from_version_file:
return sanitize_version(tag)

file_sha = get_latest_file_commit(version_file, root=root)
log.log(DEBUG, "File SHA-256: %r", file_sha)
file_sha = get_latest_file_commit(version_file, root=root)
log.log(DEBUG, "File SHA-256: %r", file_sha)

ccount = count_since(file_sha, root=root) if file_sha else None
log.log(INFO, "Commits count between HEAD and last version file change: %r", ccount)
ccount = count_since(file_sha, root=root) if file_sha else None
log.log(INFO, "Commits count between HEAD and last version file change: %r", ccount)

elif not head_sha:
log.log(INFO, "Not a git repo, or repo without any branch")
Expand Down
89 changes: 89 additions & 0 deletions tests/test_integration/test_version_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,92 @@ def test_version_file_git_not_executable(repo, create_config, count_commits, tmp
tmp_path = tmp_path_factory.mktemp("bin")
tmp_path.joinpath("git").touch()
assert get_version(repo, env={"PATH": os.fspath(tmp_path)}) == "1.0.0"


@pytest.mark.flaky(reruns=3) # sha and full_sha can start with 0 which are removed, just try again
def test_version_file_prefer_tag_off_by_default(repo, create_config):
# without prefer_tag, the version file wins even when a tag is present
create_file(repo, "VERSION.txt", "1.0.0")
create_config(
repo,
{
"version_file": "VERSION.txt",
"count_commits_from_version_file": True,
},
)
create_tag(repo, "1.2.3")

assert get_version(repo) == "1.0.0"


@pytest.mark.flaky(reruns=3) # sha and full_sha can start with 0 which are removed, just try again
@pytest.mark.parametrize("count_commits_from_version_file", [True, False])
def test_version_file_prefer_tag_on_tagged_head(repo, create_config, count_commits_from_version_file):
# prefer_tag makes the tag win over the version file content on a tagged HEAD
create_file(repo, "VERSION.txt", "1.0.0")
create_config(
repo,
{
"version_file": "VERSION.txt",
"count_commits_from_version_file": count_commits_from_version_file,
"prefer_tag": True,
},
)
create_tag(repo, "1.2.3")

assert get_version(repo) == "1.2.3"


@pytest.mark.flaky(reruns=3) # sha and full_sha can start with 0 which are removed, just try again
def test_version_file_prefer_tag_dev_commits(repo, create_config):
# prefer_tag + commits after the tag: tag is used as the version source,
# dev template kicks in because HEAD is not on the tag
create_file(repo, "VERSION.txt", "1.0.0")
create_config(
repo,
{
"version_file": "VERSION.txt",
"count_commits_from_version_file": True,
"prefer_tag": True,
},
)
create_tag(repo, "1.2.3")
# create_file with default add=True/commit=True creates the commit after the tag
create_file(repo, "work.txt", "work after tag")

sha = get_sha(repo)
assert get_version(repo) == f"1.2.3.post1+git.{sha}"


@pytest.mark.flaky(reruns=3) # sha and full_sha can start with 0 which are removed, just try again
def test_version_file_prefer_tag_without_any_tag(repo, create_config):
# prefer_tag with no tags in the repo: version file is still the source
# (count_commits_from_version_file is off here so the file content is
# returned as-is, without the dev suffix)
create_file(repo, "VERSION.txt", "1.0.0")
create_config(
repo,
{
"version_file": "VERSION.txt",
"prefer_tag": True,
},
)

assert get_version(repo) == "1.0.0"


@pytest.mark.flaky(reruns=3) # sha and full_sha can start with 0 which are removed, just try again
def test_version_file_prefer_tag_v_prefix(repo, create_config):
# tag with a "v" prefix is compared to the version file without the prefix
create_file(repo, "VERSION.txt", "1.2.3")
create_config(
repo,
{
"version_file": "VERSION.txt",
"count_commits_from_version_file": True,
"prefer_tag": True,
},
)
create_tag(repo, "v1.2.3")

assert get_version(repo) == "1.2.3"