现象
你遇到了这样一个情况:
- 一个公开的 GitHub 仓库里没有任何源代码
- 也没有
.github/workflows/目录
- 但 Releases 页面却发布了多个版本的二进制文件(如
v1.1.6到v1.1.13),涵盖 Linux、macOS、Windows 等多个平台 - 仓库里只有说明文档(如
README.md、CHANGELOG.md、LICENSE等)
这看起来似乎"无源可循",令人困惑:没有代码,没有 CI/CD 配置,只有几份文档,多平台的二进制文件从哪来?
快速结论
构建发生在别处,这个仓库只是一个"发布窗口"。
二进制文件不是在当前仓库里构建的,而是从另一个私有仓库构建完成后,通过 GitHub API、gh 命令行工具或 CI/CD 脚本跨仓库上传到这里的 Releases。
证据拆解
1. 仓库文件列表:只有文档,没有源码
从仓库的文件列表可以看到,公开仓库里只有文档类文件:
README.mdREADME.zh-CN.mdCHANGELOG.mdLICENSEassets/目录
没有任何源码文件(如 .c、.go、.rs、.py 等),也没有 .github/workflows/ 目录。
这说明:当前仓库不包含任何可构建的源代码。
2. Release 发布说明:自述来源
在 Releases 页面中,每个版本的发布说明里明确写着:
Official binaries for v1.1.13 (built from private source).
这直接印证了:二进制文件来自私有源码仓库,而非当前仓库。
3. Release Assets:多平台二进制文件
从 Releases 的 Assets 列表可以看到,每个版本都提供了多个平台的二进制文件:
- Linux 版本(
.tar.gz) - macOS 版本(
.dmg),包括 Intel 和 ARM 两种架构 - Windows 版本(
.zip)
这些文件涵盖了三大操作系统、多种架构,且都附带了 SHA256 校验值。这种多平台、带校验和的发布方式,通常来自自动化构建流程(如 GitHub Actions、本地自动化脚本),而非手动在网页上一个一个拖拽上传。
4. 补充观察:Release 里的 "Source code" 是什么?
细心的读者可能会发现,Release Assets 中除了多平台二进制文件外,还有 Source code (zip) 和 Source code (tar.gz) 两个文件。
这是 GitHub 自动生成 的,并非发布者上传。每个 Release 关联一个 Git Tag,GitHub 会自动将该 Tag 时刻仓库的文件快照打包成 zip 和 tar.gz 供下载。
由于这个公开仓库里只有文档,所以这两个源码包解压后也只有 README、CHANGELOG 等文档,并不包含真正的业务源代码。
真正的源码在哪里?在背后那个私有仓库里。
完整的运作流程
| 步骤 | 发生位置 | 操作内容 |
|---|---|---|
| ① | 私有仓库 | 源码开发和版本管理 |
| ② | 私有仓库 | CI/CD 自动编译、构建多平台二进制文件 |
| ③ | 私有仓库 CI | 通过 API、gh 或 CI 脚本将二进制上传到公开仓库的 Release |
| ④ | 本地或脚本 | 同步文档(README、CHANGELOG)到公开仓库 |
步骤 ③ 是核心:构建和上传是分离的——构建在私有仓库完成,上传目标却是公开仓库。
在 GitHub 上实现私有仓库构建、公共仓库发布
如果你也想在自己的 GitHub 项目中采用这种"源码私有、发布公开"的模式,以下是完整的实践指南。
第一步:创建两个仓库
在 GitHub 上同时创建两个仓库:
| 仓库类型 | 名称示例 | 作用 |
|---|---|---|
| 私有仓库 | myapp-private | 存放所有源代码,进行开发和版本管理 |
| 公共仓库 | myapp-releases | 仅作为发布窗口,存放二进制文件和文档 |
这种"公私分离"的架构,既保护了核心代码,又能向用户提供便捷的下载入口。
第二步:配置 Personal Access Token(PAT)——最关键的一步
这是实现跨仓库发布最关键的一步。
为什么必须使用 PAT?
GitHub Actions 中默认的 GITHUB_TOKEN 是一个由 GitHub 自动生成的临时凭证,它的权限范围被严格限制在当前工作流所在的仓库内。也就是说,私有仓库的 GITHUB_TOKEN 只能访问该私有仓库本身,无法向另一个公共仓库推送任何内容。
因此,要打通私有仓库和公共仓库,必须使用一个自定义的、拥有目标公共仓库写入权限的 PAT。
操作步骤:
- 生成 PAT:在 GitHub 个人设置中,进入 Settings → Developer settings → Personal access tokens → Fine-grained tokens,点击生成新 Token。
配置权限:
- 在 Repository access 中选择 Only select repositories,然后勾选你的公共仓库(即发布目标)。
- 在 Permissions 中,找到 Repository permissions,将 Contents 权限设置为 Read and write。
- 复制 Token:生成后立即复制保存(页面关闭后无法再次查看)。
- 添加到私有仓库 Secrets:进入私有仓库的 Settings → Secrets and variables → Actions,点击 New repository secret,名称设为
PUBLIC_REPO_TOKEN,值粘贴刚才复制的 PAT。
第三步:在私有仓库中配置工作流
在私有仓库的 .github/workflows/release.yml 中编写 CI/CD 工作流:
1 | name: Build and Publish |
第四步:触发发布
当你在私有仓库中推送一个 tag(如 git tag v1.0.0 && git push origin v1.0.0)时,工作流会自动:
- 检出源码
- 构建二进制文件
- 将二进制文件上传到公共仓库的 Releases 页面
关键提醒总结
| 关键点 | 说明 |
|---|---|
| 必须使用 PAT | 默认 GITHUB_TOKEN 权限仅限当前仓库,无法跨仓库操作 |
| PAT 权限要正确 | 需具备目标公共仓库的 contents: write 权限 |
| Token 存储在私有仓库 | 作为 Secret 保存,确保安全性 |
| 指定目标仓库 | 在使用 Action 或 gh 命令时,必须明确指定 repository 参数 |
跨仓库上传的实现方式
在实际生产环境中,GitHub 上有多种方式可以实现从私有仓库构建并上传到公共仓库 Release 的功能。以下是几种主流方案,开发者可以根据自己的偏好和项目需求灵活选择。
自动化方式
方式一:softprops/action-gh-release(社区最推荐)
这是目前 GitHub 生态中实现"私有仓库构建,公共仓库发布"模式最主流、社区最推荐的解决方案。
为什么它被广泛推荐?
- 原生跨仓库支持:与其他默认受限在当前仓库的 Action 不同,
softprops/action-gh-release在设计上就通过repository参数原生支持指定目标仓库,这是它区别于很多同类 Action 的核心优势。 - 一站式解决方案:它集成了创建 Release、上传资产、自动生成 Release Notes 等完整流程,极大地简化了原本需要多次调用 API 的复杂操作,可以说是开箱即用。
- 社区验证充分:在 GitHub 社区中,它常被作为解决"私有构建、公开发布"需求的参考方案被推荐,大量开发者已在生产环境中验证了其可靠性。
在私有仓库的工作流中的配置示例:
1 | - name: Upload to public release |
方式二:官方 gh CLI
GitHub 官方提供的 gh 命令行工具同样原生支持跨仓库操作。这种方式代码量适中,是 GitHub 官方的推荐做法之一。
在私有仓库的 Actions 工作流中使用:
1 | gh release create v1.1.0 ./binary \ |
你也可以把创建和上传拆开,先 gh release create 创建 Release,再 gh release upload 上传资产,流程上更灵活。
方式三:其他专门上传 Assets 的 Action
除了 softprops/action-gh-release,GitHub Marketplace 上还有一些专门负责"上传资产"的轻量级 Action:
alexellis/upload-assets:专注于上传资产,支持 glob 模式匹配(如./bin/*),配置简洁。shogo82148/actions-upload-release-asset:是已停止维护的官方actions/upload-release-asset的替代品,更为轻量。
这些方案通常不与"创建 Release"耦合,适合已经通过其他方式(如 gh 命令或手动)创建好 Release、只需要往里面塞文件的场景。
方式四:直接调用 GitHub API(curl)
这是自由度最高的方式,你可以完全控制每一个 HTTP 请求,精确指定上传时机、重试策略、错误处理等细节。
典型流程是分两步走:
1 | # 第一步:获取目标仓库指定 tag 的 Release ID |
这种方式代码量较大,但能让你对流程有绝对的控制权,适合有高度定制化需求的场景。
手动方式(可行但较少用于多平台发布)
手动操作同样支持,项目管理员或拥有写入权限的协作者可以通过以下方式上传:
- 网页端直接拖拽:进入仓库的 Releases 页面,编辑或创建 Release,在附件区域直接拖拽或选择本地文件上传。
ghCLI 本地执行:在本地终端使用gh release upload <tag> <文件路径> -R <用户名/公开仓库名>命令上传。- 本地 API 调用:通过
curl等工具手动调用 GitHub API 上传。
不过从该仓库的多平台覆盖情况来看(Linux、macOS Intel/ARM、Windows),手动方式需要逐一构建并上传四个平台的二进制文件,工作量较大且容易出错。自动化方式是必然选择。
GitHub 是否允许这种跨仓库上传操作?
允许,这是 GitHub 平台设计支持的合法操作。
理由如下:
- 机制上完全可行:无论是网页端、
gh命令行工具,还是 GitHub API,都允许用户将文件上传到任意一个有写入权限的仓库。只要上传者(或 CI 使用的 Token)对该公开仓库拥有写入权限,就可以向其 Release 上传文件。 - 已有成熟实践:这并非理论可行,而是一个被广泛验证的模式。在社区讨论中,已有开发者分享通过私有仓库构建,再使用
softprops/action-gh-release等 Action 将二进制文件推送到另一个公开仓库的 Release 的完整流程。甚至像 Valkey 这样的知名项目,也设计了专门的自动化系统来管理跨多个仓库的构建与发布流程。这些都证明该模式被 GitHub 平台所接纳。 - 符合平台设计初衷:GitHub 的 Releases 功能本身就是设计为支持外部上传的,
GITHUB_TOKEN的权限模型也允许在细粒度控制下进行跨仓库操作。平台并不限制 Release Asset 必须来源于本仓库的构建。
如何验证这个结论?
如果你想自己确认,可以尝试以下方法:
- 查看仓库文件列表:确认是否存在源码文件和
.github/workflows/目录。如果没有,说明当前仓库不具备构建能力。 - 查看 Releases 页面的发布说明:是否提到二进制文件来源,比如 "built from private source"。
- 查看 Release 关联的 Tag/Commit:GitHub Release 必须关联一个 Tag。如果该 Tag 对应的 commit 仅修改了 README 等文档,或 Tag 是通过 API 动态创建的轻量级标签(lightweight tag),而不包含任何源码快照,则说明构建不在此仓库发生。
- 确认 Assets 上传者:如果显示"Uploaded by"且是仓库所有者或协作者,说明是通过认证方式(网页、CLI 或 API)上传的。
- 检查 "Source code" 压缩包内容:下载 Release 中的
Source code (zip),解压后如果只包含文档,则进一步证明该仓库本身不包含业务源码。
安全信任问题:如何确保二进制文件可信?
这是源码不公开时用户最关心的问题。负责任的发布者通常会采取以下措施来建立信任:
1. GPG 签名
发布者用私钥对二进制文件签名,用户通过公钥验证文件完整性和来源真实性:
1 | gpg --verify binary.sig binary |
2. Sigstore / cosign 签名
使用无密钥签名基础设施,提供更现代、更安全的验证方式:
1 | cosign verify-blob --certificate binary.pem --signature binary.sig binary |
3. SLSA 证明
SLSA(Supply-chain Levels for Software Artifacts)是一套由 Google 等公司推动的供应链安全标准。简单来说,它提供了一份"构建过程履历表"——用密码学方法证明二进制文件确实是由声明的源码、在可信的构建环境中产出的,且构建过程本身未被篡改。SLSA 定义了从 Level 1 到 Level 4 的递进式安全等级,等级越高,对构建流程的防篡改和可审计性要求越严格。
4. 校验和文件
除了 SHA256 值,还会提供单独的 checksums.txt 文件并签名,确保下载文件的完整性。
作为用户,应优先选择提供上述验证手段的发布者。否则,即使二进制文件来源明确,也无法完全排除构建过程被篡改或文件在传输过程中被替换的风险。
为什么要把源码放在私有仓库?
常见的原因包括:
- 商业软件:源码闭源,只提供编译好的二进制供用户下载
- 收费软件:需要授权才能获取源码或使用
- 策略分离:公开仓库仅作为"下载站"和"文档站",源码在内部管理
这是一种非常常见的开源/闭源混合发布策略,许多知名项目(如部分企业级工具)都采用类似模式。
总结
| 疑问 | 答案 |
|---|---|
| 没有 workflow,怎么构建的? | 构建发生在私有仓库,不是这个公开仓库 |
| Release 里的文件哪来的? | 从私有仓库通过 API、gh 或 CI Action 跨仓库上传 |
| 跨仓库上传推荐用什么工具? | softprops/action-gh-release 是最主流的方案,gh CLI、直接调用 API 或上传专用 Action 也都是可行选择 |
| 跨仓库上传的关键是什么? | 必须使用 PAT,而非默认的 GITHUB_TOKEN,后者权限仅限当前仓库 |
| 多平台二进制怎么来的? | 私有仓库的 CI/CD 自动编译(或本地自动化脚本),再统一上传 |
| "Source code" 包里是什么? | GitHub 自动生成的仓库快照,此仓库里只有文档,所以包里也只是文档 |
| 手动上传可行吗? | 可行,网页拖拽、gh CLI 本地执行或 API 调用都支持,但多平台场景下效率较低 |
| GitHub 允许这种操作吗? | 允许,这是平台设计支持的合法操作,已有广泛成熟实践 |
| 如何确保二进制安全可信? | 查看是否提供 GPG/Sigstore/SLSA 签名和校验和验证 |
| 如何验证我的判断? | 查文件列表、Release Notes、Tag Commit、下载 Source Code 包验证 |
| 这个公开仓库的作用是什么? | 文档展示 + 二进制下载入口 |
| 这个模式常见吗? | 非常常见,尤其是闭源商业项目 |
所以,你的观察完全正确:这个公开仓库确实没有 CI/CD 配置,但 Release 里的多平台二进制文件也绝非凭空出现——它们来自另一个"幕后"的私有源码仓库。两者通过 GitHub 的跨仓库 Release 上传能力连接在一起,构成了一个"源码私有、发布公开"的典型架构。作为用户,在享受便捷下载的同时,也别忘了验证文件的真实性和完整性。
评论已关闭