openubmc-bingo 0.5.275__py3-none-any.whl → 0.6.0__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 +22 -10
- bmcgo/cli/cli.py +86 -39
- bmcgo/cli/config.conan2.yaml +9 -0
- bmcgo/codegen/lua/codegen.py +1 -1
- bmcgo/codegen/lua/script/gen_intf_rpc_json.py +15 -14
- bmcgo/component/analysis/analysis.py +8 -8
- bmcgo/component/analysis/intf_validation.py +23 -12
- bmcgo/component/build.py +76 -14
- bmcgo/component/component_dt_version_parse.py +3 -2
- bmcgo/component/component_helper.py +43 -15
- bmcgo/component/coverage/incremental_cov.py +2 -2
- bmcgo/component/deploy.py +68 -14
- bmcgo/component/gen.py +1 -1
- bmcgo/component/package_info.py +128 -38
- bmcgo/component/template_v2/conanbase.py.mako +352 -0
- bmcgo/component/template_v2/conanfile.deploy.py.mako +26 -0
- bmcgo/component/test.py +53 -20
- bmcgo/frame.py +7 -3
- bmcgo/functional/analysis.py +3 -2
- bmcgo/functional/check.py +10 -6
- bmcgo/functional/conan_index_build.py +79 -20
- bmcgo/functional/csr_build.py +11 -3
- bmcgo/functional/diff.py +1 -1
- bmcgo/functional/fetch.py +1 -1
- bmcgo/functional/full_component.py +32 -24
- bmcgo/functional/git_history.py +220 -0
- bmcgo/functional/maintain.py +55 -12
- bmcgo/functional/new.py +1 -1
- bmcgo/functional/schema_valid.py +2 -2
- bmcgo/logger.py +2 -3
- bmcgo/misc.py +130 -9
- bmcgo/tasks/conan/__init__.py +10 -0
- bmcgo/tasks/conan/conanfile.py +45 -0
- bmcgo/tasks/task.py +27 -4
- bmcgo/tasks/task_build_conan.py +433 -65
- bmcgo/tasks/task_buildgppbin.py +8 -2
- bmcgo/tasks/task_download_buildtools.py +76 -0
- bmcgo/tasks/task_download_dependency.py +1 -0
- bmcgo/tasks/task_hpm_envir_prepare.py +1 -1
- bmcgo/utils/build_conans.py +231 -0
- bmcgo/utils/component_post.py +6 -4
- bmcgo/utils/component_version_check.py +10 -5
- bmcgo/utils/config.py +76 -40
- bmcgo/utils/fetch_component_code.py +44 -25
- bmcgo/utils/installations/install_plans/qemu.yml +6 -0
- bmcgo/utils/installations/install_plans/studio.yml +6 -0
- bmcgo/utils/installations/install_workflow.py +28 -2
- bmcgo/utils/merge_csr.py +124 -0
- bmcgo/utils/tools.py +239 -117
- bmcgo/worker.py +2 -2
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/METADATA +4 -2
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/RECORD +56 -46
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/WHEEL +0 -0
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/entry_points.txt +0 -0
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/top_level.txt +0 -0
|
@@ -11,11 +11,9 @@
|
|
|
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 pathlib
|
|
15
14
|
import subprocess
|
|
16
|
-
import shutil
|
|
17
|
-
import stat
|
|
18
15
|
import json
|
|
16
|
+
import re
|
|
19
17
|
import yaml
|
|
20
18
|
|
|
21
19
|
from bmcgo import errors
|
|
@@ -68,21 +66,31 @@ class BmcgoCommand:
|
|
|
68
66
|
def __init__(self, bconfig: BmcgoConfig, *args):
|
|
69
67
|
self.bconfig = bconfig
|
|
70
68
|
parser = tool.create_common_parser("Conan Index Build")
|
|
69
|
+
parser.add_argument("--conan2", help="是否构建conan2.x版本的组件包", action=misc.STORE_TRUE)
|
|
71
70
|
self.args, _ = parser.parse_known_args(*args)
|
|
71
|
+
self.conan2 = self.args.conan2
|
|
72
72
|
self.path = ""
|
|
73
73
|
self.version = ""
|
|
74
|
+
self.name = ""
|
|
75
|
+
self.user = ""
|
|
76
|
+
self.channel = ""
|
|
74
77
|
self.conan_package = ""
|
|
75
78
|
self.upload = False
|
|
76
79
|
self.remote = None
|
|
77
80
|
self.options = []
|
|
78
|
-
self.stage =
|
|
81
|
+
self.stage = self.args.stage
|
|
79
82
|
self.enable_luajit = False
|
|
80
83
|
self.from_source = False
|
|
81
84
|
self.build_type = 'debug'
|
|
82
85
|
self.asan = False
|
|
83
86
|
self.profile = ''
|
|
87
|
+
self.recipe_folder = self.bconfig.conan_index.folder
|
|
88
|
+
if misc.conan_v2():
|
|
89
|
+
recipes2_folder = os.path.join(self.bconfig.conan_index.folder, "..", "recipes2")
|
|
90
|
+
recipes2_folder = os.path.realpath(recipes2_folder)
|
|
91
|
+
if os.path.isdir(recipes2_folder):
|
|
92
|
+
self.recipe_folder = recipes2_folder
|
|
84
93
|
self.initialize()
|
|
85
|
-
self.channel = f"@{misc.ConanUserEnum.CONAN_USER_DEV.value}/{self.stage}"
|
|
86
94
|
|
|
87
95
|
@staticmethod
|
|
88
96
|
def run_command(command, ignore_error=False, sudo=False, **kwargs):
|
|
@@ -109,13 +117,15 @@ class BmcgoCommand:
|
|
|
109
117
|
os.environ["LUA_PATH"] = f"{conan_bin}/?.lua"
|
|
110
118
|
|
|
111
119
|
def initialize(self):
|
|
120
|
+
if not self.args.conan_package:
|
|
121
|
+
msg = "构建参数错误,请指定有效的-cp参数,例:kmc/1.0.1 或 kmc/1.0.1@openubmc/stable"
|
|
122
|
+
raise errors.BmcGoException(msg)
|
|
112
123
|
self.set_package(self.args.conan_package)
|
|
113
124
|
self.upload = self.args.upload_package
|
|
114
125
|
if self.args.remote:
|
|
115
126
|
self.remote = self.args.remote
|
|
116
127
|
if self.args.options:
|
|
117
128
|
self.options = self.args.options
|
|
118
|
-
self.stage = self.args.stage
|
|
119
129
|
self.enable_luajit = self.args.enable_luajit
|
|
120
130
|
self.from_source = self.args.from_source
|
|
121
131
|
self.build_type = self.args.build_type
|
|
@@ -126,15 +136,25 @@ class BmcgoCommand:
|
|
|
126
136
|
|
|
127
137
|
# 入参可以是huawei_secure_c/1.0.0样式
|
|
128
138
|
def set_package(self, path: str):
|
|
129
|
-
os.chdir(self.
|
|
130
|
-
split =
|
|
131
|
-
if len(split) != 2:
|
|
132
|
-
raise errors.BmcGoException(f"包名称({path})错误,例:kmc/1.0.1")
|
|
139
|
+
os.chdir(self.recipe_folder)
|
|
140
|
+
split = re.split('/|@', path)
|
|
141
|
+
if len(split) != 2 and len(split) != 4:
|
|
142
|
+
raise errors.BmcGoException(f"包名称({path})错误,例:kmc/1.0.1 或 kmc/1.0.1@openubmc/stable")
|
|
143
|
+
self.name = split[0].lower()
|
|
144
|
+
if len(split) == 2:
|
|
145
|
+
if self.stage == "dev":
|
|
146
|
+
self.user = misc.conan_user_dev()
|
|
147
|
+
else:
|
|
148
|
+
self.user = misc.conan_user()
|
|
149
|
+
self.channel = self.stage
|
|
150
|
+
else:
|
|
151
|
+
self.user = split[2]
|
|
152
|
+
self.channel = split[3]
|
|
133
153
|
|
|
134
154
|
if not os.path.isdir(split[0]):
|
|
135
155
|
raise errors.BmcGoException(f"包路径({split[0]})不存在,或不是文件夹")
|
|
136
156
|
|
|
137
|
-
config_yaml = os.path.join(self.
|
|
157
|
+
config_yaml = os.path.join(self.recipe_folder, split[0], "config.yml")
|
|
138
158
|
with open(config_yaml) as f:
|
|
139
159
|
config_data = yaml.safe_load(f)
|
|
140
160
|
config_data = config_data.get('versions', None)
|
|
@@ -148,18 +168,47 @@ class BmcgoCommand:
|
|
|
148
168
|
raise errors.BmcGoException(f"Unkown folder, config.yml path: {config_yaml}, version: {split[1]}")
|
|
149
169
|
self.path = "{}/{}".format(split[0], folder)
|
|
150
170
|
self.version = split[1]
|
|
171
|
+
if misc.conan_v2():
|
|
172
|
+
self.version = self.version.lower()
|
|
151
173
|
if self.stage != "dev":
|
|
152
174
|
self.tag_check()
|
|
153
175
|
|
|
154
|
-
def
|
|
176
|
+
def run_v2(self):
|
|
177
|
+
log.info("Start build package")
|
|
178
|
+
if self.build_type == "debug":
|
|
179
|
+
setting = "-s build_type=Debug"
|
|
180
|
+
else:
|
|
181
|
+
setting = "-s build_type=Release"
|
|
182
|
+
options = " "
|
|
183
|
+
if self.asan:
|
|
184
|
+
options += f"-o {self.name}/*:asan=True"
|
|
185
|
+
for option in self.options:
|
|
186
|
+
options += f" -o {option}"
|
|
187
|
+
|
|
188
|
+
dt_stat = os.environ.get("BINGO_DT_RUN", "off")
|
|
189
|
+
show_log = True if dt_stat == "off" else False
|
|
190
|
+
pkg = self.name + "/" + self.version + "@" + self.user + "/" + self.channel
|
|
191
|
+
append_cmd = f"-r {self.remote}" if self.remote else ""
|
|
192
|
+
cmd = "conan create . --name={} --version={} -pr={} {} {} {}".format(
|
|
193
|
+
self.name, self.version, self.profile, setting, append_cmd, options
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
cmd += " --user={} --channel={}".format(self.user, self.channel)
|
|
197
|
+
if self.from_source:
|
|
198
|
+
cmd += " --build=*"
|
|
199
|
+
else:
|
|
200
|
+
cmd += f" --build={self.name}/* --build=missing"
|
|
201
|
+
self.run_command(cmd, show_log=show_log)
|
|
202
|
+
|
|
203
|
+
if not self.upload:
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
cmd = "conan upload {} -r {}".format(pkg, self.remote)
|
|
207
|
+
self.run_command(cmd)
|
|
208
|
+
log.info("===>>>Upload package successfully, pkg: {}".format(pkg))
|
|
209
|
+
|
|
210
|
+
def run_v1(self):
|
|
155
211
|
self.check_luac()
|
|
156
|
-
if self.path == "" or self.version == "":
|
|
157
|
-
raise errors.BmcGoException(f"Path({self.path}) or version({self.version}) error")
|
|
158
|
-
os.chdir(self.bconfig.conan_index.folder)
|
|
159
|
-
os.chdir(self.path)
|
|
160
|
-
if self.stage != misc.StageEnum.STAGE_DEV.value:
|
|
161
|
-
# rc和stable模式
|
|
162
|
-
self.channel = f"@{tool.conan_user}/{self.stage}"
|
|
163
212
|
log.info("Start build package")
|
|
164
213
|
if self.build_type == "dt":
|
|
165
214
|
setting = "-s build_type=Dt"
|
|
@@ -177,7 +226,7 @@ class BmcgoCommand:
|
|
|
177
226
|
|
|
178
227
|
dt_stat = os.environ.get("BMCGO_DT_RUN", "off")
|
|
179
228
|
show_log = True if dt_stat == "off" else False
|
|
180
|
-
pkg =
|
|
229
|
+
pkg = self.name + "/" + self.version + "@" + self.user + "/" + self.channel
|
|
181
230
|
append_cmd = f"-r {self.remote}" if self.remote else ""
|
|
182
231
|
cmd = "conan create . {} -pr={} -pr:b profile.dt.ini {} {} -tf None {}".format(
|
|
183
232
|
pkg, self.profile, setting, append_cmd, options
|
|
@@ -206,6 +255,16 @@ class BmcgoCommand:
|
|
|
206
255
|
self.run_command(cmd)
|
|
207
256
|
log.info("===>>>Upload package successfully, pkg: {}:{}".format(pkg, i["id"]))
|
|
208
257
|
|
|
258
|
+
def run(self):
|
|
259
|
+
if self.path == "" or self.version == "":
|
|
260
|
+
raise errors.BmcGoException(f"Path({self.path}) or version({self.version}) error")
|
|
261
|
+
|
|
262
|
+
os.chdir(self.path)
|
|
263
|
+
if misc.conan_v1():
|
|
264
|
+
self.run_v1()
|
|
265
|
+
else:
|
|
266
|
+
self.run_v2()
|
|
267
|
+
|
|
209
268
|
def tag_check(self):
|
|
210
269
|
yaml_file = f"{self.path}/conandata.yml"
|
|
211
270
|
if os.path.exists(yaml_file) is False:
|
bmcgo/functional/csr_build.py
CHANGED
|
@@ -35,6 +35,7 @@ from bmcgo import errors
|
|
|
35
35
|
from bmcgo.misc import CommandInfo
|
|
36
36
|
from bmcgo.utils.tools import Tools
|
|
37
37
|
from bmcgo.utils.buffer import Buffer
|
|
38
|
+
from bmcgo.utils.merge_csr import Merger
|
|
38
39
|
from bmcgo.bmcgo_config import BmcgoConfig
|
|
39
40
|
from bmcgo.functional.simple_sign import BmcgoCommand as SimpleSign
|
|
40
41
|
|
|
@@ -45,7 +46,7 @@ cwd = os.getcwd()
|
|
|
45
46
|
|
|
46
47
|
SR_UPGRADE = "SRUpgrade"
|
|
47
48
|
TIANCHI = "bmc.dev.Board.TianChi"
|
|
48
|
-
HPM_PACK_PATH = "/usr/share/
|
|
49
|
+
HPM_PACK_PATH = "/usr/share/bingo/csr_packet"
|
|
49
50
|
EEPROM_SIZE_LIMIT_CONFIG = "/usr/share/bmcgo/schema/eepromSizeLimit.json"
|
|
50
51
|
|
|
51
52
|
JSON_DATA_FORMAT = 0x01
|
|
@@ -205,6 +206,8 @@ class BmcgoCommand:
|
|
|
205
206
|
self.target_dir = None
|
|
206
207
|
self.eeprom_sign_strategy = None
|
|
207
208
|
self.hpm_sign_strategy = None
|
|
209
|
+
self.tmp_dir = None
|
|
210
|
+
self.merger = Merger(self.output_path)
|
|
208
211
|
|
|
209
212
|
@staticmethod
|
|
210
213
|
def get_oem_data(dir_path: str, comp_name: str):
|
|
@@ -294,6 +297,9 @@ class BmcgoCommand:
|
|
|
294
297
|
|
|
295
298
|
def run(self):
|
|
296
299
|
self.check_args()
|
|
300
|
+
tmp = tempfile.TemporaryDirectory()
|
|
301
|
+
self.tmp_dir = tmp.name
|
|
302
|
+
self.merger.update_tmp_dir(self.tmp_dir)
|
|
297
303
|
if not os.path.exists(self.output_path):
|
|
298
304
|
raise Exception(f"输出路径{self.output_path}不存在")
|
|
299
305
|
sr_make_options_list = []
|
|
@@ -338,6 +344,8 @@ class BmcgoCommand:
|
|
|
338
344
|
finally:
|
|
339
345
|
if os.path.exists(self.work_dir):
|
|
340
346
|
shutil.rmtree(self.work_dir)
|
|
347
|
+
if os.path.exists(self.tmp_dir):
|
|
348
|
+
shutil.rmtree(self.tmp_dir)
|
|
341
349
|
|
|
342
350
|
def check_args(self):
|
|
343
351
|
has_single_need_arg = self.bin or self.json or self.hpm or self.all
|
|
@@ -351,7 +359,7 @@ class BmcgoCommand:
|
|
|
351
359
|
|
|
352
360
|
def get_sr_file_and_oem(self):
|
|
353
361
|
sr_make_options_list = []
|
|
354
|
-
csr_path = self.csr_path
|
|
362
|
+
csr_path = self.merger.get_single_csr(self.csr_path)
|
|
355
363
|
oem_path = self.oem_path
|
|
356
364
|
oem_data = bytearray()
|
|
357
365
|
if not os.path.exists(csr_path):
|
|
@@ -382,7 +390,7 @@ class BmcgoCommand:
|
|
|
382
390
|
|
|
383
391
|
def get_sr_files(self):
|
|
384
392
|
dir_path = Path(self.csr_path)
|
|
385
|
-
sr_files =
|
|
393
|
+
sr_files = self.merger.get_multi_files(dir_path)
|
|
386
394
|
sr_num = len(sr_files)
|
|
387
395
|
if sr_num == 1:
|
|
388
396
|
log.info("开始执行CSR出包任务...")
|
bmcgo/functional/diff.py
CHANGED
|
@@ -105,7 +105,7 @@ class BmcgoCommand:
|
|
|
105
105
|
tempfile = NamedTemporaryFile()
|
|
106
106
|
# 过滤只包含scm的信息, 并将其生成为字典对象
|
|
107
107
|
ret = tools.run_command(f"conan info {version} --json {tempfile.name} \
|
|
108
|
-
-r {misc.
|
|
108
|
+
-r {misc.conan_remote()}", ignore_error=True, command_echo=False, capture_output=True)
|
|
109
109
|
file_handler = open(tempfile.name, "r")
|
|
110
110
|
conan_comps = json.load(file_handler)
|
|
111
111
|
version_msg = ""
|
bmcgo/functional/fetch.py
CHANGED
|
@@ -163,7 +163,7 @@ framework、bmc_core、security、hardware、ras、energy、om、interface、pro
|
|
|
163
163
|
stage = self.stage
|
|
164
164
|
if stage != misc.StageEnum.STAGE_STABLE.value:
|
|
165
165
|
stage = misc.StageEnum.STAGE_RC.value
|
|
166
|
-
user_channel = f"@{
|
|
166
|
+
user_channel = f"@{misc.conan_user()}/{stage}"
|
|
167
167
|
com_package[index] += user_channel
|
|
168
168
|
|
|
169
169
|
def __load_config_json(self, stage):
|
|
@@ -23,8 +23,6 @@ import functools
|
|
|
23
23
|
import random
|
|
24
24
|
from concurrent.futures import ProcessPoolExecutor, Future
|
|
25
25
|
from typing import List
|
|
26
|
-
|
|
27
|
-
from conans.model.manifest import FileTreeManifest
|
|
28
26
|
import yaml
|
|
29
27
|
|
|
30
28
|
from bmcgo import misc
|
|
@@ -71,7 +69,7 @@ class BuildComponent:
|
|
|
71
69
|
build_type: str,
|
|
72
70
|
options: dict,
|
|
73
71
|
code_path: str,
|
|
74
|
-
remote=misc.
|
|
72
|
+
remote=misc.conan_remote(),
|
|
75
73
|
service_json="mds/service.json",
|
|
76
74
|
upload=False,
|
|
77
75
|
):
|
|
@@ -90,12 +88,15 @@ class BuildComponent:
|
|
|
90
88
|
def run(self):
|
|
91
89
|
os.chdir(os.path.join(self.code_path, self.comp_name))
|
|
92
90
|
for key, value in self.options.items():
|
|
93
|
-
|
|
91
|
+
if misc.conan_v2():
|
|
92
|
+
self.option_cmd = self.option_cmd + f" -o {self.comp_name}/*:{key}={value}"
|
|
93
|
+
else:
|
|
94
|
+
self.option_cmd = self.option_cmd + f" -o {self.comp_name}:{key}={value}"
|
|
94
95
|
command = (
|
|
95
96
|
f"--remote {self.remote} -nc --stage {self.stage} --build_type {self.build_type.lower()}"
|
|
96
97
|
f" --profile {self.profile} {self.option_cmd}"
|
|
97
98
|
)
|
|
98
|
-
log.info(f"执行构建命令
|
|
99
|
+
log.info(f"执行构建命令{misc.tool_name()} build {command}")
|
|
99
100
|
args = command.split()
|
|
100
101
|
build = BuildComp(self.bconfig, args, service_json=self.service_json)
|
|
101
102
|
# 恢复工作区为clean状态, 保证conan export时scm信息准确
|
|
@@ -112,7 +113,7 @@ class BmcgoCommand:
|
|
|
112
113
|
def __init__(self, bconfig: BmcgoConfig, *args):
|
|
113
114
|
parser = argparse.ArgumentParser(description="Fetch component source code and build all binaries.")
|
|
114
115
|
parser.add_argument("-comp", "--component", help="软件包名, 示例: oms/1.2.6", required=True)
|
|
115
|
-
parser.add_argument("-r", "--remote", help="远端仓库名称", default=misc.
|
|
116
|
+
parser.add_argument("-r", "--remote", help="远端仓库名称", default=misc.conan_remote())
|
|
116
117
|
parser.add_argument(
|
|
117
118
|
"-p",
|
|
118
119
|
"--path",
|
|
@@ -136,9 +137,11 @@ class BmcgoCommand:
|
|
|
136
137
|
self.upload = parsed_args.upload
|
|
137
138
|
self.config_file = parsed_args.config_file
|
|
138
139
|
self.skip_fetch = parsed_args.skip_fetch
|
|
139
|
-
self.profile_list = ["profile.dt.ini"
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
self.profile_list = ["profile.dt.ini"]
|
|
141
|
+
if misc.conan_v1():
|
|
142
|
+
self.profile_list.append("profile.luajit.ini")
|
|
143
|
+
self.stage_list = [misc.StageEnum.STAGE_STABLE]
|
|
144
|
+
self.built_type_list = [misc.BuildTypeEnum.DEBUG, misc.BuildTypeEnum.RELEASE]
|
|
142
145
|
if parsed_args.build_type:
|
|
143
146
|
self.built_type_list = parsed_args.build_type
|
|
144
147
|
self.options_dict = {}
|
|
@@ -185,7 +188,7 @@ class BmcgoCommand:
|
|
|
185
188
|
|
|
186
189
|
# 添加module_symver选项
|
|
187
190
|
module_symvers_path = os.path.join(SDK_PATH, MODULE_SYMVERS)
|
|
188
|
-
module_symver_key, module_symver_value = tool.
|
|
191
|
+
module_symver_key, module_symver_value = tool.get_171x_module_symver_option(module_symvers_path)
|
|
189
192
|
if module_symver_key in options_dict:
|
|
190
193
|
options_dict[module_symver_key] = [module_symver_value]
|
|
191
194
|
if config_file:
|
|
@@ -237,24 +240,28 @@ class BmcgoCommand:
|
|
|
237
240
|
@staticmethod
|
|
238
241
|
def _replace_dev_version(temp_service_json: str):
|
|
239
242
|
version_parse = ComponentDtVersionParse(serv_file=temp_service_json)
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
if misc.conan_v1():
|
|
244
|
+
for pkg in version_parse.conan_list:
|
|
245
|
+
component = pkg[misc.CONAN]
|
|
246
|
+
if "@" not in component:
|
|
247
|
+
continue
|
|
248
|
+
comp_version, user_channel = component.split("@")
|
|
249
|
+
if user_channel.endswith(STAGE_DEV):
|
|
250
|
+
pkg[misc.CONAN] = f"{comp_version}{ComponentHelper.get_user_channel(STAGE_DEV)}"
|
|
248
251
|
version_parse.write_to_serv_file()
|
|
249
252
|
|
|
250
253
|
@functools.cached_property
|
|
251
254
|
def _full_reference(self) -> str:
|
|
252
|
-
|
|
255
|
+
if misc.conan_v1():
|
|
256
|
+
comp_pkg = self.comp if "@" in self.comp else f"{self.comp}@{CONAN_USER}.release/{STAGE_STABLE}"
|
|
257
|
+
else:
|
|
258
|
+
comp_pkg = self.comp if "@" in self.comp else f"{self.comp}@{misc.conan_user()}/{STAGE_STABLE}"
|
|
253
259
|
return comp_pkg
|
|
254
260
|
|
|
255
261
|
@functools.cached_property
|
|
256
262
|
def _is_self_developed(self) -> bool:
|
|
257
|
-
|
|
263
|
+
args = "--only-recipe" if misc.conan_v2() else "--recipe"
|
|
264
|
+
tool.run_command(f"conan download {self._full_reference} {args} -r {self.remote}")
|
|
258
265
|
comp_package_path = os.path.join(tool.conan_data, self.comp_name)
|
|
259
266
|
ret = tool.run_command(f"find {comp_package_path} -name 'conanbase.py'", capture_output=True)
|
|
260
267
|
return bool(ret.stdout)
|
|
@@ -348,10 +355,11 @@ class BmcgoCommand:
|
|
|
348
355
|
random.shuffle(all_build_params)
|
|
349
356
|
for build_args in all_build_params:
|
|
350
357
|
profile, stage, build_type, *option_values = build_args
|
|
351
|
-
if
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
358
|
+
if misc.conan_v1():
|
|
359
|
+
if profile == "profile.dt.ini" and build_type.lower() != BUILD_TYPE_DT.lower():
|
|
360
|
+
continue
|
|
361
|
+
if profile != "profile.dt.ini" and build_type.lower() == BUILD_TYPE_DT.lower():
|
|
362
|
+
continue
|
|
355
363
|
build_options = dict(zip(options_dict.keys(), option_values))
|
|
356
364
|
task = BuildComponent(
|
|
357
365
|
self.comp,
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
4
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
5
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
6
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
7
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
8
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
9
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
10
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
11
|
+
# See the Mulan PSL v2 for more details.
|
|
12
|
+
import argparse
|
|
13
|
+
import datetime
|
|
14
|
+
import json
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
import sys
|
|
18
|
+
from datetime import timezone, timedelta
|
|
19
|
+
from typing import List, Tuple, Dict, Optional, Any
|
|
20
|
+
from git import Repo, Commit, Diff
|
|
21
|
+
from git.exc import InvalidGitRepositoryError, GitCommandError
|
|
22
|
+
from bmcgo import misc
|
|
23
|
+
from bmcgo.utils.tools import Tools
|
|
24
|
+
from bmcgo.bmcgo_config import BmcgoConfig
|
|
25
|
+
from bmcgo import errors
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
tools = Tools()
|
|
29
|
+
log = tools.log
|
|
30
|
+
beijing_timezone = timezone(timedelta(hours=8))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
command_info: misc.CommandInfo = misc.CommandInfo(
|
|
34
|
+
group=misc.GRP_COMP,
|
|
35
|
+
name="git_history",
|
|
36
|
+
description=["获取指定开始时间与结束时间之间提交信息,并生成release note"],
|
|
37
|
+
hidden=False
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def if_available(bconfig: BmcgoConfig):
|
|
42
|
+
if bconfig.component is None:
|
|
43
|
+
return False
|
|
44
|
+
return True
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class BmcgoCommand:
|
|
48
|
+
def __init__(self, bconfig: BmcgoConfig, *args):
|
|
49
|
+
self.bconfig = bconfig
|
|
50
|
+
parser = argparse.ArgumentParser(prog="bmcgo git_history", description="生成Git仓库的Release Note", add_help=True,
|
|
51
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
52
|
+
parser.add_argument("--start_date", "-s", help="起始日期YYYY-MM-DD", required=True)
|
|
53
|
+
parser.add_argument("--end_date", "-e", help="结束日期YYYY-MM-DD,默认为当前日期")
|
|
54
|
+
args, kwargs = parser.parse_known_args(*args)
|
|
55
|
+
self.start_date = args.start_date
|
|
56
|
+
self.end_date = args.end_date
|
|
57
|
+
self.output_file = f"./{self.start_date}_{self.end_date}_releaseNote.log"
|
|
58
|
+
self.service_json_path = "mds/service.json"
|
|
59
|
+
self.repo = Repo(".")
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def validate_date(date_str: str) -> datetime.datetime:
|
|
63
|
+
try:
|
|
64
|
+
date = datetime.datetime.strptime(date_str, "%Y-%m-%d")
|
|
65
|
+
except ValueError as e:
|
|
66
|
+
raise errors.BmcGoException(f"日期格式不正确: {date_str},请使用YYYY-MM-DD格式,错误信息{e}")
|
|
67
|
+
current_date = datetime.datetime.now(beijing_timezone)
|
|
68
|
+
if date.date() > current_date.date():
|
|
69
|
+
raise errors.BmcGoException(f"日期 {date_str} 超过当前日期")
|
|
70
|
+
return date
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def extract_version_from_diff(self, diff: Diff) -> Optional[str]:
|
|
74
|
+
try:
|
|
75
|
+
diff_text = diff.diff.decode('utf-8') if diff.diff else str(diff)
|
|
76
|
+
version_pattern = r'[-+]\s*"version"\s*:\s*"([^"]+)"'
|
|
77
|
+
matches = re.findall(version_pattern, diff_text)
|
|
78
|
+
if matches:
|
|
79
|
+
return matches[-1]
|
|
80
|
+
except (UnicodeDecodeError, AttributeError):
|
|
81
|
+
pass
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def is_merge_into_main(s):
|
|
86
|
+
pattern = r'^merge .+ into main$'
|
|
87
|
+
# 使用re.match进行匹配,忽略大小写
|
|
88
|
+
match = re.match(pattern, s, re.IGNORECASE)
|
|
89
|
+
return match is not None
|
|
90
|
+
|
|
91
|
+
def get_current_version(self) -> str:
|
|
92
|
+
try:
|
|
93
|
+
with open(self.service_json_path, 'r') as f:
|
|
94
|
+
service_data = json.load(f)
|
|
95
|
+
return service_data.get('version', 'unknown')
|
|
96
|
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
97
|
+
raise errors.BmcGoException(f"无法读取或解析 {self.service_json_path}: {e}")
|
|
98
|
+
|
|
99
|
+
def get_commits_in_range(self, start_date: str, end_date: str) -> List[Commit]:
|
|
100
|
+
start_dt = self.validate_date(start_date)
|
|
101
|
+
if end_date is None:
|
|
102
|
+
end_dt = datetime.datetime.now(beijing_timezone)
|
|
103
|
+
else:
|
|
104
|
+
try:
|
|
105
|
+
end_dt = self.validate_date(end_date)
|
|
106
|
+
except errors.BmcGoException as e:
|
|
107
|
+
if "超过当前日期" in str(e):
|
|
108
|
+
log.warning(f"警告: {e},将使用当前日期作为结束日期")
|
|
109
|
+
end_dt = datetime.datetime.now(beijing_timezone).replace(tzinfo=None)
|
|
110
|
+
else:
|
|
111
|
+
raise
|
|
112
|
+
if end_dt < start_dt:
|
|
113
|
+
raise errors.BmcGoException("结束日期不能早于起始日期")
|
|
114
|
+
since_str = start_dt.strftime("%Y-%m-%d")
|
|
115
|
+
until_str = end_dt.strftime("%Y-%m-%d")
|
|
116
|
+
try:
|
|
117
|
+
commits = list(self.repo.iter_commits(
|
|
118
|
+
since=since_str,
|
|
119
|
+
until=until_str
|
|
120
|
+
))
|
|
121
|
+
except GitCommandError as e:
|
|
122
|
+
raise errors.BmcGoException(f"获取commit记录时出错: {e}")
|
|
123
|
+
return commits
|
|
124
|
+
|
|
125
|
+
def get_version_from_commit(self, commit_hash: str) -> Optional[str]:
|
|
126
|
+
try:
|
|
127
|
+
commit = self.repo.commit(commit_hash)
|
|
128
|
+
try:
|
|
129
|
+
file_content = (commit.tree / self.service_json_path).data_stream.read().decode('utf-8')
|
|
130
|
+
data = json.loads(file_content)
|
|
131
|
+
version = data.get('version')
|
|
132
|
+
return version
|
|
133
|
+
except (KeyError, AttributeError):
|
|
134
|
+
log.error(f"在commit {commit_hash} 中找不到文件: {self.service_json_path}")
|
|
135
|
+
return None
|
|
136
|
+
except json.JSONDecodeError:
|
|
137
|
+
log.error(f"在commit {commit_hash} 中的 {self.service_json_path} 文件不是有效的JSON格式")
|
|
138
|
+
return None
|
|
139
|
+
except Exception as e:
|
|
140
|
+
log.error(f"获取commit {commit_hash} 的版本信息时出错: {e}")
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def get_version_info_for_commits(self, commits: List[Commit]) -> Dict[str, Any]:
|
|
144
|
+
version_info = {
|
|
145
|
+
'current_version': self.get_current_version(),
|
|
146
|
+
'commit_versions': {}
|
|
147
|
+
}
|
|
148
|
+
# 按时间顺序处理commit(从旧到新)
|
|
149
|
+
for commit in reversed(commits):
|
|
150
|
+
try:
|
|
151
|
+
if not self.is_merge_into_main(str(commit.summary)):
|
|
152
|
+
continue
|
|
153
|
+
modified_version = self.get_version_from_commit(commit.hexsha)
|
|
154
|
+
message = commit.message.strip()
|
|
155
|
+
lines = message.splitlines()
|
|
156
|
+
if len(lines) >= 3:
|
|
157
|
+
message = lines[2]
|
|
158
|
+
version_info['commit_versions'][commit.hexsha] = {
|
|
159
|
+
'date': commit.committed_datetime.strftime("%Y-%m-%d"),
|
|
160
|
+
'message': message,
|
|
161
|
+
'version': modified_version if modified_version else version_info['current_version']
|
|
162
|
+
}
|
|
163
|
+
if modified_version:
|
|
164
|
+
version_info['current_version'] = modified_version
|
|
165
|
+
except Exception as e:
|
|
166
|
+
log.error(f"处理commit {commit.hexsha} 时出错: {e}")
|
|
167
|
+
message = commit.message.strip()
|
|
168
|
+
lines = message.splitlines()
|
|
169
|
+
if len(lines) >= 3:
|
|
170
|
+
message = lines[2]
|
|
171
|
+
version_info['commit_versions'][commit.hexsha] = {
|
|
172
|
+
'date': commit.committed_datetime.strftime("%Y-%m-%d"),
|
|
173
|
+
'message': message,
|
|
174
|
+
'version': version_info['current_version']
|
|
175
|
+
}
|
|
176
|
+
return version_info
|
|
177
|
+
|
|
178
|
+
def generate_release_note(self,
|
|
179
|
+
start_date: str,
|
|
180
|
+
end_date: str,
|
|
181
|
+
output_file: str) -> str:
|
|
182
|
+
release_note = f"从{start_date}至{end_date}特性提交如下:\n"
|
|
183
|
+
commits = self.get_commits_in_range(start_date, end_date)
|
|
184
|
+
if not commits:
|
|
185
|
+
try:
|
|
186
|
+
release_note += f"无更新\n"
|
|
187
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
188
|
+
f.write(release_note)
|
|
189
|
+
log.info(f"Release Note已保存到: {output_file}")
|
|
190
|
+
except IOError as e:
|
|
191
|
+
log.error(f"保存文件时出错: {e}")
|
|
192
|
+
version_info = self.get_version_info_for_commits(commits)
|
|
193
|
+
|
|
194
|
+
for commit in commits:
|
|
195
|
+
commit_info = version_info['commit_versions'].get(commit.hexsha, {})
|
|
196
|
+
if len(commit_info) == 0:
|
|
197
|
+
continue
|
|
198
|
+
release_note += f"-commit ID: {commit.hexsha}\n"
|
|
199
|
+
release_note += f"-修改描述: {commit_info.get('message')}\n"
|
|
200
|
+
release_note += f"-版本号: {commit_info.get('version')}\n"
|
|
201
|
+
release_note += f"-发布日期:{commit_info.get('date')}\n\n"
|
|
202
|
+
if output_file:
|
|
203
|
+
try:
|
|
204
|
+
with open(output_file, 'w', encoding='utf-8') as f:
|
|
205
|
+
f.write(release_note)
|
|
206
|
+
log.info(release_note)
|
|
207
|
+
log.info(f"Release Note已保存到: {output_file}")
|
|
208
|
+
except IOError as e:
|
|
209
|
+
log.error(f"保存文件时出错: {e}")
|
|
210
|
+
return release_note
|
|
211
|
+
|
|
212
|
+
def run(self):
|
|
213
|
+
try:
|
|
214
|
+
_ = self.generate_release_note(
|
|
215
|
+
start_date=self.start_date,
|
|
216
|
+
end_date=self.end_date,
|
|
217
|
+
output_file=self.output_file
|
|
218
|
+
)
|
|
219
|
+
except (ValueError, RuntimeError) as e:
|
|
220
|
+
log.error(f"错误: {e}")
|