Appearance
完整 Workflow 示例
基础版本
适用于单平台构建的项目:
yaml
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
attestations: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run build
- name: Attest Build Provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/*'
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true多平台构建版本
适用于需要在多个操作系统上构建的项目:
重要:多平台构建的正确方式
使用 matrix 策略构建多个平台时,不要在每个构建 job 中都创建 Release。这会导致多个 job 同时尝试创建同一个 Release,产生冲突。
正确做法:
- 每个构建 job 使用
upload-artifact上传构建产物 - 创建一个单独的
releasejob,使用needs: build等待所有构建完成 - 在
releasejob 中下载所有 artifacts 并统一创建 Release
yaml
name: Multi-Platform Release
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: app-linux
- os: windows-latest
artifact_name: app-windows.exe
- os: macos-latest
artifact_name: app-macos
runs-on: ${{ matrix.os }}
permissions:
id-token: write
contents: write
attestations: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: npm ci
- name: Build
run: npm run build
- name: Attest Build Provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/${{ matrix.artifact_name }}'
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: dist/${{ matrix.artifact_name }}
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: artifacts/**/*
generate_release_notes: trueGo 项目示例
yaml
name: Release Go Application
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
include:
- goos: linux
goarch: amd64
- goos: windows
goarch: amd64
- goos: darwin
goarch: amd64
- goos: darwin
goarch: arm64
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
attestations: write
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Build
env:
GOOS: ${{ matrix.goos }}
GOARCH: ${{ matrix.goarch }}
run: |
output="myapp-${{ matrix.goos }}-${{ matrix.goarch }}"
if [ "${{ matrix.goos }}" = "windows" ]; then
output="${output}.exe"
fi
go build -o "dist/${output}" .
- name: Attest
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/*'
- name: Upload
uses: actions/upload-artifact@v4
with:
name: myapp-${{ matrix.goos }}-${{ matrix.goarch }}
path: dist/*
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- uses: softprops/action-gh-release@v2
with:
files: dist/*Python 项目示例
yaml
name: Release Python Package
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: write
attestations: write
steps:
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Build Tools
run: pip install build
- name: Build Package
run: python -m build
- name: Attest
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/*'
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/*Swift 项目示例 - SPM CLI 工具
关于 macOS 签名与公证
本模板面向没有 Apple 开发者账号的开发者,不做 codesign / 公证。信任建立在三点上:
swift build --arch arm64 --arch x86_64会自动为产物生成 ad-hoc 签名,满足 Apple Silicon 所需的「可执行文件必须签名」硬性要求,无需任何证书attest-build-provenance由 GitHub + sigstore 背书,下载者可独立验证产物来源- 下载者首次运行需手动解除 Gatekeeper 隔离(命令见下方「下载者使用说明」)
适用对象:Swift Package Manager 管理的可执行文件(CLI 工具),下游使用者为开发者,交付物为单个 Mach-O 二进制。macOS 应用(.app bundle / DMG 分发)请看下一节「Swift 项目示例 - macOS 应用」。iOS App / XCFramework 分发涉及代码签名与公证,不在本模板覆盖范围。
yaml
name: Release Swift Package
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: macos-latest
permissions:
id-token: write
contents: write
attestations: write
steps:
- uses: actions/checkout@v4
# macOS runner 已预装 Swift 与 Xcode;如需锁定 Xcode 版本,取消下面注释:
# - uses: maxim-lobanov/setup-xcode@v1
# with:
# xcode-version: latest-stable
- name: Build Universal Binary
run: swift build -c release --arch arm64 --arch x86_64 --product myapp
- name: Package
run: |
mkdir -p dist
tar -czf dist/myapp-macos-universal.tar.gz \
-C .build/apple/Products/Release myapp
- name: Attest Build Provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/*'
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true下载者使用说明
| 步骤 | 命令 |
|---|---|
| 验证构件来源 | gh attestation verify myapp-macos-universal.tar.gz --owner <your-org> |
| 解压 | tar -xzf myapp-macos-universal.tar.gz |
| 解除 Gatekeeper 隔离(首次运行) | xattr -d com.apple.quarantine myapp |
| 运行 | ./myapp |
Swift 项目示例 - macOS 应用
关于无开发者账号的 macOS App 分发
本模板面向没有 Apple 开发者账号的开发者发布 macOS 应用。信任链建立在:
- ad-hoc 签名(
CODE_SIGN_IDENTITY=-)——Apple Silicon 硬性要求可执行文件必须签名,ad-hoc 是免证书的官方替代方案。xcodebuild archive完成后再显式codesign --force --deep --sign -一次,确保 bundle 内所有嵌入 framework / helper 一并签上。 xattr -cr剥除构建过程附带的扩展属性,避免 DMG 内文件带 quarantine 导致用户首次打开被 Gatekeeper 直接拒。attest-build-provenance对最终 DMG 签名,下载者可用gh attestation verify独立校验。- 下载者首次挂载 DMG 后仍需手动解除 Gatekeeper 隔离(命令见下方「下载者使用说明」)。
适用对象:用 Xcode 工程(.xcodeproj)构建的 macOS 应用,交付物为 .app bundle 封装进 DMG。不做公证 (notarize)、不走 MAS。
AI 填充规则
本模板含 4 个占位符,按以下规则填写:
<PROJECT>.xcodeproj:在项目根及子目录(maxdepth 3)查找*.xcodeproj。若 唯一 命中Foo.xcodeproj,填Foo.xcodeproj;若命中多个或位于*.xcworkspace内,保留<PROJECT>.xcodeproj占位符并提示用户补值。<SCHEME>:默认取<PROJECT>同名(绝大多数项目约定如此)。若xcodebuild -list显示 scheme 名与 project 名不一致,保留<SCHEME>占位符并提示用户补值。<APP_NAME>:.appbundle 的名字,通常等于<SCHEME>。若 Info.plist 的CFBundleName与 scheme 不同以 CFBundleName 为准。<VOLUME_NAME>:DMG 挂载后的卷名,建议等于<APP_NAME>。
填完后 Archive 步骤、codesign 步骤、DMG 步骤中的 .app 路径都会自动一致。
yaml
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to build (e.g. v1.0.0)'
required: true
default: 'v1.0.0'
permissions:
contents: write
id-token: write
attestations: write
jobs:
build:
name: Build & Release
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# 锁定 Xcode 版本避免 macos-latest 镜像漂移;如需特定版本改用
# maxim-lobanov/setup-xcode@v1 with xcode-version: '15.4'
- name: Select Xcode
run: |
sudo xcode-select -s /Applications/Xcode.app
xcodebuild -version
- name: Resolve version
id: version
run: |
TAG="${{ inputs.tag }}"
if [ -z "$TAG" ]; then TAG="$GITHUB_REF_NAME"; fi
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
# ad-hoc 签名 (CODE_SIGN_IDENTITY=-) 是免证书发布的关键;
# 不能改成 CODE_SIGNING_ALLOWED=NO,那样 archive 会跳过签名,
# 产物在 Apple Silicon 上打不开
- name: Archive (ad-hoc signed)
run: |
mkdir -p build
xcodebuild \
-project <PROJECT>.xcodeproj \
-scheme <SCHEME> \
-configuration Release \
-destination 'generic/platform=macOS' \
-archivePath build/<APP_NAME>.xcarchive \
CODE_SIGN_IDENTITY=- \
CODE_SIGN_STYLE=Manual \
CODE_SIGNING_REQUIRED=YES \
CODE_SIGNING_ALLOWED=YES \
DEVELOPMENT_TEAM= \
archive
- name: Verify signature
run: codesign -dv build/<APP_NAME>.xcarchive/Products/Applications/<APP_NAME>.app 2>&1 || true
# 剥除构建过程带进来的扩展属性 (xattr),再整包重签 ad-hoc
# 否则 DMG 内的 .app 会被 Gatekeeper 因 com.apple.quarantine 拒绝
- name: Strip extended attributes and re-sign ad-hoc
run: |
APP=build/<APP_NAME>.xcarchive/Products/Applications/<APP_NAME>.app
xattr -cr "$APP"
codesign --force --deep --sign - --options runtime "$APP"
codesign -dv "$APP" 2>&1 | head -5
xattr -lr "$APP" || true
- name: Package DMG
env:
VERSION: ${{ steps.version.outputs.version }}
run: |
STAGE=build/dmg-stage
mkdir -p "$STAGE"
cp -R build/<APP_NAME>.xcarchive/Products/Applications/<APP_NAME>.app "$STAGE/"
xattr -cr "$STAGE/<APP_NAME>.app"
# 在 DMG 内加 Applications 软链,用户挂载后直接拖拽即可安装
ln -s /Applications "$STAGE/Applications"
hdiutil create \
-volname "<VOLUME_NAME>" \
-srcfolder "$STAGE" \
-ov -format UDZO \
"build/<APP_NAME>-${VERSION}.dmg"
xattr -cr "build/<APP_NAME>-${VERSION}.dmg"
ls -la "build/<APP_NAME>-${VERSION}.dmg"
- name: Attest build provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: build/<APP_NAME>-*.dmg
# 使用 gh CLI 而非 softprops/action-gh-release,方便实现
# "首次 create、后续 upload --clobber" 的幂等发布
- name: Create or update GitHub Release
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
NOTES_FILE=release_notes.md
if [ ! -f "$NOTES_FILE" ]; then
echo "# <APP_NAME> ${VERSION}" > "$NOTES_FILE"
echo "" >> "$NOTES_FILE"
echo "Auto-generated release from tag ${TAG}." >> "$NOTES_FILE"
fi
if gh release view "$TAG" >/dev/null 2>&1; then
gh release upload "$TAG" "build/<APP_NAME>-${VERSION}.dmg" --clobber
else
gh release create "$TAG" \
--title "<APP_NAME> ${VERSION}" \
--notes-file "$NOTES_FILE" \
"build/<APP_NAME>-${VERSION}.dmg"
fi下载者使用说明
DMG 分发的 macOS 应用安装步骤:
| 步骤 | 命令 / 操作 |
|---|---|
| 验证构件来源 | gh attestation verify <APP_NAME>-<VERSION>.dmg --owner <your-org> |
| 挂载 DMG | 双击 DMG 文件,Finder 自动挂载 |
| 安装 | 把 <APP_NAME>.app 拖拽到同窗口内的 Applications 软链 |
| 首次启动被 Gatekeeper 拦截 | 在 系统设置 → 隐私与安全性 点击"仍要打开";或终端执行 xattr -cr /Applications/<APP_NAME>.app |
| 运行 | Launchpad / Spotlight 打开 <APP_NAME> |
本地验证 workflow 语法
推送 tag 前可本地预跑:
bash
# 方式 1:检查 yml 语法(需先 brew install actionlint)
actionlint .github/workflows/release.yml
# 方式 2:用 act 本地模拟执行(需先 brew install act,且本机需 Docker)
# 注意:macOS 任务无法在 act 中真实跑,只能到 xcode-select 步骤前
act push -W .github/workflows/release.yml --container-architecture linux/amd64
# 方式 3:push 一个 test tag 到远程后立即删,触发远程构建验证
git tag v0.0.0-test && git push origin v0.0.0-test
# 查看运行日志后删除
git tag -d v0.0.0-test && git push origin :refs/tags/v0.0.0-testCodeQL workflow(Swift - Xcode 工程)
本节模板与上面「Swift 项目示例」职责完全不同:上面那份 release workflow 由 tag 触发,用 swift build 打包 CLI 上线;这里的 CodeQL workflow 由 push / pull_request / schedule 触发,用 xcodebuild 完成 CodeQL 扫描所需的编译追踪(trace),产物本身不会被发布。
为什么 Xcode 16+ 工程不能继续走 GitHub Default CodeQL:Default 模式背后是 codeql-action/autobuild,它通过解析 .xcodeproj 的 PBXSourcesBuildPhase.files 定位 Swift 源。Xcode 16 起新建工程默认使用 PBXFileSystemSynchronizedRootGroup 由目录同步推导源文件,PBXSourcesBuildPhase.files 为空数组,autobuild 直接报 no-swift-target: All targets contain no Swift source files 失败。落地此 workflow 后,必须在 GitHub 仓库 Settings → Code security → Code scanning 中把 CodeQL 切换到 Advanced 模式,让 GitHub 承认本文件;Default 与 Advanced 二选一,不能共存。
AI 填充规则
按以下规则填写 <PROJECT> 与 <SCHEME> 占位符:
- 在项目根及子目录(
maxdepth 3)查找*.xcodeproj:若 唯一 命中Foo.xcodeproj,则-project Foo.xcodeproj -scheme Foo(同名启发式)。 - 若命中 多个
*.xcodeproj,或项目位于*.xcworkspace内,保留<PROJECT>.xcodeproj/<SCHEME>占位符不要自作主张,并在生成结果旁提示用户根据xcodebuild -list输出补值。
yaml
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
jobs:
analyze:
name: Analyze (Swift)
runs-on: macos-latest
timeout-minutes: 30
permissions:
security-events: write
contents: read
actions: read
packages: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: swift
build-mode: manual
# macOS runner 已预装 Xcode;如需锁定版本,取消下面注释:
# - uses: maxim-lobanov/setup-xcode@v1
# with:
# xcode-version: latest-stable
- name: Build with xcodebuild
run: |
xcodebuild build \
-project <PROJECT>.xcodeproj \
-scheme <SCHEME> \
-configuration Debug \
CODE_SIGNING_ALLOWED=NO \
CODE_SIGNING_REQUIRED=NO \
CODE_SIGN_IDENTITY=""
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:swift"本地 / 远程预跑
提交前可以用 act 在本地空跑一次 workflow 语法(不会真正执行 CodeQL 上传),或者推送到一个独立分支后用 gh CLI 远程触发:
bash
# 本地预跑(需要 Docker;CodeQL 步骤会被 mock 但能验证 yml 语法和 xcodebuild 参数)
act -W .github/workflows/codeql.yml -j analyze --container-architecture linux/amd64 --dryrun
# 远程触发(推送到分支后)
gh workflow run codeql.yml --ref <your-branch>
gh run watchCodeQL workflow(Swift - SPM)
适用于仅有 Package.swift、没有 .xcodeproj 的纯 Swift Package Manager 项目。SPM 的 Default CodeQL 多数情况下能跑通,但 CodeQL swift extractor 在 2.25.x 对 SPM 的 Package.resolved / 依赖解析仍偶发问题;为统一判定规则、保证扫描结果的确定性,含 .swift 的 SPM 项目同样走 Advanced + 自带 workflow。
与上面 Xcode 工程模板的差异:构建命令换成 swift build,不需要任何 CODE_SIGN* 环境变量(SPM 产物默认 ad-hoc 签名,CodeQL trace 不要求真实证书);其余触发条件、runner、权限、timeout 完全一致。
AI 填充规则
按以下规则决定 swift build 是否带 --product:
- 若
Package.swift定义 单个 executable productmycli:swift build --product mycli。 - 若
Package.swift仅 定义 library products(无 executable):swift build,不要 带--product。 - 若
Package.swift定义 多个 executable products:取Package.swift中第一个出现的 executable product 名作为--product的值。
yaml
name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 6 * * 1'
jobs:
analyze:
name: Analyze (Swift)
runs-on: macos-latest
timeout-minutes: 30
permissions:
security-events: write
contents: read
actions: read
packages: read
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: swift
build-mode: manual
- name: Build with Swift Package Manager
# 含 executable 时:swift build -c debug --product <PRODUCT>
# 仅 library 时: swift build -c debug (删去 --product 段)
run: swift build -c debug --product <PRODUCT>
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "/language:swift"本地 / 远程预跑
bash
# 本地校验 yml 语法
act -W .github/workflows/codeql.yml -j analyze --container-architecture linux/amd64 --dryrun
# 推到分支后远程触发并实时观看日志
gh workflow run codeql.yml --ref <your-branch>
gh run watchPyInstaller 多平台可执行文件
使用 PyInstaller 构建跨平台桌面应用时,需要注意各平台的打包差异:
macOS 注意事项
- macOS GUI 应用必须使用
--onedir生成.app包 - 使用
--onefile只会生成裸二进制文件,下载后可能无法直接运行 - 建议将产物打包为
.zip以保留文件权限和目录结构
yaml
name: Build and Release
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
include:
- os: ubuntu-latest
artifact_name: myapp-linux.zip
- os: windows-latest
artifact_name: myapp-windows.exe
- os: macos-latest
artifact_name: myapp-macos.zip
runs-on: ${{ matrix.os }}
permissions:
id-token: write
contents: write
attestations: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Dependencies
run: pip install pyinstaller
# Linux: 单文件可执行,打包为 zip 保留权限
- name: Build Executable (Linux)
if: runner.os == 'Linux'
run: |
pyinstaller --onefile --name "myapp-linux" app.py
mkdir -p dist_final
chmod +x dist/myapp-linux
cd dist && zip ../dist_final/myapp-linux.zip myapp-linux
# macOS: 必须用 --onedir 生成 .app 包,再打包为 zip
- name: Build Executable (macOS)
if: runner.os == 'macOS'
run: |
pyinstaller --onedir --windowed --name "myapp" app.py
mkdir -p dist_final
cd dist && zip -r ../dist_final/myapp-macos.zip myapp.app
# Windows: 直接生成 .exe
- name: Build Executable (Windows)
if: runner.os == 'Windows'
run: |
pyinstaller --onefile --windowed --name "myapp-windows" app.py
mkdir dist_final
copy dist\myapp-windows.exe dist_final\
- name: Attest Build Provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist_final/*'
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact_name }}
path: dist_final/*
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: artifacts/*
generate_release_notes: true用户使用说明
| 平台 | 下载文件 | 使用方式 |
|---|---|---|
| macOS | myapp-macos.zip | 解压后双击 myapp.app |
| Linux | myapp-linux.zip | 解压后运行 ./myapp-linux |
| Windows | myapp-windows.exe | 直接双击运行 |
Electron 应用示例
yaml
name: Release Electron App
on:
push:
tags:
- 'v*'
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
permissions:
id-token: write
contents: write
attestations: write
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Dependencies
run: npm ci
- name: Build Electron App
run: npm run electron:build
- name: Attest
uses: actions/attest-build-provenance@v2
with:
subject-path: |
dist/*.exe
dist/*.dmg
dist/*.AppImage
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: electron-${{ matrix.os }}
path: |
dist/*.exe
dist/*.dmg
dist/*.AppImage
release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- uses: softprops/action-gh-release@v2
with:
files: dist/*使用说明
- 选择适合你项目的模板
- 复制到
.github/workflows/release.yml - 根据项目需要修改构建步骤
- 推送代码并创建 tag 触发构建