From 628b9e264151fee069605f91dfe64ec697f4438b Mon Sep 17 00:00:00 2001 From: dennis <488132230@qq.com> Date: Mon, 4 Aug 2025 21:22:05 +0800 Subject: [PATCH 01/27] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=BA=90=E9=85=8D=E7=BD=AE=E5=92=8Crosdep=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E5=AE=89=E8=A3=85=E5=B7=A5=E5=85=B7=E7=BD=91=E7=BB=9C?= =?UTF-8?q?=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 增强rosdep工具: - 添加pip源选择功能功能 2. 改进系统源配置工具: - 添加源选择方式说明 - 提供两种源选择方法(自动选择最快源/手动选择源) - 扩展get_source_by_system函数支持返回所有源 这些改进提高了安装脚本的用户体验和网络适应性,特别是在网络条件不稳定的环境中。 --- tools/tool_config_rosdep.py | 76 +++++++++++++- tools/tool_config_system_source.py | 154 +++++++++++++++++++++++++++-- 2 files changed, 218 insertions(+), 12 deletions(-) diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index 7216dfe..e0e357c 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +import time +import http.client +from urllib.parse import urlparse from .base import BaseTool from .base import PrintUtils,CmdTask,FileUtils,AptUtils,ChooseTask from .base import osversion @@ -9,13 +12,78 @@ def __init__(self): self.type = BaseTool.TYPE_CONFIG self.name = "模板工程" self.author = '小鱼' + + def test_source_speed(self, url): + """测试源的速度 + + Args: + url (str): 源的URL + + Returns: + float: 响应时间(秒) + """ + try: + start_time = time.time() + parsed_url = urlparse(url) + conn = http.client.HTTPSConnection(parsed_url.netloc, timeout=5) + conn.request("HEAD", parsed_url.path) + resp = conn.getresponse() + conn.close() + end_time = time.time() + return end_time - start_time + except Exception as e: + PrintUtils.print_error(f"测试源 {url} 失败: {str(e)}") + return float('inf') # 返回无穷大表示连接失败 + + def choose_pip_source(self): + """选择pip源 + + Returns: + str: 选择的源URL + """ + sources = { + "清华源": "https://pypi.tuna.tsinghua.edu.cn/simple", + "阿里云": "https://mirrors.aliyun.com/pypi/simple", + "中国科技大学": "https://pypi.mirrors.ustc.edu.cn/simple", + "华为云": "https://repo.huaweicloud.com/repository/pypi/simple" + } + + # 构建选择字典 + choose_dict = {} + i = 1 + for name, url in sources.items(): + choose_dict[i] = f"{name} - {url}" + i += 1 + + PrintUtils.print_info("请选择要使用的pip源:") + choose_index, choose_content = ChooseTask(choose_dict, "请选择pip源:").run() + + if choose_index == 0: + PrintUtils.print_info("未选择源,默认使用中国科技大学源") + return sources["中国科技大学"] + + try: + # choose_index 已经是整数,直接使用 + selected_name = list(sources.keys())[choose_index-1] + return sources[selected_name] + except (IndexError) as e: + PrintUtils.print_error(f"选择源时出错: {str(e)},使用中国科技大学源") + return sources["中国科技大学"] def install_rosdepc(self): CmdTask("sudo apt install python3-pip -y", 0).run() - cmd_ret = CmdTask("sudo pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple rosdepc").run() + + # 选择源 + selected_source = self.choose_pip_source() + PrintUtils.print_success(f"您选择了: {selected_source}") + + # 直接使用 --break-system-packages 参数安装,避免第一次安装失败 + PrintUtils.print_info(f"正在使用 {selected_source} 安装 rosdepc...") + cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc --break-system-packages").run() if cmd_ret[0]!=0: - # fix: https://fishros.org.cn/forum/topic/2981 - cmd_ret = CmdTask("sudo pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple rosdepc --break-system-packages").run() + # 如果安装失败,尝试不带参数安装 + PrintUtils.print_warning("安装失败,尝试使用其他方式安装...") + cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc").run() CmdTask("sudo rosdepc init", 0).run() CmdTask("sudo rosdepc fix-permissions", 0).run() PrintUtils.print_info('已为您安装好rosdepc,请使用:\nrosdepc update \n进行测试更新,最后欢迎关注微信公众号《鱼香ROS》') @@ -23,4 +91,4 @@ def install_rosdepc(self): def run(self): #正式的运行 - self.install_rosdepc() + self.install_rosdepc() \ No newline at end of file diff --git a/tools/tool_config_system_source.py b/tools/tool_config_system_source.py index df618f5..b9ba0a4 100644 --- a/tools/tool_config_system_source.py +++ b/tools/tool_config_system_source.py @@ -79,10 +79,18 @@ def clean_old_source(self): FileUtils.delete('/etc/apt/sources.list.d') # fix add source failed before config system source CmdTask('sudo mkdir -p /etc/apt/sources.list.d').run() + + # 添加选择源的方式 + PrintUtils.print_info("源选择方式说明:") + PrintUtils.print_info("1. 自动测速选择最快的源: 系统将自动测试各个源的速度,并选择最快的源") + PrintUtils.print_info("2. 根据测速结果手动选择源: 系统将测试各个源的速度,然后让您从测试结果中选择") + + dic_source_method = {1:"自动测速选择最快的源", 2:"根据测速结果手动选择源"} + self.source_method_code, _ = ChooseTask(dic_source_method, "请选择源的选择方式").run() - def get_source_by_system(self,system,codename,arch,failed_sources=[]): + def get_source_by_system(self,system,codename,arch,failed_sources=[], return_all=False): # 实际测试发现,阿里云虽然延时很低,但是带宽也低的离谱,一点都不用心,删掉了 ubuntu_amd64_sources = [ "https://mirrors.tuna.tsinghua.edu.cn/ubuntu", @@ -143,6 +151,15 @@ def get_source_by_system(self,system,codename,arch,failed_sources=[]): PrintUtils.print_delay('接下来将进行自动测速以为您选择最快的源:') fast_source = AptUtils.get_fast_url(sources) + # 如果需要返回所有源和模板(不进行测速) + if return_all: + # 直接返回源列表和模板 + if len(failed_sources) > 0: + filtered_sources = [source for source in sources if source not in failed_sources] + return filtered_sources, template + return sources, template + + # 正常返回最快的源 if len(failed_sources)>0: PrintUtils.print_warn('接下来为您排除已经失败的源') for source in fast_source: @@ -155,6 +172,77 @@ def get_source_by_system(self,system,codename,arch,failed_sources=[]): return None,None + + + for source in sources: + if "tsinghua" in source: + tsinghua_sources.append(source) + elif "ustc" in source: + ustc_sources.append(source) + elif "archive.ubuntu" in source or "deb.debian" in source: + official_sources.append(source) + elif "kernel" in source: + kernel_sources.append(source) + elif "aliyun" in source: + aliyun_sources.append(source) + else: + other_sources.append(source) + + # 添加到选择字典 + index = 1 + + # 添加清华源 + if tsinghua_sources: + for source in tsinghua_sources: + source_dict[index] = source + index += 1 + + # 添加中科大源 + if ustc_sources: + for source in ustc_sources: + source_dict[index] = source + index += 1 + + # 添加阿里云源 + if aliyun_sources: + for source in aliyun_sources: + source_dict[index] = source + index += 1 + + # 添加官方源 + if official_sources: + for source in official_sources: + source_dict[index] = source + index += 1 + + # 添加kernel源 + if kernel_sources: + for source in kernel_sources: + source_dict[index] = source + index += 1 + + # 添加其他源 + if other_sources: + for source in other_sources: + source_dict[index] = source + index += 1 + + PrintUtils.print_info("请选择您想使用的镜像源:") + code, source = ChooseTask(source_dict, "请选择一个镜像源").run() + + if not source: + return None, None + + return source, template + + + + # 去除末尾的斜杠 + if source.endswith("/"): + source = source[:-1] + + return source, template + def replace_source(self,failed_sources=[]): arch = AptUtils.getArch() name = osversion.get_name() @@ -165,10 +253,44 @@ def replace_source(self,failed_sources=[]): system = 'debian' else: return None + PrintUtils.print_delay('检测到当前系统:{} 架构:{} 代号:{},正在为你搜索适合的源...'.format(system,arch,codename)) - source,template = self.get_source_by_system(system,codename,arch,failed_sources) + + # 根据用户选择的方式获取源 + if hasattr(self, 'source_method_code'): + if self.source_method_code == 2: + # 根据测速结果手动选择源 + sorted_sources, template = self.get_source_by_system(system, codename, arch, failed_sources, return_all=True) + if not sorted_sources: + return None + + # 创建选择字典 + source_dict = {} + for i, src in enumerate(sorted_sources, 1): + source_dict[i] = src + + PrintUtils.print_info("请从测速结果中选择您想使用的镜像源:") + code, source = ChooseTask(source_dict, "请选择一个镜像源").run() + + if not source: + return None + else: + # 自动测速选择源 + source, template = self.get_source_by_system(system, codename, arch, failed_sources) + else: + # 自动测速选择源 + source, template = self.get_source_by_system(system, codename, arch, failed_sources) + if not source: return None - PrintUtils.print_success('为您选择最快镜像源:{}'.format(source)) + + if hasattr(self, 'source_method_code'): + if self.source_method_code == 2: + PrintUtils.print_success('您选择的镜像源:{}'.format(source)) + else: + PrintUtils.print_success('为您选择最快镜像源:{}'.format(source)) + else: + PrintUtils.print_success('为您选择最快镜像源:{}'.format(source)) + FileUtils.new('/etc/apt/','sources.list',template.replace("",codename).replace('',source)) return source @@ -181,11 +303,27 @@ def change_sys_source(self): if source: PrintUtils.print_delay("替换镜像源完成,尝试进行更新....") result = CmdTask('sudo apt update',100).run() - while result[0]!= 0: - failed_sources.append(source) - PrintUtils.print_warn("更新失败,尝试更换其他源") - source = self.replace_source(failed_sources) - result = CmdTask('sudo apt update',100).run() + + # 如果是手动选择源且更新失败,提示用户重新选择 + if result[0] != 0 and hasattr(self, 'source_method_code') and self.source_method_code == 2: + PrintUtils.print_warn("您选择的源更新失败,请重新选择其他源") + while result[0] != 0: + failed_sources.append(source) + source = self.replace_source(failed_sources) + if not source: + PrintUtils.print_error("没有找到合适的镜像源,臣妾告退!") + return + result = CmdTask('sudo apt update',100).run() + # 如果是自动测速选择源且更新失败,自动尝试其他源 + elif result[0] != 0: + while result[0] != 0: + failed_sources.append(source) + PrintUtils.print_warn("更新失败,尝试更换其他源") + source = self.replace_source(failed_sources) + if not source: + PrintUtils.print_error("没有找到合适的镜像源,臣妾告退!") + return + result = CmdTask('sudo apt update',100).run() else: PrintUtils.print_error("没有找到合适的镜像源,臣妾告退!") From d1a9315fefacb50ac660e2d20a3100aabbc51fd8 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Fri, 29 Aug 2025 13:27:05 +0800 Subject: [PATCH 02/27] =?UTF-8?q?feat(tools):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E4=B8=AD=E7=A7=91=E5=A4=A7=20ROS=20=E9=95=9C?= =?UTF-8?q?=E5=83=8F=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 base.py 中的文件写入逻辑,使用临时文件和 sudo 权限 - 优化 tool_install_ros.py 中的ROS镜像源选择逻辑,让用户可以自行选择ROS镜像源 --- tools/base.py | 51 ++++++++- tools/tool_install_ros.py | 231 +++++++++++++++++++++++++------------- 2 files changed, 200 insertions(+), 82 deletions(-) diff --git a/tools/base.py b/tools/base.py index bd2f357..ae4ef98 100644 --- a/tools/base.py +++ b/tools/base.py @@ -52,9 +52,49 @@ def gen_config_file(self): config_yaml['chooses'] = chooses config_yaml['time'] = str(time.time()) - with open("/tmp/fish_install.yaml", "w", encoding="utf-8") as f: - if have_yaml_module: - yaml.dump(config_yaml, f,allow_unicode=True) + # 先写入临时文件,再使用sudo移动到目标位置 + temp_path = "/tmp/fish_install_temp.yaml" + target_path = "/tmp/fish_install.yaml" + try: + with open(temp_path, "w", encoding="utf-8") as f: + if have_yaml_module: + yaml.dump(config_yaml, f, allow_unicode=True) + + # 检查目标文件是否存在 + if os.path.exists(target_path): + print("检测到已存在的配置文件: {}".format(target_path)) + user_input = input("是否替换该文件?[y/N]: ") + if user_input.lower() not in ['y', 'yes']: + print("取消替换,保留原配置文件") + os.remove(temp_path) # 删除临时文件 + return + + # 先尝试删除目标文件(如果存在),避免mv命令的交互提示 + if os.path.exists(target_path): + try: + os.remove(target_path) + except PermissionError: + # 如果普通权限无法删除,则使用sudo + print("使用sudo权限删除已存在的配置文件...") + os.system("sudo rm -f {}".format(target_path)) + + # 使用mv命令移动文件,避免权限问题 + result = os.system("mv {} {}".format(temp_path, target_path)) + if result == 0: + print("配置文件已保存至: {}".format(target_path)) + else: + # 如果普通权限移动失败,则尝试使用sudo + print("尝试使用sudo权限保存配置文件...") + result = os.system("sudo mv {} {}".format(temp_path, target_path)) + if result == 0: + print("配置文件已保存至: {} (使用sudo权限)".format(target_path)) + else: + print("配置文件保存失败") + except Exception as e: + print("配置文件生成过程中发生错误: {}".format(str(e))) + # 清理临时文件(如果存在) + if os.path.exists(temp_path): + os.remove(temp_path) def get_input_value(self): if self.default_input_queue.qsize()>0: @@ -1219,8 +1259,11 @@ def new(path,name=None,data=''): if not os.path.exists(path): CmdTask("sudo mkdir -p {}".format(path),3).run() if name!=None: - with open(path+name,"w") as f: + # 使用临时文件和sudo权限来创建受保护的文件 + temp_file = "/tmp/{}".format(name) + with open(temp_file, "w") as f: f.write(data) + CmdTask("sudo mv {} {}".format(temp_file, path+name), 3).run() return True @staticmethod diff --git a/tools/tool_install_ros.py b/tools/tool_install_ros.py index 2fa6376..a008942 100644 --- a/tools/tool_install_ros.py +++ b/tools/tool_install_ros.py @@ -86,6 +86,7 @@ def get_desktop_version(name): ros_mirror_dic = { "tsinghua":{"ROS1":"http://mirrors.tuna.tsinghua.edu.cn/ros/ubuntu/","ROS2":"http://mirrors.tuna.tsinghua.edu.cn/ros2/ubuntu/"}, + "ustc":{"ROS1":"https://mirrors.ustc.edu.cn/ros/ubuntu/","ROS2":"https://mirrors.ustc.edu.cn/ros2/ubuntu/"}, "huawei":{"ROS1":"https://repo.huaweicloud.com/ros/ubuntu/","ROS2":"https://repo.huaweicloud.com/ros2/ubuntu/"}, "packages.ros":{"ROS1":"http://packages.ros.org/ros/ubuntu/","ROS2":"http://packages.ros.org/ros2/ubuntu/"}, "https.packages.ros":{"ROS1":"https://packages.ros.org/ros/ubuntu/","ROS2":"https://packages.ros.org/ros2/ubuntu/"}, @@ -95,13 +96,13 @@ def get_desktop_version(name): ros_dist_dic = { 'artful':{"packages.ros"}, - 'bionic':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'bionic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'buster':{"packages.ros"}, 'cosmic':{"packages.ros"}, 'disco':{"packages.ros"}, 'eoan':{"packages.ros"}, - 'focal':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'jessie':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'focal':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'jessie':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'lucid':{"packages.ros"}, 'maverick':{"packages.ros"}, 'natty':{"packages.ros"}, @@ -110,32 +111,46 @@ def get_desktop_version(name): 'quantal':{"packages.ros"}, 'raring':{"packages.ros"}, 'saucy':{"packages.ros"}, - 'stretch':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'trusty':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'stretch':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'trusty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'utopic':{"packages.ros"}, 'vivid':{"packages.ros"}, 'wheezy':{"packages.ros"}, 'wily':{"packages.ros"}, - 'xenial':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'xenial':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'yakkety':{"packages.ros"}, 'zesty':{"packages.ros"}, + 'focal':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'jammy':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'kinetic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'lunar':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'melodic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'noble':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'trusty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'utopic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'xenial':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'yakkety':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'zesty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, } ros2_dist_dic = { - 'bionic':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'bullseye':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'bionic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'bullseye':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'buster':{"packages.ros"}, - 'cosmic':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'disco':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'eoan':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'focal':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'jessie':{"tsinghua","huawei"}, - 'jammy':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'noble':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'stretch':{"tsinghua","huawei","packages.ros","https.packages.ros"}, - 'trusty':{"tsinghua","huawei"}, - 'xenial':{"tsinghua","huawei","packages.ros","https.packages.ros"}, + 'cosmic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'disco':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'eoan':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'focal':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'jessie':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'jammy':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'noble':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'stretch':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'trusty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'utopic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'xenial':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'yakkety':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, + 'zesty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, } @@ -151,11 +166,11 @@ def get_mirror_by_code(self,code,arch='amd64',first_choose="tsinghua"): """ 获取镜像通过系统版本号 """ - ros1_choose_queue = [first_choose,"tsinghua","huawei","packages.ros"] - ros2_choose_queue = [first_choose,"tsinghua","huawei","packages.ros"] + ros1_choose_queue = [first_choose,"tsinghua","ustc","huawei","packages.ros"] + ros2_choose_queue = [first_choose,"tsinghua","ustc","huawei","packages.ros"] # armhf架构,优先使用官方源 - if arch=='armhf': ros2_choose_queue =["packages.ros","tsinghua","huawei"] + if arch=='armhf': ros2_choose_queue =["packages.ros","tsinghua","ustc","huawei"] mirror = [] # 确认源里有对应的系统的,比如jammy @@ -174,61 +189,62 @@ def get_mirror_by_code(self,code,arch='amd64',first_choose="tsinghua"): # mirror.append(ros_mirror_dic['packages.ros']['ROS2']) return mirror - - def add_key(self): - PrintUtils.print_success(tr.tr('============正在添加ROS源密钥=================')) - # check apt - if not AptUtils.checkapt(): - pass - # install dep - AptUtils.install_pkg('curl') - AptUtils.install_pkg('gnupg2') - - # add key - PrintUtils.print_success(tr.tr('正在挑选最快的密钥服务:{}').format(key_urls)) - key_url = AptUtils.get_fast_url(key_urls) - if not key_url: - PrintUtils.print_error(tr.tr("获取密钥失败")) - return - key_url = key_url[0] - PrintUtils.print_success(tr.tr('已自动选择最快密钥服务:{}').format(key_url)) - - cmd_result = CmdTask("curl -s {} | sudo apt-key add -".format(key_url)).run() - if cmd_result[0]!=0: - cmd_result = CmdTask("curl -s {} | sudo apt-key add -".format(key_url)).run() - # 针对近期密钥更新问题 - CmdTask("sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654",10).run() - if cmd_result[0]!=0: - PrintUtils.print_info(tr.tr("导入密钥失败,开始更换导入方式并二次尝试...")) - cmd_result = CmdTask("sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654",10).run() - - # 针对trusted.gpg.d问题解决方案 - if FileUtils.check_result(cmd_result,['trusted.gpg.d']): - cmd_result = CmdTask("curl -s {} | sudo gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/ros.gpg --import".format(key_url)).run() - cmd_result = CmdTask("sudo chmod 644 /etc/apt/trusted.gpg.d/ros.gpg",10).run() - - return cmd_result - - - def check_sys_source(self): - # 更换系统源 - dic = {1:"更换系统源再继续安装",2:"不更换继续安装"} - PrintUtils.print_warn("=========接下来这一步很很很很重要,如果不知道怎么选请选择1========") - code,result = ChooseTask(dic, "新手或首次安装一定要一定要一定要换源并清理三方源,换源!!!系统默认国外源容易失败!!").run() - if code==1: - tool = run_tool_file('tools.tool_config_system_source',authorun=False) - tool.change_sys_source() - - def get_all_instsll_ros_pkgs(self): - AptUtils.checkapt() - dic_base = AptUtils.search_package('ros-base','ros-[A-Za-z]+-ros-base',"ros-","-base") - if dic_base== None: return None - ros_name = {} - for a in dic_base.keys(): - ros_name[RosVersions.get_version_string(a)] = a - if len(ros_name) == 0: - return None - return ros_name + def select_mirror(self): + """ + 让用户选择镜像源 + """ + # 检查当前系统是否支持中科大镜像 + codename = osversion.get_codename() + supported_mirrors = [] + + if codename in ros_dist_dic.keys() or codename in ros2_dist_dic.keys(): + if "ustc" in ros_dist_dic.get(codename, []) or "ustc" in ros2_dist_dic.get(codename, []): + supported_mirrors.append("ustc") + + if codename in ros_dist_dic.keys() or codename in ros2_dist_dic.keys(): + if "tsinghua" in ros_dist_dic.get(codename, []) or "tsinghua" in ros2_dist_dic.get(codename, []): + supported_mirrors.append("tsinghua") + + if codename in ros_dist_dic.keys() or codename in ros2_dist_dic.keys(): + if "huawei" in ros_dist_dic.get(codename, []) or "huawei" in ros2_dist_dic.get(codename, []): + supported_mirrors.append("huawei") + + # 如果系统支持多个镜像源,则让用户选择 + if len(supported_mirrors) > 1: + mirror_dict = {} + count = 1 + for mirror in supported_mirrors: + if mirror == "ustc": + mirror_dict[count] = "中科大镜像源 (推荐国内用户使用)" + elif mirror == "tsinghua": + mirror_dict[count] = "清华镜像源 (容易被封禁)" + elif mirror == "huawei": + mirror_dict[count] = "华为镜像源" + count += 1 + + mirror_dict[count] = "ROS官方源 (国外用户或需要最新版本时使用)" + + code, result = ChooseTask(mirror_dict, "检测到您的系统支持多个ROS镜像源,请选择您想要使用的ROS镜像源(默认清华):").run() + if code == 0: + return "tsinghua" # 默认返回清华源 + elif code == count: + return "packages.ros" # 官方源 + else: + # 根据选择返回对应的镜像源 + for key, value in mirror_dict.items(): + if key == code: + if "中科大" in value: + return "ustc" + elif "清华" in value: + return "tsinghua" + elif "华为" in value: + return "huawei" + else: + # 系统只支持默认的清华源 + PrintUtils.print_info("您的系统默认使用清华镜像源") + return "tsinghua" + + return "tsinghua" def add_source(self): """ @@ -237,9 +253,13 @@ def add_source(self): arch = AptUtils.getArch() if arch==None: return False + # 让用户选择镜像源 + selected_mirror = self.select_mirror() + PrintUtils.print_info("您选择的镜像源: {}".format(selected_mirror)) + #add source 1 - mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch) - PrintUtils.print_info("根据您的系统,为您推荐安装源为{}".format(mirrors)) + mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch,first_choose=selected_mirror) + PrintUtils.print_info("根据您的系统和选择,为您推荐安装源为{}".format(mirrors)) source_data = '' for mirror in mirrors: source_data += 'deb [arch={}] {} {} main\n'.format(arch,mirror,osversion.get_codename()) @@ -297,6 +317,61 @@ def add_source(self): if not AptUtils.checkapt(): PrintUtils.print_error("四次换源后都失败了,请及时联系小鱼获取解决方案并处理!") + def add_key(self): + PrintUtils.print_success(tr.tr('============正在添加ROS源密钥=================')) + # check apt + if not AptUtils.checkapt(): + pass + # install dep + AptUtils.install_pkg('curl') + AptUtils.install_pkg('gnupg2') + + # add key + PrintUtils.print_success(tr.tr('正在挑选最快的密钥服务:{}').format(key_urls)) + key_url = AptUtils.get_fast_url(key_urls) + if not key_url: + PrintUtils.print_error(tr.tr("获取密钥失败")) + return + key_url = key_url[0] + PrintUtils.print_success(tr.tr('已自动选择最快密钥服务:{}').format(key_url)) + + cmd_result = CmdTask("curl -s {} | sudo apt-key add -".format(key_url)).run() + if cmd_result[0]!=0: + cmd_result = CmdTask("curl -s {} | sudo apt-key add -".format(key_url)).run() + # 针对近期密钥更新问题 + CmdTask("sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654",10).run() + if cmd_result[0]!=0: + PrintUtils.print_info(tr.tr("导入密钥失败,开始更换导入方式并二次尝试...")) + cmd_result = CmdTask("sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys F42ED6FBAB17C654",10).run() + + # 针对trusted.gpg.d问题解决方案 + if FileUtils.check_result(cmd_result,['trusted.gpg.d']): + cmd_result = CmdTask("curl -s {} | sudo gpg --no-default-keyring --keyring gnupg-ring:/etc/apt/trusted.gpg.d/ros.gpg --import".format(key_url)).run() + cmd_result = CmdTask("sudo chmod 644 /etc/apt/trusted.gpg.d/ros.gpg",10).run() + + return cmd_result + + + def check_sys_source(self): + # 更换系统源 + dic = {1:"更换系统源再继续安装",2:"不更换继续安装"} + PrintUtils.print_warn("=========接下来这一步很很很很重要,如果不知道怎么选请选择1========") + code,result = ChooseTask(dic, "新手或首次安装一定要一定要一定要换源并清理三方源,换源!!!系统默认国外源容易失败!!").run() + if code==1: + tool = run_tool_file('tools.tool_config_system_source',authorun=False) + tool.change_sys_source() + + def get_all_instsll_ros_pkgs(self): + AptUtils.checkapt() + dic_base = AptUtils.search_package('ros-base','ros-[A-Za-z]+-ros-base',"ros-","-base") + if dic_base== None: return None + ros_name = {} + for a in dic_base.keys(): + ros_name[RosVersions.get_version_string(a)] = a + if len(ros_name) == 0: + return None + return ros_name + def support_install(self): # check support if (osversion.get_codename() not in ros_dist_dic.keys()) and (osversion.get_codename() not in ros2_dist_dic.keys()): From ade9adb7886d733b53f802ebd943f55c77f51402 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Fri, 29 Aug 2025 13:32:33 +0800 Subject: [PATCH 03/27] =?UTF-8?q?refactor(tools):=20=E4=BC=98=E5=8C=96=20R?= =?UTF-8?q?OS=20=E6=BA=90=E6=B7=BB=E5=8A=A0=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除多次固定源添加尝试,改为让用户重新选择镜像源 - 在首次尝试失败后,提示用户重新选择镜像源进行尝试 - 如果重新选择的镜像源成功添加,继续进行后续操作 - 如果用户选择相同的镜像源且四次尝试均失败,提示联系小鱼获取解决方案 --- tools/tool_install_ros.py | 66 +++++++++++++-------------------------- 1 file changed, 21 insertions(+), 45 deletions(-) diff --git a/tools/tool_install_ros.py b/tools/tool_install_ros.py index a008942..9b8654a 100644 --- a/tools/tool_install_ros.py +++ b/tools/tool_install_ros.py @@ -271,51 +271,27 @@ def add_source(self): PrintUtils.print_success("恭喜,成功添加ROS源,接下来可以使用apt安装ROS或者使用[1]一键安装ROS安装!") return - #add source2 - PrintUtils.print_warn("换源后更新失败,第二次开始切换源,尝试更换ROS2源为华为源!") - mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch,first_choose="huawei") - PrintUtils.print_info("根据您的系统,为您推荐安装源为{}".format(mirrors)) - source_data = '' - for mirror in mirrors: - source_data += 'deb [arch={}] {} {} main\n'.format(arch,mirror,osversion.get_codename()) - FileUtils.delete('/etc/apt/sources.list.d/ros-fish.list') - FileUtils.new('/etc/apt/sources.list.d/',"ros-fish.list",source_data) - ros_pkg = self.get_all_instsll_ros_pkgs() - if ros_pkg and len(ros_pkg)>1: - PrintUtils.print_success("恭喜,成功添加ROS源,接下来可以使用apt安装ROS或者使用[1]一键安装ROS安装!") - return - - - PrintUtils.print_warn("换源后更新失败,第三次开始切换源,尝试使用https-ROS官方源~!") - mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch,first_choose="https.packages.ros") - PrintUtils.print_info("根据您的系统,为您推荐安装源为{}".format(mirrors)) - source_data = '' - for mirror in mirrors: - source_data += 'deb [arch={}] {} {} main\n'.format(arch,mirror,osversion.get_codename()) - FileUtils.delete('/etc/apt/sources.list.d/ros-fish.list') - FileUtils.new('/etc/apt/sources.list.d/',"ros-fish.list",source_data) - ros_pkg = self.get_all_instsll_ros_pkgs() - if ros_pkg and len(ros_pkg)>1: - PrintUtils.print_success("恭喜,成功添加ROS源,接下来可以使用apt安装ROS或者使用[1]一键安装ROS安装!") - return - - #add source2 - PrintUtils.print_warn("换源后更新失败,第四次开始切换源,尝试更换ROS源为http-ROS官方源!") - mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch,first_choose="packages.ros") - PrintUtils.print_info("根据您的系统,为您推荐安装源为{}".format(mirrors)) - source_data = '' - for mirror in mirrors: - source_data += 'deb [arch={}] {} {} main\n'.format(arch,mirror,osversion.get_codename()) - FileUtils.delete('/etc/apt/sources.list.d/ros-fish.list') - FileUtils.new('/etc/apt/sources.list.d/',"ros-fish.list",source_data) - ros_pkg = self.get_all_instsll_ros_pkgs() - if ros_pkg and len(ros_pkg)>1: - PrintUtils.print_success("恭喜,成功添加ROS源,接下来可以使用apt安装ROS或者使用[1]一键安装ROS安装!") - return - - # echo >>/etc/apt/apt.conf.d/99verify-peer.conf "Acquire { https::Verify-Peer false }" - if not AptUtils.checkapt(): PrintUtils.print_error("四次换源后都失败了,请及时联系小鱼获取解决方案并处理!") - + # 如果第一次尝试失败,让用户重新选择镜像源 + PrintUtils.print_warn("换源后更新失败,您可以重新选择镜像源再尝试!") + retry_mirror = self.select_mirror() + while retry_mirror != selected_mirror: + PrintUtils.print_info("您重新选择的镜像源: {}".format(retry_mirror)) + mirrors = self.get_mirror_by_code(osversion.get_codename(),arch=arch,first_choose=retry_mirror) + PrintUtils.print_info("根据您的系统和选择,为您推荐安装源为{}".format(mirrors)) + source_data = '' + for mirror in mirrors: + source_data += 'deb [arch={}] {} {} main\n'.format(arch,mirror,osversion.get_codename()) + FileUtils.delete('/etc/apt/sources.list.d/ros-fish.list') + FileUtils.new('/etc/apt/sources.list.d/',"ros-fish.list",source_data) + ros_pkg = self.get_all_instsll_ros_pkgs() + if ros_pkg and len(ros_pkg)>1: + PrintUtils.print_success("恭喜,成功添加ROS源,接下来可以使用apt安装ROS或者使用[1]一键安装ROS安装!") + return + else: + PrintUtils.print_warn("换源后更新失败,您可以重新选择镜像源再尝试!") + retry_mirror = self.select_mirror() + else: + PrintUtils.print_error("您选择了相同的镜像源,四次换源后都失败了,请及时联系小鱼获取解决方案并处理!") def add_key(self): PrintUtils.print_success(tr.tr('============正在添加ROS源密钥=================')) From 49657eb93f164370145334d8579f39bf4ec7dd69 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Fri, 29 Aug 2025 15:23:00 +0800 Subject: [PATCH 04/27] =?UTF-8?q?refactor(tools):=20=E4=BC=98=E5=8C=96=20R?= =?UTF-8?q?OS=20=E5=AE=89=E8=A3=85=E5=B7=A5=E5=85=B7=E7=9A=84=E9=80=80?= =?UTF-8?q?=E5=87=BA=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在多个 ROS 安装工具中统一退出操作的处理方式 - 使用 PrintUtils.print_error() 替换直接 print,提高错误信息的可读性 - 返回 False 以明确表示退出操作失败 - 涉及工具:tool_install_ros.py, tool_install_ros1_systemdefault.py, tool_install_ros_with_docker.py --- tools/tool_install_ros.py | 6 +++--- tools/tool_install_ros1_systemdefault.py | 4 ++-- tools/tool_install_ros_with_docker.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/tool_install_ros.py b/tools/tool_install_ros.py index 9b8654a..85acd84 100644 --- a/tools/tool_install_ros.py +++ b/tools/tool_install_ros.py @@ -380,13 +380,13 @@ def choose_and_install_ros(self): if code==0: PrintUtils.print_error("你选择退出") PrintUtils.print_delay('是因为没有自己想要的ROS版本吗?ROS版本和操作系统版本是有对应关系的哦,所以可能是你的系统版本{}不对!具体请查看:https://fishros.org.cn/forum/topic/96'.format(str(str(osversion.get_name())+str(osversion.get_version())))) - return + return False version_dic = {1:rosname+"桌面版",2:rosname+"基础版(小)"} code,name = ChooseTask(version_dic,"请选择安装的具体版本(如果不知道怎么选,请选1桌面版):",False).run() if code==0: - print("你选择退出。。。。") - return + PrintUtils.print_error("你选择退出。。。。") + return False install_tool = 'aptitude' install_tool_apt = 'apt' diff --git a/tools/tool_install_ros1_systemdefault.py b/tools/tool_install_ros1_systemdefault.py index ad4b83b..ee7ab47 100644 --- a/tools/tool_install_ros1_systemdefault.py +++ b/tools/tool_install_ros1_systemdefault.py @@ -31,8 +31,8 @@ def install_system_ros(self): code,name = ChooseTask(version_dic,"请选择安装的具体版本(如果不知道怎么选,请选1桌面版):",False).run() if code==0: - print("你选择退出。。。。") - return + PrintUtils.print_error("你选择退出。。。。") + return False install_tool = 'aptitude' install_tool_apt = 'apt' diff --git a/tools/tool_install_ros_with_docker.py b/tools/tool_install_ros_with_docker.py index 8be6c29..68ca263 100644 --- a/tools/tool_install_ros_with_docker.py +++ b/tools/tool_install_ros_with_docker.py @@ -132,7 +132,7 @@ def choose_image_version(self): code,result = ChooseTask(RosVersions.get_vesion_list(),"请选择你要安装的ROS版本名称(请注意ROS1和ROS2区别):",True).run() if code==0: PrintUtils.print_error("你选择退出。。。。") - return + return False version_info,rosname = RosVersions.get_version_string(result) PrintUtils.print_info("你选择了{}".format(version_info)) return rosname @@ -249,7 +249,7 @@ def install_use_tool(self): def install_ros_with_docker(self): rosname = self.choose_image_version() - if not rosname: return + if not rosname: return False self.install_docker() self.download_image(rosname) From e220cc840daddef672bbf4d9df43a5891e5cca14 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Wed, 17 Sep 2025 16:30:28 +0800 Subject: [PATCH 05/27] =?UTF-8?q?fix(tools):=20=E4=BF=AE=E5=A4=8Drosdepc?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=B8=ADprint=5Fwarn=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E8=B0=83=E7=94=A8=20-=20=E4=BF=AE=E5=A4=8Drosdepc?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E4=B8=ADprint=5Fwarn=E7=9A=84=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E8=B0=83=E7=94=A8=20-=20=E8=B0=83=E6=95=B4=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E7=9A=84=E9=A1=BA=E5=BA=8F=EF=BC=8C=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=B8=8D=E5=B8=A6=E5=8F=82=E6=95=B0=E7=9A=84?= =?UTF-8?q?=E5=AE=89=E8=A3=85=20-=20=E5=B0=86=E9=95=9C=E5=83=8F=E6=BA=90?= =?UTF-8?q?=E7=9A=84=E5=90=8D=E7=A7=B0=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=85=A8?= =?UTF-8?q?=E5=90=8D=E8=80=8C=E9=9D=9E=E7=AE=80=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/tool_config_rosdep.py | 67 ++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index e0e357c..128817b 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -44,7 +44,7 @@ def choose_pip_source(self): sources = { "清华源": "https://pypi.tuna.tsinghua.edu.cn/simple", "阿里云": "https://mirrors.aliyun.com/pypi/simple", - "中国科技大学": "https://pypi.mirrors.ustc.edu.cn/simple", + "中国科学技术大学": "https://pypi.mirrors.ustc.edu.cn/simple", "华为云": "https://repo.huaweicloud.com/repository/pypi/simple" } @@ -59,36 +59,65 @@ def choose_pip_source(self): choose_index, choose_content = ChooseTask(choose_dict, "请选择pip源:").run() if choose_index == 0: - PrintUtils.print_info("未选择源,默认使用中国科技大学源") - return sources["中国科技大学"] + PrintUtils.print_info("未选择源,默认使用中国科学技术大学源") + return sources["中国科学技术大学"] try: # choose_index 已经是整数,直接使用 selected_name = list(sources.keys())[choose_index-1] return sources[selected_name] except (IndexError) as e: - PrintUtils.print_error(f"选择源时出错: {str(e)},使用中国科技大学源") - return sources["中国科技大学"] + PrintUtils.print_error(f"选择源时出错: {str(e)},使用中国科学技术大学源") + return sources["中国科学技术大学"] def install_rosdepc(self): - CmdTask("sudo apt install python3-pip -y", 0).run() + """ + 安装rosdepc工具,用于ROS依赖管理 + """ + # 安装python3-pip + pip_install_result = CmdTask("sudo apt install python3-pip -y", 0).run() + if pip_install_result[0] != 0: + PrintUtils.print_error("安装python3-pip失败") + return False - # 选择源 + # 选择pip源 selected_source = self.choose_pip_source() PrintUtils.print_success(f"您选择了: {selected_source}") - # 直接使用 --break-system-packages 参数安装,避免第一次安装失败 + # 先尝试不带参数安装rosdepc PrintUtils.print_info(f"正在使用 {selected_source} 安装 rosdepc...") - cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc --break-system-packages").run() - if cmd_ret[0]!=0: - # 如果安装失败,尝试不带参数安装 - PrintUtils.print_warning("安装失败,尝试使用其他方式安装...") - cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc").run() - CmdTask("sudo rosdepc init", 0).run() - CmdTask("sudo rosdepc fix-permissions", 0).run() + cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc").run() + + # 如果不带参数安装失败,尝试使用 --break-system-packages 参数安装 + if cmd_ret[0] != 0: + PrintUtils.print_warn("安装失败,尝试使用 --break-system-packages 参数安装...") + cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc --break-system-packages").run() + # 如果仍然失败,返回False + if cmd_ret[0] != 0: + PrintUtils.print_error("两种方式安装rosdepc均失败") + return False + + # 初始化rosdepc + init_result = CmdTask("sudo rosdepc init", 0).run() + if init_result[0] != 0: + PrintUtils.print_error("rosdepc初始化失败") + return False + + # 修复权限问题 + fix_result = CmdTask("sudo rosdepc fix-permissions", 0).run() + if fix_result[0] != 0: + PrintUtils.print_error("修复rosdepc权限失败") + return False + PrintUtils.print_info('已为您安装好rosdepc,请使用:\nrosdepc update \n进行测试更新,最后欢迎关注微信公众号《鱼香ROS》') - - + return True + def run(self): - #正式的运行 - self.install_rosdepc() \ No newline at end of file + """ + 运行rosdepc安装工具 + + Returns: + bool: 安装成功返回True,失败返回False + """ + # 正式的运行 + return self.install_rosdepc() \ No newline at end of file From a982a560a6ba69c0e80b0ef6a831696825ddd7a3 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Wed, 17 Sep 2025 16:36:40 +0800 Subject: [PATCH 06/27] =?UTF-8?q?refactor(tools):=20=E7=B2=BE=E7=AE=80=20t?= =?UTF-8?q?ool=5Fconfig=5Frosdep.py=20=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除了未使用的导入模块 - 移除了未使用的 test_source_speed 函数 --- tools/tool_config_rosdep.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index 128817b..8faded6 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -1,40 +1,12 @@ # -*- coding: utf-8 -*- -import time -import http.client -from urllib.parse import urlparse from .base import BaseTool from .base import PrintUtils,CmdTask,FileUtils,AptUtils,ChooseTask -from .base import osversion -from .base import run_tool_file class Tool(BaseTool): def __init__(self): self.type = BaseTool.TYPE_CONFIG self.name = "模板工程" self.author = '小鱼' - - def test_source_speed(self, url): - """测试源的速度 - - Args: - url (str): 源的URL - - Returns: - float: 响应时间(秒) - """ - try: - start_time = time.time() - parsed_url = urlparse(url) - conn = http.client.HTTPSConnection(parsed_url.netloc, timeout=5) - conn.request("HEAD", parsed_url.path) - resp = conn.getresponse() - conn.close() - end_time = time.time() - return end_time - start_time - except Exception as e: - PrintUtils.print_error(f"测试源 {url} 失败: {str(e)}") - return float('inf') # 返回无穷大表示连接失败 - def choose_pip_source(self): """选择pip源 From c6e78127e54c6be5912211b4f5d6f87e5602549f Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Wed, 17 Sep 2025 16:38:30 +0800 Subject: [PATCH 07/27] =?UTF-8?q?refactor(tools):=20=E7=B2=BE=E7=AE=80=20t?= =?UTF-8?q?ool=5Fconfig=5Frosdep.py=20=E4=B8=AD=E7=9A=84=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除了未使用的 FileUtils 和 AptUtils 模块 - 优化了代码结构,提高了代码的可读性和维护性 --- tools/tool_config_rosdep.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index 8faded6..45dc258 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from .base import BaseTool -from .base import PrintUtils,CmdTask,FileUtils,AptUtils,ChooseTask +from .base import PrintUtils,CmdTask,ChooseTask class Tool(BaseTool): def __init__(self): From b8a6c5d13f5c68e61f349f97f0b4391d5008c8a3 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Wed, 17 Sep 2025 17:34:25 +0800 Subject: [PATCH 08/27] =?UTF-8?q?refactor(install):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84=E5=92=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - wget相关文件之前提前创建本地相关文件夹防止被记录报错 --- install.py | 6 +++--- tools/tool_config_rosdep.py | 10 +++++----- tools/translation/translator.py | 2 ++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/install.py b/install.py index 1f11463..a9d8623 100644 --- a/install.py +++ b/install.py @@ -53,7 +53,8 @@ tracking = None def main(): - # download base + os.system("mkdir -p /tmp/fishinstall/tools/translation/assets") + url_prefix = os.environ.get('FISHROS_URL','http://mirror.fishros.com/install') os.system("wget {} -O /tmp/fishinstall/{} --no-check-certificate".format(base_url,base_url.replace(url_prefix,''))) @@ -62,7 +63,6 @@ def main(): from tools.base import run_tool_file,download_tools from tools.base import config_helper,tr - # download translations CmdTask("wget {} -O /tmp/fishinstall/{} --no-check-certificate".format(translator_url,translator_url.replace(url_prefix,''))).run() importlib.import_module("tools.translation.translator").Linguist() @@ -149,7 +149,7 @@ def main(): for text,end in tracing.logs: print(text, file=f,end=end) # 打印输出到文件中 for text in tracing.err_logs: - print(text, file=f) # 打印输出到文件中 + print(text, file=f) # 打印输出到文件中 if tracing.need_report: print("") input('检测到本次运行出现失败命令,直接退出按Ctrl+C,按任意键上传日志并退出\n') diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index 45dc258..05fc944 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -50,7 +50,7 @@ def install_rosdepc(self): pip_install_result = CmdTask("sudo apt install python3-pip -y", 0).run() if pip_install_result[0] != 0: PrintUtils.print_error("安装python3-pip失败") - return False + return 1 # 选择pip源 selected_source = self.choose_pip_source() @@ -67,22 +67,22 @@ def install_rosdepc(self): # 如果仍然失败,返回False if cmd_ret[0] != 0: PrintUtils.print_error("两种方式安装rosdepc均失败") - return False + return 2 # 初始化rosdepc init_result = CmdTask("sudo rosdepc init", 0).run() if init_result[0] != 0: PrintUtils.print_error("rosdepc初始化失败") - return False + return 3 # 修复权限问题 fix_result = CmdTask("sudo rosdepc fix-permissions", 0).run() if fix_result[0] != 0: PrintUtils.print_error("修复rosdepc权限失败") - return False + return 4 PrintUtils.print_info('已为您安装好rosdepc,请使用:\nrosdepc update \n进行测试更新,最后欢迎关注微信公众号《鱼香ROS》') - return True + return 0 def run(self): """ diff --git a/tools/translation/translator.py b/tools/translation/translator.py index 5d935f3..50eeb57 100644 --- a/tools/translation/translator.py +++ b/tools/translation/translator.py @@ -30,6 +30,8 @@ def __init__(self): self._currentLocale = locale.getdefaultlocale()[0] # Load the translation file. self.lang = self._currentLocale + # Create directory for downloads + CmdTask("mkdir -p /tmp/fishinstall/tools/translation/assets").run() for lang in _suported_languages: CmdTask("wget {} -O /tmp/fishinstall/{} --no-check-certificate".format(lang_url.format(lang), lang_url.format(lang).replace(url_prefix, ''))).run() From 1ee4dc2ad71d31f5cc2108ee50e8c5f303cdd939 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Thu, 18 Sep 2025 12:36:51 +0800 Subject: [PATCH 09/27] =?UTF-8?q?refactor(base.py,=20tool=5Fconfig=5Frosde?= =?UTF-8?q?p.py):=20=E6=9B=B4=E6=96=B0=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E5=8C=96=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 `base.py` 和 `tool_config_rosdep.py` 文件中的 f-string 格式化改为 `.format()` 方法,以保持代码风格的一致性。 --- tools/base.py | 2 +- tools/tool_config_rosdep.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tools/base.py b/tools/base.py index ae4ef98..22ae6b9 100644 --- a/tools/base.py +++ b/tools/base.py @@ -1472,7 +1472,7 @@ def get_fast_url(urls,timeout=1.5): conn.close() except Exception as e: # 如果请求失败,记录为 None 或者一个很大的延时 - # print(f"Error accessing {url}: {e}") + # print("Error accessing {}: {}".format(url, e)) PrintUtils.print_info("- {}\t\t超时".format(url)) latencies[url] = float('inf') diff --git a/tools/tool_config_rosdep.py b/tools/tool_config_rosdep.py index 05fc944..0b3434d 100644 --- a/tools/tool_config_rosdep.py +++ b/tools/tool_config_rosdep.py @@ -24,7 +24,7 @@ def choose_pip_source(self): choose_dict = {} i = 1 for name, url in sources.items(): - choose_dict[i] = f"{name} - {url}" + choose_dict[i] = "{} - {}".format(name, url) i += 1 PrintUtils.print_info("请选择要使用的pip源:") @@ -39,7 +39,7 @@ def choose_pip_source(self): selected_name = list(sources.keys())[choose_index-1] return sources[selected_name] except (IndexError) as e: - PrintUtils.print_error(f"选择源时出错: {str(e)},使用中国科学技术大学源") + PrintUtils.print_error("选择源时出错: {},使用中国科学技术大学源".format(str(e))) return sources["中国科学技术大学"] def install_rosdepc(self): @@ -54,16 +54,16 @@ def install_rosdepc(self): # 选择pip源 selected_source = self.choose_pip_source() - PrintUtils.print_success(f"您选择了: {selected_source}") + PrintUtils.print_success("您选择了: {}".format(selected_source)) # 先尝试不带参数安装rosdepc - PrintUtils.print_info(f"正在使用 {selected_source} 安装 rosdepc...") - cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc").run() + PrintUtils.print_info("正在使用 {} 安装 rosdepc...".format(selected_source)) + cmd_ret = CmdTask("sudo pip3 install -i {} rosdepc".format(selected_source)).run() # 如果不带参数安装失败,尝试使用 --break-system-packages 参数安装 if cmd_ret[0] != 0: PrintUtils.print_warn("安装失败,尝试使用 --break-system-packages 参数安装...") - cmd_ret = CmdTask(f"sudo pip3 install -i {selected_source} rosdepc --break-system-packages").run() + cmd_ret = CmdTask("sudo pip3 install -i {} rosdepc --break-system-packages".format(selected_source)).run() # 如果仍然失败,返回False if cmd_ret[0] != 0: PrintUtils.print_error("两种方式安装rosdepc均失败") From f47382243c2dbbf3808d2a382fcf26814368ebe9 Mon Sep 17 00:00:00 2001 From: Dennis <488132230@qq.com> Date: Thu, 18 Sep 2025 17:16:01 +0800 Subject: [PATCH 10/27] =?UTF-8?q?fix(tool):=E7=A7=BB=E9=99=A4=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=B8=8D=E6=94=AF=E6=8C=81ros1=E7=9A=84=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E7=BC=96=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tools/tool_install_ros.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tools/tool_install_ros.py b/tools/tool_install_ros.py index 85acd84..d3fefff 100644 --- a/tools/tool_install_ros.py +++ b/tools/tool_install_ros.py @@ -120,17 +120,6 @@ def get_desktop_version(name): 'xenial':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, 'yakkety':{"packages.ros"}, 'zesty':{"packages.ros"}, - 'focal':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'jammy':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'kinetic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'lunar':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'melodic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'noble':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'trusty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'utopic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'xenial':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'yakkety':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'zesty':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, } From c0222ea6c51025085cddfae65cece65f9a2cbfcb Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 18:48:17 +0800 Subject: [PATCH 11/27] =?UTF-8?q?feat(test):=20=E6=B7=BB=E5=8A=A0=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8C=96=E6=B5=8B=E8=AF=95=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E5=92=8C=E6=B5=8B=E8=AF=95=20runner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 GitHub Actions 工作流用于测试安装脚本,支持多 Ubuntu 版本。 添加 test_runner.py 脚本,实现基于 YAML 配置的自动化测试逻辑。 新增 fish_install_test.yaml 测试配置文件,包含多个安装项的测试用例。 更新 .gitignore 忽略 IFLOW.md 和 .pyc 文件。 --- .github/workflows/test-install.yml | 32 +++++ .gitignore | 2 +- tests/fish_install_test.yaml | 32 +++++ tests/test_runner.py | 218 +++++++++++++++++++++++++++++ 4 files changed, 283 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test-install.yml create mode 100644 tests/fish_install_test.yaml create mode 100644 tests/test_runner.py diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml new file mode 100644 index 0000000..376f158 --- /dev/null +++ b/.github/workflows/test-install.yml @@ -0,0 +1,32 @@ +name: Test Install Script + +on: + push: + branches: [ dev ] + pull_request: + branches: [ dev ] + +jobs: + test-install: + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04] + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + - name: Install dependencies + run: | + sudo apt update + sudo apt install -y python3-yaml python3-distro + + - name: Run tests + run: | + cd tests && python3 test_runner.py \ No newline at end of file diff --git a/.gitignore b/.gitignore index fd20fdd..8ec013b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ - +IFLOW.md *.pyc diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml new file mode 100644 index 0000000..095d31d --- /dev/null +++ b/tests/fish_install_test.yaml @@ -0,0 +1,32 @@ +# 测试配置文件,用于 GitHub Actions 自动化测试 +# 格式: chooses: [{choose: <选项ID>, desc: <选项描述>}] + +# 测试用例 1: 安装 NodeJS +- name: "Install NodeJS" + chooses: + - { choose: 6, desc: "一键安装:NodeJS环境" } + +# 测试用例 2: 配置系统源 +- name: "Configure System Source" + chooses: + - { choose: 5, desc: "一键配置:系统源(更换系统源,支持全版本Ubuntu系统)" } + - { choose: 1, desc: "更换源继续安装" } + - { choose: 2, desc: "清理三方源" } + +# 测试用例 3: 安装 VSCode +- name: "Install VSCode" + chooses: + - { choose: 7, desc: "一键安装:VsCode开发工具" } + +# 测试用例 4: 安装 Docker +- name: "Install Docker" + chooses: + - { choose: 8, desc: "一键安装:Docker" } + +# 测试用例 5: 安装 ROS (仅测试核心流程,不实际安装完整ROS以节省时间) +# 注意:此测试用例可能需要根据实际环境进行调整 +- name: "Install ROS (Simulation)" + chooses: + - { choose: 16, desc: "一键安装:系统自带ROS (!!警告!!仅供特殊情况下使用)" } + - { choose: 1, desc: "更换源继续安装" } + - { choose: 2, desc: "清理三方源" } \ No newline at end of file diff --git a/tests/test_runner.py b/tests/test_runner.py new file mode 100644 index 0000000..d3d43ac --- /dev/null +++ b/tests/test_runner.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import yaml +import subprocess +import os +import sys +import time +import json + +# 将项目根目录添加到 Python 路径中,以便能找到 tools 模块 +sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) + +def load_test_cases(config_file): + """加载测试用例""" + try: + with open(config_file, 'r', encoding='utf-8') as f: + test_cases = yaml.safe_load(f) + return test_cases + except Exception as e: + print(f"加载测试配置文件失败: {e}") + return [] + +def run_install_test(test_case): + """运行单个安装测试""" + name = test_case.get('name', 'Unknown Test') + chooses = test_case.get('chooses', []) + + print(f"开始测试: {name}") + + # 创建临时配置文件路径 + temp_config = "/tmp/fish_install_test_temp.yaml" + + # 确保 /tmp 目录存在 + os.makedirs("/tmp", exist_ok=True) + + # 创建临时配置文件 + config_data = {'chooses': chooses} + try: + with open(temp_config, 'w', encoding='utf-8') as f: + yaml.dump(config_data, f, allow_unicode=True) + print(f"已创建临时配置文件: {temp_config}") + except Exception as e: + print(f"创建临时配置文件失败: {e}") + return False, "" + + # 备份原始的 fish_install.yaml (如果存在) + original_config = "../fish_install.yaml" + backup_config = "../fish_install.yaml.backup" + if os.path.exists(original_config): + try: + os.rename(original_config, backup_config) + print(f"已备份原始配置文件至: {backup_config}") + except Exception as e: + print(f"备份原始配置文件失败: {e}") + # 即使备份失败也继续执行,因为我们会在最后恢复 + + # 将临时配置文件复制为当前配置文件 + try: + import shutil + shutil.copy(temp_config, original_config) + print(f"已将临时配置文件复制为: {original_config}") + except Exception as e: + print(f"复制配置文件失败: {e}") + # 恢复备份的配置文件 + if os.path.exists(backup_config): + try: + os.rename(backup_config, original_config) + print(f"已恢复备份的配置文件: {original_config}") + except: + pass + # 清理临时文件 + if os.path.exists(temp_config): + os.remove(temp_config) + return False, "" + + # 初始化输出和错误信息 + output = "" + error = "" + + # 运行安装脚本 + try: + # 使用 -u 参数确保输出不被缓冲,以便实时查看日志 + # 直接运行 install.py,它会自动检测并使用 ../fish_install.yaml + process = subprocess.Popen( + [sys.executable, "../install.py"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + bufsize=1 + ) + + # 等待进程结束 (设置一个合理的超时时间,例如300秒) + stdout, _ = process.communicate(timeout=300) + output = stdout + + # 打印输出 + print("=== 脚本输出开始 ===") + print(stdout) + print("=== 脚本输出结束 ===") + + # 检查退出码 + if process.returncode == 0: + print(f"测试通过: {name}") + return True, output + else: + print(f"测试失败: {name} (退出码: {process.returncode})") + return False, output + except subprocess.TimeoutExpired: + print(f"测试超时: {name} (超过300秒)") + # 终止进程 + process.kill() + stdout, _ = process.communicate() + output = stdout + return False, output + except Exception as e: + print(f"运行测试时发生异常: {e}") + error = str(e) + return False, output + "\n" + error + finally: + # 恢复备份的配置文件 (如果存在) + if os.path.exists(backup_config): + try: + os.rename(backup_config, original_config) + print(f"已恢复备份的配置文件: {original_config}") + except Exception as e: + print(f"恢复备份的配置文件失败: {e}") + # 如果没有备份文件,但创建了原始配置文件,则删除它 + elif os.path.exists(original_config): + try: + os.remove(original_config) + print(f"已删除临时创建的配置文件: {original_config}") + except Exception as e: + print(f"删除临时配置文件失败: {e}") + # 清理临时配置文件 + if os.path.exists(temp_config): + try: + os.remove(temp_config) + print(f"已清理临时配置文件: {temp_config}") + except Exception as e: + print(f"清理临时配置文件失败: {e}") + +def main(): + """主函数""" + config_file = "fish_install_test.yaml" + + # 检查配置文件是否存在 + if not os.path.exists(config_file): + print(f"错误: 找不到测试配置文件 {config_file}") + sys.exit(1) + + # 加载测试用例 + test_cases = load_test_cases(config_file) + if not test_cases: + print("错误: 没有找到有效的测试用例") + sys.exit(1) + + print(f"共找到 {len(test_cases)} 个测试用例") + + # 运行所有测试用例并收集结果 + results = [] + passed = 0 + failed = 0 + + for i, test_case in enumerate(test_cases): + print(f"\n--- 测试用例 {i+1}/{len(test_cases)} ---") + success, output = run_install_test(test_case) + case_name = test_case.get('name', f'Test Case {i+1}') + + result = { + "name": case_name, + "success": success, + "output": output + } + results.append(result) + + if success: + passed += 1 + else: + failed += 1 + # 在测试用例之间添加延迟,避免系统资源冲突 + time.sleep(2) + + # 生成详细的测试报告 + report = { + "summary": { + "total": len(test_cases), + "passed": passed, + "failed": failed + }, + "details": results + } + + # 将报告保存为 JSON 文件 + report_file = "test_report.json" + try: + with open(report_file, 'w', encoding='utf-8') as f: + json.dump(report, f, ensure_ascii=False, indent=2) + print(f"\n详细测试报告已保存至: {report_file}") + except Exception as e: + print(f"保存测试报告失败: {e}") + + # 输出测试结果摘要 + print("\n=== 测试结果摘要 ===") + print(f"通过: {passed}") + print(f"失败: {failed}") + print(f"总计: {len(test_cases)}") + + if failed > 0: + print("部分测试失败,请检查日志和测试报告。") + sys.exit(1) + else: + print("所有测试通过!") + sys.exit(0) + +if __name__ == "__main__": + main() From 355653f6dfa5020f2d5a5f5362c9911f066cd03d Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 18:50:29 +0800 Subject: [PATCH 12/27] =?UTF-8?q?ci(test-install):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E4=BE=9D=E8=B5=96=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将依赖安装步骤拆分为系统依赖和Python依赖两个独立步骤, 以提高工作流的清晰度和可维护性。系统依赖包括python3-yaml 和python3-distro,Python依赖通过pip安装pyyaml。 --- .github/workflows/test-install.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 376f158..0450fe2 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -22,11 +22,16 @@ jobs: with: python-version: '3.8' - - name: Install dependencies + - name: Install system dependencies run: | sudo apt update sudo apt install -y python3-yaml python3-distro + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + pip install pyyaml + - name: Run tests run: | cd tests && python3 test_runner.py \ No newline at end of file From 15ebf6886b378bb61ef5904c5dd4484f1ec87ea6 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 18:54:55 +0800 Subject: [PATCH 13/27] =?UTF-8?q?feat(test):=20=E5=A2=9E=E5=BC=BA=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E8=BF=90=E8=A1=8C=E5=99=A8=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在测试运行器中新增对输出内容的错误检查机制,能够识别脚本输出中 的常见错误关键字(如 ModuleNotFoundError、ImportError 等), 并在检测到异常时标记测试失败。同时更新 GitHub Actions 工作流, 安装 distro 模块以支持更全面的测试环境依赖。 --- .github/workflows/test-install.yml | 2 +- tests/test_runner.py | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 0450fe2..d6c5ae4 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -30,7 +30,7 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - pip install pyyaml + pip install pyyaml distro - name: Run tests run: | diff --git a/tests/test_runner.py b/tests/test_runner.py index d3d43ac..4cae777 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -7,6 +7,7 @@ import sys import time import json +import re # 将项目根目录添加到 Python 路径中,以便能找到 tools 模块 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) @@ -21,6 +22,23 @@ def load_test_cases(config_file): print(f"加载测试配置文件失败: {e}") return [] +def check_output_for_errors(output): + """检查输出中是否包含错误信息""" + error_keywords = [ + "ModuleNotFoundError", + "ImportError", + "Exception", + "Error:", + "Traceback", + "检测到程序发生异常退出" + ] + + for line in output.split('\n'): + for keyword in error_keywords: + if keyword in line: + return True + return False + def run_install_test(test_case): """运行单个安装测试""" name = test_case.get('name', 'Unknown Test') @@ -100,12 +118,15 @@ def run_install_test(test_case): print(stdout) print("=== 脚本输出结束 ===") - # 检查退出码 - if process.returncode == 0: + # 检查退出码和输出中的错误信息 + if process.returncode == 0 and not check_output_for_errors(output): print(f"测试通过: {name}") return True, output else: - print(f"测试失败: {name} (退出码: {process.returncode})") + if process.returncode != 0: + print(f"测试失败: {name} (退出码: {process.returncode})") + else: + print(f"测试失败: {name} (脚本中检测到错误)") return False, output except subprocess.TimeoutExpired: print(f"测试超时: {name} (超过300秒)") From de552eb272a4967093aef36f589bbd8780cb0c81 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:15:09 +0800 Subject: [PATCH 14/27] =?UTF-8?q?fix(install):=20=E8=B7=B3=E8=BF=87GitHub?= =?UTF-8?q?=20Actions=E7=8E=AF=E5=A2=83=E4=B8=AD=E7=9A=84=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E7=94=9F=E6=88=90=E5=92=8C=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 GitHub Actions 环境中运行时,跳过需要用户交互的配置文件生成 以及相关欢迎和帮助信息的打印操作,避免因等待输入导致流程卡住。 --- install.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/install.py b/install.py index a9d8623..5853d9e 100644 --- a/install.py +++ b/install.py @@ -121,11 +121,15 @@ def main(): else: download_tools(code,tools,url_prefix) run_tool_file(tools[code]['tool'].replace("/",".")) - config_helper.gen_config_file() - PrintUtils.print_delay(tr.tr("欢迎加入机器人学习交流QQ群:438144612(入群口令:一键安装)"),0.05) - PrintUtils.print_delay(tr.tr("鱼香小铺正式开业,最低499可入手一台能建图会导航的移动机器人,淘宝搜店:鱼香ROS 或打开链接查看:https://item.taobao.com/item.htm?id=696573635888"),0.001) - PrintUtils.print_delay(tr.tr("如在使用过程中遇到问题,请打开:https://fishros.org.cn/forum 进行反馈"),0.001) + # 检查是否在 GitHub Actions 环境中运行 + # 如果是,则跳过生成配置文件和后续的打印操作,因为这些操作需要用户输入 + if os.environ.get('GITHUB_ACTIONS') != 'true': + config_helper.gen_config_file() + + PrintUtils.print_delay(tr.tr("欢迎加入机器人学习交流QQ群:438144612(入群口令:一键安装)"),0.05) + PrintUtils.print_delay(tr.tr("鱼香小铺正式开业,最低499可入手一台能建图会导航的移动机器人,淘宝搜店:鱼香ROS 或打开链接查看:https://item.taobao.com/item.htm?id=696573635888"),0.001) + PrintUtils.print_delay(tr.tr("如在使用过程中遇到问题,请打开:https://fishros.org.cn/forum 进行反馈"),0.001) if __name__=='__main__': run_exc = [] From 29cfa27f3c7f56e9ce017f6e4c89acf76c3e4295 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:28:25 +0800 Subject: [PATCH 15/27] =?UTF-8?q?fix(tests):=20=E6=9B=B4=E6=96=B0=20fish?= =?UTF-8?q?=5Finstall=5Ftest.yaml=20=E6=B5=8B=E8=AF=95=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除部分测试用例的配置,包括 "Install NodeJS" 和 "Install VSCode"。 修改 "Install ROS" 测试用例的选项描述和选择逻辑,以适应实际安装流程。 注释掉 "Configure System Source" 测试用例,暂不执行。 --- tests/fish_install_test.yaml | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index 095d31d..00ecbc9 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -1,32 +1,21 @@ # 测试配置文件,用于 GitHub Actions 自动化测试 # 格式: chooses: [{choose: <选项ID>, desc: <选项描述>}] -# 测试用例 1: 安装 NodeJS -- name: "Install NodeJS" - chooses: - - { choose: 6, desc: "一键安装:NodeJS环境" } - -# 测试用例 2: 配置系统源 -- name: "Configure System Source" - chooses: - - { choose: 5, desc: "一键配置:系统源(更换系统源,支持全版本Ubuntu系统)" } - - { choose: 1, desc: "更换源继续安装" } - - { choose: 2, desc: "清理三方源" } -# 测试用例 3: 安装 VSCode -- name: "Install VSCode" - chooses: - - { choose: 7, desc: "一键安装:VsCode开发工具" } +# # 测试用例 2: 配置系统源 +# - name: "Configure System Source" +# chooses: +# - { choose: 5, desc: "一键配置:系统源(更换系统源,支持全版本Ubuntu系统)" } +# - { choose: 1, desc: "更换源继续安装" } +# - { choose: 2, desc: "清理三方源" } -# 测试用例 4: 安装 Docker -- name: "Install Docker" - chooses: - - { choose: 8, desc: "一键安装:Docker" } # 测试用例 5: 安装 ROS (仅测试核心流程,不实际安装完整ROS以节省时间) # 注意:此测试用例可能需要根据实际环境进行调整 -- name: "Install ROS (Simulation)" +- name: "Install ROS " chooses: - - { choose: 16, desc: "一键安装:系统自带ROS (!!警告!!仅供特殊情况下使用)" } + - { choose: 1, desc: "一键安装ROS" } - { choose: 1, desc: "更换源继续安装" } - - { choose: 2, desc: "清理三方源" } \ No newline at end of file + - { choose: 2, desc: "清理三方源" } + - { choose: 1, desc: "选择第一个ros 版本" } + - { choose: 1, desc: "安装桌面版" } From 5898519a009b9e782db6b23abc960887780952e7 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:31:41 +0800 Subject: [PATCH 16/27] =?UTF-8?q?fix(tools):=20=E5=A4=84=E7=90=86=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E5=8C=96=E6=B5=8B=E8=AF=95=E7=8E=AF=E5=A2=83=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=20EOFError=20=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 `ChooseTask` 和 `ChooseWithCategoriesTask` 类中,添加对 `input()` 函数在自动化测试环境下可能引发的 `EOFError` 异常处理。当捕获到该异常时,若存在配置文件提供的默认选项则使用它,否则默认选择退出(编号 0),并提示用户当前处于自动化环境。 --- tests/fish_install_test.yaml | 3 +-- tools/base.py | 26 ++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index 00ecbc9..ffe9ede 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -10,8 +10,7 @@ # - { choose: 2, desc: "清理三方源" } -# 测试用例 5: 安装 ROS (仅测试核心流程,不实际安装完整ROS以节省时间) -# 注意:此测试用例可能需要根据实际环境进行调整 +# 测试用例 5: 安装 ROS - name: "Install ROS " chooses: - { choose: 1, desc: "一键安装ROS" } diff --git a/tools/base.py b/tools/base.py index 22ae6b9..4eb4cc8 100644 --- a/tools/base.py +++ b/tools/base.py @@ -1112,8 +1112,17 @@ def __choose(data,tips,array): choose = str(choose_item['choose']) PrintUtils.print_text(tr.tr("为您从配置文件找到默认选项:")+str(choose_item)) else: - choose = input(tr.tr("请输入[]内的数字以选择:")) - choose_item = None + try: + choose = input(tr.tr("请输入[]内的数字以选择:")) + choose_item = None + except EOFError: + # 在自动化测试环境中,input()可能会引发EOFError + # 如果是从配置文件读取的选项,则使用它,否则返回默认值0(退出) + if choose_item: + choose = str(choose_item['choose']) + else: + choose = "0" # 默认选择退出 + PrintUtils.print_text(tr.tr("检测到自动化环境,使用默认选项: {}").format(choose)) # Input From Queue if choose.isdecimal() : if (int(choose) in dic.keys() ) or (int(choose)==0): @@ -1163,8 +1172,17 @@ def __choose(data,tips,array,categories): choose_id = str(choose_item['choose']) print(tr.tr("为您从配置文件找到默认选项:")+str(choose_item)) else: - choose_id = input(tr.tr("请输入[]内的数字以选择:")) - choose_item = None + try: + choose_id = input(tr.tr("请输入[]内的数字以选择:")) + choose_item = None + except EOFError: + # 在自动化测试环境中,input()可能会引发EOFError + # 如果是从配置文件读取的选项,则使用它,否则返回默认值0(退出) + if choose_item: + choose_id = str(choose_item['choose']) + else: + choose_id = "0" # 默认选择退出 + PrintUtils.print_text(tr.tr("检测到自动化环境,使用默认选项: {}").format(choose_id)) # Input From Queue if choose_id.isdecimal() : if int(choose_id) in tool_ids : From d4d12bcf05dd5ee0744390116bcca03df1a155b4 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:36:16 +0800 Subject: [PATCH 17/27] =?UTF-8?q?test(fish=5Finstall=5Ftest):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20ROS=20=E5=AE=89=E8=A3=85=E6=B5=8B=E8=AF=95=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将测试用例 5 的名称从 "Install ROS " 修正为 "Install ROS" - 修改一键安装 ROS 的描述,明确支持 ROS/ROS2 及树莓派 Jetson 平台 - 调整 ROS 版本选择描述,统一使用 melodic(ROS1) 标注 - 移除冗余的系统源配置相关注释内容 --- tests/fish_install_test.yaml | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index ffe9ede..8a14d01 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -1,20 +1,11 @@ # 测试配置文件,用于 GitHub Actions 自动化测试 # 格式: chooses: [{choose: <选项ID>, desc: <选项描述>}] - -# # 测试用例 2: 配置系统源 -# - name: "Configure System Source" -# chooses: -# - { choose: 5, desc: "一键配置:系统源(更换系统源,支持全版本Ubuntu系统)" } -# - { choose: 1, desc: "更换源继续安装" } -# - { choose: 2, desc: "清理三方源" } - - -# 测试用例 5: 安装 ROS -- name: "Install ROS " +# 测试用例 1: 安装 ROS +- name: "Install ROS" chooses: - - { choose: 1, desc: "一键安装ROS" } + - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 1, desc: "更换源继续安装" } - { choose: 2, desc: "清理三方源" } - - { choose: 1, desc: "选择第一个ros 版本" } - - { choose: 1, desc: "安装桌面版" } + - { choose: 1, desc: "melodic(ROS1)" } + - { choose: 1, desc: "melodic(ROS1)桌面版" } \ No newline at end of file From 6281462754d026d0b6548d6049942a82bb2aa504 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:40:20 +0800 Subject: [PATCH 18/27] =?UTF-8?q?fix(tests):=20=E6=9B=B4=E6=96=B0=20fish?= =?UTF-8?q?=5Finstall=5Ftest.yaml=20=E4=B8=AD=E7=9A=84=E9=80=89=E9=A1=B9?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复了安装测试配置文件中的选项描述不准确的问题,优化了源选择相关选项的表述,并新增自动测速选项。同时调整了 test_runner.py 配置文件复制逻辑,确保配置文件能正确复制到多个目标位置。 此外,移除了 base.py 中重复的配置项加载代码,避免重复处理 choose_queue。 --- tests/fish_install_test.yaml | 5 +++-- tests/test_runner.py | 7 ++++++- tools/base.py | 2 -- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index 8a14d01..af839f0 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -5,7 +5,8 @@ - name: "Install ROS" chooses: - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - - { choose: 1, desc: "更换源继续安装" } - - { choose: 2, desc: "清理三方源" } + - { choose: 1, desc: "添加ROS/ROS2源" } + - { choose: 2, desc: "更换系统源并清理第三方源" } + - { choose: 1, desc: "自动测速选择最快的源" } - { choose: 1, desc: "melodic(ROS1)" } - { choose: 1, desc: "melodic(ROS1)桌面版" } \ No newline at end of file diff --git a/tests/test_runner.py b/tests/test_runner.py index 4cae777..6bfbb5e 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -73,11 +73,16 @@ def run_install_test(test_case): print(f"备份原始配置文件失败: {e}") # 即使备份失败也继续执行,因为我们会在最后恢复 - # 将临时配置文件复制为当前配置文件 + # 将临时配置文件复制为当前配置文件和/tmp/fishinstall/tools/fish_install.yaml try: import shutil shutil.copy(temp_config, original_config) print(f"已将临时配置文件复制为: {original_config}") + + # 同时将配置文件复制到/tmp/fishinstall/tools/目录下 + fishinstall_config = "/tmp/fishinstall/tools/fish_install.yaml" + shutil.copy(temp_config, fishinstall_config) + print(f"已将临时配置文件复制为: {fishinstall_config}") except Exception as e: print(f"复制配置文件失败: {e}") # 恢复备份的配置文件 diff --git a/tools/base.py b/tools/base.py index 4eb4cc8..5efc396 100644 --- a/tools/base.py +++ b/tools/base.py @@ -130,8 +130,6 @@ def get_default_queue(self,param_file_path): else: config_yaml = yaml.load(config_data) - for choose in config_yaml['chooses']: - choose_queue.put(choose) for choose in config_yaml['chooses']: choose_queue.put(choose) From c4973b65a4797d4914d7beebc45cbf72140d3c8f Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:42:47 +0800 Subject: [PATCH 19/27] =?UTF-8?q?fix(tests):=20=E7=A1=AE=E4=BF=9D=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=89=8D=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E5=AD=98=E5=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在测试运行器中添加目录创建逻辑,确保在复制 fish_install.yaml 配置文件之前, 目标目录 /tmp/fishinstall/tools/ 已经存在,避免因目录不存在导致的文件复制失败。 --- tests/test_runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_runner.py b/tests/test_runner.py index 6bfbb5e..6ff2475 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -81,6 +81,8 @@ def run_install_test(test_case): # 同时将配置文件复制到/tmp/fishinstall/tools/目录下 fishinstall_config = "/tmp/fishinstall/tools/fish_install.yaml" + # 确保目录存在 + os.makedirs(os.path.dirname(fishinstall_config), exist_ok=True) shutil.copy(temp_config, fishinstall_config) print(f"已将临时配置文件复制为: {fishinstall_config}") except Exception as e: From 7a0a6ad84a11014aa8aeff287d24a89afd0cd76e Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:47:00 +0800 Subject: [PATCH 20/27] =?UTF-8?q?feat(test):=20=E4=B8=BA=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=99=A8=E6=B7=BB=E5=8A=A0=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E9=85=8D=E7=BD=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在 test_runner.py 中更新 run_install_test 函数,通过环境变量传递 FISH_INSTALL_CONFIG 配置文件路径,以支持更灵活的测试配置。 同时修改 tools/base.py 中的 ConfigHelper 类,优先从环境变量读取 配置文件路径,若未设置则使用默认值 ./fish_install.yaml。 --- tests/test_runner.py | 3 ++- tools/base.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_runner.py b/tests/test_runner.py index 6ff2475..c6cee5a 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -113,7 +113,8 @@ def run_install_test(test_case): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, - bufsize=1 + bufsize=1, + env={**os.environ, 'FISH_INSTALL_CONFIG': '../fish_install.yaml'} ) # 等待进程结束 (设置一个合理的超时时间,例如300秒) diff --git a/tools/base.py b/tools/base.py index 5efc396..c1f2b69 100644 --- a/tools/base.py +++ b/tools/base.py @@ -33,7 +33,9 @@ def __init__(self,record_file=None): self.record_input_queue = Queue() self.record_file = record_file - if self.record_file==None: self.record_file = "./fish_install.yaml" + if self.record_file==None: + # 首先检查环境变量 + self.record_file = os.environ.get('FISH_INSTALL_CONFIG', "./fish_install.yaml") self.default_input_queue = self.get_default_queue(self.record_file) def record_input(self,item): From 2fb07fc822a97d9441bc3a9459dc5e6323a80cea Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 19:59:21 +0800 Subject: [PATCH 21/27] =?UTF-8?q?test(fish=5Finstall=5Ftest):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0ROS=E5=AE=89=E8=A3=85=E9=80=89=E9=A1=B9=E6=8F=8F?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将"添加ROS/ROS2源"选项修改为"更换系统源再继续安装",并更新镜像源选项描述为"中科大镜像源 (推荐国内用户使用)",以提供更准确的安装引导信息。 --- tests/fish_install_test.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index af839f0..252c61c 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -5,8 +5,7 @@ - name: "Install ROS" chooses: - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - - { choose: 1, desc: "添加ROS/ROS2源" } + - { choose: 1, desc: "更换系统源再继续安装" } - { choose: 2, desc: "更换系统源并清理第三方源" } - { choose: 1, desc: "自动测速选择最快的源" } - - { choose: 1, desc: "melodic(ROS1)" } - - { choose: 1, desc: "melodic(ROS1)桌面版" } \ No newline at end of file + - { choose: 1, desc: "中科大镜像源 (推荐国内用户使用)" } \ No newline at end of file From a051bef3430affa174ef49568307e34e913ae86c Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 20:04:19 +0800 Subject: [PATCH 22/27] =?UTF-8?q?feat(tests):=20=E6=B7=BB=E5=8A=A0HTML?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E6=B5=8B=E8=AF=95=E6=8A=A5=E5=91=8A=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在测试运行器中新增generate_html_report函数,用于生成带样式的HTML测试报告。 报告包含测试摘要和详细结果,支持通过/失败状态的视觉区分。 在main函数中调用该功能,将报告保存为test_report.html文件。 --- tests/generate_report.py | 146 +++++++++++++++++++++++++++++++++++++++ tests/test_runner.py | 136 ++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+) create mode 100644 tests/generate_report.py diff --git a/tests/generate_report.py b/tests/generate_report.py new file mode 100644 index 0000000..3d8ee60 --- /dev/null +++ b/tests/generate_report.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import json +import time +import os + +def generate_html_report(report, output_file): + """生成HTML格式的测试报告""" + html_content = """ + + + + + + 一键安装工具测试报告 + + + +
+

