Prhub

#24348 Add release workflow for sgl-deep-gemm wheels

原始 PR 作者 Fridge003 合并时间 2026-05-04 15:58 文件变更 5 提交数 3 评论 9 代码增减 +408 / -0

执行摘要

新增 sgl-deep-gemm wheel 发布工作流

此前 sgl-deep-gemm 缺少自动化发布流程,依赖手动构建上传。本 PR 通过标准化 workflow 降低发布出错概率,确保 cu129/cu130 双版本、双架构的 wheel 分发一致性。参考 PR body:'Add .github/workflows/release-whl-deepgemm.yml to build and publish sgl-deep-gemm wheels.'

建议精读:对于负责 CI/CD 或 wheel 发布的成员,本 PR 提供了完整的跨架构、跨 CUDA 版本的 wheel 发布范例。特别值得关注的设计决策包括:

  • 利用 local version tag 区分 cu129/cu130,并在上传 PyPI 时剥离,符合 PyPI 规范。
  • 通过 pip wheel unpack/repack 修改平台 tag,而非重新编译,确保与 manylinux 兼容。
  • 建立双发布渠道(PyPI + GH Release),平衡便利性和版本管理。
    对于其他 Python 绑定的发布流程可参考此模式。
讨论亮点

所有 review 评论均由作者 @Fridge003 在合并前提出并解决,主要涉及:

  • 文件组织:要求将 Dockerfile 移入 docker/,构建和重命名脚本移入 scripts/,保持仓库结构一致。
  • 构建参数化:为 Dockerfile 添加 TORCH_VERTVM_FFI_VER ARG,支持 future 版本覆盖。
  • 工作流 UI 友好性:将 workflow 显示名改为 'Release sgl-deep-gemm',在 target/branch 输入描述中标明默认值,提升 workflow_dispatch 可用性。
    无明显未解决争议。

实现拆解

  1. Workflow 定义 (.github/workflows/release-whl-deepgemm.yml):通过 workflow_dispatch 接收 versiontarget(all/cu129/cu130)、branch 参数。构建矩阵为 [cu129, cu130] × [x86_64, aarch64],使用自托管 runner(x64-kernel-build-node / arm-kernel-build-node)。并发控制确保同一时间只有一个运行。

  2. Docker 构建环境 (docker/sgl-deep-gemm.Dockerfile):基于 pytorch/manylinux2_28-builderpytorch/manylinuxaarch64-builder,安装指定版本的 torch(2.11.0)和 tvm-ffi(0.1.9),并创建 libcuda.so 符号链接以解决链接问题。

  3. 构建脚本 (scripts/build_sgl_deep_gemm.sh):参数化 Python 版本、CUDA 版本、DeepGEMM 源码路径和架构。动态选择基础镜像标签,在 Docker 内执行构建,输出默认 py3-none-any 的 wheel。

  4. 重命名脚本 (scripts/rename_sgl_deep_gemm_whl.sh):通过 pip wheel unpack 解包 wheel,修改 METADATA 中的 Version(追加 +cu<ver>)和 WHEEL 中的 Tag(改为 py3-none-manylinux2014_<arch>),再重新打包,满足多平台兼容性要求。

  5. 索引更新脚本 (scripts/update_deepgemm_whl_index.py):为 cu129 版本生成 PEP 503 简单索引 HTML,包含 SHA256 哈希,托管在 sgl-project/whl 的 Release 中,用户可通过 --extra-index-url https://sgl-project.github.io/whl/cu129/ 安装。

  6. 发布分离策略:cu130 wheels 去除 +cu130 local version tag 后上传至 PyPI(使用 SGL_DEEP_GEMM_PYPI_TOKEN);cu129 wheels 直接作为 GitHub Release 发布到 sgl-project/whl,并更新索引页面。此设计复用已有的 release-whl-kernel.yml 的发布基础设施(runner、token 等)。

文件 模块 状态 重要度
scripts/update_deepgemm_whl_index.py 索引生成 added 7.87
.github/workflows/release-whl-deepgemm.yml 发布流水线 added 6.02
scripts/build_sgl_deep_gemm.sh 构建脚本 added 5.0
scripts/rename_sgl_deep_gemm_whl.sh 打包处理 added 5.0
docker/sgl-deep-gemm.Dockerfile 构建环境 added 4.76

关键符号

update_wheel_index main

关键源码片段

