diff --git a/.git_archival.txt b/.git_archival.txt new file mode 100644 index 0000000..3994ec0 --- /dev/null +++ b/.git_archival.txt @@ -0,0 +1,4 @@ +node: $Format:%H$ +node-date: $Format:%cI$ +describe-name: $Format:%(describe:tags=true)$ +ref-names: $Format:%D$ diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..a94cb2f --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +.git_archival.txt export-subst diff --git a/.gitignore b/.gitignore index a720064..6850808 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,8 @@ target/ # IDE .idea/ +# Virtual Environments venv/ + +# Build Artifacts +dronecan_gui_tool/_version_generated.py diff --git a/dronecan_gui_tool/main.py b/dronecan_gui_tool/main.py index 32b1650..18bc95d 100644 --- a/dronecan_gui_tool/main.py +++ b/dronecan_gui_tool/main.py @@ -42,12 +42,23 @@ logging.basicConfig(stream=sys.stderr, level=logging_level, format='%(asctime)s %(levelname)s %(name)s %(message)s') - -from .version import __version__ +try: + from .version import __version_tuple__ +except ModuleNotFoundError: + __version_tuple__ = (0, 0, 0, "unknown") +__version__ = [x for x in __version_tuple__ if isinstance(x, int)] if args.version: - v = '.'.join(map(str, __version__)) + metadata_parts = [x for x in __version_tuple__ if isinstance(x, str)] + is_clean_release = len(metadata_parts) == 0 + is_dirty = any(".d" in part for part in metadata_parts) + version_info = '.'.join(map(str, __version__)) + metadata_info = '.'.join(map(str, metadata_parts)) print("DroneCAN GUI Tool is an application for DroneCAN bus management and diagnostics") - print(f"DroneCAN GUI Tool Version: {v}") + print(f"DroneCAN GUI Tool Version: {version_info}") + if not is_clean_release: + print(f"Development Build: {metadata_info}") + if is_dirty: + print("Warning: Built from a dirty working tree!") sys.exit(0) log_file = tempfile.NamedTemporaryFile(mode='w', prefix='dronecan_gui_tool-', suffix='.log', delete=False) diff --git a/dronecan_gui_tool/version.py b/dronecan_gui_tool/version.py index c29544a..35de53a 100644 --- a/dronecan_gui_tool/version.py +++ b/dronecan_gui_tool/version.py @@ -8,7 +8,43 @@ # Andrew Tridgell # # -__version__ = 1, 2, 28 +import re +# Note: This version is determined dynamically at build time or runtime. +__version_tuple__ = (1, 2, 28) +import os + +_root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +_is_source = os.path.exists(os.path.join(_root_dir, 'setup.py')) + +try: + # 1. Try to get the version from setuptools_scm (live git repository state) + from setuptools_scm import get_version + if os.path.exists(os.path.join(_root_dir, '.git', 'shallow')): + raise Exception("Shallow clone detected, falling back to manual version") + _version_str = get_version(root=_root_dir, version_scheme='post-release') + # Parse the version string into a tuple + _parts = [] + for _part in re.split(r'[-.+]', _version_str): + if _part: + try: + _parts.append(int(_part)) + except ValueError: + _parts.append(_part) + __version_tuple__ = tuple(_parts) +except Exception as e: + if not _is_source: + # 2. Try to import the generated version information (built wheels/MSIs) + try: + from ._version_generated import __version_tuple__ # noqa: F401 + except ImportError: + # 3. Fall back to the manually updated version + print("Warning: setuptools_scm is not available or failed with: ", e, + " and _version_generated.py not found. " + "Falling back to manual version.") + else: + print("Warning: setuptools_scm is not available or failed with: ", e, + " and running from source. " + "Falling back to manual version.") diff --git a/dronecan_gui_tool/widgets/about_window.py b/dronecan_gui_tool/widgets/about_window.py index 4ae739a..1b48905 100644 --- a/dronecan_gui_tool/widgets/about_window.py +++ b/dronecan_gui_tool/widgets/about_window.py @@ -7,21 +7,32 @@ # import dronecan -from ..version import __version__ +try: + from ..version import __version_tuple__ +except ImportError: + __version_tuple__ = (0, 0, 0, "unknown") from . import get_icon, get_app_icon from PyQt6.QtWidgets import QDialog, QTableWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, \ QTableWidgetItem, QHeaderView from PyQt6.QtGui import QIcon from PyQt6.QtCore import Qt, PYQT_VERSION_STR, QSize +numeric_parts = [x for x in __version_tuple__ if isinstance(x, int)] +metadata_parts = [x for x in __version_tuple__ if isinstance(x, str)] +is_clean_release = len(metadata_parts) == 0 +is_dirty = any((part == "dirty") or (".d" in part) for part in metadata_parts) +version_info = '.'.join(map(str, numeric_parts)) +metadata_info = '.'.join(map(str, metadata_parts)) -ABOUT_TEXT = (''' -

DroneCAN GUI Tool v{0}

-Cross-platform application for DroneCAN bus management and diagnostics. +ABOUT_TEXT = (f""" +

DroneCAN GUI Tool v{version_info}

+{f"

Dev Build {'(Dirty)' if is_dirty else ''}: {metadata_info}

" if not is_clean_release else ""} -This application is distributed under the terms of the MIT software license. The source repository and the bug \ -tracker are located at https://github.com/DroneCAN/gui_tool. -'''.format('.'.join(map(str, __version__)))).strip().replace('\n', '\n
') +

Cross-platform application for DroneCAN bus management and diagnostics.

+ +

This application is distributed under the terms of the MIT software license. The source repository and the bug \ +tracker are located at https://github.com/DroneCAN/gui_tool.

+""") def _list_3rd_party(): diff --git a/pyproject.toml b/pyproject.toml index cb8aec3..f564d34 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,5 +2,5 @@ # you need to drop setup_requires from setup.py as well. # https://peps.python.org/pep-0518/#rationale [build-system] -requires = ["setuptools>=42",'setuptools_git>=1.0'] +requires = ["setuptools>=42", "setuptools_scm[toml]>=6.2"] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 7650984..330ca4a 100755 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ upgrade_code = '{D5CD6E19-2545-32C7-A62A-4595B28BCDC3}' sys.path.append(os.path.join(SOURCE_DIR, PACKAGE_NAME)) -from version import __version__ +from version import __version_tuple__ assert sys.version_info[0] == 3, 'Python 3 is required' @@ -36,7 +36,6 @@ # args = dict( name=PACKAGE_NAME, - version='.'.join(map(str, __version__)), packages=find_packages(), install_requires=[ 'setuptools>=18.5', @@ -89,6 +88,16 @@ package_data={'DroneCAN_GUI_Tool': [ 'icons/*.png', 'icons/*.ico']} ) +is_shallow_clone = os.path.exists(os.path.join(SOURCE_DIR, '.git', 'shallow')) +if is_shallow_clone: + args['version'] = '.'.join(map(str, __version_tuple__[0:3])) +else: + args['use_scm_version'] = { + "write_to": "dronecan_gui_tool/_version_generated.py", + "version_scheme": "post-release", + "fallback_version": '.'.join(map(str, __version_tuple__[0:3])) + } + if 'install' in sys.argv and (sys.platform.startswith('linux') or sys.platform.startswith('darwin')): # Delegating the desktop integration work to 'install_freedesktop' args.setdefault('setup_requires', []).append('install_freedesktop')