一键安装工具测试报告

+

生成时间: """ + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + """

+
+ +
+

测试摘要

+

总计: """ + str(report["summary"]["total"]) + """

+

通过: """ + str(report["summary"]["passed"]) + """

+

失败: """ + str(report["summary"]["failed"]) + """

+
+ +
+

详细测试结果

+""" + + for test_case in report["details"]: + status_class = "passed" if test_case["success"] else "failed" + status_text = "通过" if test_case["success"] else "失败" + status_style = "status-passed" if test_case["success"] else "status-failed" + + html_content += f""" +
+
+ {test_case["name"]} + {status_text} +
+
输出日志:
+
{test_case["output"]}
+
+""" + + html_content += """ +
+ + + + +""" + + with open(output_file, 'w', encoding='utf-8') as f: + f.write(html_content) + + +if __name__ == "__main__": + # 读取JSON测试报告 + report_file = "test_report.json" + if os.path.exists(report_file): + with open(report_file, 'r', encoding='utf-8') as f: + report = json.load(f) + + # 生成HTML报告 + html_report_file = "test_report.html" + generate_html_report(report, html_report_file) + print(f"HTML测试报告已生成: {html_report_file}") + else: + print(f"找不到测试报告文件: {report_file}") \ No newline at end of file diff --git a/tests/test_runner.py b/tests/test_runner.py index c6cee5a..260fdc7 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -39,7 +39,135 @@ def check_output_for_errors(output): return True return False + +def generate_html_report(report, output_file): + """生成HTML格式的测试报告""" + html_content = """ + + + + + + 一键安装工具测试报告 + + + +
+

