首页
/ g4f (gpt4free) 多平台发布构建流水线全解:从版本 Tag 到 PyPI、Docker 与原生启动器

g4f (gpt4free) 多平台发布构建流水线全解:从版本 Tag 到 PyPI、Docker 与原生启动器

2026-09-04 18:13:44作者:管翌锬

本文围绕 gpt4free 仓库的发布构建工作流(docs/build-workflow.md 所描述的完整打包流程)展开,讲清楚“推一个版本 Tag 之后,一条 GitHub Actions 流水线如何并行产出 PyPI 包、多平台可执行启动器与 Docker 镜像,并自动创建 GitHub Release”。读完本文,你将掌握 g4f 各发布产物的触发方式、版本判定逻辑、各构建 Job 的源码级实现,以及本地复现打包时的关键脚本与参数。

一、工作流总览:一条流水线,七类产物

docs/build-workflow.md 指出,仓库通过 .github/workflows/build-packages.yml 工作流在推送版本 Tag 时自动构建多种包格式。原文档列出的产物清单如下:

  1. PyPI 包 —— Python wheel 与源码分发包(sdist)
  2. Windows 可执行文件 —— 独立 .exe(文档描述为 Nuitka 时代产物,见第四节)
  3. Linux 可执行文件 —— Linux 独立二进制
  4. macOS 可执行文件 —— macOS 独立二进制(x64 与 ARM64)
  5. Debian 包 —— 面向 Ubuntu/Debian 的 .deb(amd64、arm64、armhf)
  6. WinGet 包 —— Windows Package Manager 清单
  7. Docker 镜像 —— 多架构容器镜像

对照当前仓库中 build-packages.yml 的实际内容,可以看到这条流水线由 4 个核心 Job 串联:

