Skip to content

feat: Auto-derive version from git tags - #97

Merged
hughsie merged 1 commit into
hughsie:mainfrom
baranee:version-changes
Apr 28, 2026
Merged

hughsie merged 1 commit into
hughsie:mainfrom
baranee:version-changes

Conversation

@baranee

@baranee baranee commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

Use setuptools_scm to automatically generate version from git tags, ensuring CycloneDX metadata always has the correct package version.

Fixes #96

Summary by CodeRabbit

  • New Features

    • Adds a command-line entrypoint for users.
    • Exposes a runtime version that is included in generated tool outputs (e.g., SBOM/CycloneDX).
  • Chores

    • Moves to tag-driven automated versioning with dynamic version generation at build time.
    • Migrates packaging to modern pyproject configuration and simplifies the release workflow (clean packaging, signed tags, streamlined upload, and no post-release bump commit).

@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@baranee has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 35 minutes and 9 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 35 minutes and 9 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 910b3b42-485d-4a72-a1c1-8430cf7849ef

📥 Commits

Reviewing files that changed from the base of the PR and between 444a28e and 633b235.

📒 Files selected for processing (7)
  • RELEASE.md
  • pyproject.toml
  • setup.py
  • uswid/__init__.py
  • uswid/_version.py
  • uswid/format_cyclonedx.py
  • uswid/test_uswid.py
📝 Walkthrough

Walkthrough

Project moves from manual version bumps to tag-driven dynamic versioning: versions are derived from Git tags via setuptools_scm, written to uswid/_version.py at build time, exposed as uswid.__version__, and propagated into package metadata, tests, and the release workflow.

Changes

Cohort / File(s) Summary
Project Build Configuration
pyproject.toml, setup.py
Added pyproject.toml configuring setuptools.build_meta and setuptools_scm (dynamic version); removed explicit version from setup.py.
Versioning Modules
uswid/_version.py, uswid/__init__.py
Added generated uswid/_version.py (default "0.0.0") and updated __init__ to expose __version__ by importing uswid._version.version with fallback to installed distribution or "0.0.0+unknown".
Runtime Metadata & Tests
uswid/format_cyclonedx.py, uswid/test_uswid.py
Removed hardcoded CycloneDX tool version; use uswid.__version__ at runtime and update tests to expect the runtime-derived version.
Release Documentation & Workflow
RELEASE.md
Refactored into Pre-/Release/Post-Release sections; switch to tag-driven versioning (<last-tag>.. shortlog), create signed annotated tag X.Y.Z with commit message Release X.Y.Z, clean dist/ before build, upload dist/uswid-*, and simplify post-release push instructions; note that setuptools_scm writes uswid/_version.py during build.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Developer
    participant Repo as "Git Repo"
    participant Tag as "Git Tag"
    participant Build as "Build (setuptools)"
    participant SCM as "setuptools_scm"
    participant VersionFile as "uswid/_version.py"
    participant Dist as "dist/*"
    participant PyPI as "Twine / PyPI"

    Developer->>Repo: commit changes
    Developer->>Repo: create annotated signed tag "X.Y.Z"
    Repo->>SCM: tags available
    Developer->>Build: run build (sdist/wheel)
    Build->>SCM: query Git tags -> compute version
    SCM->>VersionFile: write computed version into `uswid/_version.py`
    Build->>Dist: produce packages (wheel, sdist)
    Developer->>PyPI: twine upload `dist/uswid-*`
    PyPI->>PyPI: publish package version derived from tag
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇
I hopped through tags beneath the moon's soft glow,
setuptools_scm whispered the version low,
_version.py sprouted when builds began,
tests and metadata know the tag's bright plan,
a tidy hop — commit, tag, build, and go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Auto-derive version from git tags' accurately summarizes the main change: implementing automatic version derivation from git tags via setuptools_scm.
Linked Issues check ✅ Passed The PR fully addresses issue #96 by implementing setuptools_scm to auto-derive package version from git tags, ensuring CycloneDX metadata reflects the correct tagged release version.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing tag-driven versioning: pyproject.toml configuration, version module setup, release workflow documentation, and test updates are all in scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@baranee
baranee marked this pull request as ready for review April 20, 2026 11:32

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
uswid/format_cyclonedx.py (1)

17-17: Prefer importing from the sibling module to avoid package-self re-entry.

from uswid import __version__ re-enters the package __init__.py, which itself imports this module (from uswid.format_cyclonedx import uSwidFormatCycloneDX). It works today only because __init__.py binds __version__ before importing format_cyclonedx — a brittle ordering constraint. Importing directly from the sibling private module is safer and faster:

-from uswid import __version__
+from ._version import version as __version__
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@uswid/format_cyclonedx.py` at line 17, Replace the absolute package import of
__version__ in uswid/format_cyclonedx.py with a direct import from the sibling
private module (i.e., import __version__ from the module named _version using a
relative/sibling import) so the module no longer re-enters the package; update
the import at the top of the file where __version__ is currently pulled in (the
module defining uSwidFormatCycloneDX references this import) to import from
_version instead of from uswid.
pyproject.toml (1)

16-19: Replace write_to with version_file to address deprecation in setuptools_scm 8+.

The write_to option was deprecated in setuptools_scm v8.0.0 and emits warnings in current versions. Since your build requirement specifies setuptools_scm without a version constraint, users will pick up the latest version and encounter these warnings. Use version_file instead, which is the supported replacement.

Suggested change
 [tool.setuptools_scm]
 version_scheme = "guess-next-dev"
 local_scheme = "no-local-version"
-write_to = "uswid/_version.py"
+version_file = "uswid/_version.py"

Consider pinning setuptools_scm>=8 in [build-system].requires to make the minimum version explicit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pyproject.toml` around lines 16 - 19, Replace the deprecated setuptools_scm
option: in the pyproject.toml [tool.setuptools_scm] table change the write_to
setting to version_file (e.g., replace write_to = "uswid/_version.py" with
version_file = "uswid/_version.py") so setuptools_scm v8+ stops emitting
deprecation warnings; also consider adding or updating [build-system].requires
to pin setuptools_scm>=8 to make the minimum supported version explicit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pyproject.toml`:
- Around line 8-14: The [project] table is incomplete: move the static metadata
currently only in setup.py into pyproject.toml or mark them as dynamic so
setuptools doesn't drop them. Update the [project] section (or its dynamic list)
to include at minimum dependencies, authors, license (and license-files),
classifiers, keywords, urls, and scripts (or add them to the dynamic array),
ensuring the existing values from setup.py are copied into the corresponding
keys in the [project] table; confirm names exactly match PEP 621 keys (e.g.,
authors, dependencies, classifiers, urls, scripts).

In `@RELEASE.md`:
- Line 18: The twine upload command currently uses a broad glob "twine upload
dist/uswid-*" which can pick up stale artifacts; update the release workflow so
either (a) clean the dist/ directory before running "make pkg" (delete contents
of dist/) or (b) restore a version-scoped glob when calling twine (e.g., use the
package's exact version string to form "dist/uswid-X.Y.Z*") so only the new
release artifacts are uploaded; change the invocation around the release steps
(where "twine upload dist/uswid-*" is used) to implement one of these fixes.

In `@uswid/_version.py`:
- Line 8: The placeholder version variable in uswid._version.py (version =
"0.0.0") can end up in CycloneDX output; update the module to detect this
sentinel at runtime and replace it by querying installed metadata: if version ==
"0.0.0" then attempt to import importlib.metadata and call
importlib.metadata.version("uswid") (catch exceptions and fall back to the
placeholder if not found), so any consumer importing uswid._version or
__version__ gets the real installed package version; alternatively set the
sentinel to "0.0.0+unknown" if you prefer explicit unknown marker—adjust any
import/export of __version__ accordingly.

---

Nitpick comments:
In `@pyproject.toml`:
- Around line 16-19: Replace the deprecated setuptools_scm option: in the
pyproject.toml [tool.setuptools_scm] table change the write_to setting to
version_file (e.g., replace write_to = "uswid/_version.py" with version_file =
"uswid/_version.py") so setuptools_scm v8+ stops emitting deprecation warnings;
also consider adding or updating [build-system].requires to pin
setuptools_scm>=8 to make the minimum supported version explicit.

In `@uswid/format_cyclonedx.py`:
- Line 17: Replace the absolute package import of __version__ in
uswid/format_cyclonedx.py with a direct import from the sibling private module
(i.e., import __version__ from the module named _version using a
relative/sibling import) so the module no longer re-enters the package; update
the import at the top of the file where __version__ is currently pulled in (the
module defining uSwidFormatCycloneDX references this import) to import from
_version instead of from uswid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d695d214-2709-4681-83de-2a8bb5bcbbd8

📥 Commits

Reviewing files that changed from the base of the PR and between 379c0c4 and 386aaf5.

📒 Files selected for processing (7)
  • RELEASE.md
  • pyproject.toml
  • setup.py
  • uswid/__init__.py
  • uswid/_version.py
  • uswid/format_cyclonedx.py
  • uswid/test_uswid.py
💤 Files with no reviewable changes (1)
  • setup.py

Comment thread pyproject.toml Resolved
Comment thread RELEASE.md Resolved
Comment thread uswid/_version.py Resolved
@baranee

baranee commented Apr 20, 2026

Copy link
Copy Markdown
Contributor Author

Didn't realize AI code review is enabled, will attempt to address those concerns

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
uswid/__init__.py (1)

9-20: Narrow the blind except Exception.

importlib.metadata.version() raises PackageNotFoundError when the distribution isn't installed; catching bare Exception here will also mask unrelated bugs (and is flagged by Ruff BLE001). Prefer catching the specific error.

♻️ Proposed refactor
     if __version__ == "0.0.0":
         try:
-            from importlib.metadata import version as _meta_version
-
-            __version__ = _meta_version("uswid")
-        except Exception:
+            from importlib.metadata import (
+                version as _meta_version,
+                PackageNotFoundError,
+            )
+
+            __version__ = _meta_version("uswid")
+        except (ImportError, PackageNotFoundError):
             __version__ = "0.0.0+unknown"

Also worth noting: the "0.0.0" sentinel check is tightly coupled to the literal default written into uswid/_version.py. If that default is ever changed, this branch will silently stop taking effect — consider documenting the coupling in _version.py or using a more explicit sentinel (e.g., "0.0.0+placeholder").

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@uswid/__init__.py` around lines 9 - 20, The except Exception block that wraps
the call to importlib.metadata.version(_meta_version) should be narrowed to
catch the specific PackageNotFoundError instead of a bare Exception; update the
import to bring in PackageNotFoundError (e.g., from importlib.metadata import
version as _meta_version, PackageNotFoundError) and replace "except Exception:"
with "except PackageNotFoundError:" so only the case where the distribution
isn't installed is swallowed, and also consider documenting the coupling between
the "__version__" sentinel value in _version.py and this check or switch to an
explicit sentinel like "0.0.0+placeholder" to make the intent clear.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pyproject.toml`:
- Around line 28-32: The pyproject declares "description" and "readme" as
dynamic in the dynamic = ["version","description","readme"] list but provides no
provider, causing builds to fail; fix by either adding static entries for
description and readme under [project] (set the project.description and
project.readme fields) or keep them dynamic and add a [tool.setuptools.dynamic]
section that provides sources (e.g. file providers) for description and readme
so setuptools can resolve them during build; update the pyproject.toml
accordingly referencing the dynamic list and the new [tool.setuptools.dynamic]
providers or the static project fields.

---

Nitpick comments:
In `@uswid/__init__.py`:
- Around line 9-20: The except Exception block that wraps the call to
importlib.metadata.version(_meta_version) should be narrowed to catch the
specific PackageNotFoundError instead of a bare Exception; update the import to
bring in PackageNotFoundError (e.g., from importlib.metadata import version as
_meta_version, PackageNotFoundError) and replace "except Exception:" with
"except PackageNotFoundError:" so only the case where the distribution isn't
installed is swallowed, and also consider documenting the coupling between the
"__version__" sentinel value in _version.py and this check or switch to an
explicit sentinel like "0.0.0+placeholder" to make the intent clear.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52b1c051-180c-4198-992a-41858baec10f

📥 Commits

Reviewing files that changed from the base of the PR and between 386aaf5 and 03fb443.

📒 Files selected for processing (3)
  • RELEASE.md
  • pyproject.toml
  • uswid/__init__.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • RELEASE.md

Comment thread pyproject.toml Resolved
@baranee
baranee force-pushed the version-changes branch 2 times, most recently from 5d5d319 to 444a28e Compare April 23, 2026 09:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pyproject.toml`:
- Line 4: The pyproject.toml uses the deprecated setuptools_scm option write_to;
replace it with version_file to avoid sdist/wheel build issues by updating the
setuptools_scm configuration block in pyproject.toml: remove the write_to key
and add version_file pointing to the target file path (same location intended
previously), ensuring the setuptools_scm section (the tool.setuptools_scm or
[tool.setuptools_scm] table) references version_file instead of write_to so
builds use the new option.
- Line 3: Update pyproject.toml to require a newer setuptools by replacing the
current "setuptools>=61" requirement with "setuptools>=77" (or at least 77.0.0)
so PEP 639 project.license-files is supported, and convert the project.license
TOML table into a plain SPDX string (replace the license table entry with a
simple "project.license = \"SPDX-Identifier\"" value) to remove the deprecated
table syntax while keeping project.license-files intact.

