chore: update devcontainer configuration and add setup scripts
All checks were successful
CI/CD Pipeline / playwright (push) Successful in 1m33s
CI/CD Pipeline / build-and-deploy (push) Successful in 1m53s
测试最新依赖 / build-and-test (push) Successful in 1m31s
测试最新依赖 / playwright (push) Successful in 2m26s

This commit is contained in:
严浩
2025-11-18 00:47:42 +08:00
parent 3affb21d99
commit e6731afff5
6 changed files with 188 additions and 187 deletions

View File

@@ -1,100 +0,0 @@
# docker build -t my-dev-container .
FROM debian:13.1-slim
# 元数据
LABEL maintainer="i@oo1.dev" \
description="Development container for Claude and Gemini development"
# 构建参数
ARG GIT_DELTA_VERSION=0.18.2
ARG ZSH_IN_DOCKER_VERSION=1.2.1
ARG TZ=Asia/Shanghai
# 环境变量
ENV PNPM_HOME="/home/usr_vscode/.local/share/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
ENV LANG=zh_CN.UTF-8 \
LC_ALL=zh_CN.UTF-8 \
TZ=${TZ} \
# Set the default shell to zsh rather than sh
SHELL=/bin/zsh \
# Set the default editor and visual
EDITOR=nano \
VISUAL=nano \
POWERLEVEL9K_DISABLE_GITSTATUS=true
# 安装所有系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
# 语言包
locales \
# 基础工具
ca-certificates wget curl \
# 开发工具
less git procps sudo fzf zsh man-db unzip gnupg2 gh \
# 网络工具
iptables ipset iproute2 dnsutils aggregate \
# 编辑器和实用工具
jq nano vim \
# 配置中文环境
&& sed -i 's/^# *zh_CN.UTF-8/zh_CN.UTF-8/' /etc/locale.gen \
&& locale-gen zh_CN.UTF-8 \
&& update-locale LANG=zh_CN.UTF-8 \
# 清理缓存
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# 创建用户和配置 sudo
RUN groupadd --gid 1000 usr_vscode \
&& useradd --uid 1000 --gid usr_vscode --shell /bin/zsh --create-home usr_vscode \
&& echo "usr_vscode ALL=(root) NOPASSWD: ALL" > /etc/sudoers.d/usr_vscode_user_config \
&& chmod 0440 /etc/sudoers.d/usr_vscode_user_config \
&& cat /etc/passwd
# 配置命令历史
RUN mkdir -p /commandhistory \
&& touch /commandhistory/.bash_history \
&& chown -R usr_vscode:usr_vscode /commandhistory
# 创建工作目录
RUN mkdir -p /wrkspc /home/usr_vscode/.claude \
&& chown -R usr_vscode:usr_vscode /wrkspc /home/usr_vscode/.claude
# 安装 git-delta https://github.com/dandavison/delta/releases
RUN ARCH=$(dpkg --print-architecture) && \
curl -L -o "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
WORKDIR /wrkspc
# 切换到非 root 用户
USER usr_vscode
# 配置 Zsh https://github.com/deluan/zsh-in-docker/releases
# Default powerline10k theme
RUN sh -c "$(curl -L -o - https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
-p git \
-p fzf \
-a "source <(fzf --zsh)" \
-a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
-x \
# -a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
# -a "source /usr/share/doc/fzf/examples/completion.zsh" \
# https://github.com/deluan/zsh-in-docker/blob/55a4e476f78f37204af9dfc0290a92e52cf881b1/zsh-in-docker.sh#L121-L123
&& sed -i 's/^export LANG=.*/export LANG="zh_CN.UTF-8"/' /home/usr_vscode/.zshrc \
&& sed -i 's/^export LANGUAGE=.*/export LANGUAGE="zh_CN:zh"/' /home/usr_vscode/.zshrc \
&& sed -i 's/^export LC_ALL=.*/export LC_ALL="zh_CN.UTF-8"/' /home/usr_vscode/.zshrc \
# >>>>>
&& echo "" >> /home/usr_vscode/.zshrc \
&& echo "alias gemini='gemini --yolo -m gemini-2.5-pro'" >> /home/usr_vscode/.zshrc \
&& echo "alias claude='claude --dangerously-skip-permissions'" >> /home/usr_vscode/.zshrc \
&& echo '# [[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path zsh)"' >> /home/usr_vscode/.zshrc
# 安装 pnpm
# https://pnpm.io/installation#in-a-docker-container
RUN wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.zshrc" SHELL="$(which zsh)" zsh - \
&& pnpm --version \
&& mkdir /home/usr_vscode/.pnpm-store \
&& mkdir -p /home/usr_vscode/.cache/pnpm \
# /home/usr_vscode/.config/pnpm/rc
&& pnpm config --global set store-dir /home/usr_vscode/.pnpm-store

View File

@@ -1,41 +1,31 @@
/**
*
* https://containers.dev/implementors/json_reference/#variables-in-devcontainerjson
* https://code.claude.com/docs/zh-CN/devcontainer
* https://github.com/Kilo-Org/kilocode/blob/main/cli/Dockerfile
*/
{ {
"name": "SBX", "image": "ghcr.io/yanhao98/h-devcontainer:oncreatecommand.d",
"image": "my-dev-container", "runArgs": ["--name=${localWorkspaceFolderBasename}-devcontainer"],
// "build": { "dockerfile": "Dockerfile", "args": {} },
"runArgs": [
// -----
// "--network=host",
// "--add-host=myservice.local:127.0.0.1",
// -----
// "--env-file",
// ".devcontainer/.env",
// -----
"--cap-add=NET_ADMIN",
"--cap-add=NET_RAW",
"--add-host=host.docker.internal:host-gateway",
"--name=${localWorkspaceFolderBasename}-devcontainer"
],
"forwardPorts": [4730, 4731], // vscode://settings/remote.localPortHost -> 默认只监听 localhost "forwardPorts": [4730, 4731], // vscode://settings/remote.localPortHost -> 默认只监听 localhost
"portsAttributes": { "portsAttributes": {
"4730": { "label": "开发服务器端口", "onAutoForward": "notify" }, "4730": { "label": "开发服务器端口", "onAutoForward": "notify" },
"4731": { "label": "预览服务器端口", "onAutoForward": "notify" } "4731": { "label": "预览服务器端口", "onAutoForward": "notify" }
}, },
"remoteEnv": {
"ANTHROPIC_AUTH_TOKEN": "${localEnv:ANTHROPIC_AUTH_TOKEN}",
"ANTHROPIC_BASE_URL": "${localEnv:ANTHROPIC_BASE_URL}",
"GEMINI_API_KEY": "${localEnv:GEMINI_API_KEY}",
"GOOGLE_GEMINI_BASE_URL": "${localEnv:GOOGLE_GEMINI_BASE_URL}",
"PATH": "${containerEnv:PATH}:${containerWorkspaceFolder}/node_modules/.bin:${containerEnv:HOME}/.bun/bin:${containerEnv:HOME}/.bun/bin/bun-node-fallback-bin"
},
"containerEnv": {
"HOME": "/home/usr_vscode",
// "NODE_OPTIONS": "--max-old-space-size=4096",
"TZ": "${localEnv:TZ:Asia/Shanghai}"
},
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [
// AI // AI
"github.copilot-chat", "github.copilot-chat",
"vicanent.gcmp",
"anthropic.claude-code", "anthropic.claude-code",
"google.gemini-cli-vscode-ide-companion", "google.gemini-cli-vscode-ide-companion",
"kilocode.kilo-code", "vicanent.gcmp",
"alibaba-cloud.tongyi-lingma",
// >>>>> // >>>>>
// "eamodio.gitlens", // "eamodio.gitlens",
"tu6ge.naive-ui-intelligence", "tu6ge.naive-ui-intelligence",
@@ -46,7 +36,6 @@
"vue.volar", "vue.volar",
// <<<<< // <<<<<
// 代码质量 / 格式化 / Lint // 代码质量 / 格式化 / Lint
"editorconfig.editorconfig",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"oxc.oxc-vscode", "oxc.oxc-vscode",
@@ -58,13 +47,14 @@
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"
}, },
"chat.extensionUnification.enabled": true, "chat.extensionUnification.enabled": true,
"chat.tools.terminal.autoApprove": { "/.*/": true }, "chat.tools.terminal.autoApprove": {
"chat.tools.terminal.ignoreDefaultAutoApproveRules": false, "/.*/": true,
"git push": false
},
// * 尽管使用了“/.*/”,但有些还是会失败,因为有几个错误的默认值: // * 尽管使用了“/.*/”,但有些还是会失败,因为有几个错误的默认值:
// * https://github.com/microsoft/vscode/issues/266651#issuecomment-3292581459 // * https://github.com/microsoft/vscode/issues/266651#issuecomment-3292581459
"chat.tools.terminal.ignoreDefaultAutoApproveRules": true,
"tasks": { "tasks": {
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
@@ -72,24 +62,16 @@
"type": "npm", "type": "npm",
"script": "dev", "script": "dev",
"label": "🚀 Dev: Run on Folder Open", "label": "🚀 Dev: Run on Folder Open",
"detail": "启动开发服务器", "runOptions": {
"runOptions": { "runOn": "folderOpen" }, "runOn": "folderOpen"
"problemMatcher": [], },
"isBackground": true, "isBackground": true,
"presentation": { "reveal": "always", "panel": "dedicated" }, "presentation": {
"group": { "kind": "build", "isDefault": false } "panel": "dedicated"
}
} }
] ]
}, },
"terminal.integrated.defaultProfile.linux": "💲zsh",
"terminal.integrated.profiles.linux": {
"💲zsh": {
"path": "zsh",
"icon": "terminal-cmd",
"overrideName": true,
"color": "terminal.ansiGreen"
}
},
// https://stackoverflow.com/questions/75708866/vscode-dev-container-fails-to-load-ms-vscode-js-debug-extension-correctly // https://stackoverflow.com/questions/75708866/vscode-dev-container-fails-to-load-ms-vscode-js-debug-extension-correctly
// https://davidwesst.com/blog/missing-bootloader-in-vscode-devcontainer/ // https://davidwesst.com/blog/missing-bootloader-in-vscode-devcontainer/
/** /**
@@ -100,62 +82,61 @@
* Error: illegal value for flag --max-old-space-size=4096--max-old-space-size=4096 of type size_t * Error: illegal value for flag --max-old-space-size=4096--max-old-space-size=4096 of type size_t
* "disabled" * "disabled"
*/ */
"debug.javascript.autoAttachFilter": "disabled" // "debug.javascript.autoAttachFilter": "disabled",
"terminal.integrated.defaultProfile.linux": "💲zsh",
"terminal.integrated.profiles.linux": {
"💲zsh": {
"path": "zsh",
"icon": "terminal-cmd",
"overrideName": true,
"color": "terminal.ansiGreen"
}
}
} }
} }
},
"containerEnv": {
// "CLAUDE_CONFIG_DIR": "/home/usr_vscode/.claude",
"ANTHROPIC_AUTH_TOKEN": "${localEnv:ANTHROPIC_AUTH_TOKEN}",
"ANTHROPIC_BASE_URL": "${localEnv:ANTHROPIC_BASE_URL}",
"GEMINI_API_KEY": "${localEnv:GEMINI_API_KEY}",
"GOOGLE_GEMINI_BASE_URL": "${localEnv:GOOGLE_GEMINI_BASE_URL}",
"NODE_OPTIONS": "--max-old-space-size=4096",
"CLAUDE_CONFIG_DIR": "/home/usr_vscode/.claude",
"DEVCONTAINER": "true", // https://github.com/anthropics/claude-code/blob/1fe9e369a7c30805189cbbb72eb69c15ed4ec96b/.devcontainer/Dockerfile#L42
"SANDBOX": "sandbox-devcontainer", // gemini 显示这个。
"TZ": "${localEnv:TZ:Asia/Shanghai}"
}, },
"mounts": [ "mounts": [
"source=${localWorkspaceFolderBasename}---bashhistory,target=/commandhistory,type=volume", {
"source=${localWorkspaceFolderBasename}---iflow,target=/home/usr_vscode/.iflow,type=volume", // 不挂载可能会遇到:`Cannot run macOS (Mach-O) executable in Docker: Exec format error`
"source=${localWorkspaceFolderBasename}---gemini,target=/home/usr_vscode/.gemini,type=volume", "type": "volume",
"source=${localWorkspaceFolderBasename}---claude-code-router,target=/home/usr_vscode/.claude-code-router,type=volume", "source": "${localWorkspaceFolderBasename}-node_modules",
"source=${localWorkspaceFolderBasename}---claude-code,target=/home/usr_vscode/.claude,type=volume", "target": "${containerWorkspaceFolder}/node_modules"
"source=${localWorkspaceFolderBasename}---pnpm-store,target=/home/usr_vscode/.pnpm-store,type=volume", },
"source=${localWorkspaceFolderBasename}---cache-pnpm,target=/home/usr_vscode/.cache/pnpm,type=volume", {
"source=${localWorkspaceFolderBasename}---node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" "type": "volume",
"source": "devcontainer-pnpm-store",
"target": "/home/usr_vscode/.pnpm-store"
},
{
"type": "volume",
"source": "devcontainer-bun-install-cache",
"target": "/home/usr_vscode/.bun/install/cache"
// rm -rf ~/.bun/install/cache/*
},
{
"type": "bind",
"source": "${localWorkspaceFolder}/.devcontainer/onCreateCommand.d",
"target": "/usr/local/etc/onCreateCommand.d"
}
], ],
/** /**
* *
1. initializeCommand () 1. initializeCommand ()
2. 2.
3. onCreateCommand () 3. onCreateCommand ()
4. updateContentCommand () 4. updateContentCommand ()
5. postCreateCommand () 5. postCreateCommand ()
6. 6.
7. postStartCommand () 7. postStartCommand ()
8. VS Code 8. VS Code
9. postAttachCommand () 9. postAttachCommand ()
*/ */
"initializeCommand": /* */ "echo '准备创建容器...'", "initializeCommand": "echo '↘️ 准备创建容器...'; docker pull ghcr.io/yanhao98/h-devcontainer:oncreatecommand.d;",
"onCreateCommand": /* */ { "onCreateCommand": "echo '↘️ 容器首次创建!'; /usr/local/bin/onCreateCommand.sh",
"setup-node": "pnpm env use lts --global", "updateContentCommand": "echo '↘️ 容器内容已更新!'; zsh -c 'time pnpm install;'",
"fix-claude-code-router": "sudo chown -R usr_vscode /home/usr_vscode/.claude-code-router", "postCreateCommand": "echo '↘️ 容器已创建!';",
"fix-claude": "sudo chown -R usr_vscode /home/usr_vscode/.claude", "postStartCommand": "echo '↘️ 容器启动了!'",
"fix-gemini": "sudo chown -R usr_vscode /home/usr_vscode/.gemini", "postAttachCommand": "echo '↘️ VS Code 已附加到容器!'; zsh -c 'source ~/.zshrc; echo \"$(date +%Y-%m-%dT%H:%M:%S%z) - Welcome to your Dev Container!\"';",
"fix-iflow": "sudo chown -R usr_vscode /home/usr_vscode/.iflow",
"fix-node_modules": "sudo chown usr_vscode node_modules"
},
"updateContentCommand": /* */ "pnpm install",
"postCreateCommand": /* */ {
"install-global-cli": "pnpm install -g @google/gemini-cli@latest @anthropic-ai/claude-code@latest @musistudio/claude-code-router@latest @iflow-ai/iflow-cli@latest"
},
"postStartCommand": /* */ "echo '容器已启动!'",
"postAttachCommand": /* */ "echo '开发环境已就绪!${containerWorkspaceFolder}'",
// "updateRemoteUserUID": true,
"waitFor": "updateContentCommand", "waitFor": "updateContentCommand",
// "workspaceMount": "source=${localWorkspaceFolder},target=/${localWorkspaceFolderBasename},type=bind,consistency=delegated",
// "workspaceFolder": "/${localWorkspaceFolderBasename}",
"remoteUser": "usr_vscode" "remoteUser": "usr_vscode"
} }

