如何无缝回退Artifactory上的Python包版本且不影响客户
背景
我开发的Python包持续交付到JFrog Artifactory,客户机器通过定时任务自动执行以下操作:
pip install --upgrade foo # 拉取Artifactory上的最新版本 foo do_something # 调用包功能
问题
当Artifactory上的最新版本出现bug时,能否不修改客户的定时任务或对客户机器做任何干预,实现无缝回退到旧版本?
初始思路卡点
我原本计划通过latest自定义属性实现版本切换:
- 每次发布新版本时,给它打上
latest标签并移除旧版本的该标签 - 将客户的
pip install --upgrade foo改为安装带有latest属性的版本 - 回退时只需把目标旧版本打上
latest标签 - 修复bug后发布新版本并重新打上
latest标签
这个思路能让客户自动获取当前推荐的版本,但目前找不到pip直接基于Artifactory自定义属性latest指定安装版本的方法。
可行替代方案
方案1:利用Artifactory属性筛选构造索引URL
Artifactory支持通过URL参数过滤带有指定属性的包,你可以构造专属索引地址给客户使用:
pip install --upgrade foo --index-url "https://your-artifactory-domain/api/pypi/your-pypi-repo/simple/?properties=latest=true"
这样pip只会从带有latest=true属性的包中选择版本,回退时只需修改Artifactory中对应版本的属性(给旧版本打上latest=true,移除新版本的该属性),客户下次执行定时任务时就会自动安装回退的版本。
方案2:维护一个"元包"作为版本入口
创建一个名为foo-latest的轻量级元包,这个包本身无业务代码,仅在setup.py或pyproject.toml中声明对foo指定版本的依赖:
- 发布新版本
foo时,更新foo-latest的依赖为foo==x.y.z并同步发布到Artifactory - 回退时,更新
foo-latest的依赖为旧版本foo==a.b.c并重新发布 - 客户的定时任务改为执行
pip install --upgrade foo-latest,之后依然调用foo do_something
这种方式完全不需要修改客户的执行逻辑,仅通过维护元包的依赖版本实现切换,兼容性更好,无需依赖Artifactory的属性筛选功能。
方案3:使用预发布版本标记"推荐版本"
每次发布正式版本后,额外发布一个带有latest后缀的预发布版本,比如正式版是1.2.3,对应预发布版为1.2.3+latest(注:pip不会将+后的本地版本标识符纳入版本排序逻辑)。回退时,重新发布旧版本的+latest预发布版,客户的执行命令改为:
pip install --upgrade foo==*+latest
回退时只需在Artifactory中保留目标旧版本的+latest预发布版,移除新版本的对应版本即可。
内容的提问来源于stack exchange,提问作者bustharime

