diff --git a/mytonctrl/mytonctrl.py b/mytonctrl/mytonctrl.py index 1462852b..d03aa9af 100755 --- a/mytonctrl/mytonctrl.py +++ b/mytonctrl/mytonctrl.py @@ -45,11 +45,14 @@ from mytonctrl.console_cmd import add_command, check_usage_one_arg, check_usage_args_min_max_len from mytonctrl.migrate import run_migrations from mytonctrl.utils import GetItemFromList, timestamp2utcdatetime, fix_git_config, is_hex, GetColorInt, \ - pop_user_from_args, pop_arg_from_args + pop_user_from_args, pop_arg_from_args, get_clang_major_version, get_os_version from mytoninstaller.archive_blocks import download_blocks from mytoninstaller.utils import get_ton_storage_port +CLANG_VERSION_REQUIRED = 21 + + def Init(local, ton, console, argv): # Load translate table with get_package_resource_path('mytonctrl', 'resources/translate.json') as translate_path: @@ -370,8 +373,8 @@ def Upgrade(local, ton, args: list): ton.SetSettings("validatorConsole", validatorConsole) clang_version = get_clang_major_version() - if clang_version is None or clang_version < 16: - text = f"{{red}}WARNING: THIS UPGRADE WILL MOST PROBABLY FAIL DUE TO A WRONG CLANG VERSION: {clang_version}, REQUIRED VERSION IS 16. RECOMMENDED TO EXIT NOW AND UPGRADE CLANG AS PER INSTRUCTIONS: https://gist.github.com/neodix42/e4b1b68d2d5dd3dec75b5221657f05d7{{endc}}\n" + if clang_version is None or clang_version < CLANG_VERSION_REQUIRED: + text = f"{{red}}WARNING: THIS UPGRADE WILL MOST PROBABLY FAIL DUE TO A WRONG CLANG VERSION: {clang_version}, REQUIRED VERSION IS {CLANG_VERSION_REQUIRED}. RECOMMENDED TO EXIT NOW AND UPGRADE CLANG AS PER INSTRUCTIONS: https://gist.github.com/neodix42/24d6a401e928f7e895fcc8e7b7c5c24a{{endc}}\n" color_print(text) if input("Continue with upgrade anyway? [Y/n]\n").strip().lower() not in ('y', ''): print('aborted.') @@ -400,37 +403,6 @@ def upgrade_btc_teleport(local, ton, reinstall=False, branch: str = 'master', us local.try_function(module.init, args=[reinstall, branch, user]) -def get_clang_major_version(): - try: - process = subprocess.run(["clang", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - text=True, timeout=3) - if process.returncode != 0: - return None - - output = process.stdout - - lines = output.strip().split('\n') - if not lines: - return None - - first_line = lines[0] - if "clang version" not in first_line: - return None - - version_part = first_line.split("clang version")[1].strip() - major_version = version_part.split('.')[0] - - major_version = ''.join(c for c in major_version if c.isdigit()) - - if not major_version: - return None - - return int(major_version) - except Exception as e: - print(f"Error checking clang version: {type(e)}: {e}") - return None - - def rollback_to_mtc1(local, ton, args): color_print("{red}Warning: this is dangerous, please make sure you've backed up mytoncore's db.{endc}") a = input("Do you want to continue? [Y/n]\n") @@ -555,7 +527,14 @@ def check_adnl(local, ton): print_warning(local, error) #end define -def warnings(local, ton): +def check_ubuntu_version(local: MyPyClass): + distro, ver = get_os_version() + if distro == 'ubuntu': + if ver not in ['22.04', '24.04']: + warning = local.translate("ubuntu_version_warning").format(ver) + print_warning(local, warning) + +def warnings(local: MyPyClass, ton: MyTonCore): local.try_function(check_disk_usage, args=[local, ton]) local.try_function(check_sync, args=[local, ton]) local.try_function(check_adnl, args=[local, ton]) @@ -563,8 +542,7 @@ def warnings(local, ton): local.try_function(check_vps, args=[local, ton]) local.try_function(check_tg_channel, args=[local, ton]) local.try_function(check_slashed, args=[local, ton]) -#end define - + local.try_function(check_ubuntu_version, args=[local]) def CheckTonUpdate(local): git_path = "/usr/src/ton" diff --git a/mytonctrl/resources/translate.json b/mytonctrl/resources/translate.json index dc64564c..49b9244f 100644 --- a/mytonctrl/resources/translate.json +++ b/mytonctrl/resources/translate.json @@ -674,6 +674,11 @@ "ru": "{{red}}Вы были оштрафованы на {0} TON за низкую эффективность в предыдущем раунде.{{endc}}", "zh_TW": "{{red}}您因上一輪效率低而被罰款 {0} TON。{{endc}}" }, + "ubuntu_version_warning": { + "en": "{{red}}Ubuntu version must be 22.04 or 24.04. Found {0}. {{endc}}", + "ru": "{{red}}Версия Ubuntu должна быть 22.04 или 24.04. Найдена {0}. {{endc}}", + "zh_TW": "{{red}}Ubuntu 版本必須是 22.04 或 24.04。找到 {0}。{{endc}}" + }, "add_custom_overlay_cmd": { "en": "Add custom overlay", "ru": "Добавить пользовательский оверлей", diff --git a/mytonctrl/utils.py b/mytonctrl/utils.py index ee75a9ca..c1a4aa8f 100644 --- a/mytonctrl/utils.py +++ b/mytonctrl/utils.py @@ -76,3 +76,58 @@ def pop_arg_from_args(args: typing.List[str], arg_name: str) -> typing.Optional[ def pop_user_from_args(args: list) -> typing.Optional[str]: return pop_arg_from_args(args, '-u') + + +def get_clang_major_version(): + try: + process = subprocess.run(["clang", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, + text=True, timeout=3) + if process.returncode != 0: + return None + + output = process.stdout + + lines = output.strip().split('\n') + if not lines: + return None + + first_line = lines[0] + if "clang version" not in first_line: + return None + + version_part = first_line.split("clang version")[1].strip() + major_version = version_part.split('.')[0] + + major_version = ''.join(c for c in major_version if c.isdigit()) + + if not major_version: + return None + + return int(major_version) + except Exception as e: + print(f"Error checking clang version: {type(e)}: {e}") + return None + + +def get_os_version() -> typing.Tuple[typing.Optional[str], typing.Optional[str]]: + os_release_path = "/etc/os-release" + + if not os.path.exists(os_release_path): + return None, None + + data: typing.Dict[str, str] = {} + with open(os_release_path) as f: + for line in f: + if "=" not in line: + continue + key, val = line.strip().split("=", 1) + data[key] = val.strip('"') + + distro = data.get("ID") + version = ( + data.get("VERSION_ID") + or data.get("BUILD_ID") + or data.get("VERSION") + ) + + return distro, version diff --git a/tests/conftest.py b/tests/conftest.py index 21d8a827..e49a134d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -84,7 +84,16 @@ def execute(self, command: str, no_color: bool = False) -> str: ... -class MyMyPyConsole(MyPyConsole): +class TestMyPyConsole(MyPyConsole): + + def run_pre_up(self, no_color: bool = False): + output = io.StringIO() + with redirect_stderr(output), redirect_stdout(output): + self.startFunction() + output = output.getvalue() + if no_color: + output = remove_colors(output) + return output def execute(self, command: str, no_color: bool = False) -> str: output = io.StringIO() @@ -98,9 +107,8 @@ def execute(self, command: str, no_color: bool = False) -> str: @pytest.fixture() -def cli(local, ton) -> ConsoleProtocol: - console = MyMyPyConsole() - console.start_function = None # todo: do not forget about start function +def cli(local, ton) -> TestMyPyConsole: + console = TestMyPyConsole() mp = pytest.MonkeyPatch() mp.setattr(MyTonCore, "using_pool", lambda self: True) mp.setattr(MyTonCore, "using_nominator_pool", lambda self: True) @@ -108,5 +116,4 @@ def cli(local, ton) -> ConsoleProtocol: Init(local, ton, console, argv=[]) mp.undo() console.debug = True - # console.Run() return console diff --git a/tests/integration/test_basic_commands.py b/tests/integration/test_basic_commands.py index 8baf1fe1..26f68ff5 100644 --- a/tests/integration/test_basic_commands.py +++ b/tests/integration/test_basic_commands.py @@ -72,7 +72,7 @@ def fake_run_as_root(run_args): return 0 monkeypatch.setattr(mytonctrl_module, "run_as_root", fake_run_as_root) - monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 16) + monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 21) monkeypatch.setattr(MyTonCore, "using_validator", lambda self: False) with get_package_resource_path('mytonctrl', 'scripts/upgrade.sh') as upg_path: assert upg_path.is_file() @@ -98,7 +98,7 @@ def fake_SetSettings(self, name, value): assert captured_settings["liteClient"]["liteServer"]["pubkeyPath"] == "/var/ton-work/keys/liteserver.pub" assert calls["run_args"] == ["bash", upg_path, "-a", "author", "-r", "repo", "-b", "branch"] - # clang version is < 16, abort + # clang version is < 21, abort calls = {} monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 14) monkeypatch.setattr('builtins.input', lambda _: "n") @@ -106,7 +106,7 @@ def fake_SetSettings(self, name, value): assert "aborted." in output assert not calls - # clang version is < 16, proceed + # clang version is < 21, proceed monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 14) monkeypatch.setattr('builtins.input', lambda _: "y") output = cli.execute("upgrade") @@ -115,7 +115,7 @@ def fake_SetSettings(self, name, value): assert calls["run_args"] == ["bash", upg_path, "-a", "author", "-r", "repo", "-b", "branch"] # call upgrade_btc_teleport if using validator - monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 16) + monkeypatch.setattr(mytonctrl_module, "get_clang_major_version", lambda: 21) calls = {} monkeypatch.setattr(MyTonCore, "using_validator", lambda self: True) teleport_calls = {} diff --git a/tests/integration/test_preup.py b/tests/integration/test_preup.py new file mode 100644 index 00000000..cfec78f5 --- /dev/null +++ b/tests/integration/test_preup.py @@ -0,0 +1,67 @@ +from pytest_mock import MockerFixture + +from mytonctrl import mytonctrl + + +def test_warnings(cli, monkeypatch, mocker: MockerFixture): + monkeypatch.setattr(mytonctrl, 'CheckMytonctrlUpdate', lambda *_: None) + monkeypatch.setattr(mytonctrl, 'check_installer_user', lambda *_: None) + monkeypatch.setattr(mytonctrl, 'check_vport', lambda *_: None) + + # test check_ubuntu_version + + monkeypatch.setattr(mytonctrl.os.path, 'exists', lambda _: True) + res = ''' +PRETTY_NAME="Ubuntu 22.04.4 LTS" +NAME="Ubuntu" +VERSION_ID="22.04" +VERSION="22.04.4 LTS (Jammy Jellyfish)" +VERSION_CODENAME=jammy +ID=ubuntu +''' + mock = mocker.mock_open(read_data=res) + monkeypatch.setattr('builtins.open', mock) + output = cli.run_pre_up() + assert 'Ubuntu' not in output + + monkeypatch.setattr(mytonctrl.os.path, 'exists', lambda _: True) + res = ''' + PRETTY_NAME="Ubuntu 24.04.4 LTS" + NAME="Ubuntu" + VERSION_ID="24.04" + VERSION="24.04.3 LTS (Noble Numbat)" + VERSION_CODENAME=noble + ID=ubuntu + ''' + mock = mocker.mock_open(read_data=res) + monkeypatch.setattr('builtins.open', mock) + output = cli.run_pre_up() + assert 'Ubuntu' not in output + + res = ''' +PRETTY_NAME="Ubuntu 20.04.4 LTS" +NAME="Ubuntu" +VERSION_ID="20.04" +VERSION="20.04.4 LTS (Focal Fossa)" +VERSION_CODENAME=focal +ID=ubuntu +''' + mock = mocker.mock_open(read_data=res) + monkeypatch.setattr('builtins.open', mock) + output = cli.run_pre_up() + assert 'Ubuntu version must be 22.04 or 24.04. Found 20.04.' in output + + res = ''' +PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" +NAME="Debian GNU/Linux" +VERSION_ID="12" +VERSION="12 (bookworm)" +VERSION_CODENAME=bookworm +ID=debian +''' + mock = mocker.mock_open(read_data=res) + monkeypatch.setattr('builtins.open', mock) + output = cli.run_pre_up() + assert 'Ubuntu' not in output + + # todo: other warnings