一键安装工具测试报告

+

生成时间: """ + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + """

+
+ +
+

测试摘要

+

总计: """ + str(report["summary"]["total"]) + """

+

通过: """ + str(report["summary"]["passed"]) + """

+

失败: """ + str(report["summary"]["failed"]) + """

+
+ +
+

详细测试结果

+""" + + for test_case in report["details"]: + status_class = "passed" if test_case["success"] else "failed" + status_text = "通过" if test_case["success"] else "失败" + status_style = "status-passed" if test_case["success"] else "status-failed" + + html_content += f""" +
+
+ {test_case["name"]} + {status_text} +
+
输出日志:
+
{test_case["output"]}
+
+""" + + html_content += """ +
+ +
+

测试报告由一键安装工具自动生成

+
+ + +""" + + with open(output_file, 'w', encoding='utf-8') as f: + f.write(html_content) + + def run_install_test(test_case): + """运行单个安装测试""" name = test_case.get('name', 'Unknown Test') chooses = test_case.get('chooses', []) @@ -230,6 +358,14 @@ def main(): except Exception as e: print(f"保存测试报告失败: {e}") + # 生成HTML格式的测试报告 + html_report_file = "test_report.html" + try: + generate_html_report(report, html_report_file) + print(f"HTML测试报告已保存至: {html_report_file}") + except Exception as e: + print(f"生成HTML测试报告失败: {e}") + # 输出测试结果摘要 print("\n=== 测试结果摘要 ===") print(f"通过: {passed}") From a2fbcd65f139a5659504a73cb63348c652072926 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 20:07:28 +0800 Subject: [PATCH 23/27] =?UTF-8?q?ci(test-install):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=8A=A5=E5=91=8A=E4=B8=8A=E4=BC=A0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在GitHub Actions工作流中添加了测试报告上传步骤,确保在所有情况下都能上传 test_report.json和test_report.html文件作为artifact保存,便于后续查看和分析 测试结果 --- .github/workflows/test-install.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index d6c5ae4..7837ead 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -34,4 +34,14 @@ jobs: - name: Run tests run: | - cd tests && python3 test_runner.py \ No newline at end of file + cd tests && python3 test_runner.py + + - name: Upload test reports + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-reports-${{ matrix.os }} + path: | + tests/test_report.json + tests/test_report.html + if-no-files-found: ignore \ No newline at end of file From 83147560e4fdd0f63aa9a7878b911ab3cc9865f3 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 20:09:53 +0800 Subject: [PATCH 24/27] =?UTF-8?q?build(github-actions):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=B5=8B=E8=AF=95=E5=B7=A5=E4=BD=9C=E6=B5=81=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E4=B8=8A=E4=BC=A0=E6=9E=84=E4=BB=B6=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 test-install.yml 工作流中的 actions/upload-artifact 操作从 v3 版本 升级到 v4 版本,以使用最新的功能和改进。 --- .github/workflows/test-install.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 7837ead..2f9588c 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -37,7 +37,7 @@ jobs: cd tests && python3 test_runner.py - name: Upload test reports - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: test-reports-${{ matrix.os }} From 67d853101f011292ba3e0c9107242c74e7769c5f Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 20:13:38 +0800 Subject: [PATCH 25/27] =?UTF-8?q?feat(tests):=20=E6=9B=B4=E6=96=B0fish?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E6=B5=8B=E8=AF=95=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为fish安装测试添加新的选项配置, 包括humble(ROS2)和桌面端完整版选项, 以支持更全面的安装场景测试。 --- tests/fish_install_test.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index 252c61c..563a4d9 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -8,4 +8,6 @@ - { choose: 1, desc: "更换系统源再继续安装" } - { choose: 2, desc: "更换系统源并清理第三方源" } - { choose: 1, desc: "自动测速选择最快的源" } - - { choose: 1, desc: "中科大镜像源 (推荐国内用户使用)" } \ No newline at end of file + - { choose: 1, desc: "中科大镜像源 (推荐国内用户使用)" } + - { choose: 1, desc: "humble(ROS2)" } + - { choose: 1, desc: "桌面端完整版" } \ No newline at end of file From 72a3a2def96cf259da4c3edcc737d04e96791d8c Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Thu, 9 Oct 2025 20:25:43 +0800 Subject: [PATCH 26/27] =?UTF-8?q?test(test=5Frunner):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=AE=89=E8=A3=85=E6=B5=8B=E8=AF=95=E7=9A=84=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E8=87=B310=E5=88=86=E9=92=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将测试运行器中的进程超时时间从300秒增加到600秒,以避免在较慢的环境中出现误报超时错误。 --- tests/test_runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_runner.py b/tests/test_runner.py index 260fdc7..1e70a06 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -245,8 +245,8 @@ def run_install_test(test_case): env={**os.environ, 'FISH_INSTALL_CONFIG': '../fish_install.yaml'} ) - # 等待进程结束 (设置一个合理的超时时间,例如300秒) - stdout, _ = process.communicate(timeout=300) + # 等待进程结束 (设置一个更长的超时时间,例如600秒=10分钟) + stdout, _ = process.communicate(timeout=600) output = stdout # 打印输出 @@ -265,7 +265,7 @@ def run_install_test(test_case): print(f"测试失败: {name} (脚本中检测到错误)") return False, output except subprocess.TimeoutExpired: - print(f"测试超时: {name} (超过300秒)") + print(f"测试超时: {name} (超过600秒)") # 终止进程 process.kill() stdout, _ = process.communicate() From 7d002603c1157750b9b156a702d35b6f45e1d893 Mon Sep 17 00:00:00 2001 From: awan-deng <2152621419@qq.com> Date: Fri, 10 Oct 2025 18:29:30 +0800 Subject: [PATCH 27/27] =?UTF-8?q?build(workflow):=20=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=AE=89=E8=A3=85=E8=84=9A=E6=9C=AC=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=9A=84=E5=88=86=E6=94=AF=E5=92=8C=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 GitHub Actions 中 test-install 工作流触发分支增加 master, 并补充更多 Ubuntu 版本(18.04、24.04、25.10)以增强兼容性测试。 docs(readme): 修复 README 中 nodejs 开发环境描述的括号不匹配问题 --- .github/workflows/test-install.yml | 6 +++--- .gitignore | 3 +-- README.md | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 2f9588c..8979d8e 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -2,15 +2,15 @@ name: Test Install Script on: push: - branches: [ dev ] + branches: [ dev, master ] pull_request: - branches: [ dev ] + branches: [ dev, master ] jobs: test-install: strategy: matrix: - os: [ubuntu-20.04, ubuntu-22.04] + os: [ubuntu-18.04, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, ubuntu-25.10] runs-on: ${{ matrix.os }} steps: diff --git a/.gitignore b/.gitignore index 8ec013b..7e99e36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ -IFLOW.md -*.pyc +*.pyc \ No newline at end of file diff --git a/README.md b/README.md index b3aaa32..57907b0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - 一键安装:ROS(支持ROS和ROS2,树莓派Jetson) [贡献@小鱼](https://github.com/fishros) - 一键安装:VsCode(支持amd64和arm64) [贡献@小鱼](https://github.com/fishros) - 一键安装:github桌面版(小鱼常用的github客户端) [贡献@小鱼](https://github.com/fishros) -- 一键安装:nodejs开发环境(通过nodejs可以预览小鱼官网噢 [贡献@小鱼](https://github.com/fishros) +- 一键安装:nodejs开发环境(通过nodejs可以预览小鱼官网噢) [贡献@小鱼](https://github.com/fishros) - 一键配置:rosdep(小鱼的rosdepc,又快又好用) [贡献@小鱼](https://github.com/fishros) - 一键配置:ROS环境(快速更新ROS环境设置,自动生成环境选择) [贡献@小鱼](https://github.com/fishros) - 一键配置:系统源(更换系统源,支持全版本Ubuntu系统) [贡献@小鱼](https://github.com/fishros)