Skip to content

Commit f6283df

Browse files
authored
feat(backend): Add check to cli tools to confirm the right commands for deployment method are used (#9687)
* refactor * rename functions * move checking for a package install out * [FR] Add check to cli tools to confirm the right commands for deployment method are used Fixes #9684 * also log pkg enviroment detection * vendor is_true * ensure content is loaded in pkg enviroment * fix tests and imporve error message * cleanup * simplify * ensure runs in misconfed enviroment
1 parent 2fbd01a commit f6283df

File tree

2 files changed

+95
-31
lines changed

2 files changed

+95
-31
lines changed

docs/docs/settings/error_codes.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ There are database migrations waiting to be applied. This might lead to integrit
9999
Some deployment methods support [auto applying of updates](../start/config.md#auto-update). See also [Perform Database Migrations](../start/install.md#perform-database-migrations).
100100
Steps very between deployment methods.
101101

102+
#### INVE-W9
103+
**Wrong Invoke Environment - Backend**
104+
105+
The command that was used to run invoke is not the one that is recommended. This might be caused by a wrong PATH variable or by thinking you are using a different deployment method.
106+
The warning text will show the recommended command for intended use.
107+
108+
102109
### INVE-I (InvenTree Information)
103110
Information — These are not errors but information messages. They might point out potential issues or just provide information.
104111

tasks.py

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,65 @@
1717
from invoke.exceptions import UnexpectedExit
1818

1919

20+
def is_true(x):
21+
"""Shortcut function to determine if a value "looks" like a boolean."""
22+
return str(x).strip().lower() in ['1', 'y', 'yes', 't', 'true', 'on']
23+
24+
2025
def is_docker_environment():
2126
"""Check if the InvenTree environment is running in a Docker container."""
22-
from src.backend.InvenTree.InvenTree.config import is_true
23-
2427
return is_true(os.environ.get('INVENTREE_DOCKER', 'False'))
2528

2629

2730
def is_rtd_environment():
2831
"""Check if the InvenTree environment is running on ReadTheDocs."""
29-
from src.backend.InvenTree.InvenTree.config import is_true
30-
3132
return is_true(os.environ.get('READTHEDOCS', 'False'))
3233

3334

3435
def is_debug_environment():
3536
"""Check if the InvenTree environment is running in a debug environment."""
36-
from src.backend.InvenTree.InvenTree.config import is_true
37-
3837
return is_true(os.environ.get('INVENTREE_DEBUG', 'False')) or is_true(
3938
os.environ.get('RUNNER_DEBUG', 'False')
4039
)
4140

4241

42+
def get_version_vals():
43+
"""Get values from the VERSION file."""
44+
version_file = local_dir().joinpath('VERSION')
45+
if not version_file.exists():
46+
return {}
47+
try:
48+
from dotenv import dotenv_values
49+
50+
return dotenv_values(version_file)
51+
except ImportError:
52+
error(
53+
'ERROR: dotenv package not installed. You might not be running in the right environment.'
54+
)
55+
return {}
56+
57+
58+
def is_pkg_installer(content: Optional[dict] = None, load_content: bool = False):
59+
"""Check if the current environment is a package installer by VERSION/environment."""
60+
if load_content:
61+
content = get_version_vals()
62+
return get_installer(content) == 'PKG'
63+
64+
65+
def is_pkg_installer_by_path():
66+
"""Check if the current environment is a package installer by checking the path."""
67+
return len(sys.argv) >= 1 and sys.argv[0].startswith(
68+
'/opt/inventree/env/bin/invoke'
69+
)
70+
71+
72+
def get_installer(content: Optional[dict] = None):
73+
"""Get the installer for the current environment or a content dict."""
74+
if content is None:
75+
content = os.environ
76+
return content.get('INVENTREE_PKG_INSTALLER', None)
77+
78+
4379
# region execution logging helpers
4480
def task_exception_handler(t, v, tb):
4581
"""Handle exceptions raised by tasks.
@@ -121,9 +157,9 @@ def wrapped(c, *args, **kwargs):
121157

122158

123159
# region environment checks
124-
def check_invoke_version():
160+
def envcheck_invoke_version():
125161
"""Check that the installed invoke version meets minimum requirements."""
126-
MIN_INVOKE_VERSION = '2.0.0'
162+
MIN_INVOKE_VERSION: str = '2.0.0'
127163

128164
min_version = tuple(map(int, MIN_INVOKE_VERSION.split('.')))
129165
invoke_version = tuple(map(int, invoke.__version__.split('.'))) # noqa: RUF048
@@ -134,14 +170,14 @@ def check_invoke_version():
134170
sys.exit(1)
135171

136172

137-
def check_invoke_path():
173+
def envcheck_invoke_path():
138174
"""Check that the path of the used invoke is correct."""
139175
if is_docker_environment() or is_rtd_environment():
140176
return
141177

142-
invoke_path = Path(invoke.__file__)
143-
env_path = Path(sys.prefix).resolve()
144-
loc_path = Path(__file__).parent.resolve()
178+
invoke_path: Path = Path(invoke.__file__)
179+
env_path: Path = Path(sys.prefix).resolve()
180+
loc_path: Path = Path(__file__).parent.resolve()
145181
if not invoke_path.is_relative_to(loc_path) and not invoke_path.is_relative_to(
146182
env_path
147183
):
@@ -152,17 +188,17 @@ def check_invoke_path():
152188
sys.exit(1)
153189

154190

155-
def check_python_version():
191+
def envcheck_python_version():
156192
"""Check that the installed python version meets minimum requirements.
157193
158194
If the python version is not sufficient, exits with a non-zero exit code.
159195
"""
160-
REQ_MAJOR = 3
161-
REQ_MINOR = 9
196+
REQ_MAJOR: int = 3
197+
REQ_MINOR: int = 9
162198

163199
version = sys.version.split(' ')[0]
164200

165-
valid = True
201+
valid: bool = True
166202

167203
if sys.version_info.major < REQ_MAJOR or (
168204
sys.version_info.major == REQ_MAJOR and sys.version_info.minor < REQ_MINOR
@@ -175,10 +211,35 @@ def check_python_version():
175211
sys.exit(1)
176212

177213

178-
if __name__ in ['__main__', 'tasks']:
179-
check_invoke_version()
180-
check_invoke_path()
181-
check_python_version()
214+
def envcheck_invoke_cmd():
215+
"""Checks if the rights invoke command for the current environment is used."""
216+
first_cmd = sys.argv[0].replace(sys.prefix, '')
217+
intendded = ['/bin/invoke', '/bin/inv']
218+
219+
correct_cmd: Optional[str] = None
220+
if is_rtd_environment() or is_docker_environment():
221+
pass
222+
elif is_pkg_installer(load_content=True) and not is_pkg_installer_by_path():
223+
correct_cmd = 'inventree run invoke'
224+
else:
225+
warning('Unknown environment, not checking used invoke command')
226+
227+
if first_cmd not in intendded:
228+
correct_cmd = correct_cmd if correct_cmd else 'invoke'
229+
error('INVE-W9 - Wrong Invoke Environment')
230+
error(
231+
f'The detected invoke command `{first_cmd}` is not the intended one for this environment, ensure you are using one of the following command(s) `{correct_cmd}`'
232+
)
233+
234+
235+
def main():
236+
"""Main function to check the execution environment."""
237+
envcheck_invoke_version()
238+
envcheck_python_version()
239+
envcheck_invoke_path()
240+
envcheck_invoke_cmd()
241+
242+
182243
# endregion
183244

184245

@@ -279,6 +340,9 @@ def manage_py_path():
279340

280341
# endregion
281342

343+
if __name__ in ['__main__', 'tasks']:
344+
main()
345+
282346

283347
def run(c, cmd, path: Optional[Path] = None, pty=False, env=None):
284348
"""Runs a given command a given path.
@@ -1399,11 +1463,12 @@ def version(c):
13991463
Environment:
14001464
Docker {is_docker_environment()}
14011465
RTD {is_rtd_environment()}
1466+
PKG {is_pkg_installer()}
14021467
14031468
Commit hash: {InvenTreeVersion.inventreeCommitHash()}
14041469
Commit date: {InvenTreeVersion.inventreeCommitDate()}"""
14051470
)
1406-
if len(sys.argv) == 1 and sys.argv[0].startswith('/opt/inventree/env/lib/python'):
1471+
if is_pkg_installer_by_path():
14071472
print(
14081473
"""
14091474
You are probably running the package installer / single-line installer. Please mention this in any bug reports!
@@ -1622,16 +1687,8 @@ def check_already_current(tag=None, sha=None):
16221687
).strip()
16231688
except Exception:
16241689
# .deb Packages contain extra information in the VERSION file
1625-
version_file = local_dir().joinpath('VERSION')
1626-
if not version_file.exists():
1627-
return
1628-
from dotenv import dotenv_values
1629-
1630-
content = dotenv_values(version_file)
1631-
if (
1632-
'INVENTREE_PKG_INSTALLER' in content
1633-
and content['INVENTREE_PKG_INSTALLER'] == 'PKG'
1634-
):
1690+
content: dict = get_version_vals()
1691+
if is_pkg_installer(content):
16351692
ref = content.get('INVENTREE_COMMIT_SHA')
16361693
info(
16371694
f'[INFO] Running in package environment, got commit "{ref}" from VERSION file'

0 commit comments

Comments
 (0)