diff --git a/scripts/lib/swift-toolchain.sh b/scripts/lib/swift-toolchain.sh index 30ac5462b06..6c01c555e53 100644 --- a/scripts/lib/swift-toolchain.sh +++ b/scripts/lib/swift-toolchain.sh @@ -4,7 +4,9 @@ REQUIRED_SWIFT_TOOLS_MAJOR=6 REQUIRED_SWIFT_TOOLS_MINOR=2 require_swift_toolchain() { - if ! xcrun xcodebuild -version >/dev/null 2>&1; then + local xcodebuild_version + if ! xcodebuild_version="$(xcrun xcodebuild -version 2>&1)"; then + printf '%s\n' "$xcodebuild_version" >&2 echo "ERROR: OpenClaw macOS app packaging requires a full Xcode developer directory." >&2 echo " Command Line Tools do not include the required SwiftUI macro plugins." >&2 echo " Use: sudo xcode-select -s /Applications/Xcode.app/Contents/Developer" >&2 diff --git a/test/scripts/package-mac-app.test.ts b/test/scripts/package-mac-app.test.ts index 7c024a3d58e..2c4a7605e2b 100644 --- a/test/scripts/package-mac-app.test.ts +++ b/test/scripts/package-mac-app.test.ts @@ -62,6 +62,7 @@ function runSwiftToolchainHarness(options: { swiftVersion: string; selectedDeveloperDir: "command-line-tools" | "custom-xcode" | "invalid" | "xcode"; developerDirOverride?: "custom-xcode" | "invalid" | "xcode"; + xcodebuildFailure?: string; }) { const root = tempDirs.make("openclaw-package-swift-root-"); const toolsDir = path.join(root, "tools"); @@ -84,9 +85,14 @@ function runSwiftToolchainHarness(options: { mkdirSync(path.dirname(xcodebuild), { recursive: true }); writeFileSync( xcodebuild, - ["#!/usr/bin/env bash", '[[ "$*" == "-version" ]] || exit 2', "echo 'Xcode 26.0'", ""].join( - "\n", - ), + [ + "#!/usr/bin/env bash", + '[[ "$*" == "-version" ]] || exit 2', + ...(options.xcodebuildFailure + ? [`printf '%s\\n' ${JSON.stringify(options.xcodebuildFailure)} >&2`, "exit 1"] + : ["echo 'Xcode 26.0'"]), + "", + ].join("\n"), "utf8", ); chmodSync(xcodebuild, 0o755); @@ -877,6 +883,23 @@ describe("package-mac-app plist stamping", () => { expect(result.stderr).toContain("requires a full Xcode developer directory"); }); + it("preserves the native Xcode failure before generic selection guidance", () => { + const diagnostic = "xcodebuild: error: SDK metadata is unavailable"; + const result = runSwiftToolchainHarness({ + swiftVersion: "6.2.1", + selectedDeveloperDir: "xcode", + xcodebuildFailure: diagnostic, + }); + + expect(result.status).toBe(1); + const diagnosticIndex = result.stderr.indexOf(diagnostic); + const guidanceIndex = result.stderr.indexOf( + "ERROR: OpenClaw macOS app packaging requires a full Xcode developer directory", + ); + expect(diagnosticIndex).toBeGreaterThanOrEqual(0); + expect(guidanceIndex).toBeGreaterThan(diagnosticIndex); + }); + it("runs Sparkle build metadata derivation from the repository root", () => { const helperBlock = getSparkleBuildHelperBlock(); const tempRoot = tempDirs.make("openclaw-package-sparkle-root-");