尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

【Bug已解决】[Build] 1.27.0 on PyPI but no release tag or notes on github 解决方案

【Bug已解决】[Build] 1.27.0 on PyPI but no release tag or notes on github 解决方案 【Bug已解决】[Build] 1.27.0 on PyPI but no release tag or notes on github 解决方案一、现象长什么样onnxruntime1.27.0已经能在 PyPI 上pip install到但去 GitHub Releases 一看没有v1.27.0的 tag也没有对应的 Release Notes源码 tag 缺失用户没法对照 changelogCI 里依赖git describe的脚本也拿不到版本。pip install onnxruntime1.27.0 # 成功 git ls-remote --tags https://github.com/microsoft/onnxruntime v1.27.0 # 无输出 gh release view v1.27.0 # release not found最小影响链# 用户想从 tag 拉对应源码 git clone --branch v1.27.0 https://github.com/microsoft/onnxruntime # 失败tag 不存在尽管 PyPI 上 1.27.0 已经发布注意这不是构建失败wheel 已经在 PyPI而是发布流程里“打 tag 写 release notes”这一步和“传 PyPI”脱节了属于发布编排问题。二、背景ONNX Runtime 的发布通常由两条相对独立的流水线组成PyPI 发布在打 tagvX.Y.Z的 commit 上触发或在一个“发布分支”上触发构建 wheel 并twine upload到 PyPI。GitHub Release 创建同一个 tag 上触发gh release create自动从 changelog / 提交生成 notes并打 tag如果 tag 还没打。常见的脱节原因PyPI 发布被手动/分支触发维护者直接在main或某个发布分支上跑了 PyPI 发布或从本地twine upload但没有先打v1.27.0tag于是 PyPI 有了包GitHub 没有 tag/release。release 创建 job 失败但 PyPI job 独立成功两条流水线needs关系没串起来PyPI job 不依赖 release job 成功所以一边成一边败用户只看到 PyPI 有包。notes 生成依赖未合并的 changelogrelease notes 是从docs/ReleaseNotes.md或自动git log生成如果那次发的 changelog 没合进触发 commitrelease 创建步骤报错或生成空 notes 后跳过。三、根因根因是PyPI 发布与 GitHub tag/release 创建没有形成原子、有序的发布单元触发源不一致PyPI 上传的触发条件分支 push / 手动和 tag 创建不是同一个事件导致“包先上、tag 后无”。缺乏先后依赖与门禁PyPI job 没有needs: create-release也没有“release 必须存在”的前置校验于是 release 缺失也不阻止 PyPI 发布。tag 创建非强制流水线里 tag 是 release 创建的“副作用”一旦 release 步骤被跳过/失败tag 就不打而包的版本号仍来自pyproject/setup.py的version不依赖 tag所以 PyPI 照常成功。所以这不是编译错而是发布编排里 tag/release 不是发布的硬前置导致 PyPI 可以先于 tag 独立成功。四、最小可运行复现下面用一段发布脚本思路 校验复现“PyPI 上传成功但 tag 缺失”的脱节#!/usr/bin/env bash set -u VERSION1.27.0 REMOTEhttps://github.com/microsoft/onnxruntime # 步骤 A构建并上传 PyPI不检查 tag echo building wheel for $VERSION ... # python -m build twine upload dist/* echo PyPI upload done (假设成功) # 步骤 B打 tag 创建 release可能独立、可能失败 if git rev-parse $VERSION /dev/null 21; then echo tag $VERSION exists else # 关键缺陷这里如果没被执行或被跳过PyPI 已有包但无 tag echo creating tag v$VERSION ... # git tag v$VERSION git push origin v$VERSION # gh release create v$VERSION --notes-file ReleaseNotes.md fi # 校验PyPI 有包但 tag 不一定有脱节 if ! git ls-remote --tags $REMOTE v$VERSION | grep -q v$VERSION; then echo ::error::PyPI 有 $VERSION 但 GitHub 无 tag 2 # 正确做法exit 1阻止“只上 PyPI 不打 tag” exit 1 fi把“创建 tag”那行注释掉再跑脚本会因最后的校验exit 1暴露脱节修复前无校验则会“PyPI 成功、GitHub 无 tag”地结束正是题面现象。五、解决方案第一层最小直接修复最小修复让发布成为原子单元——先打 tag 创建 release再或同时上传 PyPI且上传前校验 tag 存在。# .github/workflows/publish.yml 修复要点 publish: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: { fetch-depth: 0 } - name: Create tag release env: { GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} } run: | VERSION$(python -c import onnxruntime; print(onnxruntime.__version__)) git tag v$VERSION git push origin v$VERSION gh release create v$VERSION --generate-notes - name: Build and publish to PyPI # 必须等上一步成功同 job 顺序执行天然满足 run: | python -m build twine upload dist/* - name: Verify consistency run: | VERSION$(python -c import onnxruntime; print(onnxruntime.__version__)) git ls-remote --tags origin v$VERSION | grep -q v$VERSION \ || { echo ::error::tag missing after publish; exit 1; }要点(1) tag release 创建在 PyPI 上传之前且同 job 顺序执行(2) 最后校验 tag 存在缺失即 fail。这一层立刻消除“PyPI 有、GitHub 无”的脱节。六、解决方案第二层结构性改进把“发布必须包含 tag release notes PyPI 三者一致”收口成唯一的配置对象OrtPypiReleaseTagPolicyCI 读它做断言from dataclasses import dataclass, field from typing import Tuple, List dataclass(frozenTrue) class OrtPypiReleaseTagPolicy: ORT 发布一致性的单一事实来源。 # 发布必须产出的“交付物” required_deliverables: Tuple[str, ...] ( git_tag, # vX.Y.Z tag github_release, # GitHub Release notes pypi_wheel, # PyPI 包 ) # 顺序先 tag/release后 PyPI保证不脱节 order: Tuple[str, ...] (git_tag, github_release, pypi_wheel) # 是否强制三者版本号一致 enforce_version_consistency: bool True # notes 来源 notes_source: str RELEASE_NOTES.md # 缺失即失败而非静默 fail_on_missing: bool True def verify(self, done: List[str]) - List[str]: missing [d for d in self.required_deliverables if d not in done] if missing and self.fail_on_missing: raise RuntimeError(f发布缺失交付物: {missing}) return missing def describe(self) - str: return tag/release/PyPI 为原子发布单元顺序固定、版本一致 POLICY OrtPypiReleaseTagPolicy() def check_publish(done: List[str], policy: OrtPypiReleaseTagPolicy POLICY) - List[str]: return policy.verify(done)所有发布流水线读同一份POLICYtag/release/PyPI 成为原子单元脱节在 CI 里就报错。七、解决方案第三层断言 / CI 守护把“tag/release/PyPI 三者齐备且一致”做成断言。下面用 pytest 风格守护import pytest def test_three_deliverables_required(policy): assert set(policy.required_deliverables) {git_tag, github_release, pypi_wheel} def test_order_tag_before_pypi(policy): assert policy.order.index(git_tag) policy.order.index(pypi_wheel) assert policy.order.index(github_release) policy.order.index(pypi_wheel) def test_missing_tag_fails(policy): with pytest.raises(RuntimeError): check_publish([pypi_wheel, github_release]) # 故意缺 git_tag def test_complete_publish_passes(policy): assert check_publish([git_tag, github_release, pypi_wheel]) []这四组断言锁住(1) 三个交付物必含(2) tag/release 在 PyPI 之前(3) 缺 tag 即失败(4) 齐备则通过。CI 跑通即代表发布原子性被守护。八、排查清单遇到“PyPI 有包但 GitHub 无 tag/notes”确认触发源PyPI 上传是被什么事件触发的是不是绕过了打 tag 的流水线。查 release jobGitHub Release 创建步骤是否跑了、是否失败被忽略。看依赖关系PyPI job 是否needsrelease job还是完全独立。手动补救补打v1.27.0tag 并gh release create --generate-notes。统一策略对象用OrtPypiReleaseTagPolicy固化“tag→release→PyPI”顺序。CI 守护断言三个交付物齐备、版本一致缺失即失败。避免手动 twine所有上传走流水线保证和 tag 同源。九、小结[Build] 1.27.0 on PyPI but no release tag or notes on github的根因是发布编排里 PyPI 上传与 GitHub tag/release 创建不是原子、有序的单元PyPI 发布可由分支/手动触发且不依赖 tag 存在而 tag/release 创建是另一条可能失败被忽略的流水线于是 PyPI 先成功、GitHub 却无 tag 与 notes。最小修复是让发布同 job 先打 tag 创建 release 再上传 PyPI并校验 tag 存在结构性改进是用唯一的OrtPypiReleaseTagPolicy把 tag/release/PyPI 固化为原子单元、固定顺序CI 用四组断言守护“三者齐备、tag 先于 PyPI、缺即失败”。记住发布是原子动作包、tag、notes 必须一起成功或一起失败不能只上 PyPI 不打 tag。
返回列表