Job 作用 触发条件
prepare 解析版本号并判定是否为正式发布(is_release 每次运行必执行
build-pypi 构建 wheel + sdist 并上传工件 每次运行
build-g4f-go 交叉编译 Go 启动器并打包各平台 zip 每次运行
build-docker 构建并推送 Docker 镜像(armv7 / slim / 完整版) 仅正式发布(is_release == 'true'
create-release 汇总所有工件,创建 GitHub Release 仅正式发布

触发方式在 build-packages.yml 中定义得很直接:监听所有 Tag(tags: ['*']),同时支持 workflow_dispatch 手动触发,并允许在手动触发时通过 version 输入框指定版本号(留空则自动推导)。

触发构建的标准操作

与原文档一致,最典型的触发方式是推送版本 Tag:

git tag v1.2.3
git push origin v1.2.3

手动触发方式(对应 workflow_dispatch):

  1. 打开仓库的 "Actions" 页签;
  2. 选择 "Build All Packages" 工作流;
  3. 点击 "Run workflow";
  4. 可选地填写一个版本号。

构建成功后,原文档给出的产物去向与仓库实际配置完全吻合:

  • GitHub Releases:所有可执行包与 PyPI 包作为 Release 资源;
  • PyPIpip install g4f
  • Docker Hubdocker pull hlohaus789/g4f:latest(仓库中的镜像名即 hlohaus789/g4f,见 build-packages.yml 的 metadata 配置);
  • WinGetwinget install g4f(清单审批通过后生效;Release 正文模板中给出的安装命令是 winget install gpt4free,见 build-packages.yml)。

二、版本如何被确定:prepare Job 的三级版本来源

原文档"Version Handling"一节提到工作流支持三种版本来源:Git Tag(发布首选)、环境变量 G4F_VERSION、手动输入。源码中这一逻辑集中在 prepare Job(build-packages.yml):

if [[ "${GIT_REF}" =~ ^refs/tags/ ]]; then
  G4F_VERSION="${REF_NAME}"     # Tag 名即版本,is_release=true
  IS_RELEASE="true"
elif [[ -n "${{ inputs.version }}" ]]; then
  G4F_VERSION="${{ inputs.version }}"   # 手动输入,is_release=false
  IS_RELEASE="false"
else
  G4F_VERSION="0.0.0-dev"              # 未指定时的开发版兜底
  IS_RELEASE="false"
fi

要点有三:

  1. 只有 Tag 触发才算正式发布is_release 输出被下游 build-dockercreate-release 用作门禁(if: needs.prepare.outputs.is_release == 'true')。也就是说,手动触发且不带 Tag 的构建只产出工件与 Release 草稿之外的包,不会推 Docker 镜像、也不会创建 Release。
  2. 版本号向下传递的方式是 needs.prepare.outputs.version,各构建 Job 通过 env: G4F_VERSION=... 注入(例如 build-pypi 的构建步骤)。
  3. setup.py 直接读取该环境变量setup.pyversion=os.environ.get("G4F_VERSION"),包名固定为 g4f

原文档同时要求版本遵循 PEP 440 规范以保证 PyPI 兼容性。从当前仓库的 Job 名与产物命名(g4f-go-${VERSION}-...hlohaus789/g4f:${VERSION}-slim)来看,版本号会直接拼接进文件名与镜像 Tag,因此含非法字符的版本号会在发布阶段产生难以检索的资源名。

三、PyPI 包构建与发布:build-pypi Job 和独立发布工作流

build-pypi Job(build-packages.yml)的流程:

  1. actions/setup-python 安装 Python 3.x
  2. pip install --upgrade pip 后安装 buildtwine
  3. G4F_VERSION 环境变量下执行 python -m build,产出 wheel 与 sdist 到 dist/
  4. python -m twine check dist/* 校验元数据;
  5. 分别以 pypi-package(合并)、pypi-wheeldist/*.whl)、pypi-sdistdist/*.tar.gz)三个工件名上传,供后续 Release Job 下载。

值得注意的细节是:build-packages.yml 末尾的 publish-pypi Job 目前是注释状态build-packages.yml),即“推 Tag 建 Release 的这条流水线”本身不负责发 PyPI。真正把包发布到 PyPI 的是另一条独立工作流 publish-to-pypi.yml

  • 仅在 github.repository == 'xtekky/gpt4free' 且 ref 以 refs/tags/ 开头时运行(第 11 行),避免 fork 仓库误发版;
  • pypa/build 构建 wheel 与源码包,再以 pypa/gh-action-pypi-publish 基于 OIDC(permissions: id-token: write)发布,无需在密钥中存放 PyPI Token;
  • 通过 environment: pypi 绑定发布环境,属于典型的“构建与发布分离”设计。

再看打包内容本身。setup.py 定义了最小运行依赖与按功能切分的 extras:

  • 核心依赖(INSTALL_REQUIRE):requestsaiohttpbrotlipycryptodomenest-asyncio2 —— 这也是 build-g4f-go Job 中 pip 预装清单(build-packages.yml);
  • extras 包括 allslimapiguiimagesearchwebviewfilestraylocal 等,例如 api 只需 logurufastapiuvicornpython-multiparta2wsgiPyYAMLsetup.py);
  • 命令行入口(setup.py):g4f=g4f.cli:maing4f-mcp=g4f.mcp.server:maing4f-tray=g4f.tray:_tray_main,即安装后同时获得 g4f 客户端、MCP 服务器与托盘程序三个命令。

四、原生可执行产物:从 Nuitka 脚本到 g4f-go 启动器

4.1 文档描述的 Nuitka 方案

原文档指出可执行文件“使用 Nuitka 构建”,并将 scripts/build-nuitka.sh 列为定制关键点。该脚本在仓库中确实存在且完整可用,其设计值得拆解:

  • 默认值与架构归一化build-nuitka.sh):PLATFORM 默认取 uname -sARCHITECTURE 默认取 uname -mVERSIONG4F_VERSION(缺省 0.0.0-dev),输出目录 OUTPUT_DIR 缺省 distx86_64/amd64 → x64arm64/aarch64 → arm64armv7l/armhf → armv7
  • 平台差异化参数build-nuitka.sh):
平台 产物名 关键参数
Windows g4f-windows-${VERSION}-${ARCH}.exe --windows-console-mode=attach --onefile
macOS g4f-macos-${VERSION}-${ARCH} --macos-create-app-bundle --onefile
Linux g4f-linux-${VERSION}-${ARCH} --onefile
  • 通用参数与构建命令build-nuitka.sh):--standalone --remove-output --no-pyi-file --include-package=g4f 等,最终以 python -m nuitka ... g4f_cli.py 打包,并存在可选的 Windows 图标参数(projects/windows/icon.ico);
  • 构建后自检:脚本最后检查产物文件是否存在,失败则以非零码退出(build-nuitka.sh)。

入口文件 g4f_cli.py 极薄:先调用 g4f.debug.enable_logging() 打开日志,再执行 g4f.cli.main(),注释明确说明它是“Nuitka 可执行构建的入口点”。

配套还有 scripts/validate-nuitka.sh 验证脚本,包含 5 项检查:g4f_cli.py --help 可运行、python -m nuitka --version 可用、scripts/build-nuitka.sh 可执行、工作流中包含 Nuitka 字样、工作流中存在 matrix: 架构矩阵。需要说明的是,从当前工作流文本看,可执行文件 Job 已改为 g4f-go 方案,后两项断言与现状存在出入,可以推断该验证脚本属于 Nuitka 时代的产物,本地使用前应先对齐当前流水线。

4.2 当前流水线实际方案:g4f-go 交叉编译

build-packages.yml 第 86 行注释写明 "Executables (built with g4f-go, no Nuitka)":可执行文件 Job 现在改为在 ubuntu-latest 上交叉编译 Go 启动器,覆盖所有 OS/架构,并在各 zip 中携带内嵌的 CPython 运行时。其步骤:

  1. Go 1.22(actions/setup-go,依赖 g4f-go/go.mod 做缓存)+ Python 3.11;
  2. 预装 Python 侧核心依赖与 rsync zip 工具;
  3. 执行 ./fetch-python.sh 获取并合并各平台 Python 运行时;
  4. 执行 ./build-all.sh 交叉编译并逐平台打 zip;
  5. 上传合并工件 g4f-go 与各平台单独工件(g4f-go-windows-amd64g4f-go-linux-arm64 等,均设 if-no-files-found: error,缺产物即失败)。

g4f-go/build-all.sh 的默认目标矩阵为:

OS Arch 产物名
linux amd64 g4f-go
linux arm64 g4f-go
windows amd64 g4f-go.exe
darwin arm64 g4f-go
darwin amd64 g4f-go
android arm64 g4f-go

构建命令形如 CGO_ENABLED=0 GOOS=... GOARCH=... go build -trimpath -ldflags "-s -w -X main.Version=$VERSION" ...,版本号通过 -ldflags -X 注入 main.Version,与 G4F_VERSION 一致(缺省 0.1.0)。支持按 OS 过滤,如 ./build-all.sh linux

一个值得记录的设计差异来自 build-all.sh 的头部注释:运行时不在构建期嵌入,启动器首次运行时从 python.org / python-build-standalone 下载;./fetch-python.sh 只负责固定尺寸与 SHA 校验值(见 runtime.json),因此 Go 启动器本身可以脱离 Python 工具链独立构建。另外可以注意到,工作流中定义了 windows-386freebsd-amd64 的上传模式(build-packages.yml),而 build-all.sh 的默认目标表未包含这两项,说明平台覆盖以工作流工件名清单为扩展边界,从源码结构看后续可通过 TARGETS 表扩展。

五、Docker 镜像构建:仅正式发布、三类镜像

build-docker Job(build-packages.yml)仅在 is_release == 'true' 时运行,流程为:

  1. docker/setup-qemu-action + docker/setup-buildx-action 准备多架构构建环境;
  2. docker/metadata-action 生成 hlohaus789/g4f 的元数据 Tag;
  3. 使用仓库 secrets DOCKER_USERNAME / DOCKER_PASSWORD 登录 Docker Hub(这解释了原文档"Troubleshooting"中“Docker push 失败需检查仓库 secrets”一条);
  4. 依次构建并推送三类镜像,均开启 provenance: mode=maxsbom: true
镜像 Dockerfile 平台 Tag
armv7 版 docker/Dockerfile-armv7 linux/arm/v7 latest-armv7${VERSION}-armv7
slim 精简版 docker/Dockerfile-slim linux/amd64, linux/arm64 latest-slim${VERSION}-slim
完整版 docker/Dockerfile linux/amd64 由 metadata-action 生成(含 latest

三类构建均通过 build-args 注入 G4F_VERSION,与 PyPI/可执行文件共用同一版本来源,保证一次 Tag 内所有产物版本一致。

六、系统级软件包:Debian 构建脚本与 WinGet 现状

原文档将 .deb(amd64/arm64/armhf)与 WinGet 清单列为支持格式,并列出两个定制文件:scripts/build-deb.shwinget/manifests/

build-deb.sh 在当前仓库中完整可用,其打包流程:

  1. G4F_VERSION(缺省 0.0.0-dev)与 ARCH(缺省 amd64)为版本与架构变量,清理并重建 debian/g4f 目录结构(DEBIAN/usr/binusr/lib/python3/dist-packagesusr/share/docusr/share/applications);
  2. 生成 control 文件:Section: pythonPriority: optional、依赖 python3 (>= 3.10), python3-pip, python3-aiohttp, python3-requestsbuild-deb.sh);
  3. postinst 脚本负责 pip3 install 五个核心依赖(与 setup.pyINSTALL_REQUIRE 一致)并建立 /usr/local/bin/g4f → /usr/bin/g4f 软链;prerm 负责卸载时移除软链(build-deb.sh);
  4. python3 setup.py install --root=debian/g4f --prefix=/usr --install-lib=/usr/lib/python3/dist-packages --install-scripts=/usr/bin 安装文件,并将 README 压缩、LICENSE 拷为 copyright,还生成桌面 .desktop 条目。

关于 WinGet:当前仓库快照中未发现 winget/ 目录与 build-packages.yml 中对应的 manifest 生成 Job,可以推断清单目前独立维护在 Windows Package Manager 的生态仓库中,本仓库仅保留文档与 Release 说明(winget install gpt4free)作为用户入口。原文档提到的 winget/manifests/ 模板路径属于规划性描述,使用前建议以 Release 正文中的安装命令为准。

七、Release 资产汇总:create-release Job

正式发布时,create-release Job(build-packages.yml)完成最终汇聚:

  1. 下载 pypi-packageg4f-go 合并工件,再用 pattern: g4f-go-* + merge-multiple: true 拉取各平台单独工件;
  2. 通过 find/cp 将所有文件扁平化到 ./release/flat/,并打印最终资产清单,便于在日志中核对“缺失工件”(对应原文档 Troubleshooting 第 3 条);
  3. softprops/action-gh-releaseGITHUB_TOKEN 创建 Release,prerelease: false,正文模板自动写入四个安装入口:
    • pip install g4f==${VERSION}
    • 各平台 g4f-go-${VERSION}-{os}-{arch}.zip(windows-amd64、linux-amd64、linux-arm64、darwin-amd64/arm64、freebsd-amd64);
    • winget install gpt4free
    • docker pull hlohaus789/g4f:${VERSION}-slim 变体。

八、构建环境与本地定制

原文档给出的本地构建要求(Python 3.10+、Nuitka、Docker、dpkg-deb)仍然适用于按脚本复现各产物。结合仓库实际,定制构建时的关键文件对照如下:

文件 职责
g4f_cli.py 可执行构建入口(Nuitka 方案入口)
setup.py 包名、G4F_VERSION 版本注入、依赖与 console_scripts
scripts/build-nuitka.sh Nuitka 各平台单文件构建脚本
scripts/build-deb.sh Debian 包构建脚本
scripts/validate-nuitka.sh Nuitka 构建系统验证(5 项检查)
g4f-go/build-all.sh / g4f-go/fetch-python.sh Go 启动器交叉编译与 Python 运行时获取
.github/workflows/build-packages.yml 主工作流
.github/workflows/publish-to-pypi.yml PyPI 发布工作流

本地构建示例(均可在当前仓库查看后在自有环境执行):

# Nuitka 本地打包(Linux x64 默认值)
G4F_VERSION=0.0.0-dev PLATFORM=linux ARCHITECTURE=x86_64 OUTPUT_DIR=dist bash scripts/build-nuitka.sh

# Debian 包
G4F_VERSION=0.0.0-dev ARCH=amd64 bash scripts/build-deb.sh

# Go 启动器仅构建 Linux
G4F_VERSION=0.0.0-dev ./g4f-go/build-all.sh linux

九、版本规范、故障排查与安全实践

原文档的 Troubleshooting 与 Security Notes 可以结合源码逐一落地:

  1. 构建失败:检查 Python 版本与依赖。PyPI 侧工作流使用 Python 3.x,g4f-go 侧固定 3.11,Debian 包声明 python3 (>= 3.10) 依赖——从源码看 3.10+ 是各产物共同的下限;
  2. 版本错误:确保版本符合 PEP 440。版本号会被 setup.py 写进 wheel 文件名、被 build-all.sh 拼进 zip 名、被 Docker 用作 Tag,任何一处不合法都会产生难以定位的失败;
  3. 缺失工件build-g4f-go 的各平台上传步骤均设置 if-no-files-found: error,缺产物会直接使 Job 失败;create-release 也会打印 --- Final release assets --- 清单便于核对;
  4. Docker push 失败:确认 DOCKER_USERNAME/DOCKER_PASSWORD 两个 secrets 已配置(build-packages.yml),且仅在 Tag 触发的正式发布中执行推送。

安全方面,原文档提到工作流采用受信任的 Action 版本、环境隔离与密钥管理。从 build-packages.yml 可以看到具体落实:Docker 系列 Action 均锁定到具体 commit SHA(如 docker/setup-qemu-action@c7c53464...),create-release 仅申请 contents: write 最小权限,publish-to-pypi.yml 通过 OIDC id-token: write 而非明文 Token 发布 PyPI,仓库内没有硬编码凭证。

十、向构建系统贡献修改

原文档给出的贡献流程同样适用于本仓库:

  1. 先在本地测试:可用 scripts/validate-nuitka.sh 做 Nuitka 链路自检,或直接运行 scripts/build-nuitka.shscripts/build-deb.sh 验证本地打包;
  2. 同步更新文档:如本文对应的 docs/build-workflow.md
  3. 考虑向后兼容build-packages.yml 的 Job 名称、工件名(pypi-packageg4f-go-*)被 create-release 依赖,重命名需同步修改下游;
  4. 多 Python 版本测试:当前 Job 覆盖 3.x(PyPI 构建)与 3.11(g4f-go 构建)两条解释器链路,改动依赖清单(如 setup.pyINSTALL_REQUIRE)时应确认两条链路均通过。

小结:gpt4free 的发布体系是“一个 Tag、一条主工作流、四类下游渠道”。prepare Job 用三级来源确定版本并以 is_release 门禁正式发布动作;build-pypi 与独立的 publish-to-pypi.yml 分别负责构建与发布;build-g4f-go 以 Go 交叉编译替代了文档所述 Nuitka 方案(Nuitka 脚本仍保留供本地使用);build-docker 在正式发布时推送 armv7/slim/完整三类多架构镜像;create-release 汇总所有工件并生成带四个安装入口的 Release 说明。理解这套“版本 → 工件 → 渠道”的映射关系,是阅读或维护该仓库发布流程的关键。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384