openubmc-bingo 0.5.240__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 +12 -0
- bmcgo/bmcgo.py +22 -0
- bmcgo/bmcgo_config.py +176 -0
- bmcgo/cli/__init__.py +10 -0
- bmcgo/cli/cli.py +584 -0
- bmcgo/codegen/__init__.py +14 -0
- bmcgo/codegen/c/__init__.py +9 -0
- bmcgo/codegen/c/annotation.py +52 -0
- bmcgo/codegen/c/argument.py +42 -0
- bmcgo/codegen/c/codegen.py +153 -0
- bmcgo/codegen/c/comment.py +22 -0
- bmcgo/codegen/c/ctype_defination.py +353 -0
- bmcgo/codegen/c/helper.py +87 -0
- bmcgo/codegen/c/interface.py +63 -0
- bmcgo/codegen/c/method.py +82 -0
- bmcgo/codegen/c/property.py +180 -0
- bmcgo/codegen/c/renderer.py +21 -0
- bmcgo/codegen/c/signal.py +64 -0
- bmcgo/codegen/c/template/client.c.mako +145 -0
- bmcgo/codegen/c/template/client.h.mako +36 -0
- bmcgo/codegen/c/template/interface.c.mako +0 -0
- bmcgo/codegen/c/template/interface.introspect.xml.mako +99 -0
- bmcgo/codegen/c/template/micro_component.c.mako +32 -0
- bmcgo/codegen/c/template/public.c.mako +228 -0
- bmcgo/codegen/c/template/public.h.mako +128 -0
- bmcgo/codegen/c/template/server.c.mako +104 -0
- bmcgo/codegen/c/template/server.h.mako +36 -0
- bmcgo/codegen/lua/.lua-format +7 -0
- bmcgo/codegen/lua/Makefile +101 -0
- bmcgo/codegen/lua/__init__.py +9 -0
- bmcgo/codegen/lua/codegen.py +171 -0
- bmcgo/codegen/lua/proto/Makefile +87 -0
- bmcgo/codegen/lua/proto/ipmi_types.proto +17 -0
- bmcgo/codegen/lua/proto/types.proto +52 -0
- bmcgo/codegen/lua/script/check_intfs.py +161 -0
- bmcgo/codegen/lua/script/dto/__init__.py +11 -0
- bmcgo/codegen/lua/script/dto/exception.py +53 -0
- bmcgo/codegen/lua/script/dto/kepler_abstract.py +47 -0
- bmcgo/codegen/lua/script/dto/options.py +33 -0
- bmcgo/codegen/lua/script/dto/print_simple.py +19 -0
- bmcgo/codegen/lua/script/dto/redfish_api.py +241 -0
- bmcgo/codegen/lua/script/dto/url_route.py +195 -0
- bmcgo/codegen/lua/script/gen_db_json.py +444 -0
- bmcgo/codegen/lua/script/gen_depends.py +89 -0
- bmcgo/codegen/lua/script/gen_entry.py +263 -0
- bmcgo/codegen/lua/script/gen_feature_json.py +156 -0
- bmcgo/codegen/lua/script/gen_historical_local_db_json.py +88 -0
- bmcgo/codegen/lua/script/gen_intf_json.py +261 -0
- bmcgo/codegen/lua/script/gen_intf_rpc_json.py +575 -0
- bmcgo/codegen/lua/script/gen_ipmi_json.py +485 -0
- bmcgo/codegen/lua/script/gen_mdb_json.py +117 -0
- bmcgo/codegen/lua/script/gen_rpc_msg_json.py +487 -0
- bmcgo/codegen/lua/script/gen_schema.py +302 -0
- bmcgo/codegen/lua/script/ipmi_types_pb2.py +135 -0
- bmcgo/codegen/lua/script/loader/__init__.py +11 -0
- bmcgo/codegen/lua/script/loader/file_utils.py +33 -0
- bmcgo/codegen/lua/script/loader/kepler_abstract_collect.py +79 -0
- bmcgo/codegen/lua/script/loader/kepler_abstract_loader.py +47 -0
- bmcgo/codegen/lua/script/loader/redfish_loader.py +127 -0
- bmcgo/codegen/lua/script/lua_format.py +62 -0
- bmcgo/codegen/lua/script/mds_util.py +385 -0
- bmcgo/codegen/lua/script/merge_model.py +330 -0
- bmcgo/codegen/lua/script/merge_proto_algo.py +85 -0
- bmcgo/codegen/lua/script/proto_loader.py +47 -0
- bmcgo/codegen/lua/script/proto_plugin.py +140 -0
- bmcgo/codegen/lua/script/redfish_source_tree.py +118 -0
- bmcgo/codegen/lua/script/render_utils/__init__.py +38 -0
- bmcgo/codegen/lua/script/render_utils/base.py +25 -0
- bmcgo/codegen/lua/script/render_utils/client_lua.py +98 -0
- bmcgo/codegen/lua/script/render_utils/controller_lua.py +71 -0
- bmcgo/codegen/lua/script/render_utils/db_lua.py +224 -0
- bmcgo/codegen/lua/script/render_utils/error_lua.py +185 -0
- bmcgo/codegen/lua/script/render_utils/factory.py +52 -0
- bmcgo/codegen/lua/script/render_utils/ipmi_lua.py +159 -0
- bmcgo/codegen/lua/script/render_utils/ipmi_message_lua.py +24 -0
- bmcgo/codegen/lua/script/render_utils/mdb_lua.py +177 -0
- bmcgo/codegen/lua/script/render_utils/mdb_register.py +215 -0
- bmcgo/codegen/lua/script/render_utils/message_lua.py +26 -0
- bmcgo/codegen/lua/script/render_utils/messages_lua.py +156 -0
- bmcgo/codegen/lua/script/render_utils/model_lua.py +485 -0
- bmcgo/codegen/lua/script/render_utils/old_model_lua.py +429 -0
- bmcgo/codegen/lua/script/render_utils/plugin_lua.py +38 -0
- bmcgo/codegen/lua/script/render_utils/redfish_proto.py +86 -0
- bmcgo/codegen/lua/script/render_utils/request_lua.py +76 -0
- bmcgo/codegen/lua/script/render_utils/service_lua.py +130 -0
- bmcgo/codegen/lua/script/render_utils/utils_message_lua.py +125 -0
- bmcgo/codegen/lua/script/render_utils/validate_lua.py +221 -0
- bmcgo/codegen/lua/script/sep_ipmi_message_cmds.py +217 -0
- bmcgo/codegen/lua/script/template.py +166 -0
- bmcgo/codegen/lua/script/types_pb2.py +516 -0
- bmcgo/codegen/lua/script/utils.py +663 -0
- bmcgo/codegen/lua/script/validate.py +80 -0
- bmcgo/codegen/lua/script/yaml_to_json.py +73 -0
- bmcgo/codegen/lua/templates/Makefile +114 -0
- bmcgo/codegen/lua/templates/apps/Makefile +261 -0
- bmcgo/codegen/lua/templates/apps/Makefile.mdb.mk +64 -0
- bmcgo/codegen/lua/templates/apps/app.lua.mako +19 -0
- bmcgo/codegen/lua/templates/apps/class.lua.mako +35 -0
- bmcgo/codegen/lua/templates/apps/client.lua.mako +429 -0
- bmcgo/codegen/lua/templates/apps/controller.lua.mako +276 -0
- bmcgo/codegen/lua/templates/apps/datas.lua.mako +8 -0
- bmcgo/codegen/lua/templates/apps/db.lua.mako +89 -0
- bmcgo/codegen/lua/templates/apps/entry.lua.mako +128 -0
- bmcgo/codegen/lua/templates/apps/feature.lua.mako +37 -0
- bmcgo/codegen/lua/templates/apps/generate_route.mako +25 -0
- bmcgo/codegen/lua/templates/apps/impl_feature.lua.mako +72 -0
- bmcgo/codegen/lua/templates/apps/ipmi.lua.mako +97 -0
- bmcgo/codegen/lua/templates/apps/ipmi_cmd.lua.mako +18 -0
- bmcgo/codegen/lua/templates/apps/ipmi_message.lua.mako +36 -0
- bmcgo/codegen/lua/templates/apps/local_db.lua.mako +263 -0
- bmcgo/codegen/lua/templates/apps/main.lua.mako +25 -0
- bmcgo/codegen/lua/templates/apps/mc.lua.mako +77 -0
- bmcgo/codegen/lua/templates/apps/mdb.lua.mako +45 -0
- bmcgo/codegen/lua/templates/apps/mdb_interface.lua.mako +73 -0
- bmcgo/codegen/lua/templates/apps/message.lua.mako +38 -0
- bmcgo/codegen/lua/templates/apps/model.lua.mako +239 -0
- bmcgo/codegen/lua/templates/apps/orm_classes.lua.mako +16 -0
- bmcgo/codegen/lua/templates/apps/plugin.lua.mako +8 -0
- bmcgo/codegen/lua/templates/apps/redfish.proto.mako +47 -0
- bmcgo/codegen/lua/templates/apps/service.lua.mako +440 -0
- bmcgo/codegen/lua/templates/apps/signal_listen.lua.mako +19 -0
- bmcgo/codegen/lua/templates/apps/utils/default_intf.lua.mako +41 -0
- bmcgo/codegen/lua/templates/apps/utils/enum.mako +10 -0
- bmcgo/codegen/lua/templates/apps/utils/imports.mako +13 -0
- bmcgo/codegen/lua/templates/apps/utils/mdb_intf.lua.mako +25 -0
- bmcgo/codegen/lua/templates/apps/utils/mdb_obj.lua.mako +23 -0
- bmcgo/codegen/lua/templates/apps/utils/message.mako +160 -0
- bmcgo/codegen/lua/templates/apps/utils/request.lua.mako +59 -0
- bmcgo/codegen/lua/templates/apps/utils/validate.mako +83 -0
- bmcgo/codegen/lua/templates/errors.lua.mako +36 -0
- bmcgo/codegen/lua/templates/messages.lua.mako +32 -0
- bmcgo/codegen/lua/templates/new_app/.clang-format.mako +170 -0
- bmcgo/codegen/lua/templates/new_app/.gitignore.mako +26 -0
- bmcgo/codegen/lua/templates/new_app/CHANGELOG.md.mako +0 -0
- bmcgo/codegen/lua/templates/new_app/CMakeLists.txt.mako +29 -0
- bmcgo/codegen/lua/templates/new_app/Makefile.mako +25 -0
- bmcgo/codegen/lua/templates/new_app/README.md.mako +0 -0
- bmcgo/codegen/lua/templates/new_app/conanfile.py.mako +7 -0
- bmcgo/codegen/lua/templates/new_app/config.cfg.mako +6 -0
- bmcgo/codegen/lua/templates/new_app/mds/model.json.mako +3 -0
- bmcgo/codegen/lua/templates/new_app/mds/service.json.mako +21 -0
- bmcgo/codegen/lua/templates/new_app/permissions.ini.mako +16 -0
- bmcgo/codegen/lua/templates/new_app/src/lualib/${project_name}_app.lua.mako +16 -0
- bmcgo/codegen/lua/templates/new_app/src/service/main.lua.mako +25 -0
- bmcgo/codegen/lua/templates/new_app/test/integration/test_${project_name}.conf.mako +9 -0
- bmcgo/codegen/lua/templates/new_app/test/integration/test_${project_name}.lua.mako +47 -0
- bmcgo/codegen/lua/templates/new_app/test/unit/test.lua.mako +23 -0
- bmcgo/codegen/lua/templates/new_app/user_conf/rootfs/etc/systemd/system/${project_name}.service.mako +18 -0
- bmcgo/codegen/lua/templates/new_app/user_conf/rootfs/etc/systemd/system/multi-user.target.wants/${project_name}.service.link +1 -0
- bmcgo/component/__init__.py +10 -0
- bmcgo/component/analysis/analysis.py +183 -0
- bmcgo/component/analysis/build_deps.py +165 -0
- bmcgo/component/analysis/data_deps.py +333 -0
- bmcgo/component/analysis/dep-rules.json +912 -0
- bmcgo/component/analysis/dep_node.py +110 -0
- bmcgo/component/analysis/intf_deps.py +163 -0
- bmcgo/component/analysis/intf_validation.py +254 -0
- bmcgo/component/analysis/rule.py +211 -0
- bmcgo/component/analysis/smc_dfx_whitelist.json +11 -0
- bmcgo/component/analysis/sr_validation.py +391 -0
- bmcgo/component/build.py +222 -0
- bmcgo/component/component_dt_version_parse.py +348 -0
- bmcgo/component/component_helper.py +114 -0
- bmcgo/component/coverage/__init__.py +11 -0
- bmcgo/component/coverage/c_incremental_cov_report.template +53 -0
- bmcgo/component/coverage/incremental_cov.py +464 -0
- bmcgo/component/deploy.py +110 -0
- bmcgo/component/gen.py +169 -0
- bmcgo/component/package_info.py +236 -0
- bmcgo/component/template/conanbase.py.mako +278 -0
- bmcgo/component/template/conanfile.deploy.py.mako +40 -0
- bmcgo/component/test.py +947 -0
- bmcgo/errors.py +119 -0
- bmcgo/frame.py +217 -0
- bmcgo/functional/__init__.py +10 -0
- bmcgo/functional/analysis.py +96 -0
- bmcgo/functional/bmc_studio_action.py +98 -0
- bmcgo/functional/check.py +185 -0
- bmcgo/functional/conan_index_build.py +251 -0
- bmcgo/functional/config.py +332 -0
- bmcgo/functional/csr_build.py +724 -0
- bmcgo/functional/deploy.py +263 -0
- bmcgo/functional/diff.py +235 -0
- bmcgo/functional/fetch.py +235 -0
- bmcgo/functional/full_component.py +391 -0
- bmcgo/functional/maintain.py +381 -0
- bmcgo/functional/new.py +166 -0
- bmcgo/functional/schema_valid.py +111 -0
- bmcgo/functional/simple_sign.py +104 -0
- bmcgo/functional/upgrade.py +78 -0
- bmcgo/ipmigen/__init__.py +13 -0
- bmcgo/ipmigen/ctype_defination.py +82 -0
- bmcgo/ipmigen/ipmigen.py +309 -0
- bmcgo/ipmigen/template/cmd.c.mako +366 -0
- bmcgo/ipmigen/template/ipmi.c.mako +25 -0
- bmcgo/ipmigen/template/ipmi.h.mako +51 -0
- bmcgo/logger.py +176 -0
- bmcgo/misc.py +117 -0
- bmcgo/target/app.yml +17 -0
- bmcgo/target/install_sdk.yml +15 -0
- bmcgo/target/personal.yml +53 -0
- bmcgo/target/publish.yml +45 -0
- bmcgo/tasks/__init__.py +11 -0
- bmcgo/tasks/download_buildtools_hm.py +124 -0
- bmcgo/tasks/misc.py +15 -0
- bmcgo/tasks/task.py +354 -0
- bmcgo/tasks/task_build_conan.py +714 -0
- bmcgo/tasks/task_build_rootfs_img.py +595 -0
- bmcgo/tasks/task_buildgppbin.py +88 -0
- bmcgo/tasks/task_buildhpm_ext4.py +82 -0
- bmcgo/tasks/task_create_interface_config.py +122 -0
- bmcgo/tasks/task_download_buildtools.py +99 -0
- bmcgo/tasks/task_download_dependency.py +72 -0
- bmcgo/tasks/task_hpm_envir_prepare.py +112 -0
- bmcgo/tasks/task_packet_to_supporte.py +87 -0
- bmcgo/tasks/task_prepare.py +105 -0
- bmcgo/tasks/task_sign_and_pack_hpm.py +42 -0
- bmcgo/utils/__init__.py +10 -0
- bmcgo/utils/buffer.py +128 -0
- bmcgo/utils/combine_json_schemas.py +170 -0
- bmcgo/utils/component_post.py +54 -0
- bmcgo/utils/component_version_check.py +86 -0
- bmcgo/utils/config.py +1067 -0
- bmcgo/utils/fetch_component_code.py +232 -0
- bmcgo/utils/install_manager.py +61 -0
- bmcgo/utils/installations/__init__.py +10 -0
- bmcgo/utils/installations/base_installer.py +70 -0
- bmcgo/utils/installations/install_consts.py +30 -0
- bmcgo/utils/installations/install_plans/bingo.yml +11 -0
- bmcgo/utils/installations/install_workflow.py +50 -0
- bmcgo/utils/installations/installers/apt_installer.py +177 -0
- bmcgo/utils/installations/installers/pip_installer.py +46 -0
- bmcgo/utils/installations/version_util.py +100 -0
- bmcgo/utils/mapping_config_patch.py +443 -0
- bmcgo/utils/perf_analysis.py +114 -0
- bmcgo/utils/tools.py +704 -0
- bmcgo/worker.py +417 -0
- openubmc_bingo-0.5.240.dist-info/METADATA +30 -0
- openubmc_bingo-0.5.240.dist-info/RECORD +242 -0
- openubmc_bingo-0.5.240.dist-info/WHEEL +5 -0
- openubmc_bingo-0.5.240.dist-info/entry_points.txt +2 -0
- openubmc_bingo-0.5.240.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:根据conan包拉取组件代码
|
|
4
|
+
# Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import shutil
|
|
16
|
+
import subprocess
|
|
17
|
+
from multiprocessing import Pool
|
|
18
|
+
import patch_ng
|
|
19
|
+
import yaml
|
|
20
|
+
from git import Repo
|
|
21
|
+
|
|
22
|
+
from bmcgo.misc import errors
|
|
23
|
+
from bmcgo import misc
|
|
24
|
+
from bmcgo.utils.tools import Tools
|
|
25
|
+
|
|
26
|
+
tools = Tools("fetch_component_code")
|
|
27
|
+
log = tools.log
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def process_err_cb(err):
|
|
31
|
+
log.error("!!!!!!!!!!!!!!!!!!!!!!!!!!! 拉取代码失败, 错误: %s", err)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FetchComponentCode:
|
|
35
|
+
def __init__(self, packages, target_dir, conan_remote, include_open_source=True):
|
|
36
|
+
self.conan = shutil.which(misc.CONAN)
|
|
37
|
+
if self.conan is None:
|
|
38
|
+
raise RuntimeError("找不到 conan 工具")
|
|
39
|
+
self.packages = packages
|
|
40
|
+
self.target_dir = target_dir
|
|
41
|
+
self.remote = conan_remote
|
|
42
|
+
self.include_open_source = include_open_source
|
|
43
|
+
|
|
44
|
+
@staticmethod
|
|
45
|
+
def resolve_version_range(component_name, version_range):
|
|
46
|
+
cmd = f"conan info '{version_range}' --package-filter={component_name}/*" \
|
|
47
|
+
f" -r {misc.CONAN_REPO} --only None 2>/dev/null"
|
|
48
|
+
ret, output = subprocess.getstatusoutput(cmd)
|
|
49
|
+
output = output.strip()
|
|
50
|
+
if ret != 0 or not output:
|
|
51
|
+
return None
|
|
52
|
+
return output.split("\n")[-1]
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def patch(patch_file=None):
|
|
56
|
+
patchset = patch_ng.fromfile(patch_file)
|
|
57
|
+
if not patchset:
|
|
58
|
+
raise errors.BmcGoException("Failed to parse patch: %s" % (patch_file))
|
|
59
|
+
if not patchset.apply(strip=0, root="./", fuzz=False):
|
|
60
|
+
raise errors.BmcGoException("Failed to apply patch: %s" % patch_file)
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def _get_patch_changed_files(patch_file):
|
|
64
|
+
files = {}
|
|
65
|
+
for line in open(patch_file):
|
|
66
|
+
if not line.startswith("diff --git"):
|
|
67
|
+
continue
|
|
68
|
+
line = line.strip()
|
|
69
|
+
chunk = line.split()
|
|
70
|
+
a_file = chunk[-2][2:]
|
|
71
|
+
b_file = chunk[-1][2:]
|
|
72
|
+
files[a_file] = b_file
|
|
73
|
+
return files
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _apply_patches_direct(real_patch, patch_file, changed_files):
|
|
77
|
+
FetchComponentCode.patch(patch_file=real_patch)
|
|
78
|
+
for a_file, b_file in changed_files.items():
|
|
79
|
+
if a_file != b_file:
|
|
80
|
+
if a_file != "/dev/null" and b_file != "/dev/null":
|
|
81
|
+
os.rename(a_file, b_file)
|
|
82
|
+
cmd = f"git rm -f {a_file}"
|
|
83
|
+
tools.run_command(cmd)
|
|
84
|
+
elif a_file != "/dev/null":
|
|
85
|
+
cmd = f"git rm -f {a_file}"
|
|
86
|
+
tools.run_command(cmd)
|
|
87
|
+
continue
|
|
88
|
+
cmd = f"git add {b_file}"
|
|
89
|
+
tools.run_command(cmd)
|
|
90
|
+
cmd = f"git commit -m \"{patch_file}\""
|
|
91
|
+
tools.run_command(cmd)
|
|
92
|
+
|
|
93
|
+
@staticmethod
|
|
94
|
+
def __apply_patches(conandata_file, patches, code_dir):
|
|
95
|
+
cwd = os.getcwd()
|
|
96
|
+
recipe_folder = os.path.join(os.path.dirname(conandata_file), "..", "export_source")
|
|
97
|
+
recipe_folder = os.path.realpath(recipe_folder)
|
|
98
|
+
os.chdir(os.path.join(cwd, code_dir))
|
|
99
|
+
for patch in patches:
|
|
100
|
+
patch_file = patch.get("patch_file")
|
|
101
|
+
if not patch_file:
|
|
102
|
+
log.warning(f"{code_dir} 组件的conandata.yml文件缺少patch_file,跳过git apply操作")
|
|
103
|
+
real_patch = os.path.join(recipe_folder, patch_file)
|
|
104
|
+
if not os.path.isfile(real_patch):
|
|
105
|
+
log.error(f"{code_dir} 组件申明的补丁文件{real_patch}不存在,可能产生错误,请人工处理")
|
|
106
|
+
continue
|
|
107
|
+
changed_files = FetchComponentCode._get_patch_changed_files(real_patch)
|
|
108
|
+
log.info(f"{code_dir} 开始应用源码补丁{patch_file}")
|
|
109
|
+
try:
|
|
110
|
+
FetchComponentCode._apply_patches_direct(real_patch, patch_file, changed_files)
|
|
111
|
+
except errors.BmcGoException:
|
|
112
|
+
# 尝试还原文件修改
|
|
113
|
+
for a_file, b_file in changed_files.items():
|
|
114
|
+
cmd = f"git checkout -- {a_file}"
|
|
115
|
+
tools.run_command(cmd, ignore_error=True)
|
|
116
|
+
cmd = f"git checkout -- {b_file}"
|
|
117
|
+
tools.run_command(cmd, ignore_error=True)
|
|
118
|
+
cmd = "git am " + real_patch
|
|
119
|
+
tools.run_command(cmd)
|
|
120
|
+
log.info(f"{code_dir} 应用源码补丁{patch_file}")
|
|
121
|
+
os.chdir(cwd)
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def __update_code_by_commit_id(code_dir, url, commit_id):
|
|
125
|
+
if os.path.exists(code_dir):
|
|
126
|
+
repo = Repo(code_dir)
|
|
127
|
+
repo.git.fetch('origin')
|
|
128
|
+
else:
|
|
129
|
+
Repo.clone_from(url, to_path=code_dir)
|
|
130
|
+
repo = Repo(code_dir)
|
|
131
|
+
|
|
132
|
+
repo.index.reset(commit=commit_id, head=True, working_tree=True)
|
|
133
|
+
repo.git.clean('-dfx')
|
|
134
|
+
log.info("更新代码(组件: %s, 地址: %s, 提交节点: %s)", code_dir, url, commit_id)
|
|
135
|
+
|
|
136
|
+
@staticmethod
|
|
137
|
+
def __update_code_by_branch(code_dir, deps):
|
|
138
|
+
url = deps['url']
|
|
139
|
+
branch = deps['branch'].split('/')[-1]
|
|
140
|
+
|
|
141
|
+
if os.path.exists(code_dir):
|
|
142
|
+
repo = Repo(code_dir)
|
|
143
|
+
repo.git.fetch('origin', branch)
|
|
144
|
+
repo.git.pull('origin', branch)
|
|
145
|
+
repo.git.checkout(branch)
|
|
146
|
+
else:
|
|
147
|
+
Repo.clone_from(url, to_path=code_dir, branch=branch)
|
|
148
|
+
repo = Repo(code_dir)
|
|
149
|
+
log.info("更新代码(组件: %s, 地址: %s, 分支: %s)", code_dir, url, branch)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def __update_component_code_by_conandata(name, version, conandata_file):
|
|
153
|
+
with open(conandata_file, 'r') as f_:
|
|
154
|
+
conandata = yaml.safe_load(f_)
|
|
155
|
+
deps = conandata.get("sources", {}).get(version, {})
|
|
156
|
+
if 'url' in deps:
|
|
157
|
+
FetchComponentCode.__update_code_by_branch(name, deps)
|
|
158
|
+
patches = conandata.get("patches", {}).get(version)
|
|
159
|
+
if patches:
|
|
160
|
+
FetchComponentCode.__apply_patches(conandata_file, patches, name)
|
|
161
|
+
else:
|
|
162
|
+
for comp, deps in deps.items():
|
|
163
|
+
FetchComponentCode.__update_code_by_branch(comp, deps)
|
|
164
|
+
|
|
165
|
+
@staticmethod
|
|
166
|
+
def __getinfo_from_conanfile(conanfile):
|
|
167
|
+
with open(conanfile, "r") as file:
|
|
168
|
+
lines = file.readlines()
|
|
169
|
+
for idx, line in enumerate(lines):
|
|
170
|
+
if line.find('scm = {"revision":') > 0:
|
|
171
|
+
revision = re.split(r'["]', line)[-2]
|
|
172
|
+
url = re.split(r'["]', lines[idx + 2])[-2]
|
|
173
|
+
break
|
|
174
|
+
else:
|
|
175
|
+
raise RuntimeError("无法找到版本(revision)和地址(url)字段")
|
|
176
|
+
|
|
177
|
+
return revision, url
|
|
178
|
+
|
|
179
|
+
def run(self):
|
|
180
|
+
packages_to_fetch = dict()
|
|
181
|
+
for component_name, package in self.packages.items():
|
|
182
|
+
version_str = re.split("/|@", package)[1]
|
|
183
|
+
if "[" not in version_str:
|
|
184
|
+
if self.include_open_source or re.fullmatch("\d+\.\d+\.\d+", version_str) is not None:
|
|
185
|
+
packages_to_fetch[component_name] = package
|
|
186
|
+
continue
|
|
187
|
+
resolved_package = self.resolve_version_range(component_name, package)
|
|
188
|
+
if resolved_package is None:
|
|
189
|
+
log.warning("查找不到与 %s 范围匹配的版本", package)
|
|
190
|
+
else:
|
|
191
|
+
packages_to_fetch[component_name] = resolved_package
|
|
192
|
+
log.info("查找到与 %s 范围匹配的版本 %s", package, resolved_package)
|
|
193
|
+
|
|
194
|
+
process_count = min(len(packages_to_fetch), os.cpu_count())
|
|
195
|
+
log.info("创建 %u 个进程拉取代码", process_count)
|
|
196
|
+
pool = Pool(processes=process_count)
|
|
197
|
+
for component_name, conan_version in packages_to_fetch.items():
|
|
198
|
+
pool.apply_async(func=self.update_component_code, args=(component_name, conan_version),
|
|
199
|
+
error_callback=process_err_cb)
|
|
200
|
+
pool.close()
|
|
201
|
+
pool.join()
|
|
202
|
+
|
|
203
|
+
def update_component_code(self, component_name, conan_version):
|
|
204
|
+
"""
|
|
205
|
+
update component code by conan version
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
os.chdir(self.target_dir)
|
|
209
|
+
log.info("更新 %s 组件代码到 %s 开始", component_name, conan_version)
|
|
210
|
+
|
|
211
|
+
version_split = re.split(r'[/, @]', conan_version)
|
|
212
|
+
conan_dir = os.path.join(os.path.expanduser('~'), '.conan/data/', *version_split, 'export')
|
|
213
|
+
conandata = os.path.join(conan_dir, 'conandata.yml')
|
|
214
|
+
if not os.path.exists(conan_dir):
|
|
215
|
+
conan_remote_list = tools.get_conan_remote_list(self.remote)
|
|
216
|
+
tools.download_conan_recipes(conan_version, conan_remote_list)
|
|
217
|
+
if os.path.exists(conandata):
|
|
218
|
+
FetchComponentCode.__update_component_code_by_conandata(version_split[0], version_split[1], conandata)
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
conanfile = os.path.join(conan_dir, 'conanfile.py')
|
|
222
|
+
if os.path.exists(conanfile):
|
|
223
|
+
revision, url = FetchComponentCode.__getinfo_from_conanfile(conanfile)
|
|
224
|
+
FetchComponentCode.__update_code_by_commit_id(component_name, url, revision)
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
log.error("conandata(%s) 和 conanfile(%s) 都没有找到", conandata, conanfile)
|
|
228
|
+
log.info("更新组件 %s 代码到 %s 失败, 无法获取到 conan 信息", component_name,
|
|
229
|
+
conan_version)
|
|
230
|
+
except Exception as exp:
|
|
231
|
+
log.error("工作状态错误: %s", exp)
|
|
232
|
+
log.error("更新组件代码到 %s 失败", conan_version)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:安装管理器
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from bmcgo.utils.tools import Logger
|
|
16
|
+
from bmcgo.utils.installations import install_consts
|
|
17
|
+
from bmcgo.utils.installations.install_workflow import InstallWorkflow
|
|
18
|
+
from bmcgo.utils.installations.base_installer import BaseInstaller
|
|
19
|
+
|
|
20
|
+
logger = Logger(name="upgrade", log_file="/usr/share/bmcgo/install.log")
|
|
21
|
+
InstallWorkflow.logger = logger
|
|
22
|
+
BaseInstaller.logger = logger
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class InstallManager:
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self._custom_path = None
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def custom_installer_path(self):
|
|
31
|
+
return self._custom_path / install_consts.PLUGIN_INSTALLER_PATH
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def custom_install_plan_path(self):
|
|
35
|
+
return self._custom_path / install_consts.PLUGIN_INSTALL_PLAN_PATH
|
|
36
|
+
|
|
37
|
+
def install(self, app_name, operator, version, custom_path):
|
|
38
|
+
self._set_custom_path(custom_path)
|
|
39
|
+
BaseInstaller.discover_installers()
|
|
40
|
+
InstallWorkflow.discover_workflows()
|
|
41
|
+
|
|
42
|
+
workflows = []
|
|
43
|
+
if app_name == install_consts.INSTALL_ALL:
|
|
44
|
+
workflows = list(InstallWorkflow.get_all_plans())
|
|
45
|
+
else:
|
|
46
|
+
workflows = [app_name]
|
|
47
|
+
|
|
48
|
+
for wname in workflows:
|
|
49
|
+
logger.info(f"安装{wname}...")
|
|
50
|
+
plans = InstallWorkflow.parse(wname)
|
|
51
|
+
for plan in plans.get(install_consts.PLAN_STEPS, []):
|
|
52
|
+
inst_type = plan.get(install_consts.PLAN_INSTALL_TYPE)
|
|
53
|
+
BaseInstaller.get_installer(inst_type).install(plan, operator, version)
|
|
54
|
+
|
|
55
|
+
def _set_custom_path(self, custom_path: str):
|
|
56
|
+
self._custom_path = Path(custom_path).resolve()
|
|
57
|
+
if not self._custom_path.exists() or not self._custom_path.is_dir():
|
|
58
|
+
logger.warning(f"无效的地址: {self._custom_path}")
|
|
59
|
+
return
|
|
60
|
+
BaseInstaller.add_installer_dir(self.custom_installer_path)
|
|
61
|
+
InstallWorkflow.add_plan_dir(self.custom_install_plan_path)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# Copyright (c) 2024 Huawei Technologies Co., Ltd.
|
|
3
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
4
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
5
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
6
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
7
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
8
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
9
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
10
|
+
# See the Mulan PSL v2 for more details.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:安装工具工厂类
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
import abc
|
|
14
|
+
import sys
|
|
15
|
+
import importlib.util
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Dict, List, Type
|
|
18
|
+
from bmcgo.utils.installations import install_consts
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseInstaller(abc.ABC):
|
|
22
|
+
logger = None
|
|
23
|
+
_intallers: Dict[str, Type["BaseInstaller"]] = {}
|
|
24
|
+
search_paths: List[Path] = [Path(__file__).resolve().parent / install_consts.PLUGIN_INSTALLER_PATH]
|
|
25
|
+
|
|
26
|
+
def __init_subclass__(cls, installer_type: str, **kwargs):
|
|
27
|
+
super.__init_subclass__(**kwargs)
|
|
28
|
+
|
|
29
|
+
key = installer_type or cls.__name__.lower()
|
|
30
|
+
if key in cls._intallers:
|
|
31
|
+
cls.logger and cls.logger.warning(f"{installer_type}({cls._intallers[key]} 被替换为: {cls})")
|
|
32
|
+
cls._intallers[installer_type] = cls
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def add_installer_dir(cls, directory: Path):
|
|
36
|
+
if directory not in cls.search_paths:
|
|
37
|
+
cls.search_paths.append(directory)
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def discover_installers(cls):
|
|
41
|
+
for path in cls.search_paths:
|
|
42
|
+
if not path.exists():
|
|
43
|
+
cls.logger and cls.logger.warning(f"未知安装工具路径:: {str(path)},跳过")
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
for inst in path.glob("*.py"):
|
|
47
|
+
if inst.name == "__init__.py":
|
|
48
|
+
continue
|
|
49
|
+
|
|
50
|
+
module_name = inst.stem
|
|
51
|
+
spec = importlib.util.spec_from_file_location(f"installer_{module_name}", inst)
|
|
52
|
+
if spec and spec.loader:
|
|
53
|
+
module = importlib.util.module_from_spec(spec)
|
|
54
|
+
try:
|
|
55
|
+
sys.modules[module.__name__] = module
|
|
56
|
+
spec.loader.exec_module(module)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
cls.logger and cls.logger.exception(f"加载安装器 {inst} 失败: {str(e)}")
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
@classmethod
|
|
62
|
+
def get_installer(cls, installer_type: str) -> "BaseInstaller":
|
|
63
|
+
installer_cls = cls._intallers.get(installer_type)
|
|
64
|
+
if not installer_cls:
|
|
65
|
+
raise ValueError(f"未定义的安装方法:{installer_type}")
|
|
66
|
+
return installer_cls()
|
|
67
|
+
|
|
68
|
+
@abc.abstractmethod
|
|
69
|
+
def install(self, plan: Dict[str, List[str]], operator: str, version: str):
|
|
70
|
+
""" 安装入口 """
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:安装工具集常量
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
|
|
14
|
+
INSTALLATION_PATH = "instllations"
|
|
15
|
+
|
|
16
|
+
PLUGIN_INSTALLER_PATH = "installers"
|
|
17
|
+
PLUGIN_INSTALL_PLAN_PATH = "install_plans"
|
|
18
|
+
|
|
19
|
+
INSTALL_ALL = "all"
|
|
20
|
+
INSTALL_LATEST = "latest"
|
|
21
|
+
INSTALL_DEFAULT = f"{INSTALL_ALL}={INSTALL_LATEST}"
|
|
22
|
+
|
|
23
|
+
PLAN_STEPS = "install_steps"
|
|
24
|
+
PLAN_INSTALL_TYPE = "type"
|
|
25
|
+
PLAN_PACKAGE_NAME = "package_name"
|
|
26
|
+
PLAN_MODULE_NAME = "module_name"
|
|
27
|
+
PLAN_REPO_URL = "url"
|
|
28
|
+
PLAN_GPG = "gpg"
|
|
29
|
+
PLAN_CONFIG_FILE = "config_file"
|
|
30
|
+
PLAN_PUBLIC_KEY = "public_key"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
install_steps:
|
|
2
|
+
- type: pip
|
|
3
|
+
package_name: openubmc-bingo
|
|
4
|
+
module_name: bingo
|
|
5
|
+
- type: apt
|
|
6
|
+
package_name: openubmc-bingo
|
|
7
|
+
config_file: bingo.list
|
|
8
|
+
url: https://openubmc-apt-repo.obs.cn-north-4.myhuaweicloud.com/bingo_test_2
|
|
9
|
+
gpg: bingo.gpg
|
|
10
|
+
public_key: gpg_key.public
|
|
11
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:安装流程配置
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
|
|
14
|
+
from typing import Dict, List
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
import yaml
|
|
17
|
+
from bmcgo.utils.installations import install_consts
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InstallWorkflow:
|
|
21
|
+
logger = None
|
|
22
|
+
_workflows: Dict[str, List[str]] = {}
|
|
23
|
+
search_paths: List[Path] = [Path(__file__).resolve().parent / install_consts.PLUGIN_INSTALL_PLAN_PATH]
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def discover_workflows(cls):
|
|
27
|
+
for path in cls.search_paths:
|
|
28
|
+
if not path.exists():
|
|
29
|
+
cls.logger and cls.logger.warning(f"未找到安装配置路径:w{str(path)}")
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
for yml_file in path.glob("*.yml"):
|
|
33
|
+
with open(yml_file) as conf:
|
|
34
|
+
cls._workflows[yml_file.stem] = yaml.safe_load(conf)
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def parse(cls, name: str) -> List[str]:
|
|
38
|
+
workflow = cls._workflows.get(name)
|
|
39
|
+
if not workflow:
|
|
40
|
+
raise ValueError(f"未找到安装配置:{str(workflow)}")
|
|
41
|
+
return workflow
|
|
42
|
+
|
|
43
|
+
@classmethod
|
|
44
|
+
def add_plan_dir(cls, directory: Path):
|
|
45
|
+
if directory not in cls.search_paths:
|
|
46
|
+
cls.search_paths.append(directory)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def get_all_plans(cls):
|
|
50
|
+
return cls._workflows.keys()
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding=utf-8
|
|
3
|
+
# 描述:apt安装工具
|
|
4
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
5
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
6
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
7
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
8
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
9
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
10
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
11
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
12
|
+
# See the Mulan PSL v2 for more details.
|
|
13
|
+
import os
|
|
14
|
+
import base64
|
|
15
|
+
import tempfile
|
|
16
|
+
import requests
|
|
17
|
+
from typing import Dict, List
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from bmcgo.utils.tools import Tools
|
|
20
|
+
from bmcgo.utils.installations import install_consts
|
|
21
|
+
from bmcgo.utils.installations.version_util import PkgVersion
|
|
22
|
+
from bmcgo.utils.installations.base_installer import BaseInstaller
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
tool = Tools("apt_install")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AptInstaller(BaseInstaller, installer_type="apt"):
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self._repo_url = None
|
|
31
|
+
self._gpg_file = None
|
|
32
|
+
self._config_file = None
|
|
33
|
+
self._repo_public_key = None
|
|
34
|
+
self._pkg_name = None
|
|
35
|
+
self._pkg_version = None
|
|
36
|
+
|
|
37
|
+
def install(self, plan: Dict[str, List[str]], operator: str, version: str):
|
|
38
|
+
self._parse_plan(plan)
|
|
39
|
+
self._install_key()
|
|
40
|
+
|
|
41
|
+
if not self._check_repo():
|
|
42
|
+
self.logger and self.logger.info("未检测到仓库配置,开始配置")
|
|
43
|
+
self._config_repo()
|
|
44
|
+
|
|
45
|
+
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
|
+
try:
|
|
114
|
+
key_data = requests.get(self._repo_public_key).content
|
|
115
|
+
except Exception as e:
|
|
116
|
+
raise RuntimeError("下载公钥失败")
|
|
117
|
+
|
|
118
|
+
lines = key_data.splitlines()
|
|
119
|
+
in_block = False
|
|
120
|
+
b64data = []
|
|
121
|
+
|
|
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
|
+
|
|
132
|
+
if not b64data:
|
|
133
|
+
raise ValueError("公钥出错")
|
|
134
|
+
|
|
135
|
+
dearmor = base64.b64decode(b"".join(b64data))
|
|
136
|
+
|
|
137
|
+
with open(self._gpg_file, "wb") as f:
|
|
138
|
+
f.write(dearmor)
|
|
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)
|
|
148
|
+
|
|
149
|
+
def _config_repo(self):
|
|
150
|
+
repo_line = f"deb [signed-by={self._gpg_file}] {self._repo_url} stable main\n"
|
|
151
|
+
|
|
152
|
+
self.logger and self.logger.info("配置仓资源")
|
|
153
|
+
with tempfile.NamedTemporaryFile("w", delete=False) as tmp:
|
|
154
|
+
tmp.write(repo_line)
|
|
155
|
+
tmp_path = tmp.name
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
tool.run_command(["mv", tmp_path, str(self._config_file)], sudo=True)
|
|
159
|
+
tool.run_command(["chmod", "644", str(self._config_file)], sudo=True)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
os.remove(tmp_path)
|
|
162
|
+
raise RuntimeError(f"写入仓库配置失败: {str(e)}")
|
|
163
|
+
|
|
164
|
+
def _update_cache(self):
|
|
165
|
+
self.logger and self.logger.info("更新 apt 缓存")
|
|
166
|
+
try:
|
|
167
|
+
tool.run_command(["apt-get", "update"], sudo=True)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
raise RuntimeError(f"安装失败: {str(e)}")
|
|
170
|
+
|
|
171
|
+
def _install_package(self, pkg: str):
|
|
172
|
+
self.logger and self.logger.info(f"安装: {pkg}")
|
|
173
|
+
try:
|
|
174
|
+
tool.run_command(["apt-get", "install", "-y", "--allow-downgrades", pkg], sudo=True)
|
|
175
|
+
except Exception as e:
|
|
176
|
+
raise RuntimeError(f"安装失败: {str(e)}")
|
|
177
|
+
|