上线前把关:Django 多阶段验证流水线
每次准备发版前,我都有一种隐约的不安——不是因为代码写得差,而是因为「不确定有没有漏掉什么」。DEBUG 是不是还开着?迁移文件有没有忘记生成?某个依赖有没有已知漏洞?这种不安不是靠经验积累消除的,靠的是把检查动作变成流程。
为什么要有「验证流水线」而不只是 checklist
checklist 的问题是:它是静态的,需要人记得去翻、去执行。而验证流水线是可运行的——每一步都是一条命令,结果是可见的,失败是阻断式的。
Django 项目的发布前验证,我习惯拆成几个相互独立的阶段:环境确认、代码质量、迁移状态、测试覆盖、安全扫描、配置核查、差异审阅。每个阶段产出一份简洁的报告,不通过的项要么自动修复,要么明确标出「必须人工处理后再继续」。
这种分阶段的好处是:当某一步挂掉,你立刻知道卡在哪里,而不是在一个大脚本的底部看到一条模糊的错误信息。
各阶段的核心关切
环境与依赖:pip list --outdated + pip-audit。前者让你知道有哪些包可以升级,后者告诉你有哪些包存在已知 CVE。两者的侧重不同,都不能省。虚拟环境是否激活、DJANGO_SECRET_KEY 是否注入,也应在第一阶段就确认,而不是等到测试失败才发现。
代码质量:ruff、mypy、black、isort 各司其职。我会区分「可自动修复」和「需要人工决策」两类问题——格式类问题直接 --fix,类型错误则需要理解后再处理。python manage.py check --deploy 是 Django 原生的配置完整性检查,经常能发现中间件、安全头等配置遗漏。
迁移:makemigrations --check 是一个非常有用但容易被忽视的命令——它在「模型有变化但迁移文件没生成」时以非零退出码退出,天然适合作为 CI 阻断点。migrate --plan 的 dry run 能让你在真正跑迁移前看清楚会发生什么操作,尤其在有数据修改的迁移时格外重要。
测试与覆盖率:我给不同组件设定了差异化的覆盖率目标:Model 和 Service 层要求 90%+,View 层 80% 左右。这个分层不是随意的——Model 和 Service 是业务逻辑的核心,覆盖不足意味着回归风险更高。覆盖率是门槛而不是目标,达到目标后关注的应该是「测了哪些路径」而不只是「覆盖了多少行」。
安全:bandit 做静态分析,gitleaks 扫描硬编码密钥,python manage.py check --deploy 再验一遍生产配置。三道关卡各自独立,漏网之鱼在下一层还有机会被捕获。
git diff | grep -E "print\(|DEBUG\s*=\s*True|import pdb" 这类命令能机械地揪出调试残留,比肉眼审阅可靠得多。把这几行加进发布前脚本,等于给自己的粗心加了一层兜底。
把流水线接入 CI
本地跑通了不够,真正让验证流水线发挥作用的方式是把它写进 GitHub Actions——每次 push 和 PR 自动触发。这样做有一个额外的好处:它迫使你把所有环境变量和依赖显式声明,而不是隐藏在本机环境里。
GitHub Actions 的 services 字段可以直接声明 PostgreSQL 容器,测试数据库的准备完全自动化。配合 pytest --cov 和 codecov-action,覆盖率趋势也变成可追踪的数据。
验证流水线的边界
值得明说的是:自动化验证捕捉的是「可以被机器判断的问题」——格式、覆盖率、已知漏洞、配置遗漏。它替代不了在 staging 环境里跑真实流量的验收,也替代不了 code review 里对业务逻辑和边界条件的人工判断。两者是互补而不是替代关系。
流水线的价值在于把「低层次的、可自动化的确认动作」从人工 checklist 里解放出来,让人的注意力集中到机器无法代劳的部分。
一句话心法
发布前的不安,不靠经验消除,靠把检查动作变成可执行的流程。