View File

@@ -0,0 +1,32 @@
#!/bin/zsh -eu
run_sys() {
if [[ "$EUID" -ne 0 ]]; then
sudo "$@"
else
"$@"
fi
}
if ! dpkg -s locales >/dev/null 2>&1; then
run_sys apt-get update -qq
run_sys DEBIAN_FRONTEND=noninteractive apt-get install -qq -y --no-install-recommends locales
fi
run_sys sed -i "s/^# *zh_CN.UTF-8/zh_CN.UTF-8/" /etc/locale.gen
if ! locale -a | grep -qi "^zh_CN\.utf8$"; then
run_sys locale-gen zh_CN.UTF-8
fi
zshrc="$HOME/.zshrc"
mkdir -p "${zshrc:h}"
touch "$zshrc"
if ! grep -q '^export LANG=zh_CN.UTF-8$' "$zshrc" 2>/dev/null; then
printf '\nexport LANG=zh_CN.UTF-8\n' >>"$zshrc"
fi
if ! grep -q '^export LC_ALL=zh_CN.UTF-8$' "$zshrc" 2>/dev/null; then
printf 'export LC_ALL=zh_CN.UTF-8\n' >>"$zshrc"
fi
run_sys update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8

View File

@@ -0,0 +1,3 @@
#!/bin/zsh -eu
sudo chown -R $(whoami):$(whoami) node_modules || true

View File

@@ -0,0 +1,36 @@
#!/bin/zsh -eu
TARGET_USER="usr_vscode"
if [[ "$EUID" -eq 0 ]]; then
target_home="/home/${TARGET_USER}"
else
target_home="$HOME"
fi
zshrc="${target_home}/.zshrc"
mkdir -p "${zshrc:h}"
touch "$zshrc"
vscode_marker='if [[ "$TERM_PROGRAM" == "vscode" ]]; then'
if ! grep -Fq "$vscode_marker" "$zshrc" 2>/dev/null; then
cat <<'EOF' >>"$zshrc"
if [[ "$TERM_PROGRAM" == "vscode" ]]; then
local vscode_executable
if command -v code >/dev/null 2>&1; then
vscode_executable="code"
elif command -v code-insiders >/dev/null 2>&1; then
vscode_executable="code-insiders"
fi
if [ -n "$vscode_executable" ]; then
. "$($vscode_executable --locate-shell-integration-path zsh)"
fi
fi
EOF
fi
alias_snippet="alias clean-node-modules='setopt rm_star_silent; rm -rf node_modules/.*; rm -rf node_modules/*'; unsetopt rm_star_silent"
if ! grep -Fqx -- "$alias_snippet" "$zshrc" 2>/dev/null; then
printf '\n%s\n' "$alias_snippet" >>"$zshrc"
fi

View File

@@ -0,0 +1,49 @@
#!/bin/zsh -eu
# >>>>> 一些参考资料
# https://github.com/oven-sh/bun/blob/277fc558e2039e3ab25ecebecc5a3f04c5c3199a/dockerhub/debian-slim/Dockerfile
# COPY --chown=usr_vscode:usr_vscode --from=oven/bun:1-debian /usr/local/bin/bun /usr/local/bin
# IS_BUN_AUTO_UPDATE=true SHELL=zsh bun completions
# bun install -g 将会安装到 ENV BUN_INSTALL_BIN
# ENV PATH "${BUN_INSTALL_BIN}:${PATH}"
# RUN sudo mkdir -p /usr/local/bun-node-fallback-bin && sudo ln -s /usr/local/bin/bun /usr/local/bun-node-fallback-bin/node
# ENV PATH "${PATH}:/usr/local/bun-node-fallback-bin"
# <<<<<
# fix permission
# 有可能这个目录是 volume 挂载过来的,权限会有问题。
sudo mkdir -p ~/.bun || true
sudo chown -R $(whoami):$(whoami) ~/.bun || true
curl -fsSL https://bun.com/install | bash
# https://bun.sh/docs/runtime/nodejs-compat
mkdir -p ~/.bun/bin/bun-node-fallback-bin || true
ln -sf ~/.bun/bin/bun ~/.bun/bin/bun-node-fallback-bin/node
which -a bun
bun --version
which -a bunx
bunx --version
which -a node
# >>>>> pnpm
bun add -g pnpm@latest
# echo "alias pnpm='bunx pnpm'" >> ~/.zshrc
# echo "alias pnpx='bunx pnpx'" >> ~/.zshrc
bunx pnpm config set store-dir ~/.pnpm-store
sudo chown -R $(whoami):$(whoami) ~/.pnpm-store || true
# >>>>> npm
bun add -g npm@latest
# >>>>> Claude Code 工具
bun add -g @anthropic-ai/claude-code@latest
echo "alias claude='bunx claude --dangerously-skip-permissions'" >> ~/.zshrc
# 参考: https://github.com/anthropics/claude-code/blob/1fe9e369a7c30805189cbbb72eb69c15ed4ec96b/.devcontainer/Dockerfile#L42
echo "export DEVCONTAINER=true" >> ~/.zshrc
# >>>>>> Gemini CLI 工具
bun add -g @google/gemini-cli@latest
echo "alias gemini='bunx -g gemini --yolo -m gemini-2.5-pro'" >> ~/.zshrc
echo "export SANDBOX=bun-devcontainer" >> ~/.zshrc