开发工具链(包括格式化工具)容器化,是解决“环境一致性”问题的终极方案之一。
方案构成要素
formatter镜像:一个包含black,ruff等工具的Docker镜像,作为环境中唯一的“事实来源”。- 执行脚本 (
format.sh):一个简单的Shell脚本,封装了docker run命令,方便开发者在本地调用。 - Pre-commit 集成:修改
.pre-commit-config.yaml,让它在提交时调用我们的执行脚本。 - CI/CD 集成:修改
.gitlab-ci.yml,直接使用我们的formatter镜像作为作业的运行环境。
第一步:创建格式化工具的 Docker 镜像
创建一个名为 Dockerfile 的文件。
# Dockerfile
# 选择一个轻量的官方Python镜像作为基础
FROM python:3.10-slim
# 定义工具的版本,方便管理和查看
ARG BLACK_VERSION=24.4.2
ARG RUFF_VERSION=0.12.3
# 在构建镜像时一次性安装所有工具
# --no-cache-dir 减小镜像体积
RUN pip install --no-cache-dir \
black==${BLACK_VERSION} \
ruff==${RUFF_VERSION}# 设置工作目录,容器内的代码将被挂载到这里
WORKDIR /src
# (可选)可以设置一个默认的入口点,但我们在脚本中会覆盖它
# ENTRYPOINT ["black"]
ENTRYPOINT ["tail", "-f", "/dev/null"]构建并推送镜像:
你需要将这个镜像构建好,并推送到一个团队可以访问的容器镜像仓库中(如 GitLab Container Registry, Docker Hub, Harbor 等)。
# 构建镜像,并打上标签
docker build -t your-registry.com/your-org/python-formatter:1.0 .
# 推送镜像到仓库
docker push your-registry.com/your-org/python-formatter:1.0第二步:编写便捷的执行脚本
在项目根目录创建一个名为 format.sh 的脚本。这个脚本是开发者与容器交互的桥梁。
#!/bin/bash
# format.sh
# 定义要使用的镜像名称和标签
FORMATTER_IMAGE="your-registry.com/your-org/python-formatter:1.0"
# 检查Docker是否正在运行
if ! docker info > /dev/null 2>&1; then
echo "Docker is not running. Please start Docker and try again."
exit 1
fi
# 运行容器来执行命令
# --rm: 容器退出后自动删除,保持系统干净
# -v: 将当前目录挂载到容器的/src目录,让容器可以访问代码
# $@: 将所有传递给format.sh的参数,原封不动地传递给容器内的命令
docker run --rm \
-v "$(pwd)":/src \
${FORMATTER_IMAGE} "$@"赋予脚本执行权限:
chmod +x format.sh现在,任何开发者都可以在项目根目录下通过这个脚本来执行格式化,例如:
./format.sh black .
./format.sh ruff --fix .第三步:集成到 Pre-commit
现在我们需要让 git commit 自动调用我们的 format.sh 脚本。这依然通过 repo: local 来实现,但 entry 会指向我们的脚本。
修改 .pre-commit-config.yaml:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: black-container
name: black (in container)
# entry 调用我们的脚本,并把 'black' 作为第一个参数传递进去
entry: ./format.sh black
language: script # 使用 script language
types: [python]
- id: ruff-container
name: ruff (in container)
entry: ./format.sh ruff --fix --exit-non-zero-on-fix
language: script
types: [python]工作流程解析:
- 开发者运行
git commit。 pre-commit被触发,找到black-container钩子。pre-commit执行entry中的命令:./format.sh black。format.sh脚本启动一个临时的Docker容器。- 容器内的
black命令格式化了挂载进来的代码。 - 容器执行完毕后自动销毁。
第四步:调整 CI/CD 流水线
这是最能体现容器化优势的地方。CI的配置会变得极其简单,因为它不再需要自己构建环境了。
修改 .gitlab-ci.yml,直接使用我们构建好的formatter镜像:
# .gitlab-ci.yml (容器化方案)
stages:
- lint-check
lint_job:
stage: lint-check
# 直接使用我们预先构建好的、包含所有工具的镜像!
image: your-registry.com/your-org/formatter:1.0
script:
- echo "Running checks inside the pre-built formatter container..."
# 镜像里已经有所有工具了,直接调用即可。
# 不需要任何 pip install 或 before_script
- black --check .
- ruff check .这个CI配置干净、快速、可靠,因为它跳过了所有环境准备的步骤,直接在一个“完美”的环境中运行检查。
新方案总结
至此,你已经建立了一个高度现代化、健壮且一致的开发工作流。
- 开发者入职:只需
git clone项目,确保本地安装了Docker和pre-commit,然后运行pre-commit install。完成! - 日常开发:
git commit自动在容器中完成格式化,开发者无需关心工具版本。 - CI/CD:流水线直接拉取同一个镜像进行验证,保证了与本地行为的100%一致。
- 工具升级:当需要升级
black或ruff时,只需修改Dockerfile,构建并推送一个新的镜像标签(如1.1),然后在.gitlab-ci.yml和format.sh中更新镜像标签即可。所有环境的升级都在一个地方完成,非常优雅。