Files
Arthur Tazhitdinov 02822e01b3 feat: include short SHA in CROSSPOINT_VERSION (#1728)
## Summary

* Includes short SHA in version string for better version tracking, so
it looks like `1.1.0-dev-feat-kosync-xpath-05c6cf8`
* Closes
https://github.com/crosspoint-reader/crosspoint-reader/issues/1247

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**< YES >**_
2026-04-27 15:52:09 +03:00

107 lines
3.5 KiB
Python

"""
PlatformIO pre-build script: inject git branch and short SHA into
CROSSPOINT_VERSION for the default (dev) environment.
Results in a version string like: 1.1.0-dev-feat-kosync-xpath-05c6cf8
Release environments are unaffected; they set CROSSPOINT_VERSION in the ini.
"""
import configparser
import os
import subprocess
import sys
def warn(msg):
print(f'WARNING [git_branch.py]: {msg}', file=sys.stderr)
def run_git_value(project_dir, args, label):
try:
value = subprocess.check_output(
['git', *args],
text=True, stderr=subprocess.PIPE, cwd=project_dir
).strip()
# Strip characters that would break a C string literal
return ''.join(c for c in value if c not in '"\\')
except FileNotFoundError:
warn(f'git not found on PATH; {label} suffix will be "unknown"')
return 'unknown'
except subprocess.CalledProcessError as e:
warn(
f'git command failed (exit {e.returncode}): '
f'{e.stderr.strip()}; {label} suffix will be "unknown"'
)
return 'unknown'
except OSError as e:
warn(
f'OS error reading git {label}: {e}; '
f'{label} suffix will be "unknown"'
)
return 'unknown'
except Exception as e: # pylint: disable=broad-exception-caught
warn(
f'Unexpected error reading git {label}: {e}; '
f'{label} suffix will be "unknown"'
)
return 'unknown'
def get_git_branch(project_dir):
branch = run_git_value(
project_dir, ['rev-parse', '--abbrev-ref', 'HEAD'], 'branch'
)
# Detached HEAD has no branch name.
if branch == 'HEAD':
return 'detached'
return branch
def get_git_short_sha(project_dir):
return run_git_value(
project_dir, ['rev-parse', '--short', 'HEAD'], 'short SHA'
)
def get_base_version(project_dir):
ini_path = os.path.join(project_dir, 'platformio.ini')
if not os.path.isfile(ini_path):
warn(f'platformio.ini not found at {ini_path}; base version will be "0.0.0"')
return '0.0.0'
config = configparser.ConfigParser()
config.read(ini_path)
if not config.has_option('crosspoint', 'version'):
warn('No [crosspoint] version in platformio.ini; base version will be "0.0.0"')
return '0.0.0'
return config.get('crosspoint', 'version')
def inject_version(env):
# Only applies to the dev (default) environment; release envs set the
# version via build_flags in platformio.ini and are unaffected.
if env['PIOENV'] != 'default':
return
project_dir = env['PROJECT_DIR']
base_version = get_base_version(project_dir)
branch = get_git_branch(project_dir)
short_sha = get_git_short_sha(project_dir)
version_string = f'{base_version}-dev-{branch}-{short_sha}'
env.Append(CPPDEFINES=[('CROSSPOINT_VERSION', f'\\"{version_string}\\"')])
print(f'CrossPoint build version: {version_string}')
# PlatformIO/SCons entry point — Import and env are SCons builtins injected at runtime.
# When run directly with Python (e.g. for validation), a lightweight fake env is used
# so the git/version logic can be exercised without a full build.
try:
Import('env') # noqa: F821 # type: ignore[name-defined]
inject_version(env) # noqa: F821 # type: ignore[name-defined]
except NameError:
class _Env(dict):
def Append(self, **_): pass
_project_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
inject_version(_Env({'PIOENV': 'default', 'PROJECT_DIR': _project_dir}))