In `@uswid/__init__.py`:
- Around line 15-20: Replace the broad import and exception handling so the code
uses the importlib-metadata backport on Python <3.8 and only catches the
missing-package error: import the version function from importlib.metadata if
available, otherwise from importlib_metadata (the backport), and import the
PackageNotFoundError exception from the same module; then call that version
function to set __version__ and catch only PackageNotFoundError to fall back to
"0.0.0+unknown" (references: __version__ and _meta_version/version).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 13784bd8-1757-4c54-9767-63633b736c75

📥 Commits

Reviewing files that changed from the base of the PR and between 03fb443 and 444a28e.

📒 Files selected for processing (7)
  • RELEASE.md
  • pyproject.toml
  • setup.py
  • uswid/__init__.py
  • uswid/_version.py
  • uswid/format_cyclonedx.py
  • uswid/test_uswid.py
💤 Files with no reviewable changes (1)
  • setup.py
✅ Files skipped from review due to trivial changes (1)
  • uswid/_version.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • uswid/format_cyclonedx.py
  • RELEASE.md

Comment thread pyproject.toml Resolved
Comment thread pyproject.toml Resolved
Comment thread uswid/__init__.py Resolved
Use setuptools_scm to automatically generate version from git tags,
ensuring CycloneDX metadata always has the correct package version.

Fixes hughsie#96
@hughsie
hughsie merged commit 9f0e658 into hughsie:main Apr 28, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uswid version?

2 participants