Skip to content

GitHub Releases 使用教程:版本发布、二进制分发与自动化 ​

GitHub Release 是给 Git tag 加上的发布层:一段说明文字、一批可下载的二进制资产(asset)、一个可选的讨论区。Releases 页是 GitHub 原生的软件发行入口:用户从 Releases 页下载安装包,工具链通过 /releases/latest 拉取最新版,下游仓库监听 on: release 的 published 事件自动升级依赖。

本文的行为描述以 2026-09-22 的文档与接口为准,CLI 部分按 GitHub CLI v2.97 实测。官方 actions/create-release 仓库已归档(最后 Release v1.1.4),流水线方案以 gh CLI 或社区 Action 为主。

前置条件 ​

  • 仓库有可用的默认分支与推送权限(发布 Release 需要 write)。
  • 版本策略已定:语义化版本号、tag 命名(社区惯例加 v 前缀,GitHub 不强制)、prerelease 规则。
  • 二进制产物能构建出来——推荐先用 Actions 的构建 Job 产出 artifact,发布 Job 再下载组合成 Release asset。

Release 的模型 ​

text
Git tag(指向某个提交)
  └── GitHub Release(GitHub 侧元数据)
        ├── 标题 + Markdown 正文(含 @提及自动生成的贡献者区)
        ├── Assets(任意文件,1 个 Release 最多 1000 个)
        ├── 自动附加的源码归档(zip / tar.gz)
        └── 可选:绑定到该 Release 的 Discussions 分类

模型里有三条规则最容易被误解:

  • 一个 tag 最多绑定一个 Release。REST 的 GET /repos/{owner}/{repo}/releases/tags/{tag} 返回单个对象;改内容走 edit,而非对同一 tag 重复 create。
  • draft / prerelease / latest 是三个独立开关。草稿只有推送权限者可见;prerelease 用于 rc/beta;latest 是"最新稳定版"徽章,而 /releases/latest 端点按"最近创建的非草稿、非预发布 Release"解析——版本可见性由这两套判定共同决定。
  • tag 日期 ≠ Release 日期。先建 tag 后补 Release 很常见。REST 的 latest 按 Release 的创建时间排序,且 created_at 记录的是所绑定 commit 的日期,不是草稿改为发布的日期;UI 徽章则按语义化版本自动分配,可手动勾选覆盖。两套规则并存,版本号回补旧线时要有意识地用 make_latest 控制。

最短可用路径 ​

用 gh CLI 发布 ​

bash
# 单文件 + 手写说明
gh release create v1.2.3 ./dist/app-linux.tar.gz ./dist/app-macos.zip#macOS \
  --title "v1.2.3" --notes "修复导出超时" --latest

# 让 GitHub 按提交历史自动生成说明
gh release create v1.2.3 --generate-notes --notes-start-tag v1.2.0

tag 不存在时,gh release create 会自动从默认分支最新提交创建;加 --verify-tag 可强制要求 tag 已存在,防止发错点。#macOS 是 asset 显示名语法(不改文件名)。

成功信号:

  • gh release view v1.2.3 能读到标题、正文与 asset 列表;
  • 仓库 /releases 页出现新条目;若新版本是最近创建的非草稿、非预发布 Release,/releases/latest 会 302 重定向到新版本的 tag 页;
  • gh release download v1.2.3 -p '*.tar.gz' 能把目标文件原样取到本地;
  • 本地看不到新 tag 时执行 git fetch --tags origin。

用网页发布 ​

Code → Releases → Draft a new release:选择或新建 tag、选 Target 分支、填标题正文、拖拽 binary assets、按需 Generate release notes / Set as the latest release / Pre-release / 绑定讨论分类。想给外部系统嵌入"预填发布表单"链接,可用查询参数:/releases/new?tag=v1.2.3&title=...&body=...&prerelease=1(无权限或 URL 非法返回 404)。

