已合并
[feature]:devcontainer机制打样仓合入,pre-commit机制调整 #65
孟广欣创建于 8月4日
[feature]:devcontainer机制打样仓合入,pre-commit机制调整 #65
已合并
共 6 个文件变更+311-110
| @@ -1,4 +1,4 @@ | |||
| 1 | -# Dev Container 快速入门指南 | 1 | +# Dev Container 快速指南 |
| 2 | 2 | ||
| 3 | > **真正开箱即用**:零手动配置!**首次 2 分钟** 全自动构建,**后续 10 秒** 极速开工。 | 3 | > **真正开箱即用**:零手动配置!**首次 2 分钟** 全自动构建,**后续 10 秒** 极速开工。 |
| 4 | 4 | ||
| @@ -11,43 +11,9 @@ | |||
| 11 | | **远程服务器(推荐)** | [VS Code](https://code.visualstudio.com/) + `Dev Containers` + `Remote - SSH` 插件 | Linux 服务器已启用 Docker 服务 | 高性能计算、释放本地资源 | | 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 模式) | 单机离线开发 | | 12 | | **本地 PC** | [VS Code](https://code.visualstudio.com/) + `Dev Containers` 插件 | [Docker Desktop](https://www.docker.com/products/docker-desktop/)(Linux 模式) | 单机离线开发 | |
| 13 | 13 | ||
| 14 | -> ⚠️ *注意:默认配置启用了 Host 网络模式及高权限,请务必在可信环境中使用。* | 14 | +> ⚠️ **安全提示**:默认配置启用了 Host 网络模式及高权限,请务必在可信环境中使用。 |
| 15 | 15 | ||
| 16 | -## 🚀 3 步闪电开工 | 16 | +### 可选:配置 SSH 免密登录(仅远程服务器方案,10 秒完成) |
| 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 | 17 | ||
| 52 | 为避免频繁输入密码,可在 Windows PowerShell 中粘贴执行以下脚本,按提示操作即可自动完成配置: | 18 | 为避免频繁输入密码,可在 Windows PowerShell 中粘贴执行以下脚本,按提示操作即可自动完成配置: |
| 53 | 19 | ||
| @@ -73,7 +39,38 @@ Get-Content $pubKeyPath | ssh "${user}@${ip}" "mkdir -p ~/.ssh && chmod 700 ~/.s | |||
| 73 | Write-Host "公钥上传完成,免密登录配置成功!" -ForegroundColor Green | 39 | Write-Host "公钥上传完成,免密登录配置成功!" -ForegroundColor Green |
| 74 | ``` | 40 | ``` |
| 75 | 41 | ||
| 76 | -### 2. 毁坏无忧:一键复原环境 | 42 | +## 🚀 3 步闪电开工 |
| 43 | + | ||
| 44 | +1. **打开项目**:在 VS Code 中打开本项目代码目录。 | ||
| 45 | +2. **加载容器**:点击右下角弹出的 **`Reopen in Container`** 提示(或通过 `F1` 执行同名命令)。 | ||
| 46 | +3. **进入开发**:待容器环境初始化完成(弹出的日志打印“READY”时),即可直接进行编码、编译、单元测试、调试及合入。 | ||
| 47 | + | ||
| 48 | +## ⏱️ 自动化流程与耗时说明 | ||
| 49 | + | ||
| 50 | +启动 Dev Container 后,系统将**全自动完成以下环境配置**,期间无需任何人工干预: | ||
| 51 | + | ||
| 52 | +| 阶段 | 自动化任务 | 首次耗时 | 后续启动 | 体验 | | ||
| 53 | +|:--------------|:---------------------------------| :--- | :--- | :--- | | ||
| 54 | +| **1. 环境拉取** | 拉取预置镜像并部署 VS Code Server | ~1 分钟 | 3 秒 | 全程无感 | | ||
| 55 | +| **2. 身份与挂载** | 挂载代码目录(`/workspace`)并同步 Git 权限 | ~10 秒 | 3 秒 | 全程无感 | | ||
| 56 | +| **3. 工具链加载** | 并行安装 Python 插件及 Clangd 等开发工具 | ~20 秒 | 3 秒 | 开箱即用 | | ||
| 57 | +| **总计** | **零人工干预·全自动就绪** | **⏱️ ~2 分钟** | **⚡ ~10 秒** | **一次配置,持续高效** | | ||
| 58 | + | ||
| 59 | +> **镜像说明**:因 MindStudio 镜像制作流程复杂且耗时,本方案**内置预构建镜像**。若需了解镜像细节,可参考 [《MindStudio 统一构建镜像制作指南》](https://gitcode.com/Ascend/msot/blob/master/docs/zh/common/docker_image_build_guide.md)。 | ||
| 60 | + | ||
| 61 | +## 🔨 编译与单元测试 | ||
| 62 | + | ||
| 63 | +环境就绪后,通过 VS Code 菜单栏 **`Terminal`** > **`Run Task`** 即可调用预设的自动化任务: | ||
| 64 | + | ||
| 65 | +| 任务名称 | 功能说明 | | ||
| 66 | +| :--- | :--- | | ||
| 67 | +| `Build: Release Mode` | 构建 Release 版本,产物输出至 `artifacts` 目录 | | ||
| 68 | +| `Test: Run Unit Tests` | 执行全量单元测试 | | ||
| 69 | +| `Clean: All Workspace` | 清理工作区内的所有构建缓存与临时文件 | | ||
| 70 | + | ||
| 71 | +> *也可直接在终端执行 `python3 build.py [test]` 命令,其功能与上述 Build/Test 任务一致。* | ||
| 72 | + | ||
| 73 | +## ♻️ 环境复原:毁坏无忧 | ||
| 77 | 74 | ||
| 78 | 若开发过程中容器环境搞乱或损坏,无需重新搭建:只需按 `F1` 键选择 **Dev Containers: Rebuild Container**,即可瞬间获得一个全新的纯净环境! | 75 | 若开发过程中容器环境搞乱或损坏,无需重新搭建:只需按 `F1` 键选择 **Dev Containers: Rebuild Container**,即可瞬间获得一个全新的纯净环境! |
| 79 | 76 | ||
| @@ -104,12 +101,7 @@ VS Code 远程开发依赖 SSH 端口转发。若服务端 `sshd_config` 限制 | |||
| 104 | - 在远程服务器执行:`rm -rf ~/.vscode-server` | 101 | - 在远程服务器执行:`rm -rf ~/.vscode-server` |
| 105 | - 重新发起连接,VS Code 将自动重新部署匹配的 Server 组件。 | 102 | - 重新发起连接,VS Code 将自动重新部署匹配的 Server 组件。 |
| 106 | 103 | ||
| 107 | -### 2. 代码提交响应缓慢或无反馈? | 104 | +### 2. 修改 `.vscode/settings.json` 后 `git pull` 冲突且无法更新? |
| 108 | - | ||
| 109 | -**原因分析**: | ||
| 110 | -项目默认启用 `pre-commit` 钩子。首次提交时需下载并初始化检查工具,耗时约 30~60 秒。后续提交将直接执行检查,响应通常为秒级。 | ||
| 111 | - | ||
| 112 | -### 3. 修改 `.vscode/settings.json` 后 `git pull` 冲突且无法更新? | ||
| 113 | 105 | ||
| 114 | **原因分析**: | 106 | **原因分析**: |
| 115 | 为支持个性化配置,该文件被标记为 `skip-worktree`,本地修改不会显示在 `git status` 中。当远端同步更新该文件时,Git 会拒绝覆盖本地内容以防止丢失。 | 107 | 为支持个性化配置,该文件被标记为 `skip-worktree`,本地修改不会显示在 `git status` 中。当远端同步更新该文件时,Git 会拒绝覆盖本地内容以防止丢失。 |
| @@ -16,7 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | { | 17 | { |
| 18 | "name": "mskl", | 18 | "name": "mskl", |
| 19 | - "image": "swr.cn-north-4.myhuaweicloud.com/mindstudio-image/mindstudio-build:26.1.0-0701", | 19 | + "image": "swr.cn-north-4.myhuaweicloud.com/mindstudio-image/mindstudio-build:26.2.0-0801", |
| 20 | 20 | ||
| 21 | // 镜像 WORKDIR 即 /workspace;z_cache.sh 通过设备号探测 /workspace 是否为 | 21 | // 镜像 WORKDIR 即 /workspace;z_cache.sh 通过设备号探测 /workspace 是否为 |
| 22 | // 独立挂载点,命中后自动把 ccache/uv 缓存放到 /workspace/.cache 下, | 22 | // 独立挂载点,命中后自动把 ccache/uv 缓存放到 /workspace/.cache 下, |
| @@ -55,7 +55,11 @@ | |||
| 55 | 55 | ||
| 56 | // uv 缓存持久化:将宿主机 ~/.cache/uv bind mount 到容器内。 | 56 | // uv 缓存持久化:将宿主机 ~/.cache/uv bind mount 到容器内。 |
| 57 | // 配合下方 UV_CACHE_DIR 环境变量,uv 安装/构建缓存不受容器重建影响。 | 57 | // 配合下方 UV_CACHE_DIR 环境变量,uv 安装/构建缓存不受容器重建影响。 |
| 58 | - "source=${localEnv:HOME}/.cache/uv,target=/home/mindstudio/.cache/uv,type=bind" | 58 | + "source=${localEnv:HOME}/.cache/uv,target=/home/mindstudio/.cache/uv,type=bind", |
| 59 | + | ||
| 60 | + // pre-commit Hook 环境持久化:使用 Docker named volume 保存首次初始化结果, | ||
| 61 | + // 容器重建后直接复用,无需再次下载仓库和创建各 Hook 的运行环境。 | ||
| 62 | + "source=pre-commit-cache,target=/home/mindstudio/.cache/pre-commit,type=volume" | ||
| 59 | ], | 63 | ], |
| 60 | "containerEnv": { | 64 | "containerEnv": { |
| 61 | // ctr_in.py 进入 mindstudio-build 容器时注入,保证 profile.d 启用 gcc11 | 65 | // ctr_in.py 进入 mindstudio-build 容器时注入,保证 profile.d 启用 gcc11 |
| @@ -65,7 +69,11 @@ | |||
| 65 | // /usr/local/nodejs,非 root 用户没有写权限,因此把全局 npm 包放到用户目录。 | 69 | // /usr/local/nodejs,非 root 用户没有写权限,因此把全局 npm 包放到用户目录。 |
| 66 | "NPM_CONFIG_PREFIX": "/home/mindstudio/.local", | 70 | "NPM_CONFIG_PREFIX": "/home/mindstudio/.local", |
| 67 | // 配合上方 mounts 中宿主机 uv 缓存目录挂载,固化 uv 包安装缓存路径。 | 71 | // 配合上方 mounts 中宿主机 uv 缓存目录挂载,固化 uv 包安装缓存路径。 |
| 68 | - "UV_CACHE_DIR": "/home/mindstudio/.cache/uv" | 72 | + "UV_CACHE_DIR": "/home/mindstudio/.cache/uv", |
| 73 | + | ||
| 74 | + // 显式对齐上方 named volume 的挂载目标,确保后台预热和 Git Hook 执行时 | ||
| 75 | + // 始终复用同一份 pre-commit 环境缓存。 | ||
| 76 | + "PRE_COMMIT_HOME": "/home/mindstudio/.cache/pre-commit" | ||
| 69 | }, | 77 | }, |
| 70 | "remoteEnv": { | 78 | "remoteEnv": { |
| 71 | // VS Code Server 及其扩展进程不一定读取 shell 启动文件,显式补充用户命令目录。 | 79 | // VS Code Server 及其扩展进程不一定读取 shell 启动文件,显式补充用户命令目录。 |
| @@ -53,7 +53,7 @@ else | |||
| 53 | : > "$SCRIPT_DIR/.host-gitconfig" | 53 | : > "$SCRIPT_DIR/.host-gitconfig" |
| 54 | fi | 54 | fi |
| 55 | 55 | ||
| 56 | -# ---- 准备 uv 缓存挂载源 ---- | 56 | +# ---- 准备缓存挂载源 ---- |
| 57 | # devcontainer.json 将该目录 bind mount 到 /home/mindstudio/.cache/uv。 | 57 | # devcontainer.json 将该目录 bind mount 到 /home/mindstudio/.cache/uv。 |
| 58 | # bind mount 的 source 必须在 docker create 前存在;initializeCommand 以宿主 | 58 | # bind mount 的 source 必须在 docker create 前存在;initializeCommand 以宿主 |
| 59 | # 当前用户执行,因此新建目录天然归当前用户所有。 | 59 | # 当前用户执行,因此新建目录天然归当前用户所有。 |
| @@ -31,16 +31,18 @@ | |||
| 31 | # 能够感知到这些工具链的存在。 | 31 | # 能够感知到这些工具链的存在。 |
| 32 | # | 32 | # |
| 33 | # 执行顺序(按依赖关系排列): | 33 | # 执行顺序(按依赖关系排列): |
| 34 | -# 1. fix_cache_ownership - 修复 z_cache.sh 创建的缓存目录权限 | 34 | +# 1. configure_yum_mirror - 切换至 HTTP 协议的华为云 openEuler 软件源 |
| 35 | -# 2. fix_file_watcher_limit - 提升 inotify max_user_watches 至 524288 | 35 | +# 2. fix_cache_ownership - 修复 z_cache.sh 创建的缓存目录权限 |
| 36 | -# 3. configure_user_bin - 建立用户级命令目录,重映射 npm prefix | 36 | +# 3. fix_file_watcher_limit - 提升 inotify max_user_watches 至 524288 |
| 37 | -# 4. configure_python311 - 在 shell 启动文件中启用 Python 3.11 | 37 | +# 4. configure_user_bin - 建立用户级命令目录,重映射 npm prefix |
| 38 | -# 5. sync_git_identity - 从宿主同步 Git 用户名和邮箱 | 38 | +# 5. ensure_shared_bin_path - 补齐 root 用户的共享命令路径 |
| 39 | -# 6. append_dev_hint_once - 向 .bash_profile 追加常用开发命令提示 | 39 | +# 6. configure_python311 - 在 shell 启动文件中启用 Python 3.11 |
| 40 | -# 7. install_pre_commit_hook - 自动安装 pre-commit Git Hook | 40 | +# 7. sync_git_identity - 从宿主同步 Git 用户名和邮箱 |
| 41 | -# 8. install_gitleaks - 从 OBS 下载 gitleaks 二进制(pre-commit 依赖) | 41 | +# 8. append_dev_hint_once - 向 .bash_profile 追加常用开发命令提示 |
| 42 | -# 9. ignore_vscode_settings - 隔离个人化 VS Code settings 修改 | 42 | +# 9. install_pre_commit_hook - 自动安装 pre-commit Git Hook |
| 43 | -# 10. install_git_safe_pull_alias - 安装可处理 skip-worktree 文件的拉取命令 | 43 | +# 10. warmup_pre_commit_async - 后台预创建 pre-commit Hook 环境 |
| 44 | +# 11. ignore_vscode_settings - 隔离个人化 VS Code settings 修改 | ||
| 45 | +# 12. install_git_safe_pull_alias - 安装可处理 skip-worktree 文件的拉取命令 | ||
| 44 | # | 46 | # |
| 45 | # ============================================================================= | 47 | # ============================================================================= |
| 46 | 48 | ||
| @@ -62,6 +64,106 @@ warn() { | |||
| 62 | printf '[post-create] warning: %s\n' "$*" >&2 | 64 | printf '[post-create] warning: %s\n' "$*" >&2 |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 67 | +# 以统一格式执行并展示初始化步骤。即使某一步失败也继续后续流程,避免单项 | ||
| 68 | +# 非关键配置阻止开发者进入容器;耗时用于识别初始化过程中的慢步骤。 | ||
| 69 | +run_step() { | ||
| 70 | + local step_number="$1" | ||
| 71 | + local total_steps="$2" | ||
| 72 | + local step_title="$3" | ||
| 73 | + local step_function="$4" | ||
| 74 | + local step_label | ||
| 75 | + local exit_code | ||
| 76 | + local started_at=$SECONDS | ||
| 77 | + local elapsed | ||
| 78 | + | ||
| 79 | + printf -v step_label '%02d/%02d' "$step_number" "$total_steps" | ||
| 80 | + printf '\n' | ||
| 81 | + log "------------------------------------------------------------------------" | ||
| 82 | + log "[STEP $step_label] START | $step_title" | ||
| 83 | + | ||
| 84 | + if "$step_function"; then | ||
| 85 | + elapsed=$((SECONDS - started_at)) | ||
| 86 | + log "[STEP $step_label] DONE | $step_title (${elapsed}s)" | ||
| 87 | + else | ||
| 88 | + exit_code=$? | ||
| 89 | + elapsed=$((SECONDS - started_at)) | ||
| 90 | + warn "[STEP $step_label] FAILED | $step_title (${elapsed}s, exit: $exit_code)" | ||
| 91 | + fi | ||
| 92 | + | ||
| 93 | + # run_step 自身始终成功,确保某一步失败后仍执行剩余初始化任务。 | ||
| 94 | + return 0 | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +# ============================================================================= | ||
| 98 | +# configure_yum_mirror —— 配置华为云 openEuler 软件源 | ||
| 99 | +# ============================================================================= | ||
| 100 | +# | ||
| 101 | +# 背景与问题: | ||
| 102 | +# 基础镜像默认使用 https://repo.openeuler.org,部分开发网络访问该地址较慢。 | ||
| 103 | +# 华为云 openEuler 镜像站提供相同的仓库目录结构,可直接保留当前发行版、 | ||
| 104 | +# 仓库分区和 $basearch 路径,仅替换站点前缀。 | ||
| 105 | +# | ||
| 106 | +# 解决方案: | ||
| 107 | +# 将 openEuler 官方源及 HTTPS 华为云源统一替换为: | ||
| 108 | +# http://mirrors.huaweicloud.com/openeuler | ||
| 109 | +# 修改前保留一次原始 repo 文件备份;仅在配置发生变化时清理 dnf/yum 元数据。 | ||
| 110 | +# 本地 file:// GPG Key 配置保持不变,软件包签名校验仍然启用。 | ||
| 111 | +configure_yum_mirror() { | ||
| 112 | + local repo_dir="/etc/yum.repos.d" | ||
| 113 | + local mirror_base="http://mirrors.huaweicloud.com/openeuler" | ||
| 114 | + local repo_files=("$repo_dir"/*.repo) | ||
| 115 | + local repo_file | ||
| 116 | + local backup_file | ||
| 117 | + local changed=0 | ||
| 118 | + | ||
| 119 | + if [ ! -e "${repo_files[0]}" ]; then | ||
| 120 | + warn "no yum repo files found in $repo_dir; skipping mirror configuration" | ||
| 121 | + return 0 | ||
| 122 | + fi | ||
| 123 | + | ||
| 124 | + for repo_file in "${repo_files[@]}"; do | ||
| 125 | + if ! grep -Eq 'https?://repo\.openeuler\.org|https://mirrors\.huaweicloud\.com/openeuler|https?://repo\.huaweicloud\.com/openeuler' "$repo_file"; then | ||
| 126 | + continue | ||
| 127 | + fi | ||
| 128 | + | ||
| 129 | + backup_file="${repo_file}.post-create.bak" | ||
| 130 | + if [ ! -e "$backup_file" ]; then | ||
| 131 | + sudo cp -a "$repo_file" "$backup_file" || { | ||
| 132 | + warn "failed to back up yum repo file: $repo_file" | ||
| 133 | + return 1 | ||
| 134 | + } | ||
| 135 | + fi | ||
| 136 | + | ||
| 137 | + sudo sed -E -i \ | ||
| 138 | + -e "s#https?://repo\.openeuler\.org#${mirror_base}#g" \ | ||
| 139 | + -e "s#https://mirrors\.huaweicloud\.com/openeuler#${mirror_base}#g" \ | ||
| 140 | + -e "s#https?://repo\.huaweicloud\.com/openeuler#${mirror_base}#g" \ | ||
| 141 | + "$repo_file" || { | ||
| 142 | + warn "failed to update yum repo file: $repo_file" | ||
| 143 | + return 1 | ||
| 144 | + } | ||
| 145 | + changed=1 | ||
| 146 | + done | ||
| 147 | + | ||
| 148 | + if ! grep -Eq '^[[:space:]]*baseurl=http://mirrors\.huaweicloud\.com/openeuler/' "${repo_files[@]}"; then | ||
| 149 | + warn "Huawei Cloud yum mirror was not found in active repo configuration" | ||
| 150 | + return 1 | ||
| 151 | + fi | ||
| 152 | + | ||
| 153 | + if [ "$changed" -eq 1 ]; then | ||
| 154 | + if command -v dnf >/dev/null 2>&1; then | ||
| 155 | + sudo dnf clean all >/dev/null 2>&1 || warn "failed to clean dnf metadata" | ||
| 156 | + elif command -v yum >/dev/null 2>&1; then | ||
| 157 | + sudo yum clean all >/dev/null 2>&1 || warn "failed to clean yum metadata" | ||
| 158 | + fi | ||
| 159 | + log "yum mirror changed to $mirror_base" | ||
| 160 | + else | ||
| 161 | + log "yum mirror already uses $mirror_base" | ||
| 162 | + fi | ||
| 163 | + | ||
| 164 | + log "configure_yum_mirror succeeded" | ||
| 165 | +} | ||
| 166 | + | ||
| 65 | # append_path_once: | 167 | # append_path_once: |
| 66 | # 幂等地将 $HOME/.local/bin 放到指定 shell 启动文件的 PATH 前端。 | 168 | # 幂等地将 $HOME/.local/bin 放到指定 shell 启动文件的 PATH 前端。 |
| 67 | # 解决非 root 用户(mindstudio)无法写入 /usr/local/nodejs 等系统级目录的问题。 | 169 | # 解决非 root 用户(mindstudio)无法写入 /usr/local/nodejs 等系统级目录的问题。 |
| @@ -339,6 +441,83 @@ install_pre_commit_hook() { | |||
| 339 | log "install_pre_commit_hook succeeded" | 441 | log "install_pre_commit_hook succeeded" |
| 340 | } | 442 | } |
| 341 | 443 | ||
| 444 | +# ============================================================================= | ||
| 445 | +# warmup_pre_commit_async —— 后台预创建 pre-commit Hook 环境 | ||
| 446 | +# ============================================================================= | ||
| 447 | +# | ||
| 448 | +# 背景与问题: | ||
| 449 | +# pre-commit 首次运行时需要下载 Hook 仓库并创建各自的运行环境,耗时较长。 | ||
| 450 | +# 如果等到开发者第一次提交时才执行,会明显阻塞提交流程。 | ||
| 451 | +# | ||
| 452 | +# 解决方案: | ||
| 453 | +# pre-commit Hook 安装完成后,在后台执行 `pre-commit install-hooks`,并与 | ||
| 454 | +# 后续 clangd 安装等初始化任务并行。该命令只准备 Hook 环境,不执行检查, | ||
| 455 | +# 也不会修改工作区文件。后台进程的标准输入、输出和错误均与 | ||
| 456 | +# postCreateCommand 分离,避免阻塞容器初始化完成。 | ||
| 457 | +# | ||
| 458 | +# 资源与并发控制: | ||
| 459 | +# 1. 使用 nice 和 ionice 降低 CPU、磁盘调度优先级,减少对交互操作的影响。 | ||
| 460 | +# 2. flock 可用时持有非阻塞锁,防止脚本重复执行后同时启动多个预热任务。 | ||
| 461 | +# 3. 详细结果写入缓存目录中的日志,预热失败不影响容器正常使用。 | ||
| 462 | +warmup_pre_commit_async() { | ||
| 463 | + if ! command -v pre-commit >/dev/null 2>&1; then | ||
| 464 | + warn "pre-commit is not available; skipping async warmup" | ||
| 465 | + return 0 | ||
| 466 | + fi | ||
| 467 | + | ||
| 468 | + local repo_root | ||
| 469 | + repo_root=$(git rev-parse --show-toplevel 2>/dev/null || true) | ||
| 470 | + if [ -z "$repo_root" ]; then | ||
| 471 | + warn "workspace is not a Git repository; skipping async warmup" | ||
| 472 | + return 0 | ||
| 473 | + fi | ||
| 474 | + | ||
| 475 | + if [ ! -f "$repo_root/.pre-commit-config.yaml" ]; then | ||
| 476 | + warn "pre-commit config is not available; skipping async warmup" | ||
| 477 | + return 0 | ||
| 478 | + fi | ||
| 479 | + | ||
| 480 | + local warmup_dir="$HOME/.cache" | ||
| 481 | + local log_file="$warmup_dir/pre-commit-warmup.log" | ||
| 482 | + local lock_file="$warmup_dir/pre-commit-warmup.lock" | ||
| 483 | + mkdir -p "$warmup_dir" | ||
| 484 | + | ||
| 485 | + ( | ||
| 486 | + # 忽略启动 shell 结束时可能发送的 HUP,并断开所有终端输入输出。 | ||
| 487 | + trap '' HUP | ||
| 488 | + cd "$repo_root" || exit 0 | ||
| 489 | + | ||
| 490 | + if command -v flock >/dev/null 2>&1; then | ||
| 491 | + exec 9>"$lock_file" | ||
| 492 | + if ! flock -n 9; then | ||
| 493 | + log "pre-commit environment warmup is already running" | ||
| 494 | + exit 0 | ||
| 495 | + fi | ||
| 496 | + fi | ||
| 497 | + | ||
| 498 | + # ionice 调整失败时继续运行;nice 在常规 Linux 环境中始终可用。 | ||
| 499 | + if command -v ionice >/dev/null 2>&1; then | ||
| 500 | + ionice -c 3 -p "$$" 2>/dev/null || true | ||
| 501 | + fi | ||
| 502 | + | ||
| 503 | + # 确保 $HOME/.local/bin 在 PATH 前端,使用与用户交互式 shell | ||
| 504 | + # 一致的 pre-commit 二进制(Python 3.11),避免版本不匹配导致 | ||
| 505 | + # 预热环境与用户实际使用的 Python 版本不一致而重新下载。 | ||
| 506 | + export PATH="$HOME/.local/bin:$PATH" | ||
| 507 | + | ||
| 508 | + log "pre-commit environment warmup started" | ||
| 509 | + if nice -n 10 pre-commit install-hooks; then | ||
| 510 | + log "pre-commit environment warmup succeeded" | ||
| 511 | + else | ||
| 512 | + warn "pre-commit environment warmup failed" | ||
| 513 | + fi | ||
| 514 | + ) </dev/null >>"$log_file" 2>&1 & | ||
| 515 | + | ||
| 516 | + local warmup_pid | ||
| 517 | + warmup_pid=$! | ||
| 518 | + log "pre-commit environment warmup started in background (pid: $warmup_pid, log: $log_file)" | ||
| 519 | +} | ||
| 520 | + | ||
| 342 | # ============================================================================= | 521 | # ============================================================================= |
| 343 | # fix_cache_ownership —— 修复缓存目录权限 | 522 | # fix_cache_ownership —— 修复缓存目录权限 |
| 344 | # ============================================================================= | 523 | # ============================================================================= |
| @@ -477,6 +656,7 @@ fix_file_watcher_limit() { | |||
| 477 | 656 | ||
| 478 | warn "failed to increase inotify max_user_watches; VS Code file watching may not work correctly" | 657 | warn "failed to increase inotify max_user_watches; VS Code file watching may not work correctly" |
| 479 | } | 658 | } |
| 659 | + | ||
| 480 | # ============================================================================= | 660 | # ============================================================================= |
| 481 | # ignore_vscode_settings —— 隔离个人化 VS Code Settings 修改 | 661 | # ignore_vscode_settings —— 隔离个人化 VS Code Settings 修改 |
| 482 | # ============================================================================= | 662 | # ============================================================================= |
| @@ -548,46 +728,33 @@ install_git_safe_pull_alias() { | |||
| 548 | log "install_git_safe_pull_alias succeeded" | 728 | log "install_git_safe_pull_alias succeeded" |
| 549 | } | 729 | } |
| 550 | 730 | ||
| 551 | -# ============================================================================= | 731 | +# 输出醒目的完成标识,便于开发者在 Dev Containers 启动日志中快速确认状态。 |
| 552 | -# install_gitleaks —— Gitleaks 秘密扫描二进制下载 | 732 | +print_ready_banner() { |
| 553 | -# ============================================================================= | 733 | + local green=$'\033[1;32m' |
| 554 | -# | 734 | + local reset=$'\033[0m' |
| 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 | 735 | ||
| 573 | - # 根据 CPU 架构选择对应的二进制目录 | 736 | + cat <<EOF |
| 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 | 737 | ||
| 581 | - log "downloading gitleaks (${arch}) from OBS..." | 738 | ++------------------------------------------------------------------------------+ |
| 582 | - if wget --no-host-directories -c --no-check-certificate \ | 739 | +| | |
| 583 | - -O "$target" "$url" 2>/dev/null; then | 740 | +|${green} ____ _____ _ ______ __ ${reset}| |
| 584 | - chmod +x "$target" | 741 | +|${green} | _ \| ____| / \ | _ \ \ / / ${reset}| |
| 585 | - log "gitleaks installed successfully: $($target --version 2>/dev/null || echo 'version unknown')" | 742 | +|${green} | |_) | _| / _ \ | | | \ V / ${reset}| |
| 586 | - else | 743 | +|${green} | _ <| |___ / ___ \| |_| || | ${reset}| |
| 587 | - warn "failed to download gitleaks (${arch}) from OBS; git commit may fail on pre-commit hook" | 744 | +|${green} |_| \_\_____/_/ \_\____/ |_| ${reset}| |
| 588 | - warn "URL attempted: ${url}" | 745 | +| | |
| 589 | - rm -f "$target" | 746 | +| +------------------------------------------------------------------------+ | |
| 590 | - fi | 747 | +| | DEV CONTAINER STATUS: ${green}READY${reset} | | |
| 748 | +| +------------------------------------------------------------------------+ | | ||
| 749 | +| | | ||
| 750 | +| ENVIRONMENT ${green}READY${reset} FOR DEVELOPMENT | | ||
| 751 | +| WORKSPACE /workspace | | ||
| 752 | +| | | ||
| 753 | +| Initialization complete. Start coding, building, and shipping. | | ||
| 754 | +| | | ||
| 755 | ++------------------------------------------------------------------------------+ | ||
| 756 | + | ||
| 757 | +EOF | ||
| 591 | } | 758 | } |
| 592 | 759 | ||
| 593 | # ============================================================================= | 760 | # ============================================================================= |
| @@ -595,20 +762,23 @@ install_gitleaks() { | |||
| 595 | # ============================================================================= | 762 | # ============================================================================= |
| 596 | # | 763 | # |
| 597 | # 按依赖顺序执行:先修复目录权限,再写用户配置,最后安装 Git 辅助能力。 | 764 | # 按依赖顺序执行:先修复目录权限,再写用户配置,最后安装 Git 辅助能力。 |
| 598 | -# 各模块尽量自行降级并输出 warning,非关键项失败不阻止容器启动。 | 765 | +# run_step 为每一步输出序号、状态和耗时,非关键项失败不阻止容器启动。 |
| 599 | 766 | ||
| 600 | -log "post-create setup started" | 767 | +total_steps=14 |
| 768 | +log "Dev Container initialization started ($total_steps steps)" | ||
| 601 | 769 | ||
| 602 | -fix_cache_ownership | 770 | +run_step 1 "$total_steps" "Configure Huawei Cloud yum mirror" configure_yum_mirror |
| 603 | -fix_file_watcher_limit | 771 | +run_step 2 "$total_steps" "Fix cache directory ownership" fix_cache_ownership |
| 604 | -configure_user_bin | 772 | +run_step 3 "$total_steps" "Configure file watcher limit" fix_file_watcher_limit |
| 605 | -ensure_shared_bin_path | 773 | +run_step 4 "$total_steps" "Configure user command paths" configure_user_bin |
| 606 | -configure_python311 | 774 | +run_step 5 "$total_steps" "Configure shared command paths" ensure_shared_bin_path |
| 607 | -sync_git_identity | 775 | +run_step 6 "$total_steps" "Configure Python 3.11" configure_python311 |
| 608 | -append_dev_hint_once | 776 | +run_step 7 "$total_steps" "Synchronize Git identity" sync_git_identity |
| 609 | -install_pre_commit_hook | 777 | +run_step 8 "$total_steps" "Install developer shell hints" append_dev_hint_once |
| 610 | -install_gitleaks | 778 | +run_step 9 "$total_steps" "Install pre-commit Git Hook" install_pre_commit_hook |
| 611 | -ignore_vscode_settings | 779 | +run_step 10 "$total_steps" "Start pre-commit environment warmup" warmup_pre_commit_async |
| 612 | -install_git_safe_pull_alias | 780 | +run_step 11 "$total_steps" "Configure workspace Git settings" ignore_vscode_settings |
| 781 | +run_step 12 "$total_steps" "Install Git safe-pull command" install_git_safe_pull_alias | ||
| 613 | 782 | ||
| 614 | log "post-create setup finished" | 783 | log "post-create setup finished" |
| 784 | +print_ready_banner | ||
| @@ -91,7 +91,7 @@ repos: | |||
| 91 | hooks: | 91 | hooks: |
| 92 | - id: gitleaks-offline-scan | 92 | - id: gitleaks-offline-scan |
| 93 | name: Gitleaks Secret Scan(Local Binary) | 93 | name: Gitleaks Secret Scan(Local Binary) |
| 94 | - entry: ./gitleaks | 94 | + entry: gitleaks |
| 95 | language: system | 95 | language: system |
| 96 | pass_filenames: true | 96 | pass_filenames: true |
| 97 | args: | 97 | args: |
| @@ -0,0 +1,31 @@ | |||
| 1 | +#!/usr/bin/env bash | ||
| 2 | +# ============================================================================= | ||
| 3 | +# clang-tidy wrapper — checks compile_commands.json before running clang-tidy | ||
| 4 | +# ============================================================================= | ||
| 5 | +set -euo pipefail | ||
| 6 | + | ||
| 7 | +BUILD_DIRS=("build") | ||
| 8 | + | ||
| 9 | +for dir in "${BUILD_DIRS[@]}"; do | ||
| 10 | + if [[ -f "${dir}/compile_commands.json" ]]; then | ||
| 11 | + break | ||
| 12 | + fi | ||
| 13 | +done | ||
| 14 | + | ||
| 15 | +if [[ ! -f "${dir}/compile_commands.json" ]]; then | ||
| 16 | + cat >&2 <<'EOF' | ||
| 17 | +======================================================================= | ||
| 18 | + ERROR: compile_commands.json NOT FOUND | ||
| 19 | +----------------------------------------------------------------------- | ||
| 20 | + clang-tidy requires a compilation database to work. | ||
| 21 | + Run this command to generate compile_commands.json: | ||
| 22 | + | ||
| 23 | + python3 build.py | ||
| 24 | + | ||
| 25 | + This will generate compile_commands.json under the build/ directory. | ||
| 26 | +======================================================================= | ||
| 27 | +EOF | ||
| 28 | + exit 1 | ||
| 29 | +fi | ||
| 30 | + | ||
| 31 | +exec clang-tidy "$@" | ||
🟡 Medium Priority
此行将 gitleaks hook 的 entry 从
./gitleaks(仓库根目录的本地二进制文件)改为gitleaks(依赖系统 PATH 查找)。然而,
.devcontainer/post-create.sh中的install_gitleaks函数(第 567-591 行)仍将 gitleaks 二进制文件下载到/workspace/gitleaks,其注释(第 556-558 行)明确指出这是为了满足./gitleaks配置。该函数的设计与旧的./gitleaks入口绑定——它将二进制文件放在仓库根目录,该目录通常不在 PATH 中。失效模式:在 devcontainer 内,
post-create.sh运行后,gitleaks 位于/workspace/gitleaks,但新的 entrygitleaks会在 PATH 中查找。由于/workspace通常不在 PATH 中,pre-commit hook 会因 "Executablegitleaksnot found" 而失败。即使新镜像26.2.0-0801恰好预装了 gitleaks,install_gitleaks函数仍会进行冗余下载,且其注释现已产生误导。建议:使 pre-commit entry 与 install_gitleaks 的行为保持一致。两种方案可选:(a) 将 entry 恢复为
./gitleaks以匹配现有的 install_gitleaks 逻辑;或 (b) 更新 install_gitleaks,将二进制文件安装到 PATH 中的某个目录(如$HOME/.local/bin/gitleaks,因为 configure_user_bin 已确保该目录在 PATH 中),并将 entry 保持为gitleaks。若新镜像已预装 gitleaks,方案 (b) 更佳——同时移除 install_gitleaks 函数中冗余的下载步骤。