配置GitHub使用musl静态编译CGO程序封面图

配置GitHub使用musl静态编译CGO程序

前言

这个假期,我的时间基本上都投入到一个用Go语言写的CMS的开发中:GopherInk
完全是靠AI进行开发,一个AI写代码Cosplay程序员,一个AI做审计Cosplay安全负责人,互相掐架,我Cosplay产品经理,这不比游戏好玩多了?
其实也有一定原因是我开发完成NekoEcho主题后太上头了,于是打算继续Vibe Coding下一个更有意思的玩具:毕竟NekoEcho是我自用的主题,别人用不到,开发一个开源的玩具,然后发布,大家都能玩得到(和之前的恶搞病毒一样 bushi),岂不美哉?于是乎就有了这个项目的开发。
其实开发也有一段时间了,本来打算八月初上线的,后面因为家里面有一大堆破事烦着,再加上后面测试发现被CC的时候会造成严重的性能消耗,导致在低配VPS上面CPU和内存双双爆炸引发拒绝服务。所以我决定继续完善程序,延迟发布,反正又不是什么商业产品,只是自己的一个写来玩的玩具,没有KPI,没有甲方(我就是甲方),不如好好打磨好在端出来(虽然应该不会有什么人用就是了,无人在意这一块)。
废话了,接下来切入主题。

因为我希望支持多种数据库,并且希望做到零配置部署,那必然需要用到SQLite这个数据库。然而,对于使用Go语言,要使用这个数据库就需要引入CGO,造成glibc库的版本依赖。因为Go语言标准的 net 库和 sqlite3 的 C 驱动在动态链接 glibc 时,容易在不同发行版上遭遇 GLIBC_2.x 找不到的报错。对于希望做到下载后直接运行就能部署的CMS来说,这并不合适。而使用glibc又不好搞静态编译,于是,我看向了另一个选择——musl
musl是一个精简版本的C语言库,使用这个库典型的发行版就是Alpine Linux,也是老朋友了。而在这个发行版上面,我们可以实现完全的静态编译——因为C库足够精简。
而此时,我又希望能尝试一下在GitHub用一下传说中的自动构建,那就,开始吧。

配置 Workflows

要实现自动构建这个功能,需要在GitHub上面配置一个工作流:
在仓库创建.github/workflows/目录,然后把配置文件放在里面,例如build-beta.yaml
在配置这个文件的时候,我深刻体会到了国产AI和国外AI的差距:不是智商,而是训练数据集的污染。
得益于中文互联网中最大的技术噪音源CSDN(Copy Steal pay-Download Net)复制粘贴,杂乱无章的数据源污染,我试了几个国内的AI:通义千问,Deepseek,给我的yaml配置文件都是错漏百出,基本上都是各种花式报错。而把报错复制给他们,他们口口声声认错,接着继续给你一个稳定报错的答案。我试了好几次都搞不定,直接开魔法去问ChatGPT,直接轻松秒杀。
我的需求比较复杂,不是直接构建就了事。因为我的CMS设计的时候就是可拓展的架构,功能和外观可以靠插件和主题进行能力的拓展,这一点和Typecho一样。我是把一些实现比较复杂的主题单开了一个仓库进行管理,而作为基础功能补充的功能插件:例如虚拟文件、站点地图、友链管理,这些则是和CMS项目主体一起管理。但是,我希望能在构建“官方版本”的时候,额外把访客统计,服务器状态,邮件提醒这几个比较刚需的插件一起编译进来,让想用原版的可以直接一键启动。这就需要在构建时在特定的目录拉取其他仓库进行构建了。
别忘了,我还要配置Alpine环境用于构建,接下来就是ChatGPT帮我做好的版本,有相关需求的可以直接参考。
首先是build-beta.yaml,这个负责用于构建测试:

name: Beta Build Test

on:
  push:
    tags:
      - 'v*beta*'
  workflow_dispatch:

permissions:
  contents: read

env:
  GO_VERSION: '1.25.x'

jobs:
  build-linux:
    name: Linux (${{ matrix.goarch }})
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            platform: linux/amd64
            goarm: ''
          - goarch: arm64
            platform: linux/arm64
            goarm: ''
          - goarch: 386
            platform: linux/386
            goarm: ''
          - goarch: arm
            platform: linux/arm/v7
            goarm: '7'

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up QEMU
        if: matrix.goarch == 'arm64' || matrix.goarch == 'arm'
        uses: docker/setup-qemu-action@v4

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Build static Linux binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p dist

          docker run --rm \
            --platform "${{ matrix.platform }}" \
            -v "${{ github.workspace }}:/app" \
            -w /app \
            golang:1.25-alpine \
            sh -euxc '
              apk add --no-cache gcc musl-dev

              # Builder 必须先按当前容器平台编译,不能让目标 GOOS/GOARCH 污染 go run。
              GOWORK=off GOFLAGS= \
                go build -trimpath -o /tmp/gopherink-builder ./cmd/gopherink-builder

              CGO_ENABLED=1 \
              GOOS=linux \
              GOARCH=${{ matrix.goarch }} \
              GOARM=${{ matrix.goarm }} \
              GOFLAGS= \
                /tmp/gopherink-builder \
                  -trimpath \
                  -ldflags="-linkmode external -extldflags=-static -s -w" \
                  -o "/app/dist/gopherink-linux-${{ matrix.goarch }}"
            '

      - name: Verify Linux binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-linux-${{ matrix.goarch }}"

      - name: Upload Linux artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-linux-${{ matrix.goarch }}
          archive: false
          if-no-files-found: error
          retention-days: 7

  build-darwin:
    name: macOS (${{ matrix.goarch }})
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            runner: macos-15-intel
          - goarch: arm64
            runner: macos-15

    runs-on: ${{ matrix.runner }}

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up Go
        uses: actions/setup-go@v7
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Build macOS binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p .tmp dist

          # 先构建宿主平台可执行的 builder。
          GOWORK=off GOFLAGS= \
            go build -trimpath -o .tmp/gopherink-builder ./cmd/gopherink-builder

          CGO_ENABLED=1 \
          GOOS=darwin \
          GOARCH=${{ matrix.goarch }} \
          GOFLAGS= \
            ./.tmp/gopherink-builder \
              -trimpath \
              -ldflags="-s -w" \
              -o "dist/gopherink-darwin-${{ matrix.goarch }}"

      - name: Verify macOS binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-darwin-${{ matrix.goarch }}"

      - name: Upload macOS artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-darwin-${{ matrix.goarch }}
          archive: false
          if-no-files-found: error
          retention-days: 7

  build-windows:
    name: Windows (${{ matrix.goarch }})
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            cc: x86_64-w64-mingw32-gcc
            pkg: gcc-mingw-w64-x86-64
          - goarch: 386
            cc: i686-w64-mingw32-gcc
            pkg: gcc-mingw-w64-i686

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up Go
        uses: actions/setup-go@v7
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Install MinGW
        shell: bash
        run: |
          set -euxo pipefail
          sudo apt-get update
          sudo apt-get install -y "${{ matrix.pkg }}"

      - name: Build Windows binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p .tmp dist

          # 这里不能在 GOOS=windows 的环境里执行 go run builder;
          # 否则 builder 自己会先被编译成 Windows 程序,Ubuntu 无法执行。
          GOWORK=off GOFLAGS= \
            go build -trimpath -o .tmp/gopherink-builder ./cmd/gopherink-builder

          CGO_ENABLED=1 \
          GOOS=windows \
          GOARCH=${{ matrix.goarch }} \
          CC=${{ matrix.cc }} \
          GOFLAGS= \
            ./.tmp/gopherink-builder \
              -trimpath \
              -ldflags="-linkmode external -extldflags=-static -s -w" \
              -o "dist/gopherink-windows-${{ matrix.goarch }}.exe"

      - name: Verify Windows binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-windows-${{ matrix.goarch }}.exe"

      - name: Upload Windows artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-windows-${{ matrix.goarch }}.exe
          archive: false
          if-no-files-found: error
          retention-days: 7

然后是build-release.yaml,这个负责用于构建测试:

name: Stable Build and Release

on:
  push:
    tags:
      - 'v*'
      - '!v*beta*'

permissions:
  contents: read

env:
  GO_VERSION: '1.25.x'

