chore: release GoodBuddy 0.11.0
Deploy website to GitHub Pages / Deploy static website (push) Waiting to run
Cross-platform packages / macos arm64 (push) Blocked by required conditions
Cross-platform packages / windows arm64 (push) Blocked by required conditions
Cross-platform packages / linux x64 (push) Blocked by required conditions
Cross-platform packages / macos x64 (push) Blocked by required conditions
Cross-platform packages / windows x64 (push) Blocked by required conditions
Cross-platform packages / Publish GitHub and OSS release (push) Blocked by required conditions
Cross-platform packages / Validate source (push) Waiting to run
Cross-platform packages / linux arm64 (push) Blocked by required conditions

Prepare the approved bilingual 0.11.0 release metadata and synchronize package versions. macOS packaging now signs and notarizes when all Apple credentials are available, emits unsigned packages when none are configured, and rejects partial credential sets so the six-platform release can proceed without silently misrepresenting signing status.
This commit is contained in:
mesalogo
2026-08-20 13:20:50 +08:00
parent d7cb7ba181
commit 104ac2aec1
9 changed files with 272 additions and 27 deletions
+51 -8
View File
@@ -115,7 +115,8 @@ jobs:
name: goodbuddy-production-bundle
path: out
- name: Prepare macOS signing credentials
- name: Resolve macOS signing mode
id: macos-signing
if: matrix.platform == 'macos'
shell: bash
env:
@@ -126,17 +127,51 @@ jobs:
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
run: |
set -euo pipefail
test -n "$MACOS_CERTIFICATE_BASE64"
test -n "$MACOS_CERTIFICATE_PASSWORD"
test -n "$APPLE_API_KEY_BASE64"
test -n "$APPLE_API_KEY_ID"
test -n "$APPLE_API_ISSUER"
names=(
MACOS_CERTIFICATE_BASE64
MACOS_CERTIFICATE_PASSWORD
APPLE_API_KEY_BASE64
APPLE_API_KEY_ID
APPLE_API_ISSUER
)
configured=0
missing=()
for name in "${names[@]}"; do
if [[ -n "${!name}" ]]; then
configured=$((configured + 1))
else
missing+=("$name")
fi
done
if [[ "$configured" -eq 0 ]]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
echo "::warning title=Unsigned macOS packages::Apple signing credentials are not configured. The macOS DMG and ZIP will be unsigned and unnotarized."
{
echo "### macOS signing"
echo
echo "Apple signing credentials are not configured. This target produces unsigned and unnotarized packages that Gatekeeper may block on first launch."
} >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
if [[ "$configured" -ne "${#names[@]}" ]]; then
missing_names="$(IFS=,; echo "${missing[*]}")"
echo "::error title=Incomplete macOS signing credentials::Missing: $missing_names"
exit 1
fi
echo "enabled=true" >> "$GITHUB_OUTPUT"
printf '%s' "$MACOS_CERTIFICATE_BASE64" | base64 -D > "$RUNNER_TEMP/goodbuddy-developer-id.p12"
printf '%s' "$APPLE_API_KEY_BASE64" | base64 -D > "$RUNNER_TEMP/AuthKey.p8"
test -s "$RUNNER_TEMP/goodbuddy-developer-id.p12"
test -s "$RUNNER_TEMP/AuthKey.p8"
chmod 600 "$RUNNER_TEMP/goodbuddy-developer-id.p12" "$RUNNER_TEMP/AuthKey.p8"
{
echo "### macOS signing"
echo
echo "Complete Apple signing credentials were detected. This target will be signed, notarized, and verified."
} >> "$GITHUB_STEP_SUMMARY"
- name: Build, sign and notarize macOS release packages
if: matrix.platform == 'macos'
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'true'
run: npm run release:package -- --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --skip-build
env:
ELECTRON_CACHE: ${{ runner.temp }}/electron
@@ -148,8 +183,16 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
- name: Build unsigned macOS release packages
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'false'
run: npm run release:package -- --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --skip-build --unsigned
env:
ELECTRON_CACHE: ${{ runner.temp }}/electron
ELECTRON_BUILDER_CACHE: ${{ runner.temp }}/electron-builder
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
- name: Verify macOS signature and notarization ticket
if: matrix.platform == 'macos'
if: matrix.platform == 'macos' && steps.macos-signing.outputs.enabled == 'true'
shell: bash
run: |
set -euo pipefail
+12 -5
View File
@@ -150,7 +150,11 @@ Release note: 修复左上角项目设置与消息通道项目设置不一致的
run validation and build the production bundle without running the native
package matrix. Manual builds upload 30-day GitHub Actions artifacts.
Version-tag builds verify and aggregate packages before publishing GitHub
Release assets. Signing and macOS notarization are not configured.
Release assets. The macOS jobs sign, notarize, and verify packages when all
five Apple credentials are configured. With no Apple credentials they must
use the explicit `--unsigned` path, warn that Gatekeeper may block the
packages, and still complete; a partial credential set must fail rather than
silently downgrade.
- Keep `ELECTRON_CACHE` and `ELECTRON_BUILDER_CACHE` under
`${{ runner.temp }}` in step-level workflow contexts. A cache beneath the
repository inherits the root `"type": "module"` and breaks electron-builder's
@@ -286,10 +290,13 @@ GitHub Release:
Do not report a release complete until all of the following are verified:
1. The tag workflow and all six native package jobs succeeded. In the final
release job, explicitly verify the OSS configuration, OIDC authentication,
release-index generation, immutable upload, public asset check, GitHub
Release publication, and latest-pointer steps.
1. The tag workflow and all six native package jobs succeeded. Verify the
recorded macOS signing mode: signed builds must pass `codesign`, `spctl`,
and `stapler`; unsigned builds must record the Gatekeeper caveat in the
Actions log and job summary. In the final release job, explicitly verify
the OSS configuration, OIDC authentication, release-index generation,
immutable upload, public asset check, GitHub Release publication, and
latest-pointer steps.
2. The public GitHub Release is non-draft, non-prerelease, marked Latest, and
uses the expected tag and title. Its body must exactly match the Markdown
generated from the approved packaged bilingual notes.
+17 -5
View File
@@ -156,6 +156,12 @@ Linux 的 `x64`、`arm64` 版本。生产 bundle 仅作为短期 Actions artifac
npm run release:package -- --platform <windows|macos|linux> --arch <x64|arm64>
```
在 macOS 上明确生成未签名、未公证的开发验证包:
```bash
npm run release:package -- --platform macos --arch <x64|arm64> --unsigned
```
默认发布产物为 Windows 的 NSIS 安装包与 portable ZIP、macOS 的 DMG 与
ZIP,以及 Linux 的 AppImage 与 DEB。Windows portable ZIP 解压后可直接
运行 `GoodBuddy.exe`,并包含启用便携数据目录的
@@ -195,8 +201,11 @@ git push origin "$tag"
git push github "$tag"
```
macOS 发布 job 使用 Developer ID Application 证书签名,并通过 App Store
Connect API Key 提交 Apple notarization。仓库的 Actions Secrets 需要配置:
macOS 发布 job 会先原子判断 Apple 凭据状态:以下五项 Actions Secrets 全部
存在时,使用 Developer ID Application 证书签名,并通过 App Store Connect
API Key 提交 Apple notarization;五项全部缺失时,明确生成未签名、未公证的
DMG 和 ZIP,并在 Actions 日志与摘要中警告 Gatekeeper 限制;只配置一部分时
任务失败,不能静默降级为未签名包。
- `MACOS_CERTIFICATE_BASE64`:包含证书及私钥的 `.p12` 文件经 Base64 编码后的内容。
- `MACOS_CERTIFICATE_PASSWORD`:导出 `.p12` 时设置的密码。
@@ -215,9 +224,12 @@ base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n'
`.p12` 的 Mac 钥匙串中同时包含对应私钥。API Key 建议使用团队级 App Store
Connect Key`.p8` 只能下载一次。签名材料只放入 GitHub Secrets,不提交到仓库。
macOS 打包完成后,工作流会挂载 DMG,并分别执行 `codesign`、Gatekeeper `spctl`
`stapler` 校验。缺少凭据、签名无效或 notarization ticket 不存在时,发布矩阵
会在上传产物前失败。本地没有签名凭据时仍可生成仅供开发验证的未签名包。
凭据完整时,macOS 打包完成后会挂载 DMG,并分别执行 `codesign`、Gatekeeper
`spctl` `stapler` 校验签名无效或 notarization ticket 不存在时,发布矩阵
会在上传产物前失败。完全没有凭据时,六平台矩阵和标签发布仍可完成,但 macOS
产物没有 Developer ID 签名或 Apple 公证,Gatekeeper 可能阻止首次打开。发布
工作流必须在 Actions 日志与摘要中明确这一限制;获得完整凭据后应恢复签名
发布并重新验证。
Windows 代码签名仍未配置。对外分发前还应配置 Windows 签名凭据,并重新验证
安装、升级和系统安全提示。
+50 -5
View File
@@ -153,6 +153,7 @@ function parseArguments(argv, environment = process) {
formats: [],
skipBuild: false,
dryRun: false,
unsigned: false,
help: false
}
for (let index = 0; index < argv.length; index += 1) {
@@ -176,6 +177,8 @@ function parseArguments(argv, environment = process) {
options.skipBuild = true
} else if (argument === '--dry-run') {
options.dryRun = true
} else if (argument === '--unsigned') {
options.unsigned = true
} else if (argument === '--help' || argument === '-h') {
options.help = true
} else {
@@ -191,6 +194,9 @@ function parseArguments(argv, environment = process) {
if (!supportedArchitectures.has(options.arch)) {
throw new Error(`不支持的架构:${options.arch}`)
}
if (options.unsigned && options.platform !== 'macos') {
throw new Error('--unsigned 仅支持 macOS 发布包')
}
const definition = platformDefinitions[options.platform]
const requestedFormats =
options.formats.length > 0
@@ -337,9 +343,45 @@ function buildElectronBuilderArguments(options, outputDirectory) {
`--config.nsis.artifactName=${productName}-\${version}-windows-\${arch}-setup.\${ext}`
)
}
if (options.platform === 'macos' && options.unsigned) {
builderArguments.push('--config.mac.notarize=false')
}
return builderArguments
}
function electronBuilderEnvironment(
options,
environment = process.env
) {
const builderEnvironment = {
...environment,
CSC_IDENTITY_AUTO_DISCOVERY:
environment.CSC_IDENTITY_AUTO_DISCOVERY ?? 'false'
}
if (options.platform !== 'macos' || !options.unsigned) {
return builderEnvironment
}
for (const name of [
'CSC_LINK',
'CSC_KEY_PASSWORD',
'CSC_NAME',
'CSC_INSTALLER_LINK',
'CSC_INSTALLER_KEY_PASSWORD',
'APPLE_API_KEY',
'APPLE_API_KEY_ID',
'APPLE_API_ISSUER',
'APPLE_ID',
'APPLE_APP_SPECIFIC_PASSWORD',
'APPLE_TEAM_ID',
'APPLE_KEYCHAIN',
'APPLE_KEYCHAIN_PROFILE'
]) {
delete builderEnvironment[name]
}
builderEnvironment.CSC_IDENTITY_AUTO_DISCOVERY = 'false'
return builderEnvironment
}
function detectBinaryArchitecture(buffer) {
if (
buffer.length >= 64 &&
@@ -1568,6 +1610,7 @@ function printHelp() {
--format <列表> 覆盖默认格式,逗号分隔
--skip-build 复用已有 out 生产构建
--dry-run 仅显示目标与 electron-builder 参数
--unsigned 仅用于 macOS,明确禁用代码签名与公证
默认格式:
windows: nsis, portable (ZIP)
@@ -1606,6 +1649,11 @@ async function main(argv = process.argv.slice(2)) {
`${options.platform} 包必须在对应系统构建,当前系统为 ${hostPlatform ?? process.platform}`
)
}
if (options.unsigned) {
console.warn(
'警告:正在生成未签名、未公证的 macOS 包,Gatekeeper 可能阻止用户首次打开。'
)
}
rmSync(stagingDirectory, { recursive: true, force: true })
let cleanupTargetDependencies = () => undefined
@@ -1622,11 +1670,7 @@ async function main(argv = process.argv.slice(2)) {
await run(
process.execPath,
builderArguments,
{
...process.env,
CSC_IDENTITY_AUTO_DISCOVERY:
process.env.CSC_IDENTITY_AUTO_DISCOVERY ?? 'false'
}
electronBuilderEnvironment(options)
)
const unpackedDirectory = verifyUnpackedOutput(
stagingDirectory,
@@ -1665,6 +1709,7 @@ module.exports = {
buildElectronBuilderArguments,
createPortableZip,
detectBinaryArchitecture,
electronBuilderEnvironment,
normalizePlatform,
parseArguments,
parsePackedPackageMetadata,
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "goodbuddy",
"version": "0.10.4",
"version": "0.11.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "goodbuddy",
"version": "0.10.4",
"version": "0.11.0",
"license": "0BSD",
"dependencies": {
"@agentclientprotocol/sdk": "0.25.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "goodbuddy",
"version": "0.10.4",
"version": "0.11.0",
"private": true,
"description": "Secure desktop AI workspace with controlled Agent Runtimes",
"desktopName": "GoodBuddy",
+60
View File
@@ -1,6 +1,66 @@
{
"formatVersion": 1,
"releases": [
{
"version": "0.11.0",
"releasedAt": "2026-08-20",
"notes": {
"zh-CN": {
"highlights": [
"GoodBuddy 0.11.0 新增与会话关联的定制计划任务和持久发送队列,扩展智能心跳范围与本地模型下载源,并全面提升桌面工作流的可靠性。"
],
"features": [
"**定制计划任务。** 可将单次、每日或每周任务关联到当前或新会话;重复触发会复用同一 Task,将文本结果写回会话,并可从会话列表和 Task Center 统一查看与管理。",
"**会话发送队列。** 回复生成期间仍可继续发送消息;普通消息与到期计划任务会按 Conversation 持久排队,支持删除待发送项,或中断当前回复并优先执行指定项,重启后仍可恢复。",
"**智能心跳范围。** 心跳计划现在从唯一的权威入口选择 Global 或一个、多个 Project;旧配置和历史会无损迁移,项目级记忆与行动输出会严格写入所选范围。",
"**本地模型下载源。** 可在“设置 → 平台功能 → 通用设置”中选择 ModelScope 或 Hugging Face,统一用于后续语音输入与 OCR 模型下载;下载不会静默换源或混合不同来源的文件。",
"**可配置全局快捷键。** 可启停、录制或恢复 GoodBuddy 的全局唤起快捷键;冲突或保存失败时会保留上一组可用快捷键。",
"**更清楚的项目切换。** 项目选择器现在按本地项目和远程通道分组,并显示目录或通道来源,切换时更容易辨认目标。"
],
"fixes": [
"**真实模型验证与凭据保留。** “保存并测试”现在执行有界的真实文本或图片生成并校验输出,不再只把接口成功响应视为模型可用;修改地址或临时关闭认证时,加密 API Key 会继续随连接保留,直到用户明确清除或删除连接。",
"**多专家结果展示。** 并行分析现在可以展开查看每位专家的完整输出,并在其下方显示和保存总 Agent 的综合结果。",
"**Runtime 与数据可靠性。** 强化 Runtime 子进程退出、配置原子回滚、模型包安装及持久化写入恢复;设置、知识库、魔法笔记和智能心跳失败时会更可靠地保留状态与未保存草稿。",
"**成果与通道回复。** 普通本地和消息通道回复不再重复出现在成果栏,已有重复项只会隐藏而不会删除;完整通道结果也不再被公共服务统一截断为 4,000 字符。",
"**内置浏览器设置。** 内置浏览器已移至直连模型工具,并明确它只操作 GoodBuddy 的隔离浏览器;暂时隐藏尚未生效的托管浏览器配置,同时保留原有开关状态和底层数据。",
"**界面与加载体验。** 助手工作栏可在宽屏上使用更多空间,打开会话时会可靠保持在底部;同时改进对话框、键盘操作、无障碍状态和重型页面的按需加载。"
],
"notices": [
"**计划任务限制。** 当前支持单次、每日和每周计划,应用关闭期间不能执行任务;新建任务默认使用 Execute,但仍遵守所选 Runtime、工具授权和高风险审批边界。",
"**模型测试可能产生用量。** 真实文本或图片测试会调用所选模型服务,可能产生少量 Token 或图片生成费用。",
"**模型下载源默认值。** 升级后默认使用 ModelScope;切换来源只影响之后启动的语音与 OCR 下载,已安装模型、正在进行的下载和 ZIP 导入不受影响。",
"**全局快捷键兼容性。** 默认组合为 `CommandOrControl+Shift+Space`;若操作系统或桌面环境不支持注册,仍可通过窗口或托盘使用 GoodBuddy。"
]
},
"en-US": {
"highlights": [
"GoodBuddy 0.11.0 adds conversation-backed custom scheduled tasks and a durable input queue, expands Smart Heartbeat scoping and local model download sources, and strengthens reliability across desktop workflows."
],
"features": [
"**Conversation-backed scheduled tasks.** Create once, daily, or weekly custom tasks for the current or a new conversation. Recurring runs reuse one Task, write text results back to the conversation, and can be viewed and managed from the conversation list and Task Center.",
"**Durable conversation queue.** Send more messages while a reply is running. Messages and due Scheduled Tasks are persisted and serialized per Conversation, can be removed or promoted by interrupting the current reply, and recover after restart.",
"**Scoped Smart Heartbeat.** Heartbeat plans now select Global or one or more Projects from a single authoritative editor. Existing configurations and history migrate without data loss, while project-level memory and actions remain within the selected scope.",
"**Local model download sources.** Settings → Platform Features → General now lets you choose ModelScope or Hugging Face for subsequent speech-input and OCR model downloads. Downloads never silently switch sources or mix files from different sources.",
"**Configurable global shortcut.** Enable, disable, record, or restore GoodBuddys global activation shortcut. If registration conflicts or saving fails, the last working shortcut remains active.",
"**Clearer project switching.** The project selector now groups local projects and remote channels and shows folder or channel context, making the intended destination easier to identify."
],
"fixes": [
"**Real model verification and retained credentials.** Save & Test now performs bounded real text or image generation and validates the output instead of treating a successful endpoint response as proof that the model works. Encrypted API keys remain with their connection across URL or authentication changes until explicitly cleared or the connection is deleted.",
"**Visible expert results.** Parallel analyses now let you expand each experts complete output, with the lead Agents synthesis displayed beneath them and saved with the conversation.",
"**Runtime and data reliability.** Improved Runtime child-process shutdown, atomic configuration rollback, model-package installation, and persisted-write recovery. Settings, Knowledge, Magic Notes, and Smart Heartbeat now preserve state and unsaved drafts more reliably when an operation fails.",
"**Results and channel replies.** Ordinary local and channel replies no longer appear again in Results, while legacy duplicates are hidden without deleting data. Complete channel output also reaches each platform adapter instead of being truncated by the shared service at 4,000 characters.",
"**Built-in browser settings.** The built-in browser now appears under direct-model tools and is clearly identified as GoodBuddys isolated browser. Inactive managed-profile controls are hidden while preserving the existing switch state and stored data.",
"**Layout and loading experience.** The assistant workbar can use more space on wide screens, opened conversations reliably remain at the bottom, and dialogs, keyboard behavior, accessibility states, and lazy loading for heavy pages have been improved."
],
"notices": [
"**Scheduled-task limits.** Current schedules support once, daily, and weekly triggers and cannot run while the app is closed. New tasks default to Execute but continue to honor the selected Runtime, tool authorization, and high-risk approval boundaries.",
"**Model tests may incur usage.** Real text or image tests call the selected model provider and may consume a small number of tokens or incur image-generation charges.",
"**Default model download source.** Upgrades default to ModelScope. Changing the source affects only subsequent speech and OCR downloads; installed models, active downloads, and ZIP imports are unchanged.",
"**Global shortcut compatibility.** The default accelerator is `CommandOrControl+Shift+Space`. If the operating system or desktop environment cannot register it, GoodBuddy remains available from its window or tray."
]
}
}
},
{
"version": "0.10.4",
"releasedAt": "2026-08-17",
+56 -1
View File
@@ -19,6 +19,7 @@ interface ReleaseOptions {
formats: string[]
skipBuild: boolean
dryRun: boolean
unsigned: boolean
help: boolean
}
@@ -31,6 +32,10 @@ interface ReleaseBuilderModule {
options: ReleaseOptions,
outputDirectory: string
) => string[]
electronBuilderEnvironment: (
options: ReleaseOptions,
environment?: NodeJS.ProcessEnv
) => NodeJS.ProcessEnv
createPortableZip: (
unpackedDirectory: string,
zipPath: string,
@@ -174,6 +179,7 @@ const windowsOptions: ReleaseOptions = {
formats: ['nsis', 'portable'],
skipBuild: false,
dryRun: false,
unsigned: false,
help: false
}
@@ -304,7 +310,8 @@ describe('release build arguments', () => {
'--format',
'DMG,zip,dmg',
'--skip-build',
'--dry-run'
'--dry-run',
'--unsigned'
],
{ platform: 'win32', arch: 'x64' }
)
@@ -314,6 +321,7 @@ describe('release build arguments', () => {
formats: ['dmg', 'zip'],
skipBuild: true,
dryRun: true,
unsigned: true,
help: false
})
})
@@ -322,6 +330,7 @@ describe('release build arguments', () => {
[['--platform', 'freebsd'], '不支持当前系统'],
[['--arch', 'ia32'], '不支持的架构'],
[['--format', 'rpm'], '不支持打包格式'],
[['--platform', 'linux', '--unsigned'], '仅支持 macOS'],
[['--unknown'], '未知参数']
])('rejects invalid arguments', (arguments_, message) => {
expect(() =>
@@ -341,6 +350,7 @@ describe('release build arguments', () => {
formats: ['nsis', 'portable'],
skipBuild: false,
dryRun: false,
unsigned: false,
help: false
},
'C:\\release-stage'
@@ -366,6 +376,51 @@ describe('release build arguments', () => {
).toBe(false)
})
it('explicitly disables notarization for unsigned macOS packages', () => {
const arguments_ =
releaseBuilder.buildElectronBuilderArguments(
{
...windowsOptions,
platform: 'macos',
formats: ['dmg', 'zip'],
unsigned: true
},
'/release-stage'
)
expect(arguments_).toContain('--config.mac.notarize=false')
})
it('removes signing credentials from unsigned macOS builds', () => {
const environment =
releaseBuilder.electronBuilderEnvironment(
{
...windowsOptions,
platform: 'macos',
formats: ['dmg', 'zip'],
unsigned: true
},
{
CSC_LINK: 'certificate',
CSC_KEY_PASSWORD: 'password',
APPLE_API_KEY: 'api-key',
APPLE_API_KEY_ID: 'key-id',
APPLE_API_ISSUER: 'issuer',
KEEP_ME: 'present'
}
)
expect(environment).toMatchObject({
CSC_IDENTITY_AUTO_DISCOVERY: 'false',
KEEP_ME: 'present'
})
expect(environment).not.toHaveProperty('CSC_LINK')
expect(environment).not.toHaveProperty('CSC_KEY_PASSWORD')
expect(environment).not.toHaveProperty('APPLE_API_KEY')
expect(environment).not.toHaveProperty('APPLE_API_KEY_ID')
expect(environment).not.toHaveProperty('APPLE_API_ISSUER')
})
it('pins and unpacks only the required Harness native packages', () => {
const packageJson = require('../package.json') as {
build: {
+23
View File
@@ -44,4 +44,27 @@ describe('packages workflow', () => {
[...workflow.matchAll(/--region "\$OSS_REGION"/gu)]
).toHaveLength(2)
})
it('allows fully unsigned macOS builds but rejects partial credentials', () => {
expect(workflow).toContain('id: macos-signing')
expect(workflow).toContain(
'if [[ "$configured" -eq 0 ]]; then'
)
expect(workflow).toContain(
'if [[ "$configured" -ne "${#names[@]}" ]]; then'
)
expect(workflow).toContain(
'Build unsigned macOS release packages'
)
expect(workflow).toContain('--skip-build --unsigned')
expect(workflow).toContain(
"steps.macos-signing.outputs.enabled == 'true'"
)
expect(workflow).toContain(
"steps.macos-signing.outputs.enabled == 'false'"
)
expect(workflow).toContain(
'Apple signing credentials are not configured'
)
})
})