- 桌面应用
【免费下载链接】PureMac
Free, open-source macOS cleaner. CleanMyMac alternative with zero telemetry. Native SwiftUI, scheduled auto-cleaning, Xcode/Homebrew/system cache cleanup. MIT licensed.
本文以 PureMac 开源仓库的 scripts/SECRETS.md 为核心,系统讲解 macOS 应用自动化发布(签名 + 公证 + stapling + Homebrew 分发)所需的全部 GitHub Actions 机密(Secrets)配置:从 Developer ID 证书的过滤式.p12提取、App Store Connect API Key 的本地存储,到用ghCLI 一次性写入 CI、触发发布以及排查签名被篡改事故(issue #86)的完整实战方案。读完本文,你将掌握一套可直接复用的"签名—公证"发布管线机密配置方法论,并理解 .github/workflows/release.yml 底层如何消费这些机密。
一、为什么需要这套 Secrets:签名—公证—分发链路
PureMac 是一个免费开源的 macOS 清理工具(原生 SwiftUI,零遥测),其分发物(.dmg/.zip)必须经过Apple 开发者 ID 签名(Developer ID Application)与Apple 公证(Notarization),用户首次启动才不会触发 Gatekeeper 警告。发布自动化由 tag 推送触发,核心工作流是 .github/workflows/release.yml(共 633 行,macos-15runner,60 分钟超时),完整链路如下:
push tag vX.Y.Z → 校验版本 / tag / commit → xcodegen 生成工程 + 单元测试 → xcodebuild archive(ARCHS="arm64 x86_64" 单次产出通用二进制) → codesign 签名(Developer ID + hardened runtime) → notarytool 公证 + stapler stapling → create-dmg 打包 + DMG 公证 → 重新打包 ZIP(内含已 stapled 的 .app) → 生成 SHA-256 → 发布 GitHub Release → 更新 Homebrew cask这条链路上的每一步都需要凭据(证书、密码、API Key、SSH deploy key),它们全部以 GitHub Actions Secrets 的形式注入 CI。文档明确指出:所有 Secrets 必须在下一个 tag 推送之前配齐,否则 notarize/staple 步骤会失败并发布出损坏签名(即 issue #86 的事故根因)。
另一个关键设计是:管线使用App Store Connect API Key进行公证(现代方式,无需轮换、作用域限定在单个团队),而不是传统的APPLE_ID + app-specific-password流程。这意味着 CI 只需要一个.p8私钥文件的内容即可完成公证提交。
二、七个必需 Secrets 全景
SECRETS.md标题为 "Required secrets (7)",即 6 个签名/公证凭据加 1 个 Homebrew tap 访问凭据。原文档的完整表格如下:
| Secret | Source | Notes |
|---|---|---|
BUILD_CERTIFICATE_BASE64 | Filtered Developer ID Application.p12(cert + matching private key only), base64 | base64 -i cert.p12 \| pbcopy |
P12_PASSWORD | Password set when exporting the.p12 | Random — only you and CI need it |
KEYCHAIN_PASSWORD | Random string | Only used to lock the runner's temp keychain — never leaves CI |
APP_STORE_CONNECT_KEY_ID | The 10-char ID from the.p8filename (AuthKey_XXXXXXXXXX.p8) | e.g.5G7R52L8RK |
APP_STORE_CONNECT_ISSUER_ID | UUID from https://appstoreconnect.apple.com/access/integrations/api | e.g.5de3898a-cd31-4061-850f-ae17b389e46a |
APP_STORE_CONNECT_PRIVATE_KEY | Full contents of the.p8file (-----BEGIN PRIVATE KEY-----...-----END PRIVATE KEY-----) | Paste raw, including the BEGIN/END lines |
2.1 证书与密码三件套(签名)
BUILD_CERTIFICATE_BASE64:必须是"过滤后的" Developer ID Application.p12(只含证书 + 匹配的私钥),base64 编码后写入。为什么强调"过滤"?因为从登录钥匙串直接security export出的all.p12会混入其他身份,CI 导入时可能装错证书。第三节会给出精确的过滤提取脚本。P12_PASSWORD:导出.p12时设置的密码,随机生成即可,只有你和 CI 需要知道。KEYCHAIN_PASSWORD:随机字符串,仅用于给 runner 上的临时钥匙串上锁,永远不会离开 CI。这是 GitHub 官方签名工作流的标准做法——在临时 keychain 中导入证书、用完即删。
2.2 App Store Connect 三件套(公证)
这三个 Secrets 对应 Apple 开发者后台 API 集成页面(appstoreconnect.apple.com的 Access → Integrations → API)生成的 API Key:
APP_STORE_CONNECT_KEY_ID:.p8文件名中的 10 位 ID(AuthKey_XXXXXXXXXX.p8),文档示例5G7R52L8RK。APP_STORE_CONNECT_ISSUER_ID:Issuer UUID,文档示例5de3898a-cd31-4061-850f-ae17b389e46a。APP_STORE_CONNECT_PRIVATE_KEY:.p8文件的完整内容,必须原样粘贴,包括-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----边界行。CI 中直接以字符串写入临时文件(见 release.yml,printf '%s'写入后chmod 600)。
2.3 Homebrew tap 访问(分发)
| Secret | Source | Notes |
|---|---|---|
HOMEBREW_TAP_DEPLOY_KEY | SSH private key for a write-enabled deploy key onmomenbasel/homebrew-tap | Required by release preflight and the external tap update. The in-repo cask usesGITHUB_TOKEN. |
这条 Secrets 用于更新外部 Homebrew tap(momenbasel/homebrew-tap)中的 cask;仓库内的 cask 更新则使用 GitHub 自动注入的GITHUB_TOKEN。为什么外部 tap 不用GITHUB_TOKEN?因为GITHUB_TOKEN的作用域仅限当前仓库,无法向其他仓库推送,所以必须用一把只对该 tap 仓库有写权限的SSH deploy key——这也是"最小权限"原则的体现。
三、提取 Developer ID 证书为过滤式.p12:五步脚本详解
SECRETS.md提供了一段完整的 bash 脚本,用于从登录钥匙串中精确提取"证书 + 匹配私钥"并重新打包为干净的.p12。完整继承如下:
mkdir -p ~/Desktop/PureMac-secrets && cd ~/Desktop/PureMac-secrets # 1. Export everything from login keychain P12_PWD=$(openssl rand -base64 24) echo "$P12_PWD" > P12_PASSWORD.txt security export -k login.keychain-db -t identities -f pkcs12 -P "$P12_PWD" -o all.p12 # 2. Dump to PEM, isolate the Developer ID cert + matching private key openssl pkcs12 -in all.p12 -passin "pass:$P12_PWD" -nodes -out all.pem security find-certificate -c "Developer ID Application: Moamen Basel" -p login.keychain-db > devid.crt # 3. Use python to split private keys, then match by modulus python3 - <<'PY' import re content = open("all.pem").read() for i, k in enumerate(re.findall(r"-----BEGIN PRIVATE KEY-----.*?-----END PRIVATE KEY-----", content, re.DOTALL), 1): open(f"key_{i}.pem", "w").write(k + "\n") PY CERT_MOD=$(openssl x509 -in devid.crt -modulus -noout | shasum -a 256 | awk '{print $1}') for k in key_*.pem; do if [[ "$(openssl rsa -in "$k" -modulus -noout 2>/dev/null | shasum -a 256 | awk '{print $1}')" == "$CERT_MOD" ]]; then cp "$k" devid.key && break fi done # 4. Re-pack as a clean p12 with ONLY Developer ID + key openssl pkcs12 -export \ -in devid.crt -inkey devid.key \ -name "Developer ID Application: Moamen Basel (H3WXHVTP97)" \ -out PureMac-DeveloperID.p12 \ -passout "pass:$P12_PWD" \ -macalg sha256 -keypbe AES-256-CBC -certpbe AES-256-CBC # 5. Base64 for the GH secret + scrub intermediates base64 -i PureMac-DeveloperID.p12 -o PureMac-DeveloperID.p12.b64 rm -P all.p12 all.pem devid.key key_*.pem逐步解读各步骤的原理与实战要点:
- 导出全部身份:
security export -t identities会把登录钥匙串中所有证书+私钥对导出为all.p12,密码用openssl rand -base64 24随机生成并保存。此时导出的内容是"混合"的,必须继续过滤。 - 分离证书:
openssl pkcs12 -nodes将all.p12转成未加密 PEM(all.pem,包含多个私钥),同时用security find-certificate -c "Developer ID Application: ..."按通用名(Common Name)单独抽出目标证书devid.crt。 - 按模数匹配私钥:这是脚本的精华。
openssl rsa -modulus输出私钥的模数,openssl x509 -modulus输出证书的模数,二者经过同一 SHA-256 后相等即证明"这枚私钥就是这张证书对应的私钥"。用 Python 先把all.pem中的所有私钥切成独立文件,再逐一比对模数,找到匹配项另存为devid.key。 - 重新打包干净
.p12:openssl pkcs12 -export只装入目标证书 + 匹配私钥,同时指定了现代强算法(-macalg sha256、-keypbe AES-256-CBC、-certpbe AES-256-CBC),并写入统一名称Developer ID Application: Moamen Basel (H3WXHVTP97)。 - 生成 base64 并清理:
base64得到PureMac-DeveloperID.p12.b64(正是BUILD_CERTIFICATE_BASE64的值),最后用rm -P(安全擦除)删掉中间文件,避免敏感材料残留在本机。
注意:
rm -P是 macOS/BSD 语义的安全覆写删除,仅用于清理本地中间产物;文中证书名与 Team ID(H3WXHVTP97)均为该仓库实际使用的值。
四、本地 notarytool 的 ASC API Key 存储
CI 使用原始 Secrets 完成公证,而本地应急发布(scripts/release-local.sh)则依赖钥匙串中的 notarytool profile。SECRETS.md说明该 profile(AC_NOTARY)已经配置好,可通过xcrun notarytool history --keychain-profile AC_NOTARY确认;参考配置命令为:
xcrun notarytool store-credentials AC_NOTARY \ --key ~/.appstoreconnect/private_keys/AuthKey_5G7R52L8RK.p8 \ --key-id 5G7R52L8RK \ --issuer 5de3898a-cd31-4061-850f-ae17b389e46a这个AC_NOTARYkeychain profile 被 scripts/release-local.sh(脚本头注释 L7-L10 同样给出了这段命令)消费,用于紧急热修(emergency hotfixes)。而 CI 工作流不使用 keychain profile——runner 上不依赖任何预置钥匙串,直接吃原始 Secrets(release.yml 中notarytool submit --key --key-id --issuer)。
五、用 gh CLI 一次性设置全部 Secrets
SECRETS.md给出了完整的设置命令组。首先准备本机已有的材料:
# Fill in the 4 you control: P12_PWD=$(cat ~/Desktop/PureMac-secrets/P12_PASSWORD.txt) KC_PWD=$(cat ~/Desktop/PureMac-secrets/KEYCHAIN_PASSWORD.txt) gh secret set BUILD_CERTIFICATE_BASE64 --repo momenbasel/PureMac < ~/Desktop/PureMac-secrets/PureMac-DeveloperID.p12.b64 gh secret set P12_PASSWORD --repo momenbasel/PureMac --body "$P12_PWD" gh secret set KEYCHAIN_PASSWORD --repo momenbasel/PureMac --body "$KC_PWD" gh secret set APP_STORE_CONNECT_KEY_ID --repo momenbasel/PureMac --body "5G7R52L8RK" gh secret set APP_STORE_CONNECT_ISSUER_ID --repo momenbasel/PureMac --body "5de3898a-cd31-4061-850f-ae17b389e46a" gh secret set APP_STORE_CONNECT_PRIVATE_KEY --repo momenbasel/PureMac < ~/.appstoreconnect/private_keys/AuthKey_5G7R52L8RK.p8 # Required tap deploy key: gh secret set HOMEBREW_TAP_DEPLOY_KEY --repo momenbasel/PureMac < /path/to/tap-deploy-key # Verify: gh secret list --repo momenbasel/PureMac要点:
- 文件类 Secret(
.p12.b64、.p8、deploy key)用输入重定向<直接以文件内容写入,避免经过 shell 历史;短字符串类(密码、ID、UUID)用--body。 - 三个 App Store Connect 值是"4 个你控制的"之外的部分(第 92-93 行注释说 "Fill in the 4 you control",实际指本机已持有的 P12 密码、KC 密码以及两个文件类)。
- 最后
gh secret list用于验证 7 个 Secret 全部就位。
上传完成后立即清理本机敏感文件(文档原样给出):
rm -P ~/Desktop/PureMac-secrets/PureMac-DeveloperID.p12* \ ~/Desktop/PureMac-secrets/P12_PASSWORD.txt \ ~/Desktop/PureMac-secrets/KEYCHAIN_PASSWORD.txt六、触发发布:dry run 先行,tag 推送发布
发布流程分两阶段:先用dry run完整跑一遍"构建 + 签名 + 公证"但不做任何上传与 Homebrew 更新,绿灯后再推 tag 触发真实发布。
# Dry run first (build + sign + notarize, no upload, no homebrew bump): gh workflow run release.yml --repo momenbasel/PureMac -f version=3.0.0 -f dry_run=true gh run watch --repo momenbasel/PureMac # Real release (after dry run is green): git tag v3.0.0 git push origin v3.0.0结合 release.yml 的触发定义可以更深入理解这两个模式:
- 工作流支持
push(tagv*.*.*)与workflow_dispatch(手动输入version和dry_run)两种触发。 - 手动 dispatch 被强制为 dry-run:release.yml 第 87-89 行明确,
workflow_dispatch且dry_run != true时直接报错退出——"Manual dispatch is dry-run only. Publish by pushing the reviewed tag"。也就是说真实发布只能通过推 tag 完成,人为误触发不会污染线上发布。 - 版本号必须匹配严格 SemVer 核心格式(
^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$,不允许前导零,release.yml L59-63),且必须与 project.yml 的MARKETING_VERSION(当前为3.0.0)一致(release.yml L99-106)。 - 发布 commit 必须是
origin/main的祖先,且 tag 必须恰好指向该 commit(release.yml L68-90),发布前还会git ls-remote复核远端 tag 未被移动(L393-402)。
七、发布产物与校验
SECRETS.md的 "What ships" 表格:
| Artifact | Purpose |
|---|---|
PureMac-X.Y.Z.dmg | Direct download link in release notes (signed + notarized + stapled) |
PureMac-X.Y.Z.zip | Source for the homebrew cask (notarized + stapled.appinside) |
两份产物的 SHA-256 都会写入build/CHECKSUMS.md并同步进 GitHub Release 正文(release.yml L285-302 生成表格,L326-365 组装 release notes)。需要特别注意 ZIP 的语义:cask 下载的 zip 内部必须是已经 stapled 的.app——不是先打包再公证,而是"先公证并 staple.app,再重新打包 ZIP"(release.yml L277-283),这正是修复 #86 的关键顺序之一。
发布后还会对已发布资产做一次反查校验(validate_published_assets,release.yml L368-389):hdiutil verify验证 DMG、xcrun stapler validate验证 stapled ticket、spctl --assess通过 Gatekeeper 评估、codesign -dvv检查TeamIdentifier=H3WXHVTP97、lipo -archs确认双架构、PlistBuddy核对版本号。如果 Release 已发布(不可变),则重新下载产物校验并以其 SHA 为准(L429-445);如果还是 Draft,则直接覆盖上传并转正(L412-427)。
八、CI 如何消费这些 Secrets:源码级解读
8.1 证书导入临时钥匙串(BUILD_CERTIFICATE_BASE64 / P12_PASSWORD / KEYCHAIN_PASSWORD)
release.yml 的 "Import Developer ID signing certificate" 步骤完整展示了三个 Secret 的配合:
CERT_PATH="${RUNNER_TEMP}/certificate.p12" KEYCHAIN_PATH="${RUNNER_TEMP}/app-signing.keychain-db" echo -n "${BUILD_CERTIFICATE_BASE64}" | base64 --decode -o "${CERT_PATH}" security create-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" security set-keychain-settings -lut 21600 "${KEYCHAIN_PATH}" security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" security import "${CERT_PATH}" -P "${P12_PASSWORD}" -A -t cert -f pkcs12 -k "${KEYCHAIN_PATH}" security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "${KEYCHAIN_PASSWORD}" "${KEYCHAIN_PATH}" >/dev/null关键细节:
base64 --decode还原证书 →security create-keychain用KEYCHAIN_PASSWORD建临时钥匙串(-lut 21600设置 6 小时自动锁)→security import用P12_PASSWORD解开.p12导入 →security set-key-partition-list -S apple-tool:,apple:,codesign:授权 codesign 访问,避免 GUI 弹窗。- 导入后立刻
security find-identity -v -p codesigning断言能找到 "Developer ID Application: Moamen Basel",失败即中止。
8.2 签名参数:覆盖 project.yml 的默认配置
project.yml 的默认签名是CODE_SIGN_IDENTITY: "Apple Development"+CODE_SIGN_STYLE: Automatic(仅用于日常开发构建,Debug 配置甚至禁用签名,project.yml L46-49)。而发布时 CI 在 archive 步骤显式覆盖(release.yml L162-177,与本地脚本 release-local.sh 完全一致):
ARCHS="arm64 x86_64" \ ONLY_ACTIVE_ARCH=NO \ CODE_SIGN_STYLE=Manual \ CODE_SIGN_IDENTITY="Developer ID Application: Moamen Basel (H3WXHVTP97)" \ DEVELOPMENT_TEAM="${TEAM_ID}" \ OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime" \ archive这里有两个 #86 相关的关键点:
- 必须用 Developer ID 证书:如果沿用
project.yml默认的Apple Development证书做分发,签名链不对,Gatekeeper 会拒绝(#86 根因之一)。 --options=runtime(hardened runtime)是硬性要求:没有 hardened runtime,notary 直接拒绝(#86 根因之一)。release.yml L205 还专门grep -q "flags=0x10000(runtime)"验证。
8.3 公证与 stapling:顺序即正确性
公证步骤(release.yml L219-238):先把.app用ditto -c -k --keepParent --sequesterRsrc打成notary.zip提交xcrun notarytool submit --wait --timeout 30m,然后xcrun stapler staple把票据 stapled 进.app,再stapler validate+codesign --verify+spctl --assess。DMG 同理(L259-275)。stapling 的对象是.app与.dmg本体,而不是 zip——Gatekeeper 首次启动检查的是.app上的票据,先 stapled 再 re-zip 的顺序不容颠倒(对应 #86 根因二)。
8.4 通用二进制:单次 archive 避免签名竞态
不用"分别构建后 lipo 合并",而是 archive 时直接ARCHS="arm64 x86_64"(release.yml L171),让 codesign 在归档阶段原子性地覆盖两个 slice(对应 #86 根因五:"lipo 在签名之后运行会破坏通用二进制签名")。本地脚本同样在 archive 用ARCHS="arm64 x86_64"并随后用lipo -archs验证两个 slice 都在(release-local.sh)。
8.5 清理:不留任何凭据在 runner 上
工作流最后一步(release.yml L629-633,if: always()保证即使失败也执行):security delete-keychain删除临时钥匙串,rm -f删除.p8与 tap deploy key。这与KEYCHAIN_PASSWORD"never leaves CI" 的设计呼应——所有凭据生命周期限定在单个 job 内。
九、Homebrew 发布路径:in-repo cask 与外部 tap
SECRETS.md提到两类 Homebrew 更新,对应 release.yml 中两个独立步骤:
- In-repo cask(homebrew/puremac.rb):使用
GITHUB_TOKEN(release.yml L314-318),通过git worktree在origin/main上直接改version与sha256两行(sed 替换,L524-534),推送失败会重试最多 3 次。更新前有严格守卫:拒绝降级、拒绝同版本不同 SHA 的替换(validate_formula_state,L498-512)。 - 外部 tap(momenbasel/homebrew-tap 的
Casks/puremac.rb):使用HOMEBREW_TAP_DEPLOY_KEY(SSH deploy key,仅对该 tap 有写权限)。release.yml L547-627 中,GIT_SSH_COMMAND明确指定-i ${KEY_FILE} -o IdentitiesOnly=yes -o UserKnownHostsFile=${KNOWN_HOSTS} -o StrictHostKeyChecking=yes,并把github.com的ed25519 host key 硬编码固定(L592),杜绝 TOFU(trust-on-first-use)被中间人劫持——这是官方文档与实现里都强调的"最小权限 + 主机密钥固定"实践。
另外,官方Homebrew/homebrew-cask的入口是发布后单独通过 cask bump PR更新的(SECRETS.md明确说明),不属于本管线范围。
十、#86 根因排查与管线防护
issue #86 的错误信息是code or signature have been modified。SECRETS.md列出了旧的手工发布路径未防护、而新管线已拦截的五个根因:
- 签名后修改文件:
codesign之后再跑xcodegen会破坏签名。新管线在archive之前生成工程,导出步骤之后绝不再编辑 bundle(release.yml L126-127 先xcodegen generate,之后只有签名/公证/打包)。 - 对 zip 而非 .app 做 stapling:公证了
.app却发布了 stapling 之前打的 zip。新管线先 staple.app,再重新打包 zip——Gatekeeper 首次启动检查的是.app上的 stapled ticket,不是 zip 本身。 - 误用
Apple Development证书分发:project.yml默认是Apple Development(project.yml L20),新管线在 archive 时用Developer ID Application覆盖(release.yml L174)。 - 跳过
--options=runtime:没有 hardened runtime 公证会被拒。新管线通过OTHER_CODE_SIGN_FLAGS="--timestamp --options=runtime"传入(release.yml L176),并在验证步骤强制检查flags=0x10000(runtime)(L205)。 - 通用二进制签名竞态:
lipo在签名之后运行会破坏签名。新管线 archive 用ARCHS="arm64 x86_64"一次性构建,codesign 原子覆盖两个 slice(release.yml L171)。
十一、本地镜像脚本与应急热修
SECRETS.md明确AC_NOTARYprofile 被 scripts/release-local.sh 消费,用于紧急热修。该脚本是 CI 工作流的本地镜像("Local mirror of .github/workflows/release.yml"),发布前置校验非常严格:
- 严格 SemVer 校验、必须提交干净(无未提交/未跟踪的
PureMac project.yml PureMac.xcodeproj)、构建 commit 必须等于origin/main、本地与远端 tag 必须指向当前 commit(release-local.sh); project.yml的MARKETING_VERSION必须与传入版本一致(L71-75);- 签名后
codesign --verify --deep --strict+ 检查 hardened runtime flag +lipo -archs双 slice 验证(L117-124); - 公证用
--keychain-profile AC_NOTARY(本地钥匙串),--wait --timeout 30m,stapling 后spctl --assess(L126-158); - 末尾打印 DMG/ZIP 的 SHA-256,并强调"本脚本不创建 tag、不发布 GitHub Release、不更新 Homebrew——正式发布仍由 tag 触发的 CI 完成"(L175-176)。
配套的 scripts/release-cli-local.sh 是 CLI 工具(puremac)的本地发布脚本,展示了更严格的公证可验证性:提交 notary 后不仅要求status == "Accepted",还拉取notarytool log,逐一核对归档 SHA-256 与 arm64/x86_64 两个 slice 的CDHash都出现在ticketContents中(L44-56),并生成release-verification.json留存。脚本末尾的注释也说明了一个 CLI 特有事实:签名的命令行可执行文件无法携带 stapled ticket,Apple 的接受记录以 notarization.json 为准(L74)。
十二、安全与运维最佳实践
综合SECRETS.md与工作流实现,可归纳出这套发布机密体系的几条硬性原则:
- 最小权限:外部 tap 用仅限该仓库的 SSH deploy key 而非全量 PAT;证书
.p12只含目标证书 + 匹配私钥,不夹带其他身份。 - 凭据生命周期:CI 中所有凭据(证书、
.p8、deploy key)限定在单 job 临时目录,if: always()清理;KEYCHAIN_PASSWORD只用于锁临时钥匙串,永不复用为其他密码。 - 信任根固定:GitHub 的 ed25519 host key 硬编码进
KNOWN_HOSTS,StrictHostKeyChecking=yes,避免首次连接时信任任意主机。 - 本机不留痕:上传完成后用
rm -P安全擦除.p12、密码文件与中间产物;.p8私钥仅存放在~/.appstoreconnect/private_keys/供本地公证使用。 - 先干跑后发布:任何版本变更都先
dry_run=true完整验证构建+签名+公证,再推 tag;真实发布只认 tag,且 tag/commit/版本号三重绑定(version ↔project.yml↔ tag SHA ↔origin/main)。
关联文件速查
- 机密说明文档:scripts/SECRETS.md
- 发布工作流(Secrets 消费方):.github/workflows/release.yml
- 本地镜像发布脚本(App 版):scripts/release-local.sh
- 本地镜像发布脚本(CLI 版):scripts/release-cli-local.sh
- 工程配置(
MARKETING_VERSION/ 默认签名):project.yml - Homebrew cask(in-repo):homebrew/puremac.rb 与 homebrew/puremac-cli.rb
- CLI 版本号来源:cli/Sources/puremac/PureMac.swift
- 桌面应用
【免费下载链接】PureMac
Free, open-source macOS cleaner. CleanMyMac alternative with zero telemetry. Native SwiftUI, scheduled auto-cleaning, Xcode/Homebrew/system cache cleanup. MIT licensed.
相关推荐
Realm Swift SDK XCFramework 签名指南:证书更新与 GitHub Actions Secrets 配置实战
Realm Swift SDK XCFramework 签名指南:证书更新与 GitHub Actions Secrets 配置实战 本篇指南围绕 contri
数据库移动开发嵌入式数据库Clawd on Desk macOS 发布签名与公证指南:Developer ID 证书、Team API Key 与 GitHub Actions 配置
Clawd on Desk macOS 发布签名与公证指南:Developer ID 证书、Team API Key 与 GitHub Actions 配置 C
桌面应用交互助手espanso 发布流程实战指南:基于 GitHub Actions 的跨平台 Release 制作与 macOS 签名公证
espanso 发布流程实战指南:基于 GitHub Actions 的跨平台 Release 制作与 macOS 签名公证 本篇技术指南聚焦 espanso
桌面应用CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考