Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Build App

on:
push:
branches: [ "main" ]
paths:
- ".github/workflows/build.yml"
- "Ice.xcodeproj/**"
- "Ice/**"
pull_request:
paths:
- ".github/workflows/build.yml"
- "Ice.xcodeproj/**"
- "Ice/**"
Comment on lines +9 to +14

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The paths filter doesn’t include Resources/**, but the Xcode project references files under the top-level Resources/ directory (e.g. Resources/Acknowledgements.rtf). As a result, changes to those resources won’t trigger a CI build on push/PR; consider adding Resources/** (and any other build-relevant paths) to the trigger filters.

Suggested change
- "Ice/**"
pull_request:
paths:
- ".github/workflows/build.yml"
- "Ice.xcodeproj/**"
- "Ice/**"
- "Ice/**"
- "Resources/**"
pull_request:
paths:
- ".github/workflows/build.yml"
- "Ice.xcodeproj/**"
- "Ice/**"
- "Resources/**"

Copilot uses AI. Check for mistakes.
workflow_dispatch:

concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
if: '!github.event.pull_request.merged'
runs-on: macos-latest

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runs-on: macos-latest can introduce sudden CI breakages when GitHub upgrades the default macOS/Xcode image. For more predictable builds, pin to a specific runner label (e.g. macos-14) and update intentionally when needed.

Suggested change
runs-on: macos-latest
runs-on: macos-14

Copilot uses AI. Check for mistakes.
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
SIGNING_IDENTITY: ${{ secrets.SIGNING_IDENTITY }}
steps:
Comment on lines +25 to +29

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All signing secrets are exported at the job level, which makes them available to every step and increases the chance of accidental exposure in logs (e.g., via debug output or third-party actions). Prefer scoping secrets to only the steps that need them (import/sign), and reference ${{ secrets.* }} directly in those steps instead of job-wide env.

Copilot uses AI. Check for mistakes.
- uses: actions/checkout@v6

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow uses actions/checkout@v6, but the repo’s existing workflow uses actions/checkout@v3 (see .github/workflows/lint.yml:19). To reduce maintenance and avoid unexpected differences between workflows, standardize on a single major version across workflows (either bump both, or keep this aligned with the existing one).

Suggested change
- uses: actions/checkout@v6
- uses: actions/checkout@v3

Copilot uses AI. Check for mistakes.

- name: Import code-signing certificate
if: ${{ env.BUILD_CERTIFICATE_BASE64 != '' && env.P12_PASSWORD != '' }}
uses: apple-actions/import-codesign-certs@v6
with:
p12-file-base64: ${{ env.BUILD_CERTIFICATE_BASE64 }}
p12-password: ${{ env.P12_PASSWORD }}

- name: Cache Swift Package Manager dependencies
uses: actions/cache@v5
with:
path: .build/SourcePackages
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cache key is based on hashFiles('**/Package.resolved'), but there is no Package.resolved committed in this repo, so the hash will be empty and the cache key will effectively be constant. This can cause cache collisions and stale dependency restores; consider keying off a file that actually changes with dependencies (e.g. the Xcode project’s project.pbxproj or a committed SwiftPM resolved file).

Suggested change
key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}
key: ${{ runner.os }}-spm-${{ hashFiles('Ice.xcodeproj/project.pbxproj') }}

Copilot uses AI. Check for mistakes.
restore-keys: |
${{ runner.os }}-spm-

- name: Build Ice
run: |
xcodebuild \
-project Ice.xcodeproj \
-scheme Ice \
-configuration Release \
-destination 'platform=macOS' \
-derivedDataPath .build/DerivedData \
-clonedSourcePackagesDirPath .build/SourcePackages \
CODE_SIGNING_ALLOWED=NO \
build

- name: Sign and verify app
if: ${{ env.BUILD_CERTIFICATE_BASE64 != '' && env.P12_PASSWORD != '' && env.SIGNING_IDENTITY != '' }}
run: |
APP_PATH=".build/DerivedData/Build/Products/Release/Ice.app"
if [ ! -d "$APP_PATH" ]; then
echo "Ice.app not found at $APP_PATH"
exit 1
fi

