diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 0837f6d..afc0063 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -7,10 +7,212 @@ on: branches: [ dev, master, github_ci_docker_test ] jobs: - test-install: + generate-test-matrix-by-os: + runs-on: ubuntu-latest + outputs: + bionic_matrix: ${{ steps.set-bionic-matrix.outputs.matrix }} + focal_matrix: ${{ steps.set-focal-matrix.outputs.matrix }} + jammy_matrix: ${{ steps.set-jammy-matrix.outputs.matrix }} + noble_matrix: ${{ steps.set-noble-matrix.outputs.matrix }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pyyaml + - name: Generate bionic matrix + id: set-bionic-matrix + run: | + python -c " + import yaml + import json + with open('tests/fish_install_test.yaml', 'r') as f: + test_cases = yaml.safe_load(f) + + # 过滤出bionic系统的测试用例 + bionic_tests = [case for case in test_cases if case.get('target_os_version') == 'bionic'] + matrix = {'include': []} + for i, case in enumerate(bionic_tests): + matrix['include'].append({ + 'test_name': case['name'], + 'test_index': test_cases.index(case) + }) + + print('matrix=' + json.dumps(matrix)) + " >> $GITHUB_OUTPUT + - name: Generate focal matrix + id: set-focal-matrix + run: | + python -c " + import yaml + import json + with open('tests/fish_install_test.yaml', 'r') as f: + test_cases = yaml.safe_load(f) + + # 过滤出focal系统的测试用例 + focal_tests = [case for case in test_cases if case.get('target_os_version') == 'focal'] + matrix = {'include': []} + for i, case in enumerate(focal_tests): + matrix['include'].append({ + 'test_name': case['name'], + 'test_index': test_cases.index(case) + }) + + print('matrix=' + json.dumps(matrix)) + " >> $GITHUB_OUTPUT + - name: Generate jammy matrix + id: set-jammy-matrix + run: | + python -c " + import yaml + import json + with open('tests/fish_install_test.yaml', 'r') as f: + test_cases = yaml.safe_load(f) + + # 过滤出jammy系统的测试用例 + jammy_tests = [case for case in test_cases if case.get('target_os_version') == 'jammy'] + matrix = {'include': []} + for i, case in enumerate(jammy_tests): + matrix['include'].append({ + 'test_name': case['name'], + 'test_index': test_cases.index(case) + }) + + print('matrix=' + json.dumps(matrix)) + " >> $GITHUB_OUTPUT + - name: Generate noble matrix + id: set-noble-matrix + run: | + python -c " + import yaml + import json + with open('tests/fish_install_test.yaml', 'r') as f: + test_cases = yaml.safe_load(f) + + # 过滤出noble系统的测试用例 + noble_tests = [case for case in test_cases if case.get('target_os_version') == 'noble'] + matrix = {'include': []} + for i, case in enumerate(noble_tests): + matrix['include'].append({ + 'test_name': case['name'], + 'test_index': test_cases.index(case) + }) + + print('matrix=' + json.dumps(matrix)) + " >> $GITHUB_OUTPUT + + test-install-bionic: + needs: generate-test-matrix-by-os + if: ${{ needs.generate-test-matrix-by-os.outputs.bionic_matrix != '{}' }} + strategy: + matrix: ${{ fromJSON(needs.generate-test-matrix-by-os.outputs.bionic_matrix) }} + # max-parallel: 2 + fail-fast: false # 即使某些测试失败,也继续运行其他测试 + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run tests in Docker container + run: | + TEST_CASE="bionic" + echo "Using Ubuntu version: $TEST_CASE" + + docker run --rm \ + -v ${{ github.workspace }}:${{ github.workspace }} \ + -w ${{ github.workspace }} \ + ubuntu:$TEST_CASE \ + bash -c " + set -u && + export DEBIAN_FRONTEND=noninteractive && + # Set timezone to avoid tzdata interactive prompt + ln -sf /usr/share/zoneinfo/UTC /etc/localtime && + apt update && + apt install -y locales && + locale-gen en_US.UTF-8 && + export LANG=en_US.UTF-8 && + export LC_ALL=en_US.UTF-8 && + apt update && apt install -y sudo python3 python3-pip python3-venv python3-yaml python3-distro wget && + python3 -m venv /tmp/test_env && + source /tmp/test_env/bin/activate && + pip install --upgrade pip && + pip install pyyaml distro && + cd tests && + PYTHONIOENCODING=utf-8 python3 -u test_runner.py --target-os-version $TEST_CASE --test-index ${{ matrix.test_index }} + " + - name: Upload test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-reports-${{ matrix.test_name }} + path: | + tests/test_report.json + tests/test_report.html + if-no-files-found: ignore + + test-install-focal: + needs: generate-test-matrix-by-os + if: ${{ needs.generate-test-matrix-by-os.outputs.focal_matrix != '{}' }} + strategy: + matrix: ${{ fromJSON(needs.generate-test-matrix-by-os.outputs.focal_matrix) }} + max-parallel: 2 + fail-fast: false # 即使某些测试失败,也继续运行其他测试 + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run tests in Docker container + run: | + TEST_CASE="focal" + echo "Using Ubuntu version: $TEST_CASE" + + docker run --rm \ + -v ${{ github.workspace }}:${{ github.workspace }} \ + -w ${{ github.workspace }} \ + ubuntu:$TEST_CASE \ + bash -c " + set -u && + export DEBIAN_FRONTEND=noninteractive && + # Set timezone to avoid tzdata interactive prompt + ln -sf /usr/share/zoneinfo/UTC /etc/localtime && + apt update && + apt install -y locales && + locale-gen en_US.UTF-8 && + export LANG=en_US.UTF-8 && + export LC_ALL=en_US.UTF-8 && + apt update && apt install -y sudo python3 python3-pip python3-venv python3-yaml python3-distro wget && + python3 -m venv /tmp/test_env && + source /tmp/test_env/bin/activate && + pip install --upgrade pip && + pip install pyyaml distro && + cd tests && + PYTHONIOENCODING=utf-8 python3 -u test_runner.py --target-os-version $TEST_CASE --test-index ${{ matrix.test_index }} + " + - name: Upload test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-reports-${{ matrix.test_name }} + path: | + tests/test_report.json + tests/test_report.html + if-no-files-found: ignore + + test-install-jammy: + needs: generate-test-matrix-by-os + if: ${{ needs.generate-test-matrix-by-os.outputs.jammy_matrix != '{}' }} strategy: - matrix: - ubuntu_version: [18.04, 20.04, 22.04, 24.04] + matrix: ${{ fromJSON(needs.generate-test-matrix-by-os.outputs.jammy_matrix) }} + max-parallel: 2 + fail-fast: false # 即使某些测试失败,也继续运行其他测试 runs-on: ubuntu-latest steps: @@ -19,10 +221,13 @@ jobs: - name: Run tests in Docker container run: | + TEST_CASE="jammy" + echo "Using Ubuntu version: $TEST_CASE" + docker run --rm \ -v ${{ github.workspace }}:${{ github.workspace }} \ -w ${{ github.workspace }} \ - ubuntu:${{ matrix.ubuntu_version }} \ + ubuntu:$TEST_CASE \ bash -c " set -u && export DEBIAN_FRONTEND=noninteractive && @@ -39,14 +244,64 @@ jobs: pip install --upgrade pip && pip install pyyaml distro && cd tests && - PYTHONIOENCODING=utf-8 python3 -u test_runner.py + PYTHONIOENCODING=utf-8 python3 -u test_runner.py --target-os-version $TEST_CASE --test-index ${{ matrix.test_index }} " - name: Upload test reports uses: actions/upload-artifact@v4 if: always() with: - name: test-reports-ubuntu-${{ matrix.ubuntu_version }} + name: test-reports-${{ matrix.test_name }} path: | tests/test_report.json tests/test_report.html if-no-files-found: ignore + + test-install-noble: + needs: generate-test-matrix-by-os + if: ${{ needs.generate-test-matrix-by-os.outputs.noble_matrix != '{}' }} + strategy: + matrix: ${{ fromJSON(needs.generate-test-matrix-by-os.outputs.noble_matrix) }} + max-parallel: 2 + fail-fast: false # 即使某些测试失败,也继续运行其他测试 + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run tests in Docker container + run: | + TEST_CASE="noble" + echo "Using Ubuntu version: $TEST_CASE" + + docker run --rm \ + -v ${{ github.workspace }}:${{ github.workspace }} \ + -w ${{ github.workspace }} \ + ubuntu:$TEST_CASE \ + bash -c " + set -u && + export DEBIAN_FRONTEND=noninteractive && + # Set timezone to avoid tzdata interactive prompt + ln -sf /usr/share/zoneinfo/UTC /etc/localtime && + apt update && + apt install -y locales && + locale-gen en_US.UTF-8 && + export LANG=en_US.UTF-8 && + export LC_ALL=en_US.UTF-8 && + apt update && apt install -y sudo python3 python3-pip python3-venv python3-yaml python3-distro wget && + python3 -m venv /tmp/test_env && + source /tmp/test_env/bin/activate && + pip install --upgrade pip && + pip install pyyaml distro && + cd tests && + PYTHONIOENCODING=utf-8 python3 -u test_runner.py --target-os-version $TEST_CASE --test-index ${{ matrix.test_index }} + " + - name: Upload test reports + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-reports-${{ matrix.test_name }} + path: | + tests/test_report.json + tests/test_report.html + if-no-files-found: ignore \ No newline at end of file diff --git a/tests/fish_install_test.yaml b/tests/fish_install_test.yaml index 7bea6ee..ad30f5f 100644 --- a/tests/fish_install_test.yaml +++ b/tests/fish_install_test.yaml @@ -4,15 +4,17 @@ # target_os_version: "目标系统版本代号" (可选,如果不指定则适用于所有系统) # chooses: [{choose: <选项ID>, desc: <选项描述>}] + # 为不同的 Ubuntu 版本定义具体的测试配置 -# Ubuntu 18.04 (bionic) - Melodic +################################################################################################################################################################## +# Ubuntu 18.04 (bionic) - name: "Install_ROS_bionic_source1" target_os_version: "bionic" chooses: - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 2, desc: "不更换系统源再继续安装" } - - { choose: 1, desc: "清华源" } - - { choose: 1, desc: "支持的第一个ros版本" } + - { choose: 1, desc: "中科大源" } + - { choose: 5, desc: "支持的第5个ros版本" } # 中科大,清华,华为,中山大学源不支持Ubuntu18.04安装ros2-bouncy 其余ros2没测试 - { choose: 2, desc: "基础版(小)" } - name: "Install_ROS_bionic_source2" @@ -20,8 +22,8 @@ chooses: - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 2, desc: "不更换系统源再继续安装" } - - { choose: 2, desc: "中科大源" } - - { choose: 1, desc: "支持的第一个ros版本" } + - { choose: 2, desc: "清华源" } + - { choose: 5, desc: "支持的第5个ros版本" } - { choose: 2, desc: "基础版(小)" } - name: "Install_ROS_bionic_source3" @@ -30,7 +32,7 @@ - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 2, desc: "不更换系统源再继续安装" } - { choose: 3, desc: "华为源" } - - { choose: 1, desc: "支持的第一个ros版本" } + - { choose: 5, desc: "支持的第5个ros版本" } - { choose: 2, desc: "基础版(小)" } - name: "Install_ROS_bionic_source4" @@ -39,7 +41,7 @@ - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 2, desc: "不更换系统源再继续安装" } - { choose: 4, desc: "中山大学源" } - - { choose: 1, desc: "支持的第一个ros版本" } + - { choose: 5, desc: "支持的第5个ros版本" } - { choose: 2, desc: "基础版(小)" } - name: "Install_ROS_bionic_source5" @@ -48,10 +50,11 @@ - { choose: 1, desc: "一键安装(推荐):ROS(支持ROS/ROS2,树莓派Jetson)" } - { choose: 2, desc: "不更换系统源再继续安装" } - { choose: 5, desc: "ROS官方源" } - - { choose: 1, desc: "支持的第一个ros版本" } + - { choose: 5, desc: "支持的第5个ros版本" } - { choose: 2, desc: "基础版(小)" } -# Ubuntu 20.04 (focal) - Noetic 或 Foxy +################################################################################################################################################################## +# Ubuntu 20.04 (focal) - name: "Install_ROS_focal_noetic" target_os_version: "focal" chooses: @@ -78,7 +81,8 @@ # - { choose: 1, desc: "支持的第一个ros版本" } # - { choose: 2, desc: "基础版(小)" } -# Ubuntu 22.04 (jammy) - Humble +################################################################################################################################################################## +# Ubuntu 22.04 (jammy) - name: "Install_ROS_jammy" target_os_version: "jammy" chooses: @@ -88,7 +92,8 @@ - { choose: 1, desc: "支持的第一个ros版本" } - { choose: 2, desc: "基础版(小)" } -# Ubuntu 24.04 (noble) - Jazzy +################################################################################################################################################################## +# Ubuntu 24.04 (noble) - name: "Install_ROS_noble" target_os_version: "noble" chooses: diff --git a/tests/test_runner.py b/tests/test_runner.py index 39968e1..538bdc9 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -10,6 +10,13 @@ import re import argparse +# 尝试导入select模块,用于非阻塞I/O操作 +try: + import select + HAVE_SELECT = True +except ImportError: + HAVE_SELECT = False + # 将项目根目录添加到 Python 路径中,以便能找到 tools 模块 sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")) @@ -285,17 +292,83 @@ def run_install_test(test_case): env={**os.environ, 'FISH_INSTALL_CONFIG': '../fish_install.yaml'} ) - # 实时打印输出 + # 实时打印输出,添加3分钟无日志超时检测 print("=== 脚本输出开始 ===") - while True: - output_line = process.stdout.readline() - if output_line == '' and process.poll() is not None: - break - if output_line: - print(output_line.strip()) - output += output_line - # 确保实时刷新输出 - sys.stdout.flush() + last_output_time = time.time() + timeout_seconds = 180 # 3分钟超时 + + if HAVE_SELECT: + # 在支持select的系统上使用非阻塞I/O + while True: + # 检查是否超时 + if time.time() - last_output_time > timeout_seconds: + print(f"错误: 测试 {name} 超时,超过 {timeout_seconds} 秒无日志输出,自动退出") + # 终止进程 + process.terminate() + try: + process.wait(timeout=10) # 等待最多10秒优雅退出 + except subprocess.TimeoutExpired: + process.kill() # 强制杀死进程 + process.wait() + output += f"\n错误: 测试超时,超过 {timeout_seconds} 秒无日志输出,自动退出" + return False, output + + # 检查进程是否已经结束 + if process.poll() is not None: + # 读取剩余的所有输出 + remaining_output = process.stdout.read() + if remaining_output: + print(remaining_output.strip()) + output += remaining_output + last_output_time = time.time() # 更新最后输出时间 + break + + # 非阻塞读取输出 + if select.select([process.stdout], [], [], 0.1)[0]: + output_line = process.stdout.readline() + if output_line: + print(output_line.strip()) + output += output_line + last_output_time = time.time() # 更新最后输出时间 + # 确保实时刷新输出 + sys.stdout.flush() + elif process.poll() is not None: + # 进程结束且没有更多输出 + break + else: + # 没有可读取的数据,短暂休眠 + time.sleep(0.1) + else: + # 在不支持select的系统上使用标准方法(如Windows) + while True: + # 检查是否超时 + if time.time() - last_output_time > timeout_seconds: + print(f"错误: 测试 {name} 超时,超过 {timeout_seconds} 秒无日志输出,自动退出") + # 终止进程 + process.terminate() + try: + process.wait(timeout=10) # 等待最多10秒优雅退出 + except subprocess.TimeoutExpired: + process.kill() # 强制杀死进程 + process.wait() + output += f"\n错误: 测试超时,超过 {timeout_seconds} 秒无日志输出,自动退出" + return False, output + + # 检查进程是否已经结束 + if process.poll() is not None: + break + + # 使用poll方法检查是否有数据可读 + output_line = process.stdout.readline() + if output_line: + print(output_line.strip()) + output += output_line + last_output_time = time.time() # 更新最后输出时间 + # 确保实时刷新输出 + sys.stdout.flush() + else: + # 没有输出,短暂休眠 + time.sleep(0.1) print("=== 脚本输出结束 ===") # 等待进程结束,超时时间为 2 小时 @@ -356,6 +429,7 @@ def main(): # 解析命令行参数 parser = argparse.ArgumentParser(description='运行一键安装工具测试') parser.add_argument('--target-os-version', type=str, help='目标Ubuntu版本代号 (例如: bionic, focal, jammy, noble)') + parser.add_argument('--test-index', type=int, help='特定测试用例的索引') args = parser.parse_args() target_os_version = args.target_os_version @@ -398,6 +472,23 @@ def main(): print("错误: 没有找到适用于所有系统的通用测试用例") sys.exit(1) + # 如果指定了测试用例索引,则只运行该索引的测试用例 + if args.test_index is not None: + # 直接根据索引在所有测试用例中查找 + if 0 <= args.test_index < len(all_test_cases): + target_test_case = all_test_cases[args.test_index] + # 确保该测试用例适用于当前系统版本 + if (target_test_case.get('target_os_version') == target_os_version or + (target_os_version and 'target_os_version' not in target_test_case)): + test_cases = [target_test_case] + print(f"只运行指定索引的测试用例: {target_test_case.get('name', 'Unknown Test')}") + else: + print(f"错误: 索引为 {args.test_index} 的测试用例不适用于系统版本 {target_os_version}") + sys.exit(1) + else: + print(f"错误: 测试用例索引 {args.test_index} 超出范围 (0-{len(all_test_cases)-1})") + sys.exit(1) + print(f"共找到 {len(test_cases)} 个适用于当前系统版本的测试用例") # 运行所有测试用例并收集结果 @@ -457,12 +548,14 @@ def main(): print(f"失败: {failed}") print(f"总计: {len(test_cases)}") + # 根据是否有测试失败,设置适当的退出码 + # 在并行测试环境中,这将由每个单独的job处理 if failed > 0: print("部分测试失败,请检查日志和测试报告。") - sys.exit(1) + sys.exit(1) # 有测试失败,返回非零退出码 else: print("所有测试通过!") - sys.exit(0) + sys.exit(0) # 所有测试通过,返回零退出码 if __name__ == "__main__": main() \ No newline at end of file diff --git a/tools/tool_install_ros.py b/tools/tool_install_ros.py index ba43c84..18f4d4e 100644 --- a/tools/tool_install_ros.py +++ b/tools/tool_install_ros.py @@ -58,9 +58,10 @@ def get_version(name): @staticmethod def install_depend(name): depends = RosVersions.get_version(name).deps - for dep in depends: - AptUtils.install_pkg(dep) - + if depends: + # 批量安装依赖包,提高效率 + dep_string = " ".join(depends) + AptUtils.install_pkg(dep_string) @staticmethod def tip_test_command(name): @@ -90,61 +91,59 @@ def get_desktop_version(name): "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/"}, - "repo-ros2":{"ROS2":"http://repo.ros2.org/ubuntu/"} } ros_dist_dic = { - 'artful':{"packages.ros"}, - 'bionic':{"tsinghua","ustc","huawei","mirrorz","packages.ros","https.packages.ros"}, - 'buster':{"packages.ros"}, - 'cosmic':{"packages.ros"}, - 'disco':{"packages.ros"}, - 'eoan':{"packages.ros"}, - 'focal':{"tsinghua","ustc","huawei","mirrorz","packages.ros","https.packages.ros"}, - 'jessie':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'lucid':{"packages.ros"}, - 'maverick':{"packages.ros"}, - 'natty':{"packages.ros"}, - 'oneiric':{"packages.ros"}, - 'precise':{"packages.ros"}, - 'quantal':{"packages.ros"}, - 'raring':{"packages.ros"}, - 'saucy':{"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","ustc","huawei","mirrorz","packages.ros","https.packages.ros"}, - 'yakkety':{"packages.ros"}, - 'zesty':{"packages.ros"}, + 'artful': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'bionic': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'buster': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'cosmic': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'disco': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'eoan': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'focal': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'jessie': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'lucid': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'maverick': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'natty': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'oneiric': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'precise': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'quantal': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'raring': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'saucy': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'stretch': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'trusty': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'utopic': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'vivid': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'wheezy': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'wily': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'xenial': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'yakkety': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'zesty': {"tsinghua", "ustc", "huawei", "packages.ros", }, } ros2_dist_dic = { - 'bionic':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'bullseye':{"tsinghua","ustc","huawei","packages.ros","https.packages.ros"}, - 'buster':{"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","mirrorz","packages.ros","https.packages.ros"}, - 'noble':{"tsinghua","ustc","huawei","mirrorz","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"}, + 'bionic': {"tsinghua", "mirrorz", "huawei", "packages.ros", }, + 'bookworm': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'bullseye': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'buster': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'cosmic': {"packages.ros", }, + 'disco': {"packages.ros", }, + 'eoan': {"packages.ros", }, + 'focal': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'jessie': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'jammy': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'noble': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'stretch': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'trixie': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'trusty': {"tsinghua", "ustc", "huawei", "packages.ros", }, + 'xenial': {"tsinghua", "ustc", "huawei", "mirrorz", "packages.ros", }, + 'utopic': {"packages.ros", }, + 'yakkety': {"packages.ros", }, + 'zesty': {"packages.ros", }, } - - class Tool(BaseTool): def __init__(self): self.name = "一键安装ROS和ROS2,支持树莓派Jetson"