scripts/update_deepgemm_whl_index.py core-logic

核心索引生成逻辑,是 cu129 wheel 分发的关键组件,决定了用户能否通过 --extra-index-url 安装。

# scripts/update_deepgemm_whl_index.py
# 为 sgl-deep-gemm 的 cu129 wheels 生成 PEP 503 简单索引 HTML,
# 用户可通过 --extra-index-url https://sgl-project.github.io/whl/cu129/ 安装。
import argparse
import hashlib
import pathlib
import reSUPPORTED_CUDA_VERSIONS = ["129", "130"]def update_wheel_index(cuda_version: str, wheel_dir: str) -> None:
    """遍历 wheel 目录,筛选匹配 +cu<version> 的 .whl 文件,
       计算 SHA256,生成带哈希的索引条目并写入 index.html。"""
    index_dir = pathlib.Path(f"sgl-whl/cu{cuda_version}/sgl-deep-gemm")
    index_dir.mkdir(exist_ok=True, parents=True)
    base_url = "https://github.com/sgl-project/whl/releases/download"
​
    suffix = f"+cu{cuda_version}"
    for path in sorted(pathlib.Path(wheel_dir).glob("*.whl")):
        if suffix not in path.name:
            continue # 跳过不匹配当前 CUDA 版本的 wheel
        with open(path, "rb") as f:
            sha256 = hashlib.sha256(f.read()).hexdigest()
        # 提取版本号:sgl_deep_gemm-0.1.0+cu129-py3-none-manylinux2014_x86_64.whl
        match = re.match(r"sgl_deep_gemm-([0-9][^-+]*)(?:\+cu[0-9]+)?-", path.name)
        if not match:
            continue
        ver = match.group(1)
        full_url = f"{base_url}/v{ver}/{path.name}#sha256={sha256}"
        with (index_dir / "index.html").open("a") as f:
            f.write(f'<a href="{full_url}">{path.name}</a><br>\n')def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--cuda", type=str, required=True, choices=SUPPORTED_CUDA_VERSIONS)
    parser.add_argument("--wheel-dir", type=str, default="dist")
    args = parser.parse_args()
    update_wheel_index(args.cuda, args.wheel_dir)if __name__ == "__main__":
    main()

评论区精华

文件位置调整 other

Review 要求将 Dockerfile, build.sh, rename.sh 移动到标准目录。

结论:已完成移动。 · 已解决

Dockerfile ARG 参数化 设计

要求为 TORCH_VER 和 TVM_FFI_VER 添加 ARG,支持版本覆盖。

结论:已添加 ARG。 · 已解决

Workflow UI 友好性 设计

要求将 workflow 显示名改为 'Release sgl-deep-gemm',并在输入描述中显示默认值。

结论:已修改。 · 已解决

风险与影响

  1. 外部依赖稳定性:构建依赖 sgl-project/DeepGEMM 仓库,若该仓库的分支(release-0426)变更或出现兼容性问题,工作流将失败。
  2. 自托管 Runner 环境:使用非标准 self-hosted runner,缺少隔离和环境快照,可能因磁盘空间或残留缓存导致构建失败。
  3. 秘密管理SGL_DEEP_GEMM_PYPI_TOKENGH_PAT_FOR_WHL_RELEASE 作为明文 secret 使用,若泄露可致供应链投毒。需确认为严格受限的 token。
  4. 版本冲突:cu130 wheels 上传至 PyPI 时丢掉 +cu130 版本段,同一 index 上只有一个版本;用户若未指定索引可能意外安装错版。设计上需确保发布时仅推送 cu130 到 PyPI。
  5. 无自动化测试:当前工作流仅有手动触发验证计划(见 PR body Test plan),未集成 CI 回归测试,无法在合并前自动验证构建成功。
  • 用户侧:DeepGEMM 用户现在可以通过 pip install sgl-deep-gemm(cu130)或通过 cu129 索引安装 cu129 版本,降低使用门槛。
  • 团队侧:发布流程完全自动化,从手动执行 shell 脚本变为一键 workflow dispatch,减少人为错误。
  • 系统侧:复用已有 runner 和发布仓库(sgl-project/whl),基础设施扩展成本低。
    整体影响为中等,主要惠及内部发布者和下游 DeepGEMM 用户。
外部依赖 自托管 Runner 缺少 CI 测试 PyPI 版本冲突 凭证安全

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论