.gitlab-ci.yml三阶段流水线(build→test→publish)需用python -m build生成dist/产物、pip install dist/*.whl验证二进制包、twine配合CI_JOB_TOKEN推送到GitLab PyPI仓库,并配置image、cache、artifacts等确保环境一致与产物可追溯。

直接用 .gitlab-ci.yml 配置三阶段流水线(build → test → publish),配合 python -m build 和 twine,就能完成标准化打包与发布——关键不在工具链多炫酷,而在每个环节是否可验证、可回滚、不依赖本地环境。
build 阶段必须用 python -m build 而非 setup.py sdist bdist_wheel
PyPA 已明确弃用 setup.py 直接调用方式,python -m build 是当前唯一推荐的构建入口。它自动识别 pyproject.toml 中的构建后端(如 setuptools 或 poetry-core),避免手动指定参数出错。
- 如果项目还在用
setup.py,必须先迁移至pyproject.toml,否则build命令会静默失败或生成不兼容的包 -
python -m build --sdist --wheel是显式写法,但默认行为已包含两者,无需额外加参数 - 构建产物必须输出到
dist/目录,并通过artifacts: paths暴露给后续 job,否则test和publish阶段拿不到文件
test 阶段要安装刚构建的 wheel 包,而非源码
测试必须基于最终发布的二进制包进行,否则会漏掉 pyproject.toml 中 [project.optional-dependencies] 或 [build-system] 配置错误导致的安装失败。
审计 GitHub Actions 工作流文件的密钥泄露风险,例如 pull_request_target 密钥使用、密钥回显命令及未固定版本的 Action 密钥传递。
- 正确做法是:
pip install dist/*.whl(注意通配符需 shell 展开,GitLab Runner 默认支持) - 不能写成
pip install .—— 这走的是“editable install”,绕过了打包逻辑,测的不是真实分发包 - 若测试报
ModuleNotFoundError,大概率是pyproject.toml里[project.name]和实际 import 的模块名不一致,比如写了name = "mylib"却在代码里import mylib_v2
publish 阶段必须用 CI_JOB_TOKEN 推送至 GitLab 内置 PyPI 仓库
向 GitLab 自托管的 Package Registry 发布时,twine 必须使用 gitlab-ci-token 用户 + CI_JOB_TOKEN 密码,这是唯一免配置、自动轮换的身份凭证。
立即学习“Python免费学习笔记(深入)”;
- 命令中
TWINE_USERNAME=gitlab-ci-token和TWINE_PASSWORD=$CI_JOB_TOKEN缺一不可;漏掉USERNAME会导致认证被当成匿名用户,返回403 Forbidden -
--repository-url必须拼接完整:$CI_API_V4_URL/projects/$CI_PROJECT_ID/packages/pypi,少任何一段都会 404 - 务必加
rules: - if: $CI_COMMIT_TAG,否则每次 push 都会尝试发布,污染仓库且可能因重复包名失败
容易被忽略的兼容性陷阱:Python 版本、缓存路径和 artifact 生命周期
三个看似次要的配置点,实际决定流水线是否稳定复现:
-
image: python:3.9必须和本地开发、生产环境一致;用latest或3标签会导致某天突然升级到 3.12,触发pydantic等库的语法兼容问题 -
cache: paths: [".pip-cache/"]必须匹配PIP_CACHE_DIR变量值,否则 pip 每次都重装依赖,拖慢流水线且掩盖真实网络问题 -
artifacts: paths: [dist/]默认只保留最近一次成功 job 的产物;如果publish失败,下一次重试时dist/目录为空——必须加expire_in: 1 week显式延长有效期

