Latest 语义与稳定下载链接 ​

  • /releases/latest:最近创建的非草稿、非预发布 Release——适合"永远取最新稳定版"的入口。
  • /releases/latest/download/<asset文件名>:稳定直链指向最新 Release 的某个手动上传 asset(自动附加的源码归档不在此列),安装脚本写死一条 URL 即可跟版本。
  • /releases/download/<tag>/<asset>:固定版本直链。
  • 版本动态可以只订阅 Releases 通知——只收新发布通知,不被仓库其它更新打扰;程序化消费则用 /releases 列表页与 REST GET /releases。
  • 源码归档:https://github.com/{owner}/{repo}/archive/refs/tags/{tag}.tar.gz(302 到 codeload.github.com),归档是否包含 Git LFS 对象由仓库 admin 在归档设置中决定。

创建/更新 Release 时 make_latest 三值:true(默认,自动判定)、false、legacy(按创建时间加更高语义版本判定)。草稿和 prerelease 不能成为 latest。

Assets:上传、下载与计费 ​

配额:单个 asset 须小于 2 GiB,每个 Release 最多 1000 个 asset;Release 总大小与下载带宽没有上限。

上传有四种入口:UI 拖拽、gh release upload <tag> <files>(--clobber 先删同名再传——若重传失败,原 asset 已经丢了)、REST upload_url(POST,?name=&label= 区分文件名与显示名;502 可能留下 starter 状态的空占位 asset,删除即可)、Actions(见下文)。

下载链路:browser_download_url 与 /releases/download/... 都会 302 到带签名查询串的 release-assets.githubusercontent.com 地址,签名参数约一小时过期。因此不能把重定向前 URL 之外的 CDN 地址长期缓存进配置文件;脚本下载务必跟随重定向。asset 有独立 download_count,可用于热度统计。

REST 读取二进制需带 Accept: application/octet-stream,服务端或直接重定向或流式返回。

自动生成发布说明 ​

UI 上有 Generate release notes 按钮;REST 另有 POST /repos/{o}/{r}/releases/generate-notes(参数 tag_name、previous_tag_name、target_commitish、configuration_file_path,缺省时读取 .github/release.yml,也接受 .github/release.yaml)。该端点只返回生成的标题与正文,Release 本身仍需另行创建;在 UI 里生成后也要先核对内容再点发布。

默认输出结构(无配置文件时的实际生成样式,节名不是固定契约):## What's Changed 列出合并的 PR、## New Contributors、尾部 **Full Changelog**: .../compare/<前tag>...<tag>。自定义分组放 .github/release.yml:

yaml
changelog:
  categories:
    - title: 破坏性变更
      labels: [breaking]
    - title: 新功能
      labels: [enhancement, "feature"]
    - title: 缺陷修复
      labels: [bug]
  exclude:
    labels: [ignore-for-release]
    authors: [dependabot, github-actions[bot]]

不写配置时,草稿就是一条合并 PR 列表;想按标签分类,写好 categories 并给 PR 打上对应 label。

Actions 发布流水线 ​

下游触发用 on: release,活动类型共七种:published、unpublished、created、edited、deleted、prereleased、released。默认全部类型触发,生产流水线务必显式 types: [published]。还有两个边界情况:草稿 Release 不触发 created/edited/deleted;从草稿发布的预发布版触发 published 而不是 prereleased。

把构建产物变成 Release asset 的完整链路(构建 → artifact → 发布):

yaml
name: Release

on:
  push:
    tags: ["v*"]

permissions:
  contents: write   # 创建 Release 必需;默认权限不可靠,显式声明

jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v7
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v7
        with:
          name: dist-${{ matrix.os }}
          path: dist/

  publish:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - uses: actions/download-artifact@v8
        with:
          pattern: dist-*
          merge-multiple: true
          path: bundle
      - name: Create release with assets
        run: |
          gh release create "${{ github.ref_name }}" \
            bundle/*.tar.gz \
            --generate-notes \
            --prerelease
        env:
          GH_TOKEN: ${{ github.token }}

要点:

  1. 官方 actions/create-release 已归档停维(最后 Release 2020 年),新教程别再引用;用 gh CLI 或社区维护的 softprops/action-gh-release@v3(要求同样的 contents: write)。
  2. GITHUB_TOKEN 默认只读的适用范围是三类"新":2023-02-02 之后个人账户名下的新建仓库、新建的企业、新建的非企业组织;既有企业/组织下的仓库沿用父级现有默认值。别赌默认,Release Job 一律显式 permissions: contents: write。
  3. 若 Release 指向的提交改动过 .github/workflows/,用外部 token 创建时 classic 需 workflow scope、fine-grained 需 Workflows: write——而 GITHUB_TOKEN 不能获得该授权(403/404 是常见症状)。

能基于 Releases 做什么 ​

Releases 最核心的能力是免认证的二进制分发:任何能读仓库的人都能下载 asset,这正是安装脚本、curl -fsSL .../releases/latest/download/install.sh | bash 类方案成立的前提。Actions artifact 有保留期、面向 CI 中间产物、不承诺长期公开分发——长期分发只属于 Releases。

值得主动开启的还有供应链防篡改。启用不可变 Release(Immutable releases)后,已发布 Release 不能增删改 asset、tag 不能移动或删除;Release 创建时自动生成 attestation(包含 tag、commit SHA 与 asset 摘要),gh release verify 可校验。组织策略分 No policy / All repositories / Selected repositories,只对未来的 Release 生效。

面向下游与消费者的动作基本都挂在事件和元数据上:on: release: types: [published] 触发打 bump PR、同步到内部制品库、通知发布频道——跨仓库晋级链路靠这一段收尾;只想跟版本的用户可以单独订阅 Releases 通知;shields.io 的 github/v/release 徽章从 Release 数据取最新稳定版生成;Release 绑定 Discussions 分类后,用户反馈聚在版本维度而非散落在 issue。修复漏洞的版本发布后应发 security advisory,经 GitHub 审核后可能用于向受影响仓库发送 Dependabot alerts。

不适合的场景:内部构建与临时验证件放 Actions artifact 而非 Release(Release 是面向消费者的承诺,删改有外部感知成本);超过 2 GiB 的单体文件走外部对象存储拆分;带 token 的调试构建严禁作为 asset 上传——asset 对任何有读权限者公开。

权限与治理边界 ​

能力要求
浏览 / 比较 / 下载 Release仓库 read(公开仓库无需登录)
创建 / 编辑 / 删除 Releasewrite(REST 语境写作 push access,与 write 同一档)
草稿列表可见仅 push access
决定源码归档是否含 LFS 对象admin
启用不可变策略仓库 admin;组织可强制,删除的不可变 Release 其 tag 名永久不可复用

删除 Release 默认保留 tag(gh release delete 需 --cleanup-tag 才连 tag 一起删);误删 Release 后按原 tag 重建即可恢复链接,这是最便宜的回滚路径。Release 正文随时可被覆盖改写,重要公告请同步落在 git tag 注解或仓库文档,才留得住可追溯的存档。

故障处理 ​

症状原因与处理
Actions 建 Release 返回 403/404缺 permissions: contents: write;或目标提交含 workflow 文件改动而 GITHUB_TOKEN 无权携带 workflow scope——把发布 Job 与改 workflow 的提交解耦,或改用被授权的 PAT
/releases/latest 没指向新版本新版本是 draft/prerelease,或创建时间早于现 latest;用 make_latest/UI 勾选修正
本地看不到刚发布的 taggh release create 在服务端建 tag,本地要 git fetch --tags origin
asset 下载脚本 403用了带签名的重定向后地址(一小时过期);改跟随 302 的公开下载 URL
上传报文件过大单 asset 须小于 2 GiB,拆分或转分卷/外部存储
发布说明分组不生效.github/release.yml 路径或键名错误;PR 未打对应 label;配置是在生成那一刻读取的,改完要重新点一次 Generate 才会生效
发布后想改 asset 失败仓库/组织启用了不可变 Release;只能发补丁版本,旧 asset 无法替换
gh release create 无新提交仍创建成功默认允许在上一 tag 同点重复发布;需要防呆时加 --fail-on-no-commits

验收清单 ​

  • [ ] tag 命名、latest/prerelease 策略成文,机器人不再手滑设错 latest。
  • [ ] 发布流水线显式 types: [published],发布 Job 权限最小化。
  • [ ] .github/release.yml 与 PR 标签流程对齐,生成后人工过目。
  • [ ] 安装文档使用 /releases/latest/download/ 或固定版本直链,不缓存 CDN 地址。
  • [ ] 安全修复版本配套 security advisory 流程。
  • [ ] 重要项目评估启用不可变 Release 与组织策略。

参考资料 ​

全部公开文章由同一个站点构建和发布。