codesign \
--force \
--deep \
--options runtime \
--timestamp \
--entitlements Ice/Ice.entitlements \
--sign "$SIGNING_IDENTITY" \
"$APP_PATH"

codesign --verify --deep --strict --verbose=2 "$APP_PATH"

- name: Package app
id: package
run: |
APP_PATH=".build/DerivedData/Build/Products/Release/Ice.app"
if [ ! -d "$APP_PATH" ]; then
echo "Ice.app not found at $APP_PATH"
exit 1
fi

ARCHIVE_NAME="Ice-build-${GITHUB_SHA::7}.zip"
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ARCHIVE_NAME"
echo "archive_name=$ARCHIVE_NAME" >> "$GITHUB_OUTPUT"

- name: Upload build artifact
uses: actions/upload-artifact@v7
with:
name: Ice-build-${{ github.sha }}
path: ${{ steps.package.outputs.archive_name }}
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ Ice 是一款强大的菜单栏管理工具。它的核心能力是隐藏与显

English README → https://github.com/jordanbaird/Ice#readme

CI 配置说明 → [ci.md](ci.md)

如果你不想在本地编译,可以直接使用 GitHub Actions 的 CI 构建产物(见上方 `ci.md` 说明)。

## 中文说明

Ice 是一款强大的菜单栏管理工具。它的核心能力是隐藏与显示菜单栏项目,同时也提供丰富的外观与交互选项,目标是成为最灵活的菜单栏工具之一。
Expand Down
90 changes: 90 additions & 0 deletions ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# BUILD CI 说明

1. 编译
2. 签名
3. 上传构建产物

如果你不想在本地编译,可以直接使用 CI 生成的 Artifact(zip)。

## 当前工作流

工作流文件:`.github/workflows/build.yml`

触发条件:

1. push 到 `main`,且改动命中 `Ice/**`、`Ice.xcodeproj/**` 或 workflow 本身
2. pull_request,且改动命中同样路径
3. 网页触发

执行步骤:

1. checkout 代码
2. 恢复 SwiftPM 缓存
3. `xcodebuild` 编译(`CODE_SIGNING_ALLOWED=NO`)
4. 如果配置了签名 secrets,则执行签名与验签
5. 打包 `Ice.app` 为 zip
6. 上传 Artifact

## Secrets 配置

如果你只想自动编译,不需要配置任何 secrets。

如果你希望 CI 同时完成签名,需要配置以下 3 个仓库 secrets:

1. `BUILD_CERTIFICATE_BASE64`
2. `P12_PASSWORD`
3. `SIGNING_IDENTITY`

### 1) 生成 SIGNING_IDENTITY

在 macOS 终端执行:

```bash
security find-identity -v -p codesigning
```

复制目标身份的完整文本(例如 `Apple Development: ... (...)` 或 `Developer ID Application: ... (...)`)。

### 2) 生成 p12 并得到 BUILD_CERTIFICATE_BASE64

建议用 Keychain Access 导出证书(必须包含私钥)为 `.p12`,设置导出密码。

然后在终端执行:

```bash
base64 -i signing_certificate.p12 | tr -d '\n'
```

输出的一整行就是 `BUILD_CERTIFICATE_BASE64`。

导出 p12 时设置的密码就是 `P12_PASSWORD`。

### 3) 在 GitHub 中添加 secrets

仓库页面:

`Settings -> Secrets and variables -> Actions -> New repository secret`

按名称逐项创建:

1. `BUILD_CERTIFICATE_BASE64`
2. `P12_PASSWORD`
3. `SIGNING_IDENTITY`

## 生成证书

如果需要自己签名:

1. Xcode
- 安装 Xcode,登录 Apple ID
- 让 Xcode 自动生成/下载证书
- 优点是最省事,缺点是 Xcode 体积较大

2. 手动生成私钥与证书
- 本地生成私钥与 CSR
- 到 Apple 开发者后台申请证书并下载
- 导入钥匙串后再导出 p12 给 CI 使用

3. Xcode Command Line Tools
- 可安装体积更小的命令行工具用于基础开发命令
- 但“证书自动管理能力”不如完整 Xcode 方便
Loading