• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

inventree / InvenTree / 6777376887

06 Nov 2023 10:36PM UTC coverage: 88.86% (-0.009%) from 88.869%
6777376887

push

github

web-flow
Expose API version descriptors for admins via API (#5865)

* Added endpoint to expose API version descriptors for admins

* Refactored tests and moved them to seperate file

* Switched to raw string

* add coverage exclusion

* ignore imports (possible fix to multiline imports)

* Add OpenApi Schema

* removed changes regarding coverage

* moved new functions to `version`
to keep api_version a pure static file

* clean up diff

44 of 47 new or added lines in 5 files covered. (93.62%)

12 existing lines in 1 file now uncovered.

28659 of 32252 relevant lines covered (88.86%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.41
/InvenTree/InvenTree/version.py
1
"""Version information for InvenTree.
2

3
Provides information on the current InvenTree version
4
"""
5

6
import os
1✔
7
import pathlib
1✔
8
import platform
1✔
9
import re
1✔
10
import sys
1✔
11
from datetime import datetime as dt
1✔
12
from datetime import timedelta as td
1✔
13

14
import django
1✔
15
from django.conf import settings
1✔
16

17
from dulwich.repo import NotGitRepository, Repo
1✔
18

19
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
1✔
20

21
# InvenTree software version
22
INVENTREE_SW_VERSION = "0.13.0 dev"
1✔
23

24
# Discover git
25
try:
1✔
26
    main_repo = Repo(pathlib.Path(__file__).parent.parent.parent)
1✔
27
    main_commit = main_repo[main_repo.head()]
1✔
28
except (NotGitRepository, FileNotFoundError):
×
29
    main_commit = None
×
30

31

32
def checkMinPythonVersion():
1✔
33
    """Check that the Python version is at least 3.9"""
34

35
    version = sys.version.split(" ")[0]
36
    docs = "https://docs.inventree.org/en/stable/start/intro/#python-requirements"
37

38
    msg = f"""
39
    InvenTree requires Python 3.9 or above - you are running version {version}.
1✔
40
    - Refer to the InvenTree documentation for more information:
×
41
    - {docs}
1✔
42
    """
×
43

44
    if sys.version_info.major < 3:
1✔
45
        raise RuntimeError(msg)
×
46

47
    if sys.version_info.major == 3 and sys.version_info.minor < 9:
1✔
48
        raise RuntimeError(msg)
×
49

50
    print(f"Python version {version} - {sys.executable}")
1✔
51

52

53
def inventreeInstanceName():
1✔
54
    """Returns the InstanceName settings for the current database."""
55
    import common.models
56

57
    return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
58

59

60
def inventreeInstanceTitle():
61
    """Returns the InstanceTitle for the current database."""
62
    import common.models
1✔
63

64
    if common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE_TITLE", False):
1✔
65
        return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
1✔
66
    return 'InvenTree'
1✔
67

68

69
def inventreeVersion():
1✔
70
    """Returns the InvenTree version string."""
71
    return INVENTREE_SW_VERSION.lower().strip()
72

73

74
def inventreeVersionTuple(version=None):
75
    """Return the InvenTree version string as (maj, min, sub) tuple."""
76
    if version is None:
1✔
77
        version = INVENTREE_SW_VERSION
1✔
78

79
    match = re.match(r"^.*(\d+)\.(\d+)\.(\d+).*$", str(version))
1✔
80

81
    return [int(g) for g in match.groups()]
1✔
82

83

84
def isInvenTreeDevelopmentVersion():
1✔
85
    """Return True if current InvenTree version is a "development" version."""
86
    return inventreeVersion().endswith('dev')
87

88

89
def inventreeDocsVersion():
90
    """Return the version string matching the latest documentation.
91

92
    Development -> "latest"
93
    Release -> "major.minor.sub" e.g. "0.5.2"
94
    """
95
    if isInvenTreeDevelopmentVersion():
1✔
96
        return "latest"
1✔
97
    return INVENTREE_SW_VERSION  # pragma: no cover
×
98

99

100
def inventreeDocUrl():
1✔
101
    """Return URL for InvenTree documentation site."""
102
    tag = inventreeDocsVersion()
103
    return f"https://docs.inventree.org/en/{tag}"
104

105

106
def inventreeAppUrl():
107
    """Return URL for InvenTree app site."""
108
    return f'{inventreeDocUrl()}/app/app',
×
109

110

111
def inventreeCreditsUrl():
1✔
112
    """Return URL for InvenTree credits site."""
113
    return "https://docs.inventree.org/en/latest/credits/"
114

115

116
def inventreeGithubUrl():
117
    """Return URL for InvenTree github site."""
118
    return "https://github.com/InvenTree/InvenTree/"
1✔
119

120

121
def isInvenTreeUpToDate():
1✔
122
    """Test if the InvenTree instance is "up to date" with the latest version.
123

124
    A background task periodically queries GitHub for latest version, and stores it to the database as "_INVENTREE_LATEST_VERSION"
125
    """
126
    import common.models
1✔
127
    latest = common.models.InvenTreeSetting.get_setting('_INVENTREE_LATEST_VERSION', backup_value=None, create=False)
1✔
128

129
    # No record for "latest" version - we must assume we are up to date!
130
    if not latest:
1✔
131
        return True
1✔
132

133
    # Extract "tuple" version (Python can directly compare version tuples)
134
    latest_version = inventreeVersionTuple(latest)  # pragma: no cover
1✔
135
    inventree_version = inventreeVersionTuple()  # pragma: no cover
1✔
136

137
    return inventree_version >= latest_version  # pragma: no cover
1✔
138

139

140
def inventreeApiVersion():
1✔
141
    """Returns current API version of InvenTree."""
142
    return INVENTREE_API_VERSION
143

144

145
def parse_version_text():
146
    """Parse the version text to structured data."""
147
    patched_data = INVENTREE_API_TEXT.split("\n\n")
1✔
148
    # Remove first newline on latest version
149
    patched_data[0] = patched_data[0].replace("\n", "", 1)
1✔
150

151
    version_data = {}
1✔
152
    for version in patched_data:
1✔
153
        data = version.split("\n")
1✔
154

155
        version_split = data[0].split(' -> ')
1✔
156
        version_detail = version_split[1].split(':', 1) if len(version_split) > 1 else ['', ]
1✔
157
        new_data = {
1✔
158
            "version": version_split[0].strip(),
1✔
159
            "date": version_detail[0].strip(),
1✔
160
            "gh": version_detail[1].strip() if len(version_detail) > 1 else None,
1✔
161
            "text": data[1:],
1✔
162
            "latest": False,
1✔
163
        }
164
        version_data[new_data["version"]] = new_data
1✔
165
    return version_data
1✔
166

167

168
INVENTREE_API_TEXT_DATA = parse_version_text()
1✔
NEW
169
"""Pre-processed API version text."""
×
170

171

172
def inventreeApiText(versions: int = 10, start_version: int = 0):
1✔
173
    """Returns API version descriptors.
174

175
    Args:
176
        versions: Number of versions to return. Default: 10
177
        start_version: first version to report. Defaults to return the latest {versions} versions.
178
    """
179
    version_data = INVENTREE_API_TEXT_DATA
1✔
180

181
    # Define the range of versions to return
182
    if start_version == 0:
1✔
183
        start_version = INVENTREE_API_VERSION - versions
1✔
184

185
    return {
1✔
186
        f"v{a}": version_data.get(f"v{a}", None)
1✔
187
        for a in range(start_version, start_version + versions)
1✔
188
    }
189

190

191
def inventreeDjangoVersion():
1✔
192
    """Returns the version of Django library."""
193
    return django.get_version()
194

195

196
def inventreePythonVersion():
197
    """Returns the version of python"""
UNCOV
198
    return sys.version.split(' ')[0]
×
199

200

201
def inventreeCommitHash():
1✔
202
    """Returns the git commit hash for the running codebase."""
203
    # First look in the environment variables, i.e. if running in docker
204
    commit_hash = os.environ.get('INVENTREE_COMMIT_HASH', '')
205

206
    if commit_hash:
207
        return commit_hash
208

209
    if main_commit is None:
210
        return None
211
    return main_commit.sha().hexdigest()[0:7]
212

213

214
def inventreeCommitDate():
215
    """Returns the git commit date for the running codebase."""
216
    # First look in the environment variables, e.g. if running in docker
217
    commit_date = os.environ.get('INVENTREE_COMMIT_DATE', '')
1✔
218

219
    if commit_date:
1✔
220
        return commit_date.split(' ')[0]
1✔
221

222
    if main_commit is None:
1✔
UNCOV
223
        return None
×
224

225
    commit_dt = dt.fromtimestamp(main_commit.commit_time) + td(seconds=main_commit.commit_timezone)
1✔
226
    return str(commit_dt.date())
1✔
227

228

229
def inventreeInstaller():
1✔
230
    """Returns the installer for the running codebase - if set."""
231
    # First look in the environment variables, e.g. if running in docker
232

233
    installer = os.environ.get('INVENTREE_PKG_INSTALLER', '')
234

235
    if installer:
236
        return installer
237
    elif settings.DOCKER:
238
        return 'DOC'
239
    elif main_commit is not None:
240
        return 'GIT'
241

242
    return None
243

244

245
def inventreeBranch():
246
    """Returns the branch for the running codebase - if set."""
247
    # First look in the environment variables, e.g. if running in docker
248

UNCOV
249
    branch = os.environ.get('INVENTREE_PKG_BRANCH', '')
×
250

UNCOV
251
    if branch:
×
UNCOV
252
        return branch
×
253

UNCOV
254
    if main_commit is None:
×
UNCOV
255
        return None
×
256

UNCOV
257
    try:
×
UNCOV
258
        branch = main_repo.refs.follow(b'HEAD')[0][1].decode()
×
UNCOV
259
        return branch.removeprefix('refs/heads/')
×
UNCOV
260
    except IndexError:
×
UNCOV
261
        return None  # pragma: no cover
×
262

263

264
def inventreeTarget():
1✔
265
    """Returns the target platform for the running codebase - if set."""
266
    # First look in the environment variables, e.g. if running in docker
267

268
    return os.environ.get('INVENTREE_PKG_TARGET', None)
269

270

271
def inventreePlatform():
272
    """Returns the platform for the instance."""
273
    return platform.platform(aliased=True)
1✔
274

275

276
def inventreeDatabase():
1✔
277
    """Return the InvenTree database backend e.g. 'postgresql'."""
278
    db = settings.DATABASES['default']
279
    return db.get('ENGINE', None).replace('django.db.backends.', '')
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc