把项目环境配置全自动化:新人入职从两天变成两小时

0 阅读7分钟

搭建开发环境是新人的第一道坎。本文记录用 Docker + AI 脚本将整个流程自动化,包含依赖安装、数据库初始化、配置文件生成,并适配 macOS/Linux/Windows 三平台。新人从对着文档折腾两天,变成跑一条命令、两小时后开始写代码。

image_fa14ec01.png

事情是怎么开始的

上个月团队入职了一位新同事。按照惯例,我发给他一份《开发环境搭建指南》——一份 6 页的 Notion 文档,包含:

  • Node.js 18 + pnpm 安装
  • MySQL 8.0 + Redis 7 本地安装
  • 三个私有 npm 包的认证配置
  • hosts 文件和本地 DNS 配置
  • 自签证书安装
  • 五个微服务的 .env 文件配置

第一天,他在装 MySQL 的时候遇到了版本冲突。第二天,他在配私有 npm 包的时候认证失败。第三天早上,他终于跑通了第一个服务,但第二个服务报错——有个环境变量写错了。

我看着他,想起自己两年前入职时同样折腾了三天。这个行业对新人的欢迎方式,为什么总是从折磨开始?

我花了一个周末,把整个环境配置流程做成了自动化。新同事成了这套方案的第一个用户,从拿到电脑到提交第一行代码,用了不到一个上午。

先分析:为什么环境配置这么容易崩

我把那份 6 页的文档拆解了一遍,发现环境配置的坑集中在三个地方:

问题类型典型表现根本原因
版本不一致"我这跑不起来" "我这能跑啊"文档写的是推荐版本,不是强制版本
环境差异macOS 能跑,Windows 报错路径分隔符、换行符、依赖编译差异
隐式依赖文档没写,但实际需要老员工知道,但忘了写进文档

第一个问题好解决:用 Docker 固定版本。第二个问题需要适配脚本。第三个问题最麻烦——如何把老员工的"肌肉记忆"挖出来?

我想到的办法是:让 AI 审查项目代码,找出所有隐式的环境依赖。这是后话,先说方案。

整体方案:一条命令拉起全部服务

最终搭建的 setup 命令,背后做了这些事:

setup
├── 1. 检查 Docker / Node.js / Git 是否已安装
├── 2. 拉取项目代码(含子模块)
├── 3. 自动生成 .env 文件(交互式提问)
├── 4. 拉取 Docker 镜像(MySQL / Redis / MinIO)
├── 5. 初始化数据库(建表 + 种子数据)
├── 6. 安装 npm 依赖(含私有包认证)
├── 7. 配置 hosts + 自签证书
├── 8. 启动所有微服务
└── 9. 健康检查

核心是一个 Shell 脚本,兼容 macOS、Linux 和 WSL2(Windows 用户统一用 WSL2,省去维护 PowerShell 版本的成本)。

关键模块的实现细节

1. 环境检测:提前报错,而不是跑到一半再崩

#!/bin/bash
set -euo pipefail

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

check_command() {
    if ! command -v "$1" &> /dev/null; then
        echo -e "${RED}$1 未安装,请先安装 $2${NC}"
        exit 1
    fi
    echo -e "${GREEN}${NC} $1 已安装: $($1 --version 2>&1 | head -n1)"
}

echo "=== 环境检测 ==="
check_command docker    "Docker Desktop (https://docker.com)"
check_command node      "Node.js 18+ (https://nodejs.org)"
check_command git       "Git (https://git-scm.com)"
check_command pnpm      "pnpm (npm install -g pnpm)"

# 检查 Docker 是否在运行
if ! docker info &> /dev/null; then
    echo -e "${RED}❌ Docker 未运行,请启动 Docker Desktop${NC}"
    exit 1
fi

# 检查 Node.js 版本
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_VERSION" -lt 18 ]; then
    echo -e "${RED}❌ Node.js 版本过低 ($(node -v)),需要 18+${NC}"
    exit 1
fi

做这一步的原因:过去很多环境问题都是在安装到一半时报错,前置检查可以一次性告诉用户“你需要装什么”,而不是让它装到第三步再崩、然后倒回去查原因。

2. .env 文件生成:用交互式提问代替手写

过去新同事经常在 .env 里填错值,比如把生产环境的数据库端口写进本地。我写了个引导式脚本,逐项提问并校验格式:

generate_env() {
    echo ""
    echo "=== 配置环境变量 ==="
    
    # 检测本机 IP 作为默认值
    LOCAL_IP=$(ipconfig getifaddr en0 2>/dev/null || hostname -I 2>/dev/null | awk '{print $1}')
    
    read -p "数据库主机地址 [默认: 127.0.0.1]: " DB_HOST
    DB_HOST=${DB_HOST:-127.0.0.1}
    
    read -p "数据库端口 [默认: 3306]: " DB_PORT
    DB_PORT=${DB_PORT:-3306}
    
    # 端口格式校验
    if ! [[ "$DB_PORT" =~ ^[0-9]+$ ]] || [ "$DB_PORT" -lt 1 ] || [ "$DB_PORT" -gt 65535 ]; then
        echo -e "${RED}端口格式错误${NC}"
        return 1
    fi
    
    read -p "Redis 端口 [默认: 6379]: " REDIS_PORT
    REDIS_PORT=${REDIS_PORT:-6379}
    
    read -p "本地服务域名 [默认: dev.local]: " DEV_DOMAIN
    DEV_DOMAIN=${DEV_DOMAIN:-dev.local}
    
    # 生成 .env 文件
    cat > .env << EOF
# 由 setup 脚本自动生成
DB_HOST=$DB_HOST
DB_PORT=$DB_PORT
DB_USER=dev_user
DB_PASS=dev_password_123
DB_NAME=myapp_dev
REDIS_HOST=127.0.0.1
REDIS_PORT=$REDIS_PORT
DEV_DOMAIN=$DEV_DOMAIN
EOF
    
    echo -e "${GREEN}✓ .env 文件已生成${NC}"
}

3. 用 Docker Compose 统一中间件版本

这是解决“版本不一致”的核心。不再要求同事手动安装 MySQL 和 Redis,而是用容器固定版本:

# docker-compose.dev.yml
version: '3.8'
services:
  mysql:
    image: mysql:8.0.35
    container_name: myapp-mysql-dev
    ports:
      - "${DB_PORT:-3306}:3306"
    environment:
      MYSQL_ROOT_PASSWORD: root_123
      MYSQL_DATABASE: myapp_dev
      MYSQL_USER: dev_user
      MYSQL_PASSWORD: dev_password_123
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init-scripts:/docker-entrypoint-initdb.d  # 自动初始化
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 5s
      timeout: 5s
      retries: 10

  redis:
    image: redis:7.2-alpine
    container_name: myapp-redis-dev
    ports:
      - "${REDIS_PORT:-6379}:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5

volumes:
  mysql_data:

重点healthcheck 配置让后续脚本可以等待服务就绪后再执行,避免“数据库还没起来就开始建表”的竞态问题。

4. 数据库初始化:自动建表 + 种子数据

wait_for_mysql() {
    echo "等待 MySQL 就绪..."
    until docker exec myapp-mysql-dev mysqladmin ping -h localhost --silent; do
        sleep 2
    done
    echo -e "${GREEN}✓ MySQL 已就绪${NC}"
}

init_database() {
    wait_for_mysql
    
    # 从源码中的 migration 文件自动建表
    for f in ./migrations/*.up.sql; do
        echo "执行: $f"
        docker exec -i myapp-mysql-dev mysql -u dev_user -pdev_password_123 myapp_dev < "$f"
    done
    
    # 插入种子数据(可选)
    if [ -f ./seeds/dev.sql ]; then
        read -p "是否插入测试数据?[Y/n]: " insert_seed
        if [[ "$insert_seed" =~ ^[Yy]?$ ]]; then
            docker exec -i myapp-mysql-dev mysql -u dev_user -pdev_password_123 myapp_dev < ./seeds/dev.sql
            echo -e "${GREEN}✓ 测试数据已插入${NC}"
        fi
    fi
}

5. 私有 npm 包认证:把繁琐的配置变成一条命令

setup_npm_auth() {
    echo ""
    echo "=== 配置私有 npm 包访问 ==="
    
    if [ -z "${NPM_TOKEN:-}" ]; then
        echo -e "${YELLOW}需要 GitHub 个人访问令牌来安装私有包${NC}"
        echo "获取方式: GitHub Settings → Developer settings → Personal access tokens"
        echo "需要 read:packages 权限"
        read -s -p "粘贴你的 token: " NPM_TOKEN
        echo ""
    fi
    
    # 写入 .npmrc
    cat > ~/.npmrc << EOF
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
@mycompany:registry=https://npm.pkg.github.com/
EOF
    
    echo -e "${GREEN}✓ npm 认证配置完成${NC}"
}

6. hosts 和自签证书:让本地域名可访问

setup_hosts_and_certs() {
    DOMAIN=${DEV_DOMAIN:-dev.local}
    
    # 添加 hosts 记录
    if ! grep -q "$DOMAIN" /etc/hosts; then
        echo "127.0.0.1 $DOMAIN" | sudo tee -a /etc/hosts > /dev/null
        echo -e "${GREEN}✓ hosts 已添加: $DOMAIN → 127.0.0.1${NC}"
    fi
    
    # 生成自签证书
    if [ ! -f ./certs/$DOMAIN.crt ]; then
        mkdir -p ./certs
        openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
            -keyout ./certs/$DOMAIN.key \
            -out ./certs/$DOMAIN.crt \
            -subj "/CN=$DOMAIN" \
            -addext "subjectAltName=DNS:$DOMAIN" 2>/dev/null
        echo -e "${GREEN}✓ 自签证书已生成${NC}"
        
        # macOS 信任证书
        if [[ "$OSTYPE" == "darwin"* ]]; then
            sudo security add-trusted-cert -d -r trustRoot \
                -k /Library/Keychains/System.keychain ./certs/$DOMAIN.crt 2>/dev/null || true
        fi
    fi
}

用 AI 找出文档里没写的隐式依赖

这部分是意外收获。我在让 AI 审查项目代码时,专门问了这个问题:

提示词

审查这个项目的代码,找出所有隐式的环境依赖。
"隐式依赖"指代码中硬编码的、但开发环境搭建文档中没有提到的依赖,比如:
- 特定版本的编译工具(gcc、python3 等)
- 全局安装的 CLI 工具
- 特定的文件路径或端口
- 需要提前创建的目录

请列出每一项并给出代码中的证据。

AI 返回了几个我根本没想到的点:

  1. 一个 PDF 生成服务依赖 chromium 二进制文件,通过 puppeteer 调用。文档里完全没提。
  2. 一个图片处理脚本内部调用了 sharp,需要系统级 libvips 库。macOS 上能跑,但 Linux 需要单独安装。
  3. 某段测试代码硬编码了 /tmp/uploads 路径,不存在时静默失败。

我把这些发现补充进了 Dockerfile 和 setup 脚本。这是人很难靠回忆穷举、但 AI 可以靠代码扫描补齐的工作。


最终效果

新同事入职时,流程变成了这样:

1. git clone <项目地址> && cd project
2. chmod +x setup.sh && ./setup.sh
3. 按提示输入 3 个配置项(数据库端口、域名、npm token)
4. 喝杯咖啡,等 10 分钟
5. 访问 http://dev.local:3000

第一版用了 15 分钟,新同事反馈了几个交互不友好的地方(比如 token 提示不清楚、没有进度条),我迭代了两次后稳定下来。

对比:

指标之前之后
环境搭建时间1~3 天0.5~2 小时
手动步骤约 40 步3 步
搭建失败率约 60%0%(三次测试)
文档维护手动更新,常过期脚本即文档,不过期

踩过的坑

1. Docker 镜像拉取慢

国内拉 Docker Hub 镜像很慢,脚本第一次跑可能卡在 pull 上。解决方案是在脚本开头检测网络,提示配置镜像加速器:

check_docker_mirror() {
    if ! docker info | grep -q "Mirrors"; then
        echo -e "${YELLOW}⚠ 未检测到 Docker 镜像加速器,拉取镜像可能很慢${NC}"
        echo "建议配置: Docker Desktop → Settings → Docker Engine → registry-mirrors"
    fi
}

2. Windows 路径问题

WSL2 下 /mnt/c/ 路径的性能很差,脚本检测到 WSL 后会自动把项目 clone 到 ~/ 下而非 /mnt/c/

if grep -q "microsoft" /proc/version 2>/dev/null; then
    echo "检测到 WSL 环境,项目将克隆到 ~/ 目录以获得最佳性能"
    cd ~
fi

3. 端口冲突

如果同事本地已经装过 MySQL,3306 端口会被占用。脚本主动检测并提示:

if lsof -i :3306 &> /dev/null; then
    echo -e "${YELLOW}⚠ 端口 3306 已被占用${NC}"
    read -p "是否使用其他端口?[默认 3307]: " ALT_PORT
    DB_PORT=${ALT_PORT:-3307}
fi

为什么用 Shell 而不是更“现代”的方案

有人问为什么不直接用 Dev Containers 或者 Nix。原因很简单:

  • Dev Containers 需要同事先学会 VS Code 的容器模式,增加了学习成本。
  • Nix 很强大,但我们的同事不一定愿意花时间学一个新工具链。
  • Shell 脚本:所有 Unix 系统都自带,零额外依赖,出问题可以直接看源码改。

工具选型的原则不是“最先进”,而是“让目标用户最容易上手”。

文中使用的 Claude API Key 来自 gpt108.com(该渠道提供 ChatGPT Plus、Claude Pro、Gemini Advanced、Cursor Pro 等账号及 API 充值)。笔者团队生产环境已稳定使用 3 个月,仅作技术方案记录


你们团队的开发环境是怎么搭的?新人入职需要多久才能开始写代码?有什么独特的解决方案?欢迎评论区聊聊。