jobs:
  build-linux:
    name: Linux (${{ matrix.goarch }})
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            platform: linux/amd64
            goarm: ''
          - goarch: arm64
            platform: linux/arm64
            goarm: ''
          - goarch: 386
            platform: linux/386
            goarm: ''
          - goarch: arm
            platform: linux/arm/v7
            goarm: '7'

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up QEMU
        if: matrix.goarch == 'arm64' || matrix.goarch == 'arm'
        uses: docker/setup-qemu-action@v4

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Build static Linux binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p dist

          docker run --rm \
            --platform "${{ matrix.platform }}" \
            -v "${{ github.workspace }}:/app" \
            -w /app \
            golang:1.25-alpine \
            sh -euxc '
              apk add --no-cache gcc musl-dev

              # Builder 必须先按当前容器平台编译,不能让目标 GOOS/GOARCH 污染 go run。
              GOWORK=off GOFLAGS= \
                go build -trimpath -o /tmp/gopherink-builder ./cmd/gopherink-builder

              CGO_ENABLED=1 \
              GOOS=linux \
              GOARCH=${{ matrix.goarch }} \
              GOARM=${{ matrix.goarm }} \
              GOFLAGS= \
                /tmp/gopherink-builder \
                  -trimpath \
                  -ldflags="-linkmode external -extldflags=-static -s -w" \
                  -o "/app/dist/gopherink-linux-${{ matrix.goarch }}"
            '

      - name: Verify Linux binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-linux-${{ matrix.goarch }}"

      - name: Upload Linux artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-linux-${{ matrix.goarch }}
          archive: false
          if-no-files-found: error
          retention-days: 7

  build-darwin:
    name: macOS (${{ matrix.goarch }})
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            runner: macos-15-intel
          - goarch: arm64
            runner: macos-15

    runs-on: ${{ matrix.runner }}

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up Go
        uses: actions/setup-go@v7
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Build macOS binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p .tmp dist

          # 先构建宿主平台可执行的 builder。
          GOWORK=off GOFLAGS= \
            go build -trimpath -o .tmp/gopherink-builder ./cmd/gopherink-builder

          CGO_ENABLED=1 \
          GOOS=darwin \
          GOARCH=${{ matrix.goarch }} \
          GOFLAGS= \
            ./.tmp/gopherink-builder \
              -trimpath \
              -ldflags="-s -w" \
              -o "dist/gopherink-darwin-${{ matrix.goarch }}"

      - name: Verify macOS binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-darwin-${{ matrix.goarch }}"

      - name: Upload macOS artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-darwin-${{ matrix.goarch }}
          archive: false
          if-no-files-found: error
          retention-days: 7

  build-windows:
    name: Windows (${{ matrix.goarch }})
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        include:
          - goarch: amd64
            cc: x86_64-w64-mingw32-gcc
            pkg: gcc-mingw-w64-x86-64
          - goarch: 386
            cc: i686-w64-mingw32-gcc
            pkg: gcc-mingw-w64-i686

    steps:
      - name: Checkout main repository
        uses: actions/checkout@v7

      - name: Set up Go
        uses: actions/setup-go@v7
        with:
          go-version: ${{ env.GO_VERSION }}
          cache: true

      - name: Clone plugins
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p plugins

          for repo in \
            https://github.com/Chocola-X/GopherInk-ServerInfo \
            https://github.com/Chocola-X/GopherInk-CommentNotifier \
            https://github.com/Chocola-X/GopherInk-VistorLogger
          do
            name="$(basename "$repo")"
            rm -rf "plugins/$name"
            git clone --depth 1 "$repo" "plugins/$name"
          done

      - name: Install MinGW
        shell: bash
        run: |
          set -euxo pipefail
          sudo apt-get update
          sudo apt-get install -y "${{ matrix.pkg }}"

      - name: Build Windows binary
        shell: bash
        run: |
          set -euxo pipefail
          mkdir -p .tmp dist

          # 这里不能在 GOOS=windows 的环境里执行 go run builder;
          # 否则 builder 自己会先被编译成 Windows 程序,Ubuntu 无法执行。
          GOWORK=off GOFLAGS= \
            go build -trimpath -o .tmp/gopherink-builder ./cmd/gopherink-builder

          CGO_ENABLED=1 \
          GOOS=windows \
          GOARCH=${{ matrix.goarch }} \
          CC=${{ matrix.cc }} \
          GOFLAGS= \
            ./.tmp/gopherink-builder \
              -trimpath \
              -ldflags="-linkmode external -extldflags=-static -s -w" \
              -o "dist/gopherink-windows-${{ matrix.goarch }}.exe"

      - name: Verify Windows binary
        shell: bash
        run: |
          set -euxo pipefail
          file "dist/gopherink-windows-${{ matrix.goarch }}.exe"

      - name: Upload Windows artifact
        uses: actions/upload-artifact@v7
        with:
          path: dist/gopherink-windows-${{ matrix.goarch }}.exe
          archive: false
          if-no-files-found: error
          retention-days: 7

  release:
    name: Publish Release
    # 二次保险:即使触发过滤规则以后被改动,beta 仍然不会发布。
    if: github.ref_type == 'tag' && !contains(github.ref_name, 'beta')
    needs:
      - build-linux
      - build-darwin
      - build-windows
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - name: Download build outputs
        uses: actions/download-artifact@v8
        with:
          pattern: gopherink-*
          path: dist
          merge-multiple: true

      - name: Prepare release packages
        shell: bash
        run: |
          set -euxo pipefail

          mkdir -p release package

          # Linux
          for arch in amd64 arm64 386 arm; do
            src="dist/gopherink-linux-$arch"
            pkg="GopherInk-linux-$arch"

            test -f "$src"
            mkdir -p "package/$pkg"
            cp "$src" "package/$pkg/gopherink"
            chmod +x "package/$pkg/gopherink"

            tar -C package -czf "release/$pkg.tar.gz" "$pkg"
          done

          # macOS
          for arch in amd64 arm64; do
            src="dist/gopherink-darwin-$arch"
            pkg="GopherInk-darwin-$arch"

            test -f "$src"
            mkdir -p "package/$pkg"
            cp "$src" "package/$pkg/gopherink"
            chmod +x "package/$pkg/gopherink"

            tar -C package -czf "release/$pkg.tar.gz" "$pkg"
          done

          # Windows
          for arch in amd64 386; do
            src="dist/gopherink-windows-$arch.exe"
            pkg="GopherInk-windows-$arch"

            test -f "$src"
            mkdir -p "package/$pkg"
            cp "$src" "package/$pkg/gopherink.exe"

            (
              cd package
              zip -9 -r "../release/$pkg.zip" "$pkg"
            )
          done

      - name: Generate checksums
        shell: bash
        run: |
          set -euxo pipefail
          cd release
          sha256sum *.tar.gz *.zip > SHA256SUMS.txt
          cat SHA256SUMS.txt

      - name: Publish GitHub Release
        uses: softprops/action-gh-release@v3
        with:
          tag_name: ${{ github.ref_name }}
          name: ${{ github.ref_name }}
          prerelease: false
          generate_release_notes: true
          fail_on_unmatched_files: true
          files: |
            release/*.tar.gz
            release/*.zip
            release/SHA256SUMS.txt

其实两个构建的脚本是高度相似的,只不过一个只是用于构建测试而不发布release,另一个用于构建并发布release。

详细解释

这两个配置文件看起来又长又吓人,其实骨子里逻辑非常清晰。既然都踩了这么多坑,干脆给大家把里面的核心“黑魔法”掰碎了讲讲。这两个配置(Beta版和Release版)大部分步骤是重合的,核心差异就在于触发条件是否打包发布

触发器与二次保险

build-beta.yaml 中,我设定了当推送包含 v*beta* 标签时(比如 v1.0.0-beta.1),或者手动点击按钮(workflow_dispatch)时触发。这主要是为了我自己开发阶段测试编译通不通。
而在 build-release.yaml 中,触发条件变成了拦截正常的版本号(比如 v1.0.0),并且显式排除了 beta 标签!v*beta*)。不仅如此,在最后的 release 步骤里,我还加了一道锁:

if: github.ref_type == 'tag' && !contains(github.ref_name, 'beta')

这就保证了无论怎么瞎折腾,Beta版本绝对不会被当成正式版发布出去。

多平台矩阵构建

GitHub Actions 的 Matrix 功能是真的香。我们不需要为每个系统写一遍冗长的流程,直接定义一个矩阵:

  • Linux: 包含了 amd64, arm64, 386, arm 四个架构。
  • macOS: 包含原生 Apple Silicon (arm64) 和老牌 Intel (amd64)。
  • Windows: 包含 amd64386

流水线会像影分身一样,同时启动多个任务并行构建,极大缩短了编译时间。为了让 ARM 架构的编译在普通的 Ubuntu 宿主机上跑起来,我还特意引入了 docker/setup-qemu-action@v4 来提供 QEMU 模拟器支持。

插件热插拔:构建前的“偷梁换柱”

正如前文所说,为了做出一键启动的“官方满血版”,我需要在编译时把分离的插件仓库拉过来。

for repo in \
  https://github.com/Chocola-X/GopherInk-ServerInfo \
  ...
do
  name="$(basename "$repo")"
  git clone --depth 1 "$repo" "plugins/$name"
done

这段 Shell 脚本会在真正执行 go build 之前,通过 git clone --depth 1(只拉取最新提交,省时省力)把必要的插件拉取到代码树的 plugins 目录下,随后顺理成章地被 Go 编译器一起打包进最终的二进制文件里。

搞定 CGO 交叉编译的三重境界

这部分是整个 Workflow 最值钱、也是国内很多 AI 经常胡言乱语的地方。因为用到了 C 语言写的 SQLite,必须开启 CGO_ENABLED=1,而一旦开启 CGO,交叉编译就会变成一场噩梦。来看看我是怎么分系统解决的:

  • Linux 环境 (Docker + Alpine + Musl)
    我没有在 GitHub 默认的 Ubuntu 环境里硬磕,而是直接启动了一个纯净的 golang:1.25-alpine Docker 容器。在这个容器里安装 gccmusl-dev。关键点来了:
    编译时传入参数 -ldflags="-linkmode external -extldflags=-static -s -w",强制使用外部链接器并将所有 C 库全部静态打包。这样生成的二进制文件,不管是丢进 CentOS、Debian 还是各种奇葩套壳 Linux,统统能直接跑起来,彻底告别 glibc 缺失的报错。
  • macOS 环境 (原生 Runner)
    对于 Mac,省事很多。我直接使用了 GitHub 提供的 macos-15macos-15-intel 运行环境。因为是同平台编译(Mac 编 Mac),CGO 只要开启,用系统自带的 Clang 就能顺滑通过,不用搞一堆额外的交叉编译工具链。
  • Windows 环境 (Ubuntu + MinGW)
    让 Windows 环境去编译 CGO 经常会遇到各种匪夷所思的环境变量问题,所以我是在 Ubuntu 上跨平台编译 Windows 程序

绝招就是通过 apt-get install -y gcc-mingw-w64 安装 MinGW 工具链,然后在编译时指定 CC=x86_64-w64-mingw32-gcc (64位) 或 CC=i686-w64-mingw32-gcc (32位),搭配静态链接参数,完美输出 .exe 文件。

避免宿主机污染

你可能注意到了,我在所有的编译命令前,都先单独跑了一次这一段:

GOWORK=off GOFLAGS= go build -trimpath -o /tmp/gopherink-builder ./cmd/gopherink-builder

这是因为我在项目中写了一个辅助构建工具 gopherink-builder。如果直接在开启了交叉编译环境变量(比如 GOOS=windows)的上下文中用 go run 去跑它,编译器会试图把这个 builder 编译成 Windows 程序,然后在 Ubuntu 系统上执行它——那当然会当场暴毙报错!
所以我先用宿主机的默认环境(关闭各种花哨 flag)把 builder 编译出来,再把它用在后续的目标构建流程中。

6. 打包与分发 (Release 专属)

最后,当所有平台的编译物(Artifacts)汇聚在一起后,build-release.yaml 的最后一个任务开始收尾:

  • 将 Linux 和 macOS 的二进制文件赋予执行权限(chmod +x),打包成 .tar.gz
  • 将 Windows 的 .exe 打包成 .zip
  • 生成防伪用的 SHA256SUMS.txt 校验文件。
  • 最后调用 softprops/action-gh-release,直接发版到 GitHub Releases。

至此,只要我推一个 v1.x.x 的标签,喝杯咖啡的功夫,全平台的静态编译包、校验文件就已经整整齐齐地躺在发布页里了。

评论区 (0)