openubmc-bingo 0.5.243__py3-none-any.whl → 0.5.254__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of openubmc-bingo might be problematic. Click here for more details.
- bmcgo/__init__.py +1 -1
- bmcgo/bmcgo_config.py +81 -1
- bmcgo/codegen/lua/Makefile +13 -0
- bmcgo/codegen/lua/codegen.py +6 -3
- bmcgo/codegen/lua/script/dto/options.py +1 -0
- bmcgo/codegen/lua/script/{render_utils/factory.py → factory.py} +1 -1
- bmcgo/codegen/lua/script/gen_entry.py +6 -3
- bmcgo/codegen/lua/script/gen_intf_rpc_json.py +12 -0
- bmcgo/codegen/lua/script/gen_schema.py +2 -0
- bmcgo/codegen/lua/script/merge_model.py +10 -3
- bmcgo/codegen/lua/script/render_utils/__init__.py +5 -4
- bmcgo/codegen/lua/script/render_utils/client_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/controller_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/db_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/error_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/ipmi_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/ipmi_message_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/mdb_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/message_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/messages_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/model_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/old_model_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/plugin_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/redfish_proto.py +2 -2
- bmcgo/codegen/lua/script/render_utils/request_lua.py +2 -2
- bmcgo/codegen/lua/script/render_utils/service_lua.py +3 -2
- bmcgo/codegen/lua/script/template.py +5 -1
- bmcgo/codegen/lua/script/utils.py +9 -2
- bmcgo/codegen/lua/templates/Makefile +8 -0
- bmcgo/codegen/lua/templates/apps/Makefile +27 -1
- bmcgo/codegen/lua/templates/apps/controller.lua.mako +20 -4
- bmcgo/codegen/lua/v1/script/gen_schema.py +328 -0
- bmcgo/codegen/lua/v1/script/render_utils/model_lua.py +458 -0
- bmcgo/codegen/lua/v1/templates/apps/model.lua.mako +62 -0
- bmcgo/codegen/lua/v1/templates/apps/service.lua.mako +193 -0
- bmcgo/component/build.py +26 -43
- bmcgo/component/component_helper.py +54 -0
- bmcgo/component/coverage/incremental_cov.py +25 -33
- bmcgo/frame.py +9 -6
- bmcgo/functional/conan_index_build.py +16 -41
- bmcgo/functional/config.py +7 -0
- bmcgo/functional/csr_build.py +313 -88
- bmcgo/functional/diff.py +3 -3
- bmcgo/functional/json_check.py +109 -0
- bmcgo/functional/upgrade.py +31 -1
- bmcgo/misc.py +32 -2
- bmcgo/target/install_sdk.yml +6 -0
- bmcgo/tasks/task.py +69 -43
- bmcgo/tasks/task_build_conan.py +6 -2
- bmcgo/tasks/task_build_wbd_up.py +4 -4
- bmcgo/utils/basic_enums.py +45 -0
- bmcgo/utils/config.py +20 -8
- bmcgo/utils/install_manager.py +75 -20
- bmcgo/utils/installations/base_installer.py +114 -7
- bmcgo/utils/installations/install_consts.py +3 -1
- bmcgo/utils/installations/install_plans/bingo.yml +2 -4
- bmcgo/utils/installations/install_workflow.py +6 -2
- bmcgo/utils/installations/installers/apt_installer.py +58 -136
- bmcgo/utils/installations/installers/pip_installer.py +52 -18
- bmcgo/utils/installations/version_util.py +1 -1
- bmcgo/utils/json_validator.py +241 -0
- {openubmc_bingo-0.5.243.dist-info → openubmc_bingo-0.5.254.dist-info}/METADATA +2 -1
- {openubmc_bingo-0.5.243.dist-info → openubmc_bingo-0.5.254.dist-info}/RECORD +67 -60
- /bmcgo/codegen/lua/script/{render_utils/base.py → base.py} +0 -0
- {openubmc_bingo-0.5.243.dist-info → openubmc_bingo-0.5.254.dist-info}/WHEEL +0 -0
- {openubmc_bingo-0.5.243.dist-info → openubmc_bingo-0.5.254.dist-info}/entry_points.txt +0 -0
- {openubmc_bingo-0.5.243.dist-info → openubmc_bingo-0.5.254.dist-info}/top_level.txt +0 -0
|
@@ -15,21 +15,42 @@ import sys
|
|
|
15
15
|
import importlib.util
|
|
16
16
|
from pathlib import Path
|
|
17
17
|
from typing import Dict, List, Type
|
|
18
|
+
from bmcgo.utils.tools import Tools
|
|
18
19
|
from bmcgo.utils.installations import install_consts
|
|
20
|
+
from bmcgo.utils.installations.version_util import PkgVersion
|
|
19
21
|
|
|
20
22
|
|
|
21
23
|
class BaseInstaller(abc.ABC):
|
|
22
|
-
|
|
24
|
+
tools = Tools("install")
|
|
25
|
+
logger = tools.log
|
|
23
26
|
_intallers: Dict[str, Type["BaseInstaller"]] = {}
|
|
24
27
|
search_paths: List[Path] = [Path(__file__).resolve().parent / install_consts.PLUGIN_INSTALLER_PATH]
|
|
28
|
+
type_name = None
|
|
25
29
|
|
|
26
|
-
def
|
|
30
|
+
def __init__(self):
|
|
31
|
+
self._pkg_name = None
|
|
32
|
+
self._target_ver = None
|
|
33
|
+
self._cur_ver = None
|
|
34
|
+
|
|
35
|
+
def __init_subclass__(cls, **kwargs):
|
|
27
36
|
super.__init_subclass__(**kwargs)
|
|
28
37
|
|
|
29
|
-
key =
|
|
38
|
+
key = cls.type_name or cls.__name__.lower()
|
|
30
39
|
if key in cls._intallers:
|
|
31
|
-
cls.logger and cls.logger.warning(f"{
|
|
32
|
-
cls._intallers[
|
|
40
|
+
cls.logger and cls.logger.warning(f"{key}({cls._intallers[key]} 被替换为: {cls})")
|
|
41
|
+
cls._intallers[key] = cls
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def target_version(self):
|
|
45
|
+
return self._target_ver
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def current_version(self):
|
|
49
|
+
return self._cur_ver
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def package_name(self):
|
|
53
|
+
return self._pkg_name
|
|
33
54
|
|
|
34
55
|
@classmethod
|
|
35
56
|
def add_installer_dir(cls, directory: Path):
|
|
@@ -64,7 +85,93 @@ class BaseInstaller(abc.ABC):
|
|
|
64
85
|
if not installer_cls:
|
|
65
86
|
raise ValueError(f"未定义的安装方法:{installer_type}")
|
|
66
87
|
return installer_cls()
|
|
88
|
+
|
|
89
|
+
def init(self, plan, operator, version):
|
|
90
|
+
self.parse_plan(plan)
|
|
91
|
+
|
|
92
|
+
versions = self.get_versions()
|
|
93
|
+
self.resolve_constraint(versions, operator, version)
|
|
94
|
+
|
|
95
|
+
self.get_current_version()
|
|
96
|
+
|
|
97
|
+
def pre_install(self):
|
|
98
|
+
""" 安装前检查 """
|
|
99
|
+
self.info("安装前检查")
|
|
100
|
+
|
|
101
|
+
def post_install(self):
|
|
102
|
+
""" 安装后清理 """
|
|
103
|
+
self.info("安装后清理")
|
|
104
|
+
|
|
105
|
+
def rollback(self):
|
|
106
|
+
""" 回退 """
|
|
107
|
+
self.info("回退")
|
|
108
|
+
|
|
109
|
+
def parse_plan(self, plan: Dict[str, List[str]]):
|
|
110
|
+
self._pkg_name = plan.get(install_consts.PLAN_PACKAGE_NAME)
|
|
111
|
+
if not self._pkg_name:
|
|
112
|
+
self.error(f"{install_consts.PLAN_PACKAGE_NAME} 未配置!")
|
|
113
|
+
return
|
|
114
|
+
self.parse_custom_plan(plan)
|
|
115
|
+
|
|
116
|
+
def resolve_constraint(self, versions, opt, ver):
|
|
117
|
+
if not versions:
|
|
118
|
+
self.warning("当前没有可下载版本!")
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
if ver == install_consts.INSTALL_LATEST or not opt:
|
|
122
|
+
self._target_ver = versions[0]
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
pkg_ver = PkgVersion(ver)
|
|
126
|
+
for avl_ver in versions:
|
|
127
|
+
v = PkgVersion(avl_ver)
|
|
128
|
+
if opt == ">=" and v >= pkg_ver:
|
|
129
|
+
self._target_ver = v.origin
|
|
130
|
+
break
|
|
131
|
+
elif opt == "<=" and v <= pkg_ver:
|
|
132
|
+
self._target_ver = v.origin
|
|
133
|
+
break
|
|
134
|
+
elif opt == "!=" and v != pkg_ver:
|
|
135
|
+
self._target_ver = v.origin
|
|
136
|
+
break
|
|
137
|
+
elif opt == "<" and v < pkg_ver:
|
|
138
|
+
self._target_ver = v.origin
|
|
139
|
+
break
|
|
140
|
+
elif opt == ">" and v > pkg_ver:
|
|
141
|
+
self._target_ver = v.origin
|
|
142
|
+
break
|
|
143
|
+
elif opt == "=" and v == pkg_ver:
|
|
144
|
+
self._target_ver = v.origin
|
|
145
|
+
break
|
|
146
|
+
else:
|
|
147
|
+
self.warning(f"没有找到匹配的版本:{self._pkg_name}{opt}{ver}")
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
def info(self, msg):
|
|
151
|
+
self.logger and self.logger.info(f"[{self.type_name}] {msg}")
|
|
152
|
+
|
|
153
|
+
def error(self, msg):
|
|
154
|
+
self.logger and self.logger.error(f"[{self.type_name}] {msg}")
|
|
155
|
+
|
|
156
|
+
def warning(self, msg):
|
|
157
|
+
self.logger and self.logger.warning(f"[{self.type_name}] {msg}")
|
|
158
|
+
|
|
159
|
+
@abc.abstractmethod
|
|
160
|
+
def parse_custom_plan(self, plan: Dict[str, List[str]]):
|
|
161
|
+
""" 解析 yml 配置计划 """
|
|
162
|
+
|
|
163
|
+
@abc.abstractmethod
|
|
164
|
+
def install(self, force: bool):
|
|
165
|
+
""" 安装入口 """
|
|
166
|
+
|
|
167
|
+
@abc.abstractmethod
|
|
168
|
+
def get_versions(self):
|
|
169
|
+
""" 可用版本 """
|
|
170
|
+
|
|
171
|
+
@abc.abstractmethod
|
|
172
|
+
def show_versions(self):
|
|
173
|
+
""" 列出可用版本 """
|
|
67
174
|
|
|
68
175
|
@abc.abstractmethod
|
|
69
|
-
def
|
|
70
|
-
"""
|
|
176
|
+
def get_current_version(self):
|
|
177
|
+
""" 获取当前已安装版本 """
|
|
@@ -20,6 +20,7 @@ INSTALL_ALL = "all"
|
|
|
20
20
|
INSTALL_LATEST = "latest"
|
|
21
21
|
INSTALL_DEFAULT = f"{INSTALL_ALL}={INSTALL_LATEST}"
|
|
22
22
|
|
|
23
|
+
PLAN_VERSION_HOMOGENEOUS = "version_homogeneous"
|
|
23
24
|
PLAN_STEPS = "install_steps"
|
|
24
25
|
PLAN_INSTALL_TYPE = "type"
|
|
25
26
|
PLAN_PACKAGE_NAME = "package_name"
|
|
@@ -27,4 +28,5 @@ PLAN_MODULE_NAME = "module_name"
|
|
|
27
28
|
PLAN_REPO_URL = "url"
|
|
28
29
|
PLAN_GPG = "gpg"
|
|
29
30
|
PLAN_CONFIG_FILE = "config_file"
|
|
30
|
-
PLAN_PUBLIC_KEY = "public_key"
|
|
31
|
+
PLAN_PUBLIC_KEY = "public_key"
|
|
32
|
+
PLAN_DOC = "doc"
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
+
version_homogeneous: true
|
|
1
2
|
install_steps:
|
|
2
3
|
- type: pip
|
|
3
4
|
package_name: openubmc-bingo
|
|
4
5
|
module_name: bingo
|
|
5
6
|
- type: apt
|
|
6
7
|
package_name: openubmc-bingo
|
|
7
|
-
|
|
8
|
-
url: https://openubmc-apt-repo.obs.cn-north-4.myhuaweicloud.com/bingo/
|
|
9
|
-
gpg: bingo.gpg
|
|
10
|
-
public_key: gpg_key.public
|
|
8
|
+
doc: https://www.openubmc.cn/marketplace/bingo
|
|
11
9
|
|
|
@@ -14,11 +14,15 @@
|
|
|
14
14
|
from typing import Dict, List
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
import yaml
|
|
17
|
+
from bmcgo.utils.tools import Tools
|
|
17
18
|
from bmcgo.utils.installations import install_consts
|
|
18
19
|
|
|
19
20
|
|
|
21
|
+
tools = Tools("install")
|
|
22
|
+
|
|
23
|
+
|
|
20
24
|
class InstallWorkflow:
|
|
21
|
-
logger =
|
|
25
|
+
logger = tools.log
|
|
22
26
|
_workflows: Dict[str, List[str]] = {}
|
|
23
27
|
search_paths: List[Path] = [Path(__file__).resolve().parent / install_consts.PLUGIN_INSTALL_PLAN_PATH]
|
|
24
28
|
|
|
@@ -26,7 +30,7 @@ class InstallWorkflow:
|
|
|
26
30
|
def discover_workflows(cls):
|
|
27
31
|
for path in cls.search_paths:
|
|
28
32
|
if not path.exists():
|
|
29
|
-
cls.logger and cls.logger.warning(f"未找到安装配置路径:
|
|
33
|
+
cls.logger and cls.logger.warning(f"未找到安装配置路径:{str(path)}, 跳过")
|
|
30
34
|
continue
|
|
31
35
|
|
|
32
36
|
for yml_file in path.glob("*.yml"):
|
|
@@ -11,167 +11,89 @@
|
|
|
11
11
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
12
|
# See the Mulan PSL v2 for more details.
|
|
13
13
|
import os
|
|
14
|
-
import
|
|
15
|
-
import tempfile
|
|
16
|
-
import requests
|
|
14
|
+
import shutil
|
|
17
15
|
from typing import Dict, List
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
from bmcgo.utils.tools import Tools
|
|
20
16
|
from bmcgo.utils.installations import install_consts
|
|
21
17
|
from bmcgo.utils.installations.version_util import PkgVersion
|
|
22
18
|
from bmcgo.utils.installations.base_installer import BaseInstaller
|
|
23
19
|
|
|
24
20
|
|
|
25
|
-
|
|
21
|
+
class AptInstaller(BaseInstaller):
|
|
22
|
+
type_name = "apt"
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
class AptInstaller(BaseInstaller, installer_type="apt"):
|
|
29
24
|
def __init__(self):
|
|
30
|
-
|
|
31
|
-
self.
|
|
32
|
-
self.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
self.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
self._install_key()
|
|
40
|
-
|
|
41
|
-
if not self._check_repo():
|
|
42
|
-
self.logger and self.logger.info("未检测到仓库配置,开始配置")
|
|
43
|
-
self._config_repo()
|
|
44
|
-
|
|
25
|
+
super().__init__()
|
|
26
|
+
self._apt_cmd = None
|
|
27
|
+
self._doc = None
|
|
28
|
+
|
|
29
|
+
def pre_install(self):
|
|
30
|
+
self._apt_cmd = shutil.which("apt-get")
|
|
31
|
+
if not self._apt_cmd:
|
|
32
|
+
raise RuntimeError("未找到 apt-get 指令")
|
|
33
|
+
|
|
45
34
|
self._update_cache()
|
|
46
|
-
|
|
47
|
-
target = [self._pkg_name]
|
|
48
|
-
if operator and version:
|
|
49
|
-
ver = self._resolve_constraint(operator, version)
|
|
50
|
-
if ver:
|
|
51
|
-
target.append(ver)
|
|
52
|
-
pkg = "=".join(target)
|
|
53
|
-
self._install_package(pkg)
|
|
54
|
-
self.logger and self.logger.info(f"安装{pkg}完成!")
|
|
55
|
-
|
|
56
|
-
def _get_versions(self) -> List[PkgVersion]:
|
|
57
|
-
result = tool.run_command(["apt-cache", "madison", self._pkg_name], capture_output=True)
|
|
58
|
-
if not result.stdout:
|
|
59
|
-
return []
|
|
60
|
-
return [PkgVersion(line.split("|")[1].strip()) for line in result.stdout.splitlines()]
|
|
61
|
-
|
|
62
|
-
def _resolve_constraint(self, opt: str, ver: str) -> str:
|
|
63
|
-
versions = self._get_versions()
|
|
64
|
-
if not versions:
|
|
65
|
-
self.logger and self.logger.warning("当前没有可下载版本!")
|
|
66
|
-
return None
|
|
67
|
-
|
|
68
|
-
if ver == install_consts.INSTALL_LATEST or not opt:
|
|
69
|
-
return versions[0].origin
|
|
70
|
-
|
|
71
|
-
pkg_ver = PkgVersion(ver)
|
|
72
|
-
for v in versions:
|
|
73
|
-
if opt == ">=" and v >= pkg_ver:
|
|
74
|
-
return v.origin
|
|
75
|
-
elif opt == "<=" and v <= pkg_ver:
|
|
76
|
-
return v.origin
|
|
77
|
-
elif opt == "!=" and v != pkg_ver:
|
|
78
|
-
return v.origin
|
|
79
|
-
elif opt == "<" and v < pkg_ver:
|
|
80
|
-
return v.origin
|
|
81
|
-
elif opt == ">" and v > pkg_ver:
|
|
82
|
-
return v.origin
|
|
83
|
-
elif opt == "=" and v == pkg_ver:
|
|
84
|
-
return v.origin
|
|
85
|
-
|
|
86
|
-
raise ValueError(f"没有找到匹配的版本:{opt}{ver}")
|
|
87
|
-
|
|
88
|
-
def _parse_plan(self, plan: Dict[str, List[str]]):
|
|
89
|
-
repo_url = plan.get(install_consts.PLAN_REPO_URL)
|
|
90
|
-
repo_public_key = plan.get(install_consts.PLAN_PUBLIC_KEY)
|
|
91
|
-
gpg_file = plan.get(install_consts.PLAN_GPG)
|
|
92
|
-
config_file = plan.get(install_consts.PLAN_CONFIG_FILE)
|
|
93
|
-
pkg_name = plan.get(install_consts.PLAN_PACKAGE_NAME)
|
|
94
|
-
|
|
95
|
-
if not all(val for key, val in locals().items() if key not in ["self", "plan"]):
|
|
96
|
-
values = [
|
|
97
|
-
f"{install_consts.PLAN_REPO_URL}: {repo_url}",
|
|
98
|
-
f"{install_consts.PLAN_PUBLIC_KEY}: {repo_public_key}",
|
|
99
|
-
f"{install_consts.PLAN_GPG}: {gpg_file}",
|
|
100
|
-
f"{install_consts.PLAN_CONFIG_FILE}: {config_file}",
|
|
101
|
-
f"{install_consts.PLAN_PACKAGE_NAME}: {pkg_name}"
|
|
102
|
-
]
|
|
103
|
-
raise ValueError(f"请检查安装配置文件:\n{"\n\t".join(values)}\n")
|
|
104
|
-
|
|
105
|
-
self._repo_url = repo_url
|
|
106
|
-
self._repo_public_key = f"{self._repo_url}{repo_public_key}"
|
|
107
|
-
self._gpg_file = Path("/usr/share/keyrings") / gpg_file
|
|
108
|
-
self._config_file = Path("/etc/apt/sources.list.d/") / config_file
|
|
109
|
-
self._pkg_name = pkg_name
|
|
110
|
-
|
|
111
|
-
def _install_key(self):
|
|
112
|
-
self.logger and self.logger.info("下载公钥")
|
|
113
35
|
try:
|
|
114
|
-
|
|
36
|
+
self.tools.run_command(["apt", "policy", self._pkg_name], capture_output=True)
|
|
115
37
|
except Exception as e:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
lines = key_data.splitlines()
|
|
119
|
-
in_block = False
|
|
120
|
-
b64data = []
|
|
38
|
+
self.info(f"本地源未正确配置,请参考社区文档进行配置:{self._doc}")
|
|
121
39
|
|
|
122
|
-
for line in lines:
|
|
123
|
-
if line.startswith(b"-----BEGIN PGP"):
|
|
124
|
-
in_block = True
|
|
125
|
-
continue
|
|
126
|
-
if line.startswith(b"-----END PGP"):
|
|
127
|
-
in_block = False
|
|
128
|
-
continue
|
|
129
|
-
if in_block and line.strip() and not line.startswith(b"="):
|
|
130
|
-
b64data.append(line.strip())
|
|
131
40
|
|
|
132
|
-
|
|
133
|
-
|
|
41
|
+
def install(self, force: bool):
|
|
42
|
+
if self.current_version == self.target_version and not force:
|
|
43
|
+
yes = input(f"[apt]当前版本已满足(=={self.target_version}), 是否覆盖安装?[Y/n]")
|
|
44
|
+
if yes == "n":
|
|
45
|
+
self.info(f"用户跳过覆盖安装{self._pkg_name}={self.target_version}")
|
|
46
|
+
return
|
|
134
47
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
os.chmod(self._gpg_file, 0o644)
|
|
140
|
-
|
|
141
|
-
def _check_repo(self):
|
|
142
|
-
if not self._config_file.exists():
|
|
143
|
-
return False
|
|
144
|
-
|
|
145
|
-
expect_line = f"deb [signed-by={self._gpg_file}] {self._repo_url} stable main\n"
|
|
146
|
-
with open(self._config_file) as f:
|
|
147
|
-
return any(line.strip() == expect_line.strip() for line in f)
|
|
48
|
+
pkg = f"{self._pkg_name}={self.target_version}"
|
|
49
|
+
self.info(f"开始安装{pkg}")
|
|
50
|
+
self._install_package(pkg, force)
|
|
51
|
+
self.info(f"安装{pkg}完成!")
|
|
148
52
|
|
|
149
|
-
def
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
tmp_path = tmp.name
|
|
53
|
+
def show_versions(self):
|
|
54
|
+
versions = self.get_versions()
|
|
55
|
+
if not versions:
|
|
56
|
+
self.error("未找到可用的版本")
|
|
57
|
+
return
|
|
58
|
+
self.info(f"可用版本: {", ".join(versions)}")
|
|
156
59
|
|
|
60
|
+
def get_versions(self) -> List[PkgVersion]:
|
|
61
|
+
result = self.tools.run_command(["apt-cache", "madison", self._pkg_name], capture_output=True)
|
|
62
|
+
if not result or not result.stdout:
|
|
63
|
+
return []
|
|
64
|
+
versions = [line.split("|")[1].strip() for line in result.stdout.splitlines()]
|
|
65
|
+
versions.sort(key=PkgVersion, reverse=True)
|
|
66
|
+
return versions
|
|
67
|
+
|
|
68
|
+
def get_current_version(self):
|
|
157
69
|
try:
|
|
158
|
-
|
|
159
|
-
|
|
70
|
+
result = self.tools.run_command(["dpkg-query", "-W", "-f=${Version}", self._pkg_name], capture_output=True)
|
|
71
|
+
if not result or not result.stdout:
|
|
72
|
+
return
|
|
73
|
+
self._cur_ver = result.stdout
|
|
160
74
|
except Exception as e:
|
|
161
|
-
|
|
162
|
-
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
def parse_custom_plan(self, plan: Dict[str, List[str]]):
|
|
78
|
+
self._doc = plan.get(install_consts.PLAN_DOC)
|
|
79
|
+
if not self._doc:
|
|
80
|
+
self.warning(f"未配置 {install_consts.PLAN_DOC}")
|
|
163
81
|
|
|
164
82
|
def _update_cache(self):
|
|
165
|
-
self.
|
|
83
|
+
self.info("更新 apt 缓存")
|
|
166
84
|
try:
|
|
167
|
-
|
|
85
|
+
self.tools.run_command(["apt-get", "update"], sudo=(os.getuid() != 0))
|
|
168
86
|
except Exception as e:
|
|
169
87
|
raise RuntimeError(f"安装失败: {str(e)}")
|
|
170
88
|
|
|
171
|
-
def _install_package(self, pkg: str):
|
|
172
|
-
self.
|
|
89
|
+
def _install_package(self, pkg: str, force: bool):
|
|
90
|
+
self.info(f"安装: {pkg}")
|
|
173
91
|
try:
|
|
174
|
-
|
|
92
|
+
cmd = ["apt-get", "install", "--allow-downgrades", pkg]
|
|
93
|
+
if force:
|
|
94
|
+
cmd.append("-y")
|
|
95
|
+
self.tools.run_command(cmd, sudo=(os.getuid() != 0))
|
|
96
|
+
|
|
175
97
|
except Exception as e:
|
|
176
98
|
raise RuntimeError(f"安装失败: {str(e)}")
|
|
177
99
|
|
|
@@ -11,28 +11,28 @@
|
|
|
11
11
|
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
12
|
# See the Mulan PSL v2 for more details.
|
|
13
13
|
import sys
|
|
14
|
-
import
|
|
14
|
+
from importlib.metadata import distributions
|
|
15
15
|
from typing import Dict, List
|
|
16
|
-
from bmcgo.utils.tools import Tools
|
|
17
16
|
from bmcgo.utils.installations import install_consts
|
|
17
|
+
from bmcgo.utils.installations import version_util
|
|
18
18
|
from bmcgo.utils.installations.base_installer import BaseInstaller
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
class PipInstaller(BaseInstaller):
|
|
22
|
+
type_name = "pip"
|
|
22
23
|
|
|
24
|
+
def __init__(self):
|
|
25
|
+
super().__init__()
|
|
26
|
+
self._app_name = None
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
def install(self, force):
|
|
29
|
+
if self.current_version == self.target_version and not force:
|
|
30
|
+
yes = input(f"[pip]: 当前版本已满足(=={self.target_version}), 是否覆盖安装?[Y/n]")
|
|
31
|
+
if yes == "n":
|
|
32
|
+
self.info(f"用户跳过覆盖安装{self._pkg_name}={self.target_version}")
|
|
33
|
+
return
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
app_name = plan.get(install_consts.PLAN_MODULE_NAME)
|
|
31
|
-
|
|
32
|
-
if version == install_consts.INSTALL_LATEST or not operator:
|
|
33
|
-
pkg_info = pkg_name
|
|
34
|
-
else:
|
|
35
|
-
pkg_info = "".join([pkg_name, operator, version])
|
|
35
|
+
pkg_info = f"{self._pkg_name}=={self.target_version}"
|
|
36
36
|
cmds = [sys.executable, "-m", "pip", "install", "--upgrade", pkg_info]
|
|
37
37
|
|
|
38
38
|
with open("/etc/issue", "r") as fp:
|
|
@@ -40,7 +40,41 @@ class PipInstaller(BaseInstaller, installer_type="pip"):
|
|
|
40
40
|
if issue.startswith("Ubuntu 24.04"):
|
|
41
41
|
cmds.append("--break-system-packages")
|
|
42
42
|
|
|
43
|
-
self.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
self.info(f"pip 开始安装{pkg_info}")
|
|
44
|
+
self.tools.run_command(cmds, show_log=True)
|
|
45
|
+
self.info(f"pip 安装{pkg_info}完成!")
|
|
46
|
+
|
|
47
|
+
def parse_custom_plan(self, plan: Dict[str, List[str]]):
|
|
48
|
+
self._app_name = plan.get(install_consts.PLAN_MODULE_NAME)
|
|
49
|
+
|
|
50
|
+
def show_versions(self):
|
|
51
|
+
versions_mirror = self.get_versions()
|
|
52
|
+
if not versions_mirror:
|
|
53
|
+
self.error(f"未找到可用的版本")
|
|
54
|
+
self.info(f"可用版本: {", ".join(versions_mirror)}")
|
|
55
|
+
|
|
56
|
+
def get_versions(self, index_url=None):
|
|
57
|
+
cmd = [sys.executable, "-m", "pip", "index", "versions", self._pkg_name]
|
|
58
|
+
if index_url is not None:
|
|
59
|
+
cmd.extend(["-i", index_url])
|
|
60
|
+
ret = self.tools.run_command(cmd, capture_output=True)
|
|
61
|
+
for l in ret.stdout.splitlines():
|
|
62
|
+
if l.strip().startswith("Available versions: "):
|
|
63
|
+
# 跳过 Available, versions:
|
|
64
|
+
versions = [v.strip() for v in l.replace("Available versions:", "").split(",")]
|
|
65
|
+
versions.sort(key=version_util.PkgVersion, reverse=True)
|
|
66
|
+
return versions
|
|
67
|
+
else:
|
|
68
|
+
if index_url:
|
|
69
|
+
self.error(f"从 {index_url} 获取版本号失败!")
|
|
70
|
+
else:
|
|
71
|
+
self.error("获取版本号失败!")
|
|
72
|
+
return []
|
|
73
|
+
|
|
74
|
+
def get_current_version(self):
|
|
75
|
+
for dist in distributions():
|
|
76
|
+
metadata = dist.metadata
|
|
77
|
+
if self._pkg_name == metadata["Name"]:
|
|
78
|
+
self._cur_ver = metadata["Version"]
|
|
79
|
+
break
|
|
80
|
+
|
|
@@ -89,7 +89,7 @@ class PkgVersion:
|
|
|
89
89
|
return self._digit.match(s).group()
|
|
90
90
|
return self._non_digit.match(s).group()
|
|
91
91
|
|
|
92
|
-
def __compare(self, other
|
|
92
|
+
def __compare(self, other):
|
|
93
93
|
if self.epoch != other.epoch:
|
|
94
94
|
return -1 if self.epoch < other.epoch else 1
|
|
95
95
|
|