| @@ -0,0 +1,124 @@ | |||
| 1 | +# Dev Container 快速入门指南 | ||
| 2 | + | ||
| 3 | +> **真正开箱即用**:零手动配置!**首次 2 分钟** 全自动构建,**后续 10 秒** 极速开工。 | ||
| 4 | + | ||
| 5 | +## 🛠️ 极简前置准备 | ||
| 6 | + | ||
| 7 | +无需手动配置复杂工具链,请根据场景选择以下任一基础环境: | ||
| 8 | + | ||
| 9 | +| 方案 | VS Code 安装 | Docker 服务 | 适用场景 | | ||
| 10 | +| :--- |:---------------------------------------------------------------------------------|:----------------------------------------------------------------------------| :--- | | ||
| 11 | +| **远程服务器(推荐)** | [VS Code](https://code.visualstudio.com/) + `Dev Containers` + `Remote - SSH` 插件 | Linux 服务器已启用 Docker 服务 | 高性能计算、释放本地资源 | | ||
| 12 | +| **本地 PC** | [VS Code](https://code.visualstudio.com/) + `Dev Containers` 插件 | [Docker Desktop](https://www.docker.com/products/docker-desktop/)(Linux 模式) | 单机离线开发 | | ||
| 13 | + | ||
| 14 | +> ⚠️ *注意:默认配置启用了 Host 网络模式及高权限,请务必在可信环境中使用。* | ||
| 15 | + | ||
| 16 | +## 🚀 3 步闪电开工 | ||
| 17 | + | ||
| 18 | +1. **打开项目**:在 VS Code 中打开本项目代码目录。 | ||
| 19 | +2. **加载容器**:点击右下角弹出的 **`Reopen in Container`** 提示(或通过 `F1` 执行同名命令)。 | ||
| 20 | +3. **进入开发**:待容器环境自动初始化完成后,即可直接进行编码、编译、单元测试及调试。 | ||
| 21 | + | ||
| 22 | +## 🔨 编译与单元测试 | ||
| 23 | + | ||
| 24 | +环境就绪后,通过 VS Code 菜单栏 **`Terminal`** > **`Run Task`** 即可调用预设的自动化任务: | ||
| 25 | + | ||
| 26 | +| 任务名称 | 功能说明 | | ||
| 27 | +| :--- | :--- | | ||
| 28 | +| `Build: Release Mode` | 构建 Release 版本,产物输出至 `artifacts` 目录 | | ||
| 29 | +| `Build: Debug Mode` | 构建 Debug 版本(仅 C++ 项目支持,Python 项目请忽略) | | ||
| 30 | +| `Test: Run Unit Tests` | 执行全量单元测试 | | ||
| 31 | +| `Clean: All Workspace` | 清理工作区内的所有构建缓存与临时文件 | | ||
| 32 | + | ||
| 33 | +> *也可直接在终端执行 `python3 build.py` 命令,其功能与上述任务一致。* | ||
| 34 | + | ||
| 35 | +## ⏱️ 自动化流程与耗时说明 | ||
| 36 | + | ||
| 37 | +启动 Dev Container 后,系统将**全自动完成以下环境配置**: | ||
| 38 | + | ||
| 39 | +| 阶段 | 自动化任务 | 首次耗时 | 后续启动 | 体验 | | ||
| 40 | +|:--------------|:---------------------------------| :--- | :--- | :--- | | ||
| 41 | +| **1. 环境拉取** | 拉取预置镜像并部署 VS Code Server | ~1 分钟 | 3 秒 | 全程无感 | | ||
| 42 | +| **2. 身份与挂载** | 挂载代码目录(`/workspace`)并同步 Git 权限 | ~10 秒 | 3 秒 | 全程无感 | | ||
| 43 | +| **3. 工具链加载** | 并行安装 Python 插件及 Clangd 等开发工具 | ~20 秒 | 3 秒 | 开箱即用 | | ||
| 44 | +| **总计** | **零人工干预·全自动就绪** | **⏱️ ~2 分钟** | **⚡ ~10 秒** | **一次配置,持续高效** | | ||
| 45 | + | ||
| 46 | +> **镜像说明**:因 MindStudio 镜像制作流程复杂且耗时,本方案**内置预构建镜像**。若需了解镜像细节,可参考 [《MindStudio 统一构建镜像制作指南》](https://gitcode.com/Ascend/msot/blob/master/docs/zh/common/docker_image_build_guide.md)。 | ||
| 47 | + | ||
| 48 | +## 💡 效率优化与故障恢复 | ||
| 49 | + | ||
| 50 | +### 1. 配置 SSH 免密登录(10 秒完成) | ||
| 51 | + | ||
| 52 | +为避免频繁输入密码,可在 Windows PowerShell 中粘贴执行以下脚本,按提示操作即可自动完成配置: | ||
| 53 | + | ||
| 54 | +```powershell | ||
| 55 | +# 1. 交互式输入用户名和IP地址 | ||
| 56 | +$ip = Read-Host "请输入远程服务器的IP地址" | ||
| 57 | +$user = Read-Host "请输入远程服务器的用户名" | ||
| 58 | + | ||
| 59 | +# 2. 定义本地SSH相关路径 | ||
| 60 | +$sshDir = "$env:USERPROFILE\.ssh" | ||
| 61 | +$pubKeyPath = "$sshDir\id_ed25519.pub" | ||
| 62 | + | ||
| 63 | +# 3. 检查本地是否存在公钥,若不存在则自动生成 | ||
| 64 | +if (-not (Test-Path $pubKeyPath)) { | ||
| 65 | + Write-Host "未检测到本地公钥,正在生成 ed25519 密钥对..." -ForegroundColor Yellow | ||
| 66 | + ssh-keygen -t ed25519 -C "mindstudio_devcontainer" -f "$sshDir\id_ed25519" -N '""' | ||
| 67 | + Write-Host "密钥对生成完毕。" -ForegroundColor Green | ||
| 68 | +} | ||
| 69 | + | ||
| 70 | +# 4. 上传公钥至远程服务器 | ||
| 71 | +Write-Host "正在将公钥上传至 ${user}@${ip} ..." -ForegroundColor Cyan | ||
| 72 | +Get-Content $pubKeyPath | ssh "${user}@${ip}" "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys" | ||
| 73 | +Write-Host "公钥上传完成,免密登录配置成功!" -ForegroundColor Green | ||
| 74 | +``` | ||
| 75 | + | ||
| 76 | +### 2. 毁坏无忧:一键复原环境 | ||
| 77 | + | ||
| 78 | +若开发过程中容器环境搞乱或损坏,无需重新搭建:只需按 `F1` 键选择 **Dev Containers: Rebuild Container**,即可瞬间获得一个全新的纯净环境! | ||
| 79 | + | ||
| 80 | +## ❓ FAQ | ||
| 81 | + | ||
| 82 | +### 1. VS Code 远程连接卡在“Waiting for port forwarding...”? | ||
| 83 | + | ||
| 84 | +**原因分析**: | ||
| 85 | +VS Code 远程开发依赖 SSH 端口转发。若服务端 `sshd_config` 限制过严,或远程 VS Code Server 组件异常,均会导致连接挂起。 | ||
| 86 | + | ||
| 87 | +**解决方案**: | ||
| 88 | + | ||
| 89 | +1. **检查服务端 SSH 配置** | ||
| 90 | + | ||
| 91 | + - 编辑 `/etc/ssh/sshd_config`(需 root 权限),确保以下参数已启用: | ||
| 92 | + | ||
| 93 | + ```bash | ||
| 94 | + AllowTcpForwarding yes | ||
| 95 | + GatewayPorts yes | ||
| 96 | + X11Forwarding yes | ||
| 97 | + ``` | ||
| 98 | + | ||
| 99 | + - **关键检查**:确认不存在 `PermitOpen none` 配置,若有请注释掉(`#PermitOpen none`),否则将禁用所有端口转发。 | ||
| 100 | + - 重启 SSH 服务:`sudo systemctl restart sshd` | ||
| 101 | + | ||
| 102 | +2. **清理远程 VS Code Server** | ||
| 103 | + 若配置无误仍无法连接,可能是服务端组件损坏或版本不匹配。 | ||
| 104 | + - 在远程服务器执行:`rm -rf ~/.vscode-server` | ||
| 105 | + - 重新发起连接,VS Code 将自动重新部署匹配的 Server 组件。 | ||
| 106 | + | ||
| 107 | +### 2. 代码提交响应缓慢或无反馈? | ||
| 108 | + | ||
| 109 | +**原因分析**: | ||
| 110 | +项目默认启用 `pre-commit` 钩子。首次提交时需下载并初始化检查工具,耗时约 30~60 秒。后续提交将直接执行检查,响应通常为秒级。 | ||
| 111 | + | ||
| 112 | +### 3. 修改 `.vscode/settings.json` 后 `git pull` 冲突且无法更新? | ||
| 113 | + | ||
| 114 | +**原因分析**: | ||
| 115 | +为支持个性化配置,该文件被标记为 `skip-worktree`,本地修改不会显示在 `git status` 中。当远端同步更新该文件时,Git 会拒绝覆盖本地内容以防止丢失。 | ||
| 116 | + | ||
| 117 | +**解决方案**: | ||
| 118 | +请使用封装命令更新代码(**注意**:此操作将以远端版本覆盖本地,请提前备份): | ||
| 119 | + | ||
| 120 | +```bash | ||
| 121 | +git safe-pull | ||
| 122 | +``` | ||
| 123 | + | ||
| 124 | +该命令会自动处理 `skip-worktree` 标记与本地暂存:拉取成功后应用远端版本并恢复标记;若拉取失败,本地修改将保留在 stash 中,确保数据安全。 | ||
| @@ -0,0 +1,109 @@ | |||
| 1 | +// ------------------------------------------------------------------------- | ||
| 2 | +// This file is part of the MindStudio project. | ||
| 3 | +// Copyright (c) 2025 Huawei Technologies Co.,Ltd. | ||
| 4 | +// | ||
| 5 | +// MindStudio is licensed under Mulan PSL v2. | ||
| 6 | +// You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 7 | +// You may obtain a copy of Mulan PSL v2 at: | ||
| 8 | +// | ||
| 9 | +// http://license.coscl.org.cn/MulanPSL2 | ||
| 10 | +// | ||
| 11 | +// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 12 | +// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 13 | +// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 14 | +// See the Mulan PSL v2 for more details. | ||
| 15 | +// ------------------------------------------------------------------------- | ||
| 16 | + | ||
| 17 | +{ | ||
| 18 | + "name": "mskl", | ||
| 19 | + "image": "swr.cn-north-4.myhuaweicloud.com/mindstudio-image/mindstudio-build:26.1.0-0701", | ||
| 20 | + | ||
| 21 | + // 镜像 WORKDIR 即 /workspace;z_cache.sh 通过设备号探测 /workspace 是否为 | ||
| 22 | + // 独立挂载点,命中后自动把 ccache/uv 缓存放到 /workspace/.cache 下, | ||
| 23 | + // 因此 workspaceFolder 必须与镜像约定的 /workspace 保持一致,缓存才能生效。 | ||
| 24 | + "workspaceFolder": "/workspace", | ||
| 25 | + "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=cached", | ||
| 26 | + | ||
| 27 | + // 宿主机初始化:容器创建前拉取镜像、准备 Git 配置快照,并创建 bind mount | ||
| 28 | + // 所需的 uv 缓存目录。脚本必须在 Docker daemon 所在主机执行。 | ||
| 29 | + "initializeCommand": "bash ${localWorkspaceFolder}/.devcontainer/initialize.sh", | ||
| 30 | + | ||
| 31 | + // 镜像内非 root 业务用户,镜像默认 UID/GID 为 20001。Dev Containers 在 | ||
| 32 | + // Linux 上按宿主用户更新其 UID/GID,避免向 workspace 写入 root 属主文件。 | ||
| 33 | + "remoteUser": "mindstudio", | ||
| 34 | + "updateRemoteUserUID": true, | ||
| 35 | + | ||
| 36 | + // 对齐 ctr_in.py 启动 mindstudio-build 时的 docker run 关键参数: | ||
| 37 | + // --network=host、--privileged、--ipc=host、/usr/local/sbin 只读挂载。 | ||
| 38 | + // 这些参数会放宽容器隔离边界,保留它们是为了与 MindStudio 构建镜像行为一致。 | ||
| 39 | + // ctr_in.py 的 --user root + HOST_UID/HOST_GID/HOST_HOME 用于触发镜像 | ||
| 40 | + // entrypoint 做用户重映射;devcontainer 这里交给 updateRemoteUserUID 处理, | ||
| 41 | + // 因此不再强制 containerUser=root,避免和 VS Code 的用户管理打架。 | ||
| 42 | + "runArgs": [ | ||
| 43 | + "--network=host", | ||
| 44 | + "--privileged", | ||
| 45 | + "--ipc=host", | ||
| 46 | + "--ulimit", "nproc=65535:65535", | ||
| 47 | + "--security-opt", "seccomp=unconfined" | ||
| 48 | + ], | ||
| 49 | + "mounts": [ | ||
| 50 | + // 构建工具可能调用宿主机提供的系统管理脚本,仅以只读方式暴露。 | ||
| 51 | + "source=/usr/local/sbin,target=/usr/local/sbin,type=bind,readonly", | ||
| 52 | + // initialize.sh 生成宿主 ~/.gitconfig 的快照;容器只读挂载该文件, | ||
| 53 | + // post-create.sh 仅从中读取 user.name 和 user.email,不挂载整个宿主 home。 | ||
| 54 | + "source=${localWorkspaceFolder}/.devcontainer/.host-gitconfig,target=/tmp/host-gitconfig,type=bind,readonly", | ||
| 55 | + | ||
| 56 | + // uv 缓存持久化:将宿主机 ~/.cache/uv bind mount 到容器内。 | ||
| 57 | + // 配合下方 UV_CACHE_DIR 环境变量,uv 安装/构建缓存不受容器重建影响。 | ||
| 58 | + "source=${localEnv:HOME}/.cache/uv,target=/home/mindstudio/.cache/uv,type=bind" | ||
| 59 | + ], | ||
| 60 | + "containerEnv": { | ||
| 61 | + // ctr_in.py 进入 mindstudio-build 容器时注入,保证 profile.d 启用 gcc11 | ||
| 62 | + // 时不把构建镜像专属 rpath 写入手工编译产物。 | ||
| 63 | + "GCC11_NO_RPATH": "1", | ||
| 64 | + // 用户执行 npm install -g 时,镜像默认 prefix 位于 | ||
| 65 | + // /usr/local/nodejs,非 root 用户没有写权限,因此把全局 npm 包放到用户目录。 | ||
| 66 | + "NPM_CONFIG_PREFIX": "/home/mindstudio/.local", | ||
| 67 | + // 配合上方 mounts 中宿主机 uv 缓存目录挂载,固化 uv 包安装缓存路径。 | ||
| 68 | + "UV_CACHE_DIR": "/home/mindstudio/.cache/uv" | ||
| 69 | + }, | ||
| 70 | + "remoteEnv": { | ||
| 71 | + // VS Code Server 及其扩展进程不一定读取 shell 启动文件,显式补充用户命令目录。 | ||
| 72 | + "PATH": "/home/mindstudio/.local/bin:${containerEnv:PATH}" | ||
| 73 | + }, | ||
| 74 | + | ||
| 75 | + // profile.d 中的 z_gcc11.sh / z_cann.sh / z_python311.sh 只在交互式 / | ||
| 76 | + // login shell 下自动 source(openEuler /etc/bashrc 触发),因此: | ||
| 77 | + // 1) postCreateCommand 用 `bash -lc` 显式走 login shell,才能拿到 CANN / | ||
| 78 | + // gcc11 / python3.11 环境; | ||
| 79 | + // 2) VS Code 集成终端默认也切到 login shell,交互式操作体验与镜像一致。 | ||
| 80 | + "postCreateCommand": "bash -lc '/workspace/.devcontainer/post-create.sh'", | ||
| 81 | + | ||
| 82 | + "customizations": { | ||
| 83 | + "vscode": { | ||
| 84 | + // 扩展安装由 Dev Containers 管理,容器重建后自动恢复。 | ||
| 85 | + "extensions": [ | ||
| 86 | + "ms-python.python", | ||
| 87 | + "ms-python.vscode-pylance", | ||
| 88 | + "charliermarsh.ruff", | ||
| 89 | + "shd101wyy.markdown-preview-enhanced" | ||
| 90 | + ], | ||
| 91 | + "settings": { | ||
| 92 | + // login shell 会加载镜像 /etc/profile.d 下的 CANN、GCC 和 Python 环境。 | ||
| 93 | + "terminal.integrated.defaultProfile.linux": "bash-login", | ||
| 94 | + "terminal.integrated.profiles.linux": { | ||
| 95 | + "bash-login": { | ||
| 96 | + "path": "/bin/bash", | ||
| 97 | + "args": ["-l"] | ||
| 98 | + } | ||
| 99 | + }, | ||
| 100 | + // post-create 默认激活 Python 3.11,此处跟随当前 PATH 中的 python3。 | ||
| 101 | + "python.defaultInterpreterPath": "python3", | ||
| 102 | + // 将仓库根目录加入 Pylance 的额外模块搜索路径。 | ||
| 103 | + "python.analysis.extraPaths": [ | ||
| 104 | + "${workspaceFolder}" | ||
| 105 | + ] | ||
| 106 | + } | ||
| 107 | + } | ||
| 108 | + } | ||
| 109 | +} | ||
| @@ -0,0 +1,77 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# ------------------------------------------------------------------------- | ||
| 3 | +# This file is part of the MindStudio project. | ||
| 4 | +# Copyright (c) 2025 Huawei Technologies Co.,Ltd. | ||
| 5 | +# | ||
| 6 | +# MindStudio is licensed under Mulan PSL v2. | ||
| 7 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 8 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 9 | +# | ||
| 10 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 11 | +# | ||
| 12 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 13 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 14 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 15 | +# See the Mulan PSL v2 for more details. | ||
| 16 | +# ------------------------------------------------------------------------- | ||
| 17 | + | ||
| 18 | +# git safe-pull 的实际执行脚本,由 post-create.sh 安装到 | ||
| 19 | +# $HOME/.local/bin/git-safe-pull,并注册为全局 Git alias。 | ||
| 20 | +# | ||
| 21 | +# 处理流程: | ||
| 22 | +# 1. 收集当前仓库全部 skip-worktree 文件。 | ||
| 23 | +# 2. 临时取消标记,使 Git 能识别并 stash 这些文件的本地修改。 | ||
| 24 | +# 3. 执行 git pull,并原样透传 --rebase 等调用参数。 | ||
| 25 | +# 4. pull 成功时丢弃临时 stash,以远端版本为准;失败时保留 stash。 | ||
| 26 | +# 5. 通过 EXIT trap 恢复 skip-worktree 标记。 | ||
| 27 | +# | ||
| 28 | +# 限制:本命令只处理 skip-worktree 文件,不会自动暂存其它未提交修改;其它文件 | ||
| 29 | +# 仍遵循 git pull 的标准冲突与保护行为。 | ||
| 30 | + | ||
| 31 | +set -euo pipefail | ||
| 32 | + | ||
| 33 | +# git ls-files -v 以行首 S 表示 skip-worktree。使用数组保存文件名,避免 xargs | ||
| 34 | +# 或多层 shell 引号破坏带空格的路径。 | ||
| 35 | +files=() | ||
| 36 | +while IFS= read -r line; do | ||
| 37 | + files+=("${line:2}") | ||
| 38 | +done < <(git ls-files -v | sed -n '/^S /p') | ||
| 39 | + | ||
| 40 | +if [ "${#files[@]}" -eq 0 ]; then | ||
| 41 | + # 没有特殊文件时不增加额外流程,直接用当前进程执行标准 pull。 | ||
| 42 | + exec git pull "$@" | ||
| 43 | +fi | ||
| 44 | + | ||
| 45 | +# 无论 pull、stash drop 或其它步骤在哪一点退出,都尽力恢复索引标记。 | ||
| 46 | +restore_skip_worktree() { | ||
| 47 | + git update-index --skip-worktree -- "${files[@]}" 2>/dev/null || true | ||
| 48 | +} | ||
| 49 | +trap restore_skip_worktree EXIT | ||
| 50 | + | ||
| 51 | +# 取消标记后,git stash 才能识别这些文件的本地修改。 | ||
| 52 | +git update-index --no-skip-worktree -- "${files[@]}" | ||
| 53 | + | ||
| 54 | +# 对比操作前后的 refs/stash,区分“没有本地修改”与“确实创建了新 stash”。 | ||
| 55 | +stash_before=$(git rev-parse -q --verify refs/stash 2>/dev/null || true) | ||
| 56 | +git stash push -q -m "devcontainer-safe-pull" -- "${files[@]}" || true | ||
| 57 | +stash_after=$(git rev-parse -q --verify refs/stash 2>/dev/null || true) | ||
| 58 | +stash_created=false | ||
| 59 | +if [ -n "$stash_after" ] && [ "$stash_after" != "$stash_before" ]; then | ||
| 60 | + stash_created=true | ||
| 61 | +fi | ||
| 62 | + | ||
| 63 | +if ! git pull "$@"; then | ||
| 64 | + # pull 失败时不自动 pop,避免在冲突或未完成的 merge/rebase 上叠加修改。 | ||
| 65 | + # 用户可在处理 Git 状态后按提示的提交 ID 手工恢复。 | ||
| 66 | + if [ "$stash_created" = true ]; then | ||
| 67 | + echo "[safe-pull] pull 失败,本地个性化修改已保存在 stash:$stash_after" >&2 | ||
| 68 | + fi | ||
| 69 | + exit 1 | ||
| 70 | +fi | ||
| 71 | + | ||
| 72 | +# 新 stash 固定位于栈顶;pull 成功后使用远端版本并删除该临时条目。 | ||
| 73 | +if [ "$stash_created" = true ]; then | ||
| 74 | + git stash drop -q 'stash@{0}' | ||
| 75 | +fi | ||
| 76 | + | ||
| 77 | +printf '[safe-pull] skip-worktree 文件已恢复:%s\n' "${files[*]}" | ||
| @@ -0,0 +1,60 @@ | |||
| 1 | +#!/bin/bash | ||
| 2 | +# ------------------------------------------------------------------------- | ||
| 3 | +# This file is part of the MindStudio project. | ||
| 4 | +# Copyright (c) 2025 Huawei Technologies Co.,Ltd. | ||
| 5 | +# | ||
| 6 | +# MindStudio is licensed under Mulan PSL v2. | ||
| 7 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 8 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 9 | +# | ||
| 10 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 11 | +# | ||
| 12 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 13 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 14 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 15 | +# See the Mulan PSL v2 for more details. | ||
| 16 | +# ------------------------------------------------------------------------- | ||
| 17 | + | ||
| 18 | +# --------------------------------------------------------------------------- | ||
| 19 | +# initialize.sh - devcontainer initializeCommand | ||
| 20 | +# | ||
| 21 | +# 在宿主侧、容器创建之前执行,完成三件事: | ||
| 22 | +# 1. 拉取最新镜像(镜像名从 devcontainer.json 的 image 字段读取,无需重复维护) | ||
| 23 | +# 2. 准备宿主 ~/.gitconfig 快照,供容器读取 Git 用户名和邮箱 | ||
| 24 | +# 3. 创建宿主 uv 缓存目录,避免 bind mount 的 source 路径不存在 | ||
| 25 | +# | ||
| 26 | +# 本脚本由 Dev Containers 在宿主机执行,不应依赖容器内路径或工具。 | ||
| 27 | +# --------------------------------------------------------------------------- | ||
| 28 | + | ||
| 29 | +set -euo pipefail | ||
| 30 | + | ||
| 31 | +# 脚本所在目录即 .devcontainer/ | ||
| 32 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| 33 | + | ||
| 34 | +# ---- 拉取最新镜像 ---- | ||
| 35 | +# 使用 python3 过滤 // 和 /* */ 注释后解析 JSON,比 sed+jq 更鲁棒地 | ||
| 36 | +# 处理单行注释和多行注释块。 | ||
| 37 | +IMAGE="$(python3 -c " | ||
| 38 | +import re, json | ||
| 39 | +with open('$SCRIPT_DIR/devcontainer.json') as f: | ||
| 40 | + text = re.sub(r'//.*$|/\*[\s\S]*?\*/', '', f.read(), flags=re.MULTILINE) | ||
| 41 | + print(json.loads(text)['image']) | ||
| 42 | +")" | ||
| 43 | +echo "==> Pulling image: $IMAGE" | ||
| 44 | +docker pull "$IMAGE" | ||
| 45 | + | ||
| 46 | +# ---- 准备 Git 配置快照 ---- | ||
| 47 | +# 快照文件位于仓库的 .devcontainer/ 下并被 .gitignore 排除。当前实现复制完整 | ||
| 48 | +# 配置文件;容器内 post-create.sh 只读取 user.name 和 user.email,不会把其它 | ||
| 49 | +# 配置写入容器全局 Git 配置。 | ||
| 50 | +if [ -f "$HOME/.gitconfig" ]; then | ||
| 51 | + cp "$HOME/.gitconfig" "$SCRIPT_DIR/.host-gitconfig" | ||
| 52 | +else | ||
| 53 | + : > "$SCRIPT_DIR/.host-gitconfig" | ||
| 54 | +fi | ||
| 55 | + | ||
| 56 | +# ---- 准备 uv 缓存挂载源 ---- | ||
| 57 | +# devcontainer.json 将该目录 bind mount 到 /home/mindstudio/.cache/uv。 | ||
| 58 | +# bind mount 的 source 必须在 docker create 前存在;initializeCommand 以宿主 | ||
| 59 | +# 当前用户执行,因此新建目录天然归当前用户所有。 | ||
| 60 | +install -d -m 0755 "$HOME/.cache/uv" | ||
| @@ -0,0 +1,614 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# ------------------------------------------------------------------------- | ||
| 3 | +# This file is part of the MindStudio project. | ||
| 4 | +# Copyright (c) 2025 Huawei Technologies Co.,Ltd. | ||
| 5 | +# | ||
| 6 | +# MindStudio is licensed under Mulan PSL v2. | ||
| 7 | +# You can use this software according to the terms and conditions of the Mulan PSL v2. | ||
| 8 | +# You may obtain a copy of Mulan PSL v2 at: | ||
| 9 | +# | ||
| 10 | +# http://license.coscl.org.cn/MulanPSL2 | ||
| 11 | +# | ||
| 12 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, | ||
| 13 | +# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, | ||
| 14 | +# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. | ||
| 15 | +# See the Mulan PSL v2 for more details. | ||
| 16 | +# ------------------------------------------------------------------------- | ||
| 17 | + | ||
| 18 | +# ============================================================================= | ||
| 19 | +# MindStudio devcontainer 初始化脚本 | ||
| 20 | +# ============================================================================= | ||
| 21 | +# | ||
| 22 | +# 职责说明: | ||
| 23 | +# 该脚本在 devcontainer 首次创建或重建后自动执行,负责完成用户级开发环境 | ||
| 24 | +# 初始化,确保开发者进入容器后即可直接进入编码、编译和调试状态。 | ||
| 25 | +# | ||
| 26 | +# 与 devcontainer.json 的关系: | ||
| 27 | +# devcontainer.json 通过 postCreateCommand 调用本脚本: | ||
| 28 | +# "postCreateCommand": "bash -lc '/workspace/.devcontainer/post-create.sh'" | ||
| 29 | +# 使用 login shell(-l)执行,目的是在初始化过程中自动加载镜像 | ||
| 30 | +# /etc/profile.d/ 中的 CANN、GCC 11、Python 等环境脚本,确保后续配置 | ||
| 31 | +# 能够感知到这些工具链的存在。 | ||
| 32 | +# | ||
| 33 | +# 执行顺序(按依赖关系排列): | ||
| 34 | +# 1. fix_cache_ownership - 修复 z_cache.sh 创建的缓存目录权限 | ||
| 35 | +# 2. fix_file_watcher_limit - 提升 inotify max_user_watches 至 524288 | ||
| 36 | +# 3. configure_user_bin - 建立用户级命令目录,重映射 npm prefix | ||
| 37 | +# 4. configure_python311 - 在 shell 启动文件中启用 Python 3.11 | ||
| 38 | +# 5. sync_git_identity - 从宿主同步 Git 用户名和邮箱 | ||
| 39 | +# 6. append_dev_hint_once - 向 .bash_profile 追加常用开发命令提示 | ||
| 40 | +# 7. install_pre_commit_hook - 自动安装 pre-commit Git Hook | ||
| 41 | +# 8. install_gitleaks - 从 OBS 下载 gitleaks 二进制(pre-commit 依赖) | ||
| 42 | +# 9. ignore_vscode_settings - 隔离个人化 VS Code settings 修改 | ||
| 43 | +# 10. install_git_safe_pull_alias - 安装可处理 skip-worktree 文件的拉取命令 | ||
| 44 | +# | ||
| 45 | +# ============================================================================= | ||
| 46 | + | ||
| 47 | +# 不使用 set -e:各模块自行降级并记录告警,单项失败不应阻止进入容器。 | ||
| 48 | +# -u 和 pipefail 仍用于暴露未定义变量及管道中的隐蔽错误。 | ||
| 49 | +set -uo pipefail | ||
| 50 | + | ||
| 51 | +# --------------------------------------------------------------------------- | ||
| 52 | +# 工具函数 | ||
| 53 | +# --------------------------------------------------------------------------- | ||
| 54 | + | ||
| 55 | +# 输出统一格式的信息日志,便于从 Dev Containers 启动日志中筛选。 | ||
| 56 | +log() { | ||
| 57 | + printf '[post-create] %s\n' "$*" | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +# 输出统一格式的告警日志到 stderr;告警默认不终止后续初始化。 | ||
| 61 | +warn() { | ||
| 62 | + printf '[post-create] warning: %s\n' "$*" >&2 | ||
| 63 | +} | ||
| 64 | + | ||
| 65 | +# append_path_once: | ||
| 66 | +# 幂等地将 $HOME/.local/bin 放到指定 shell 启动文件的 PATH 前端。 | ||
| 67 | +# 解决非 root 用户(mindstudio)无法写入 /usr/local/nodejs 等系统级目录的问题。 | ||
| 68 | +# 参数 $1: 目标 shell 启动文件路径(如 $HOME/.bashrc 或 $HOME/.bash_profile) | ||
| 69 | +append_path_once() { | ||
| 70 | + local file="$1" | ||
| 71 | + local line='export PATH="$HOME/.local/bin:$PATH"' | ||
| 72 | + | ||
| 73 | + touch "$file" | ||
| 74 | + if ! grep -Fqx "$line" "$file"; then | ||
| 75 | + printf '\n%s\n' "$line" >> "$file" | ||
| 76 | + fi | ||
| 77 | + | ||
| 78 | + log "append_path_once succeeded: $file" | ||
| 79 | +} | ||
| 80 | + | ||
| 81 | +# append_python311_once: | ||
| 82 | +# 幂等地将 Python 3.11 切换脚本注入到指定 shell 启动文件中。 | ||
| 83 | +# 通过 marker 注释判断是否已注入,避免重复写入。 | ||
| 84 | +# 注入内容: | ||
| 85 | +# 1. 加载镜像的 /etc/profile.d/z_python_switch.sh(Python 版本切换基础设施) | ||
| 86 | +# 2. 调用 use-python 3.11 激活 Python 3.11 环境 | ||
| 87 | +# 参数 $1: 目标 shell 启动文件路径 | ||
| 88 | +append_python311_once() { | ||
| 89 | + local file="$1" | ||
| 90 | + local marker_begin="# >>> mindstudio devcontainer python >>>" | ||
| 91 | + | ||
| 92 | + touch "$file" | ||
| 93 | + if grep -Fqx "$marker_begin" "$file"; then | ||
| 94 | + log "append_python311_once succeeded: $file" | ||
| 95 | + return 0 | ||
| 96 | + fi | ||
| 97 | + | ||
| 98 | + cat >> "$file" <<'EOF' | ||
| 99 | + | ||
| 100 | +# >>> mindstudio devcontainer python >>> | ||
| 101 | +if [ -r /etc/profile.d/z_python_switch.sh ]; then | ||
| 102 | + . /etc/profile.d/z_python_switch.sh | ||
| 103 | +fi | ||
| 104 | +if [ -r /usr/local/bin/use-python ]; then | ||
| 105 | + . /usr/local/bin/use-python 3.11 >/dev/null 2>&1 || true | ||
| 106 | + export PY311_ENV_ENABLED=1 | ||
| 107 | +fi | ||
| 108 | +# <<< mindstudio devcontainer python <<< | ||
| 109 | +EOF | ||
| 110 | + | ||
| 111 | + log "append_python311_once succeeded: $file" | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +# ============================================================================= | ||
| 115 | +# configure_user_bin —— 用户级命令与 npm 全局路径重映射 | ||
| 116 | +# ============================================================================= | ||
| 117 | +# | ||
| 118 | +# 背景与问题: | ||
| 119 | +# 构建镜像中的 npm 全局安装目录默认指向 /usr/local/nodejs,非 root 用户 | ||
| 120 | +# (mindstudio)无写入权限。如果直接在镜像内执行 npm install -g,会因权限 | ||
| 121 | +# 不足而失败。 | ||
| 122 | +# | ||
| 123 | +# 解决方案: | ||
| 124 | +# 1. 创建 $HOME/.local/bin 目录,作为用户级可执行文件存放路径。 | ||
| 125 | +# 2. 通过 npm config set prefix 将 npm 全局安装目录重映射到 $HOME/.local, | ||
| 126 | +# 这样 npm install -g 会将包安装到用户可写的目录下。 | ||
| 127 | +# 3. 将 $HOME/.local/bin 写入 .bashrc 和 .bash_profile 的 PATH 前端, | ||
| 128 | +# 确保用户级命令优先级高于系统级命令。 | ||
| 129 | +# | ||
| 130 | +# 容器环境变量配合: | ||
| 131 | +# devcontainer.json 中设置了 containerEnv: | ||
| 132 | +# NPM_CONFIG_PREFIX=/home/mindstudio/.local | ||
| 133 | +# 这个变量在 VS Code 远端会话中生效;本函数确保在交互式终端 Login Shell 中 | ||
| 134 | +# 同样生效,覆盖所有使用场景。 | ||
| 135 | +configure_user_bin() { | ||
| 136 | + mkdir -p "$HOME/.local/bin" | ||
| 137 | + | ||
| 138 | + if command -v npm >/dev/null 2>&1; then | ||
| 139 | + local current_prefix | ||
| 140 | + current_prefix=$(npm config get prefix 2>/dev/null || true) | ||
| 141 | + if [ "$current_prefix" != "$HOME/.local" ]; then | ||
| 142 | + npm config set prefix "$HOME/.local" || warn "failed to set npm prefix" | ||
| 143 | + fi | ||
| 144 | + else | ||
| 145 | + warn "npm is not available; skipping npm prefix setup" | ||
| 146 | + fi | ||
| 147 | + | ||
| 148 | + append_path_once "$HOME/.bashrc" | ||
| 149 | + append_path_once "$HOME/.bash_profile" | ||
| 150 | + | ||
| 151 | + log "configure_user_bin succeeded" | ||
| 152 | +} | ||
| 153 | + | ||
| 154 | +# ============================================================================= | ||
| 155 | +# ensure_shared_bin_path —— 跨用户命令路径补齐 | ||
| 156 | +# ============================================================================= | ||
| 157 | +# | ||
| 158 | +# 背景与问题: | ||
| 159 | +# devcontainer.json 的 containerEnv 将 NPM_CONFIG_PREFIX 硬编码为 | ||
| 160 | +# /home/mindstudio/.local,这意味着 npm install -g 始终安装到 mindstudio | ||
| 161 | +# 用户目录。当宿主机是 root 时,会话用户可能解析为 root($HOME=/root), | ||
| 162 | +# 其 $HOME/.local/bin 指向 /root/.local/bin,与 npm 实际安装目标不重合, | ||
| 163 | +# 导致 claude 等 npm 全局命令不可见。 | ||
| 164 | +# | ||
| 165 | +# 解决方案: | ||
| 166 | +# 幂等地将 /home/mindstudio/.local/bin 追加到 root 用户的 .bashrc 和 | ||
| 167 | +# .bash_profile 中,确保无论以哪个用户登录,都能找到 npm -g 安装的命令。 | ||
| 168 | +# 同时对已经安装的命令(如 claude)创建 /usr/local/bin 软链接作为兜底。 | ||
| 169 | +# | ||
| 170 | +# 幂等性保证: | ||
| 171 | +# 通过 grep -Fqx 检测目标行是否已存在,避免重复写入。 | ||
| 172 | +ensure_shared_bin_path() { | ||
| 173 | + local shared_bin="/home/mindstudio/.local/bin" | ||
| 174 | + local line="export PATH=\"$shared_bin:\$PATH\"" | ||
| 175 | + | ||
| 176 | + # 为 root 用户补齐 mindstudio bin 路径(幂等) | ||
| 177 | + # 兼容 postCreateCommand 以 root 或非 root 身份运行的场景 | ||
| 178 | + if [ -d "/root" ]; then | ||
| 179 | + for rc in "/root/.bashrc" "/root/.bash_profile"; do | ||
| 180 | + if [ "$(id -u)" = "0" ]; then | ||
| 181 | + # 当前是 root,直接操作 | ||
| 182 | + touch "$rc" 2>/dev/null || continue | ||
| 183 | + if ! grep -Fqx "$line" "$rc"; then | ||
| 184 | + printf '\n%s\n' "$line" >> "$rc" | ||
| 185 | + log "added shared bin path to $rc" | ||
| 186 | + fi | ||
| 187 | + else | ||
| 188 | + # 当前是非 root,通过 sudo 操作 | ||
| 189 | + sudo touch "$rc" 2>/dev/null || continue | ||
| 190 | + if ! sudo grep -Fqx "$line" "$rc"; then | ||
| 191 | + printf '\n%s\n' "$line" | sudo tee -a "$rc" >/dev/null | ||
| 192 | + log "added shared bin path to $rc" | ||
| 193 | + fi | ||
| 194 | + fi | ||
| 195 | + done | ||
| 196 | + fi | ||
| 197 | + | ||
| 198 | + # 兜底:将已安装的常用命令软链到系统路径,覆盖 sudo / 纯 root 等不读取 | ||
| 199 | + # mindstudio rc 文件的场景 | ||
| 200 | + if [ -d "$shared_bin" ]; then | ||
| 201 | + for cmd in claude; do | ||
| 202 | + if [ -x "$shared_bin/$cmd" ] && [ ! -e "/usr/local/bin/$cmd" ]; then | ||
| 203 | + if [ "$(id -u)" = "0" ]; then | ||
| 204 | + ln -sf "$shared_bin/$cmd" "/usr/local/bin/$cmd" 2>/dev/null || true | ||
| 205 | + else | ||
| 206 | + sudo ln -sf "$shared_bin/$cmd" "/usr/local/bin/$cmd" 2>/dev/null || true | ||
| 207 | + fi | ||
| 208 | + log "linked $cmd to /usr/local/bin/$cmd" | ||
| 209 | + fi | ||
| 210 | + done | ||
| 211 | + fi | ||
| 212 | + | ||
| 213 | + log "ensure_shared_bin_path succeeded" | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +# ============================================================================= | ||
| 217 | +# configure_python311 —— Python 3.11 环境激活 | ||
| 218 | +# ============================================================================= | ||
| 219 | +# | ||
| 220 | +# 背景与问题: | ||
| 221 | +# MindStudio 构建镜像预装了多个 Python 版本,3.11 是当前开发环境的默认版本。 | ||
| 222 | +# 镜像内 /etc/profile.d/z_python_switch.sh 和 /usr/local/bin/use-python | ||
| 223 | +# 提供了版本切换能力,但这些脚本只在 Login Shell 中自动生效。如果 shell | ||
| 224 | +# 启动文件中缺少这些调用,非 Login Shell 或子进程中可能使用错误的 Python 版本。 | ||
| 225 | +# | ||
| 226 | +# 解决方案: | ||
| 227 | +# 在 .bashrc 和 .bash_profile 中注入 Python 3.11 切换逻辑,覆盖 Login Shell | ||
| 228 | +# 和非 Login Shell 两种场景: | ||
| 229 | +# - .bashrc:覆盖 VS Code 集成终端(非 Login Shell) | ||
| 230 | +# - .bash_profile:覆盖 SSH / 外部终端(Login Shell) | ||
| 231 | +# | ||
| 232 | +# 幂等性保证: | ||
| 233 | +# 通过 marker 注释 (# >>> mindstudio devcontainer python >>>) 检测是否已 | ||
| 234 | +# 注入,避免重复写入导致环境变量被多次定义。 | ||
| 235 | +configure_python311() { | ||
| 236 | + append_python311_once "$HOME/.bashrc" | ||
| 237 | + append_python311_once "$HOME/.bash_profile" | ||
| 238 | + | ||
| 239 | + log "configure_python311 succeeded" | ||
| 240 | +} | ||
| 241 | + | ||
| 242 | +# ============================================================================= | ||
| 243 | +# sync_git_identity —— 宿主 Git 身份同步 | ||
| 244 | +# ============================================================================= | ||
| 245 | +# | ||
| 246 | +# 背景与问题: | ||
| 247 | +# 容器内的 Git 配置是全新的,如果不做身份同步,开发者在容器内的 commit 会 | ||
| 248 | +# 缺少正确的 author 信息,导致提交记录与开发者身份脱钩。但出于安全考虑, | ||
| 249 | +# 不应该将宿主整个 $HOME 目录暴露到容器中(避免密钥、token 等敏感文件泄漏)。 | ||
| 250 | +# | ||
| 251 | +# 解决方案: | ||
| 252 | +# 1. initialize.sh 在宿主机复制 ~/.gitconfig 到被 Git 忽略的快照文件。 | ||
| 253 | +# 2. devcontainer.json 通过 mounts 将该快照文件只读挂载到容器内的 | ||
| 254 | +# /tmp/host-gitconfig。 | ||
| 255 | +# 3. 本函数从 /tmp/host-gitconfig 读取 user.name 和 user.email, | ||
| 256 | +# 写入容器全局 Git 配置 (git config --global)。 | ||
| 257 | +# | ||
| 258 | +# 降级策略: | ||
| 259 | +# - 宿主没有 ~/.gitconfig 时,initializeCommand 生成空的 .host-gitconfig。 | ||
| 260 | +# 本函数检测到空文件或缺少字段时只告警,不阻塞容器创建。 | ||
| 261 | +# - 快照可能包含其它 Git 配置,但本函数只读取 user.name 和 user.email, | ||
| 262 | +# 不会把 credential、alias、include 等设置写入容器全局配置。 | ||
| 263 | +sync_git_identity() { | ||
| 264 | + local host_gitconfig="/tmp/host-gitconfig" | ||
| 265 | + local git_name="" | ||
| 266 | + local git_email="" | ||
| 267 | + | ||
| 268 | + if [ ! -s "$host_gitconfig" ]; then | ||
| 269 | + warn "host gitconfig is empty or missing; skipping git identity sync" | ||
| 270 | + log "sync_git_identity succeeded" | ||
| 271 | + return 0 | ||
| 272 | + fi | ||
| 273 | + | ||
| 274 | + git_name="$(git config -f "$host_gitconfig" --get user.name 2>/dev/null || true)" | ||
| 275 | + git_email="$(git config -f "$host_gitconfig" --get user.email 2>/dev/null || true)" | ||
| 276 | + | ||
| 277 | + if [ -n "$git_name" ]; then | ||
| 278 | + git config --global user.name "$git_name" || warn "failed to sync git user.name" | ||
| 279 | + else | ||
| 280 | + warn "host gitconfig has no user.name" | ||
| 281 | + fi | ||
| 282 | + | ||
| 283 | + if [ -n "$git_email" ]; then | ||
| 284 | + git config --global user.email "$git_email" || warn "failed to sync git user.email" | ||
| 285 | + else | ||
| 286 | + warn "host gitconfig has no user.email" | ||
| 287 | + fi | ||
| 288 | + | ||
| 289 | + log "sync_git_identity succeeded" | ||
| 290 | +} | ||
| 291 | + | ||
| 292 | +# ============================================================================= | ||
| 293 | +# install_pre_commit_hook —— pre-commit 自动安装 | ||
| 294 | +# ============================================================================= | ||
| 295 | +# | ||
| 296 | +# 背景与问题: | ||
| 297 | +# 仓库已有 .pre-commit-config.yaml 配置,但需要开发者手工执行 | ||
| 298 | +# `pre-commit install` 才能生效。在传统开发模式中,这一步容易被遗漏或忘记, | ||
| 299 | +# 导致提交时未触发质量检查,低质量代码进入仓库。 | ||
| 300 | +# | ||
| 301 | +# 解决方案: | ||
| 302 | +# 容器初始化时自动执行 pre-commit install,将 pre-commit Hook 安装到 | ||
| 303 | +# .git/hooks/pre-commit,确保每次 git commit 时自动触发。 | ||
| 304 | +# | ||
| 305 | +# 降级策略: | ||
| 306 | +# 1. pre-commit 命令不存在时只告警,不阻塞容器创建。 | ||
| 307 | +# 原因:纯 Python 工具有可能不需要 pre-commit,不应因工具缺失阻止进入容器。 | ||
| 308 | +# 2. 当前目录不是 Git 仓库时只告警,不阻塞容器创建。 | ||
| 309 | +# 原因:非 Git 场景(如镜像内临时工作区)不应因 .git 缺失而失败。 | ||
| 310 | +# 3. 安装失败时只告警,不影响容器正常使用。 | ||
| 311 | +install_pre_commit_hook() { | ||
| 312 | + # pre-commit CLI 工具和 pre_commit Python 模块是分开的: | ||
| 313 | + # - CLI(/usr/local/bin/pre-commit)用于执行 pre-commit install 等管理命令 | ||
| 314 | + # - Python 模块用于 git hook 运行时(hook 模板调用 /usr/bin/python3 -mpre_commit) | ||
| 315 | + # 两者都需要存在,否则 git commit 时会报 "No module named pre_commit" | ||
| 316 | + if ! command -v pre-commit >/dev/null 2>&1; then | ||
| 317 | + warn "pre-commit is not available; skipping hook installation" | ||
| 318 | + log "install_pre_commit_hook succeeded" | ||
| 319 | + return 0 | ||
| 320 | + fi | ||
| 321 | + | ||
| 322 | + if ! python3 -c "import pre_commit" 2>/dev/null; then | ||
| 323 | + log "pre_commit Python module not found; installing..." | ||
| 324 | + python3 -m pip install pre-commit || { | ||
| 325 | + warn "failed to install pre_commit Python module; skipping hook installation" | ||
| 326 | + log "install_pre_commit_hook succeeded" | ||
| 327 | + return 0 | ||
| 328 | + } | ||
| 329 | + fi | ||
| 330 | + | ||
| 331 | + if ! git rev-parse --git-dir >/dev/null 2>&1; then | ||
| 332 | + warn "workspace is not a git repository; skipping pre-commit hook installation" | ||
| 333 | + log "install_pre_commit_hook succeeded" | ||
| 334 | + return 0 | ||
| 335 | + fi | ||
| 336 | + | ||
| 337 | + pre-commit install || warn "pre-commit hook installation failed" | ||
| 338 | + | ||
| 339 | + log "install_pre_commit_hook succeeded" | ||
| 340 | +} | ||
| 341 | + | ||
| 342 | +# ============================================================================= | ||
| 343 | +# fix_cache_ownership —— 修复缓存目录权限 | ||
| 344 | +# ============================================================================= | ||
| 345 | +# | ||
| 346 | +# 背景与问题: | ||
| 347 | +# 镜像内置的 /etc/profile.d/z_cache.sh 在 login shell 启动时探测 bind mount | ||
| 348 | +# 路径(/workspace、/home/mindstudio 等),并自动创建 .cache/ccache 和 | ||
| 349 | +# .cache/uv 目录。但 z_cache.sh 可能在 updateRemoteUserUID 完成 UID 重映射 | ||
| 350 | +# 之前被触发(例如通过 docker run 而非 devcontainer),或由 root 身份的 | ||
| 351 | +# 进程触发,导致这些目录在宿主机上显示为 root:root,与普通文件权限不一致。 | ||
| 352 | +# | ||
| 353 | +# 典型症状: | ||
| 354 | +# - 容器内 ls -la ~/.cache → mindstudio:mindstudio(UID 已被重映射) | ||
| 355 | +# - 宿主机 ls -la ~/.cache → root:root(原始 UID 0 未变) | ||
| 356 | +# | ||
| 357 | +# 解决方案: | ||
| 358 | +# 在 post-create 阶段主动检测并修复这些缓存目录的 owner,确保与当前用户 | ||
| 359 | +# 一致。优先修复当前 shell 实际导出的 CCACHE_DIR、UV_CACHE_DIR;这可覆盖 | ||
| 360 | +# /home/<宿主用户> 被额外挂载进容器的场景。缓存父目录也必须可写,供 | ||
| 361 | +# pre-commit、pip 等工具创建各自的同级缓存。固定路径作为兜底,覆盖 | ||
| 362 | +# z_cache.sh 可能探测的工作区和容器用户目录。 | ||
| 363 | +# | ||
| 364 | +# 幂等性保证: | ||
| 365 | +# 仅在目录存在且 owner 不匹配时才执行 chown,避免不必要的文件系统操作。 | ||
| 366 | +fix_cache_ownership() { | ||
| 367 | + local dirs_to_fix=( | ||
| 368 | + "${HOME}/.cache" | ||
| 369 | + "${CCACHE_DIR:-}" | ||
| 370 | + "${UV_CACHE_DIR:-}" | ||
| 371 | + "/home/mindstudio/.cache" | ||
| 372 | + "/workspace/.cache/ccache" | ||
| 373 | + "/workspace/.cache/uv" | ||
| 374 | + "/home/mindstudio/.cache/ccache" | ||
| 375 | + "/home/mindstudio/.cache/uv" | ||
| 376 | + ) | ||
| 377 | + | ||
| 378 | + for d in "${dirs_to_fix[@]}"; do | ||
| 379 | + [ -n "$d" ] || continue | ||
| 380 | + if [ -d "$d" ]; then | ||
| 381 | + local owner | ||
| 382 | + owner=$(stat -c '%U' "$d" 2>/dev/null || true) | ||
| 383 | + if [ "$owner" != "$USER" ]; then | ||
| 384 | + log "fixing ownership of $d ($owner → $USER)" | ||
| 385 | + sudo chown -R "$USER:$USER" "$d" 2>/dev/null || \ | ||
| 386 | + warn "failed to chown $d" | ||
| 387 | + fi | ||
| 388 | + fi | ||
| 389 | + done | ||
| 390 | + | ||
| 391 | + log "fix_cache_ownership succeeded" | ||
| 392 | +} | ||
| 393 | + | ||
| 394 | +# ============================================================================= | ||
| 395 | +# append_dev_hint_once —— 常用开发命令提示 | ||
| 396 | +# ============================================================================= | ||
| 397 | +# | ||
| 398 | +# 目的: | ||
| 399 | +# 在每次打开终端时展示常用开发命令的快捷提示,降低新开发者的学习成本, | ||
| 400 | +# 让所有人都能快速知道如何编译项目和运行单元测试。 | ||
| 401 | +# | ||
| 402 | +# 展示时机: | ||
| 403 | +# 写入 $HOME/.bash_profile,在每次 Login Shell 启动时显示。 | ||
| 404 | +# | ||
| 405 | +# 幂等性保证: | ||
| 406 | +# 通过 marker 注释 (# >>> mindstudio devcontainer dev-hint >>>) 检测 | ||
| 407 | +# 是否已追加,避免每次容器重建都重复写入。 | ||
| 408 | +append_dev_hint_once() { | ||
| 409 | + local file="$HOME/.bash_profile" | ||
| 410 | + local marker="# >>> mindstudio devcontainer dev-hint >>>" | ||
| 411 | + | ||
| 412 | + touch "$file" | ||
| 413 | + if grep -Fqx "$marker" "$file"; then | ||
| 414 | + return 0 | ||
| 415 | + fi | ||
| 416 | + | ||
| 417 | + cat >> "$file" <<'EOF' | ||
| 418 | + | ||
| 419 | +# >>> mindstudio devcontainer dev-hint >>> | ||
| 420 | +printf '──────────────────────────────────────────────────────────────────────\n' | ||
| 421 | +printf '\033[1;33m 🔥 常用开发命令 (Common Development Commands):\033[0m\n' | ||
| 422 | +printf ' \033[1;36m•\033[0m 编译项目 : \033[1;32mpython3 build.py # 结果生成到 artifacts 目录\033[0m\n' | ||
| 423 | +printf ' \033[1;36m•\033[0m 单元测试 : \033[1;32mpython3 build.py test\033[0m\n' | ||
| 424 | +printf '──────────────────────────────────────────────────────────────────────\n' | ||
| 425 | +# <<< mindstudio devcontainer dev-hint <<< | ||
| 426 | +EOF | ||
| 427 | +} | ||
| 428 | + | ||
| 429 | +# ============================================================================= | ||
| 430 | +# fix_file_watcher_limit —— 增大 inotify 文件监听上限 | ||
| 431 | +# ============================================================================= | ||
| 432 | +# | ||
| 433 | +# 背景与问题: | ||
| 434 | +# Linux 内核默认的 fs.inotify.max_user_watches 通常为 8192。VS Code 的 | ||
| 435 | +# File Watcher 会为工作区中每个被监听的文件消耗一个 inotify watch,大型项目 | ||
| 436 | +# (含 node_modules、build 产物、源代码)很容易超过该限制,导致 VS Code 报错: | ||
| 437 | +# "Unable to watch for file changes. Please follow the instructions link to | ||
| 438 | +# resolve this issue." | ||
| 439 | +# | ||
| 440 | +# 这会令 VS Code 的文件变更探测能力降级,表现为: | ||
| 441 | +# - 源代码变更无法触发搜索/语法高亮刷新 | ||
| 442 | +# - Git 面板不能实时反映未暂存变更 | ||
| 443 | +# - 部分扩展(如 clangd)无法检测文件变化而重建索引 | ||
| 444 | +# | ||
| 445 | +# 解决方案: | ||
| 446 | +# 将 max_user_watches 提升到 524288(VS Code 推荐的典型值)。由于容器使用了 | ||
| 447 | +# --privileged 运行参数,具备修改内核参数的权限,可直接通过 sysctl 或向 | ||
| 448 | +# /proc/sys/fs/inotify/max_user_watches 写入目标值完成调整。 | ||
| 449 | +# | ||
| 450 | +# 降级策略: | ||
| 451 | +# 如果 sysctl 和 /proc 写入均失败(极少数受限环境),只输出告警,不阻断容器创建。 | ||
| 452 | +fix_file_watcher_limit() { | ||
| 453 | + local desired=524288 | ||
| 454 | + local current | ||
| 455 | + current=$(cat /proc/sys/fs/inotify/max_user_watches 2>/dev/null || echo 0) | ||
| 456 | + | ||
| 457 | + if [ "$current" -ge "$desired" ]; then | ||
| 458 | + log "inotify max_user_watches is already ${current} (>= ${desired})" | ||
| 459 | + return 0 | ||
| 460 | + fi | ||
| 461 | + | ||
| 462 | + log "inotify max_user_watches is ${current}, increasing to ${desired}..." | ||
| 463 | + | ||
| 464 | + # 优先通过 sysctl 设置 | ||
| 465 | + if command -v sysctl >/dev/null 2>&1; then | ||
| 466 | + if sudo sysctl -w fs.inotify.max_user_watches="$desired" >/dev/null 2>&1; then | ||
| 467 | + log "inotify max_user_watches increased to ${desired} (via sysctl)" | ||
| 468 | + return 0 | ||
| 469 | + fi | ||
| 470 | + fi | ||
| 471 | + | ||
| 472 | + # 回退:直接写 /proc | ||
| 473 | + if sudo sh -c "echo ${desired} > /proc/sys/fs/inotify/max_user_watches" 2>/dev/null; then | ||
| 474 | + log "inotify max_user_watches increased to ${desired} (via /proc)" | ||
| 475 | + return 0 | ||
| 476 | + fi | ||
| 477 | + | ||
| 478 | + warn "failed to increase inotify max_user_watches; VS Code file watching may not work correctly" | ||
| 479 | +} | ||
| 480 | +# ============================================================================= | ||
| 481 | +# ignore_vscode_settings —— 隔离个人化 VS Code Settings 修改 | ||
| 482 | +# ============================================================================= | ||
| 483 | +# | ||
| 484 | +# 背景与问题: | ||
| 485 | +# .vscode/settings.json 是工作区级别的 VS Code 配置文件,已纳入 Git 版本管理。 | ||
| 486 | +# 开发者在使用过程中可能需要根据个人偏好微调某些设置(如字体大小、主题等), | ||
| 487 | +# 这些个人化修改如果频繁出现在 git status 中会造成噪声,并可能在合入 MR 时 | ||
| 488 | +# 引入不必要的配置冲突。 | ||
| 489 | +# | ||
| 490 | +# 解决方案: | ||
| 491 | +# 通过 git update-index --skip-worktree 标记 .vscode/settings.json,减少个人化 | ||
| 492 | +# 修改在 git status 中产生的噪声。该标记只影响当前工作区,不会传播到远端。 | ||
| 493 | +# | ||
| 494 | +# 注意: | ||
| 495 | +# - skip-worktree 不是 .gitignore 的替代,文件仍然被 Git 追踪;本地修改通常 | ||
| 496 | +# 不显示在 git status 中,但远端也修改该文件时,普通 pull 仍可能拒绝覆盖。 | ||
| 497 | +# - 如果需要更新仓库中的 settings.json 模板,建议在本地另 clone 一份代码仓 | ||
| 498 | +# 或在 gitcode 页面上直接修改提交,避免本地个人偏好被误提交。 | ||
| 499 | +ignore_vscode_settings() { | ||
| 500 | + git update-index --skip-worktree .vscode/settings.json 2>/dev/null || true | ||
| 501 | + | ||
| 502 | + log "ignore_vscode_settings succeeded" | ||
| 503 | +} | ||
| 504 | + | ||
| 505 | +# ============================================================================= | ||
| 506 | +# install_git_safe_pull_alias —— 安装 git safe-pull 别名 | ||
| 507 | +# ============================================================================= | ||
| 508 | +# | ||
| 509 | +# 背景与问题: | ||
| 510 | +# ignore_vscode_settings 对 .vscode/settings.json 设置了 skip-worktree, | ||
| 511 | +# 这会让 git status 忽略该文件的本地修改。但当远程仓库也更新了同一个文件时, | ||
| 512 | +# git pull 会因为 "Your local changes would be overwritten by merge" 而失败。 | ||
| 513 | +# 普通开发者不了解 git update-index,排查和修复门槛较高。 | ||
| 514 | +# | ||
| 515 | +# 解决方案: | ||
| 516 | +# 安装一个全局 Git 别名 git safe-pull,自动处理 skip-worktree 文件: | ||
| 517 | +# 1. 找出所有被 skip-worktree 标记的文件 | ||
| 518 | +# 2. 临时取消这些标记 | ||
| 519 | +# 3. 暂存 (stash) 本地修改 | ||
| 520 | +# 4. 执行正常的 git pull(支持所有 git pull 参数) | ||
| 521 | +# 5. pull 成功时丢弃临时 stash,采用远端版本 | ||
| 522 | +# 6. pull 失败时保留 stash,避免本地修改丢失 | ||
| 523 | +# 7. 无论成功或失败,均重新设置 skip-worktree 标记 | ||
| 524 | +# | ||
| 525 | +# 使用方式: | ||
| 526 | +# git safe-pull # 等价于 git pull | ||
| 527 | +# git safe-pull --rebase # 等价于 git pull --rebase | ||
| 528 | +# | ||
| 529 | +# 实现说明: | ||
| 530 | +# 具体逻辑放在 .devcontainer/git-safe-pull.sh,避免复杂 Git shell alias 的多层 | ||
| 531 | +# 引号破坏参数。每次 post-create 都重新安装脚本并刷新 alias,以覆盖旧容器中 | ||
| 532 | +# 已存在的错误版本。 | ||
| 533 | +install_git_safe_pull_alias() { | ||
| 534 | + local source_script="/workspace/.devcontainer/git-safe-pull.sh" | ||
| 535 | + local target_script="$HOME/.local/bin/git-safe-pull" | ||
| 536 | + | ||
| 537 | + if [ ! -f "$source_script" ]; then | ||
| 538 | + warn "safe-pull source script not found: $source_script" | ||
| 539 | + return 0 | ||
| 540 | + fi | ||
| 541 | + | ||
| 542 | + install -m 0755 "$source_script" "$target_script" || { | ||
| 543 | + warn "failed to install git-safe-pull" | ||
| 544 | + return 0 | ||
| 545 | + } | ||
| 546 | + git config --global alias.safe-pull '!git-safe-pull' | ||
| 547 | + | ||
| 548 | + log "install_git_safe_pull_alias succeeded" | ||
| 549 | +} | ||
| 550 | + | ||
| 551 | +# ============================================================================= | ||
| 552 | +# install_gitleaks —— Gitleaks 秘密扫描二进制下载 | ||
| 553 | +# ============================================================================= | ||
| 554 | +# | ||
| 555 | +# 背景与问题: | ||
| 556 | +# pre-commit 配置中的 gitleaks-offline-scan hook 执行 `./gitleaks protect`, | ||
| 557 | +# 期望仓库根目录存在 gitleaks 二进制文件。如果缺失,git commit 时 | ||
| 558 | +# pre-commit hook 会因 "Executable ./gitleaks not found" 而失败。 | ||
| 559 | +# | ||
| 560 | +# 解决方案: | ||
| 561 | +# 从华为 OBS 镜像站下载预编译的 gitleaks 二进制到 /workspace/gitleaks, | ||
| 562 | +# 确保 devcontainer 创建后立即可用,无需开发者手工下载。 | ||
| 563 | +# | ||
| 564 | +# 降级策略: | ||
| 565 | +# wget 下载失败时只告警,不阻塞容器创建。 | ||
| 566 | +# 开发者仍可手工下载或使用 git commit --no-verify 绕过。 | ||
| 567 | +install_gitleaks() { | ||
| 568 | + local target="/workspace/gitleaks" | ||
| 569 | + local base_url="https://inst.obs.cn-north-4.myhuaweicloud.com/env/mirror" | ||
| 570 | + local arch="" | ||
| 571 | + local url="" | ||
| 572 | + | ||
| 573 | + # 根据 CPU 架构选择对应的二进制目录 | ||
| 574 | + case "$(uname -m)" in | ||
| 575 | + x86_64) arch="x86_64" ;; | ||
| 576 | + aarch64) arch="aarch64" ;; | ||
| 577 | + *) arch="x86_64" ;; # 默认回退到 x86_64 | ||
| 578 | + esac | ||
| 579 | + url="${base_url}/${arch}/gitleaks" | ||
| 580 | + | ||
| 581 | + log "downloading gitleaks (${arch}) from OBS..." | ||
| 582 | + if wget --no-host-directories -c --no-check-certificate \ | ||
| 583 | + -O "$target" "$url" 2>/dev/null; then | ||
| 584 | + chmod +x "$target" | ||
| 585 | + log "gitleaks installed successfully: $($target --version 2>/dev/null || echo 'version unknown')" | ||
| 586 | + else | ||
| 587 | + warn "failed to download gitleaks (${arch}) from OBS; git commit may fail on pre-commit hook" | ||
| 588 | + warn "URL attempted: ${url}" | ||
| 589 | + rm -f "$target" | ||
| 590 | + fi | ||
| 591 | +} | ||
| 592 | + | ||
| 593 | +# ============================================================================= | ||
| 594 | +# 主执行流程 | ||
| 595 | +# ============================================================================= | ||
| 596 | +# | ||
| 597 | +# 按依赖顺序执行:先修复目录权限,再写用户配置,最后安装 Git 辅助能力。 | ||
| 598 | +# 各模块尽量自行降级并输出 warning,非关键项失败不阻止容器启动。 | ||
| 599 | + | ||
| 600 | +log "post-create setup started" | ||
| 601 | + | ||
| 602 | +fix_cache_ownership | ||
| 603 | +fix_file_watcher_limit | ||
🟠 High Priority post-create.sh 第 508 行调用 建议:添加 ![]() ![]() 🟠 High Priority 变更行: 证据链:
影响:容器初始化时 inotify ![]() ![]() | |||
| 604 | +configure_user_bin | ||
| 605 | +ensure_shared_bin_path | ||
| 606 | +configure_python311 | ||
| 607 | +sync_git_identity | ||
| 608 | +append_dev_hint_once | ||
| 609 | +install_pre_commit_hook | ||
| 610 | +install_gitleaks | ||
| 611 | +ignore_vscode_settings | ||
| 612 | +install_git_safe_pull_alias | ||
| 613 | + | ||
| 614 | +log "post-create setup finished" | ||
| @@ -1,11 +1,36 @@ | |||
| 1 | -__pycache__ | 1 | +# IDE |
| 2 | -.vscode/ | ||
| 3 | .idea/ | 2 | .idea/ |
| 3 | +.vscode/settings.local.json | ||
| 4 | + | ||
| 5 | +# Python | ||
| 6 | +__pycache__/ | ||
| 7 | +*.py[cod] | ||
| 8 | +*$py.class | ||
| 9 | +.venv/ | ||
| 10 | +venv/ | ||
| 11 | +.pytest_cache/ | ||
| 12 | +.mypy_cache/ | ||
| 13 | +.ruff_cache/ | ||
| 14 | +.coverage | ||
| 15 | +htmlcov/ | ||
| 16 | +*.egg-info/ | ||
| 17 | +pip-wheel-metadata/ | ||
| 18 | +test.log | ||
| 19 | +test_output_*.cpp | ||
| 20 | + | ||
| 21 | +# Build outputs | ||
| 4 | build/ | 22 | build/ |
| 5 | build_ut/ | 23 | build_ut/ |
| 6 | output/ | 24 | output/ |
| 7 | -cmake-build-debug/ | 25 | +artifacts/ |
| 8 | dist/ | 26 | dist/ |
| 9 | -mskl.egg-info/ | 27 | +*.so |
| 28 | +*.pyd | ||
| 10 | 29 | ||
| 11 | -.codemate | 30 | +# Cache and local snapshots |
| 31 | +.cache/ | ||
| 32 | +.codemate | ||
| 33 | +.devcontainer/.host-gitconfig | ||
| 34 | + | ||
| 35 | +# Pre-commit tools | ||
| 36 | +gitleaks | ||
| @@ -19,6 +19,7 @@ repos: | |||
| 19 | - id: check-merge-conflict | 19 | - id: check-merge-conflict |
| 20 | - id: detect-private-key | 20 | - id: detect-private-key |
| 21 | - id: check-json | 21 | - id: check-json |
| 22 | + exclude: ^\.devcontainer/ | ||
| 22 | 23 | ||
| 23 | # -------------------------- Python 核心检查 -------------------------- | 24 | # -------------------------- Python 核心检查 -------------------------- |
| 24 | # Ruff:指定读取 pre-commit/pyproject.toml | 25 | # Ruff:指定读取 pre-commit/pyproject.toml |
| @@ -0,0 +1,34 @@ | |||
| 1 | +{ | ||
| 2 | + "version": "0.2.0", | ||
| 3 | + "configurations": [ | ||
| 4 | + { | ||
| 5 | + "name": "Python: Debug Active File", | ||
| 6 | + "type": "debugpy", | ||
| 7 | + "request": "launch", | ||
| 8 | + "program": "${file}", | ||
| 9 | + "console": "integratedTerminal", | ||
| 10 | + "cwd": "${workspaceFolder}", | ||
| 11 | + "env": { | ||
| 12 | + "PYTHONPATH": "${workspaceFolder}" | ||
| 13 | + }, | ||
| 14 | + "justMyCode": false | ||
| 15 | + }, | ||
| 16 | + { | ||
| 17 | + "name": "Python: Debug Pytest Case", | ||
| 18 | + "type": "debugpy", | ||
| 19 | + "request": "launch", | ||
| 20 | + "module": "pytest", | ||
| 21 | + "args": [ | ||
| 22 | + "${file}", | ||
| 23 | + "-v", | ||
| 24 | + "-s" | ||
| 25 | + ], | ||
| 26 | + "console": "integratedTerminal", | ||
| 27 | + "cwd": "${workspaceFolder}", | ||
| 28 | + "env": { | ||
| 29 | + "PYTHONPATH": "${workspaceFolder}" | ||
| 30 | + }, | ||
| 31 | + "justMyCode": false | ||
| 32 | + } | ||
| 33 | + ] | ||
| 34 | +} | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +{ | ||
| 2 | + "files.watcherExclude": { | ||
| 3 | + ".cache/**": true, | ||
| 4 | + "{build,build_ut,output,artifacts,dist}/**": true, | ||
| 5 | + "**/*.egg-info/**": true, | ||
| 6 | + "**/{.venv,node_modules,__pycache__,.pytest_cache,.ruff_cache,.mypy_cache}/**": true | ||
| 7 | + }, | ||
| 8 | + "search.exclude": { | ||
| 9 | + ".cache/**": true, | ||
| 10 | + "{build,build_ut,output,artifacts,dist}/**": true, | ||
| 11 | + "**/*.egg-info/**": true, | ||
| 12 | + "**/{.venv,node_modules,__pycache__,.pytest_cache,.ruff_cache,.mypy_cache}/**": true | ||
| 13 | + }, | ||
| 14 | + "files.exclude": { | ||
| 15 | + ".cache": true, | ||
| 16 | + "**/*.egg-info": true, | ||
| 17 | + "**/{__pycache__,.pytest_cache,.ruff_cache,.mypy_cache}": true | ||
| 18 | + }, | ||
| 19 | + "terminal.integrated.scrollback": 10000, | ||
| 20 | + "git.detectSubmodules": false, | ||
| 21 | + "git.autoRepositoryDetection": false, | ||
| 22 | + "python.defaultInterpreterPath": "python3", | ||
| 23 | + "python.terminal.activateEnvironment": false, | ||
| 24 | + "python.analysis.extraPaths": [ | ||
| 25 | + "${workspaceFolder}" | ||
| 26 | + ], | ||
| 27 | + "python.analysis.exclude": [ | ||
| 28 | + "**/node_modules", | ||
| 29 | + "**/__pycache__", | ||
| 30 | + "**/.*", | ||
| 31 | + ".venv", | ||
| 32 | + "build", | ||
| 33 | + "build_ut", | ||
| 34 | + "output", | ||
| 35 | + "artifacts", | ||
| 36 | + "dist" | ||
| 37 | + ], | ||
| 38 | + "python.testing.pytestEnabled": true, | ||
| 39 | + "python.testing.pytestArgs": [ | ||
| 40 | + "test/launcher", | ||
| 41 | + "test/op_tune" | ||
| 42 | + ] | ||
| 43 | +} | ||
| @@ -0,0 +1,62 @@ | |||
| 1 | +{ | ||
| 2 | + "version": "2.0.0", | ||
| 3 | + "tasks": [ | ||
| 4 | + { | ||
| 5 | + "label": "Build: Release Mode", | ||
| 6 | + "type": "shell", | ||
| 7 | + "command": "python3", | ||
| 8 | + "args": [ | ||
| 9 | + "${workspaceFolder}/build.py" | ||
| 10 | + ], | ||
| 11 | + "options": { | ||
| 12 | + "cwd": "${workspaceFolder}" | ||
| 13 | + }, | ||
| 14 | + "problemMatcher": [], | ||
| 15 | + "presentation": { | ||
| 16 | + "reveal": "always", | ||
| 17 | + "panel": "dedicated", | ||
| 18 | + "clear": false | ||
| 19 | + }, | ||
| 20 | + "group": { | ||
| 21 | + "kind": "build", | ||
| 22 | + "isDefault": true | ||
| 23 | + } | ||
| 24 | + }, | ||
| 25 | + { | ||
| 26 | + "label": "Test: Run Unit Tests", | ||
| 27 | + "type": "shell", | ||
| 28 | + "command": "python3", | ||
| 29 | + "args": [ | ||
| 30 | + "${workspaceFolder}/build.py", | ||
| 31 | + "test" | ||
| 32 | + ], | ||
| 33 | + "options": { | ||
| 34 | + "cwd": "${workspaceFolder}" | ||
| 35 | + }, | ||
| 36 | + "problemMatcher": [], | ||
| 37 | + "presentation": { | ||
| 38 | + "reveal": "always", | ||
| 39 | + "panel": "dedicated", | ||
| 40 | + "clear": false | ||
| 41 | + }, | ||
| 42 | + "group": { | ||
| 43 | + "kind": "test", | ||
| 44 | + "isDefault": true | ||
| 45 | + } | ||
| 46 | + }, | ||
| 47 | + { | ||
| 48 | + "label": "Workspace: Clean", | ||
| 49 | + "type": "shell", | ||
| 50 | + "command": "git clean -xdf -e .cache -e '.cache/**'", | ||
| 51 | + "options": { | ||
| 52 | + "cwd": "${workspaceFolder}" | ||
| 53 | + }, | ||
| 54 | + "problemMatcher": [], | ||
| 55 | + "presentation": { | ||
| 56 | + "reveal": "always", | ||
| 57 | + "panel": "dedicated", | ||
| 58 | + "clear": false | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + ] | ||
| 62 | +} | ||
| @@ -175,10 +175,16 @@ class BuildManager: | |||
| 175 | if build_version: | 175 | if build_version: |
| 176 | logging.info("--build-version: %s", build_version) | 176 | logging.info("--build-version: %s", build_version) |
| 177 | 177 | ||
| 178 | + extra_options = {} | ||
| 178 | for option in self.parsed_arguments.extra: | 179 | for option in self.parsed_arguments.extra: |
| 179 | key, _, value = option.partition('=') | 180 | key, _, value = option.partition('=') |
| 181 | + extra_options[key] = value | ||
| 180 | logging.info("--extra: %s = %s", key, value) | 182 | logging.info("--extra: %s = %s", key, value) |
| 181 | 183 | ||
| 184 | + if extra_options.get('only_down_deps') == 'true': | ||
| 185 | + logging.info("only_down_deps=true, exiting after dependency download.") | ||
| 186 | + return | ||
| 187 | + | ||
| 182 | if 'test' in self.parsed_arguments.command: | 188 | if 'test' in self.parsed_arguments.command: |
| 183 | self._run_unit_tests() | 189 | self._run_unit_tests() |
| 184 | else: | 190 | else: |


🟡 Medium Priority
devcontainer.json 第 43-47 行配置了
--network=host(绕过网络隔离)、--privileged(授予容器所有宿主机设备访问权及全部 capabilities)、--ipc=host(共享宿主机 IPC 命名空间)以及--security-opt seccomp=unconfined(禁用 seccomp 系统调用过滤)。虽然注释说明这是为了对齐 ctr_in.py 的行为,但这些配置结合在一起实际上完全解除了 Docker 容器安全边界,使容器内任意进程等同于拥有宿主机 root 权限。若镜像或依赖被投毒,攻击面极大。README.md 第 14 行已提示"请在可信环境中使用",但仍需代码层面确认此为有意设计。建议:若构建流程确实需要特权模式,建议:(1)在 README 中更突出地警告安全风险;(2)评估是否可以用更细粒度的
--cap-add替代--privileged(如仅加 SYS_PTRACE、NET_ADMIN 等);(3)评估--security-opt seccomp=unconfined是否可移除或替换为自定义 seccomp profile。