> For the complete documentation index, see [llms.txt](https://408550179s-organization.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://408550179s-organization.gitbook.io/blog/electron-xiang-mu-zi-dong-da-tag-da-bao-release-bing-shang-chuan-cdn.md).

# Electron项目自动打Tag打包Release并上传CDN

Electron 项目如果只是在本地打包，前几次还可以手动操作。但一旦要同时支持 macOS、Windows、自动更新、国内下载加速，就应该尽早把发布流程自动化。

我现在比较顺手的一套流程是：

```
修改 package.json version
        ↓
push main
        ↓
GitHub Actions 检测版本变化
        ↓
自动创建 vX.Y.Z tag
        ↓
触发 Release 工作流
        ↓
macOS / Windows 分平台打包
        ↓
Windows 更新文件上传七牛
        ↓
GitHub Release 写入下载说明
```

## 为什么用版本号触发 tag

直接每次 push 都打包会浪费时间，也容易产生无意义版本。更合理的方式是：只有 `package.json` 里的 `version` 变化时才创建 tag。

```yaml
name: Auto Tag on Version Bump

on:
  push:
    branches:
      - main

permissions:
  contents: write

jobs:
  tag:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Detect version bump and push tag
        env:
          BEFORE_SHA: ${{ github.event.before }}
        run: |
          set -euo pipefail

          CURRENT=$(node -p "require('./package.json').version")
          TAG="v${CURRENT}"

          PREVIOUS=$(git show "${BEFORE_SHA}:package.json" 2>/dev/null \
            | node -e "const fs=require('fs');const d=fs.readFileSync(0,'utf8');try{console.log(JSON.parse(d).version)}catch{console.log('')}" \
            || echo "")

          if [[ -n "$PREVIOUS" && "$CURRENT" == "$PREVIOUS" ]]; then
            echo "Version unchanged, skipping tag"
            exit 0
          fi

          if git ls-remote --tags origin "refs/tags/${TAG}" | grep -q .; then
            echo "Tag ${TAG} already exists, skipping"
            exit 0
          fi

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git tag -a "${TAG}" -m "Release ${CURRENT}"
          git push origin "${TAG}"
```

这段逻辑解决两个问题：

* 版本号没变，不发布。
* tag 已存在，不重复发布。

## 分平台打包

Electron 打包通常 macOS 和 Windows 环境不同，建议用 matrix：

```yaml
strategy:
  fail-fast: false
  matrix:
    include:
      - os: macos-latest
        platform: mac
        args: --mac --arm64 --x64
      - os: windows-latest
        platform: win
        args: --win --x64
```

安装依赖后，需要注意原生模块：

```yaml
- name: Install dependencies
  run: pnpm install --frozen-lockfile --ignore-scripts

- name: Rebuild native modules for Electron
  run: pnpm rebuild:native

- name: Build app
  run: pnpm build

- name: Package installer
  shell: bash
  run: |
    unset CSC_LINK CSC_KEY_PASSWORD WIN_CSC_LINK WIN_CSC_KEY_PASSWORD || true
    export CSC_IDENTITY_AUTO_DISCOVERY=false
    pnpm exec electron-builder ${{ matrix.args }} --publish never
```

如果项目里用了 `better-sqlite3`、`sharp` 这类原生依赖，`electron-rebuild` 这一步非常关键。

## Windows 自动更新文件上传七牛

Electron 的 Windows 自动更新一般需要：

* 安装包 `.exe`
* 差分文件 `.blockmap`
* 更新描述 `latest.yml`

可以在 Release 阶段把这些文件上传到七牛 CDN，应用内检查更新时就走国内 CDN。

```js
function collectWinUpdateFiles(dir) {
  const names = new Set();

  for (const filePath of listFiles(dir)) {
    const base = path.basename(filePath);
    if (base === "latest.yml") names.add(base);
    else if (/^app-setup-.+\.exe$/i.test(base)) names.add(base);
    else if (/^app-setup-.+\.exe\.blockmap$/i.test(base)) names.add(base);
  }

  if (!names.has("latest.yml")) {
    throw new Error("latest.yml not found");
  }

  const exe = [...names].find((name) => name.endsWith(".exe"));
  if (!exe) throw new Error("Windows setup exe not found");

  const files = ["latest.yml", exe];
  const blockmap = `${exe}.blockmap`;
  if (names.has(blockmap)) files.push(blockmap);

  return files;
}
```

上传 CDN 时建议保留固定目录，例如：

```
version/latest.yml
version/app-setup-1.2.3.exe
version/app-setup-1.2.3.exe.blockmap
```

这样应用里的 `publish.url` 可以一直指向同一个 CDN 目录。

## Release 说明也自动生成

上传成功后，可以把 GitHub Release 的正文写成“国内下载推荐 + 其他系统附件”的结构。

```yaml
- name: Prepare release notes
  env:
    WIN_DOWNLOAD_URL: ${{ steps.qiniu.outputs.win_download_url }}
  run: |
    cat > release-notes.md <<EOF
    ## Windows 安装包（CDN 推荐）

    [点击下载](${WIN_DOWNLOAD_URL})

    ## macOS

    请在下方 Assets 下载 arm64 或 x64 的 dmg/zip。
    EOF
```

## 注意点

* 未签名 macOS 应用可能会提示“已损坏”，正式分发最好配置 Apple Developer ID。
* Windows 自动更新的 `latest.yml` 必须和实际安装包文件名匹配。
* 上传 CDN 后，如果 GitHub Release 也上传 Windows 安装包，用户可能会分不清下载入口，可以按需删除 GitHub 的 Windows 附件。
* `concurrency` 建议设置为 release tag，避免同一版本重复打包。

这套流程搭好之后，发布一个 Electron 新版本就只剩一件事：改版本号并 push。
