From ea47502365a1578ae526f953e567351e19d6045d Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 21:54:18 +0800 Subject: [PATCH 1/9] Fix issue with placing app bundle in product directory if an existing build is there Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/main.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/admin/osx/mac-crafter/Sources/main.swift b/admin/osx/mac-crafter/Sources/main.swift index d4d49d389c2b..25e1d824c506 100644 --- a/admin/osx/mac-crafter/Sources/main.swift +++ b/admin/osx/mac-crafter/Sources/main.swift @@ -209,6 +209,9 @@ struct MacCrafter: ParsableCommand { atPath: productPath, withIntermediateDirectories: true, attributes: nil ) } + if fm.fileExists(atPath: "\(productPath)/\(appName).app") { + try fm.removeItem(atPath: "\(productPath)/\(appName).app") + } try fm.copyItem(atPath: clientAppDir, toPath: "\(productPath)/\(appName).app") print("Done!") From e3ffc3d43a4268304ea448debde70b2bd8db5a05 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 21:54:49 +0800 Subject: [PATCH 2/9] Add convenience method to save the entitlements of a bundle to a file as XML Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/Utils/Codesign.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index 16da33eb363e..027d99c32cb5 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -56,6 +56,13 @@ func recursivelyCodesign(path: String, identity: String) throws { } } +func saveCodesignEntitlements(target: String, path: String) throws { + let command = "codesign -d --entitlements \(path) --xml \(target)" + guard shell(command) == 0 else { + throw CodeSigningError.failedToCodeSign("Failed to save entitlements for \(target).") + } +} + func codesignClientAppBundle( at clientAppDir: String, withCodeSignIdentity codeSignIdentity: String ) throws { From 355f5f92376ffe95059f3ce791bb7482d55ab682 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 21:55:27 +0800 Subject: [PATCH 3/9] Fix notarisation issues for Sparkle in mac-crafter Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/Utils/Codesign.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index 027d99c32cb5..61dfe123764d 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -74,6 +74,16 @@ func codesignClientAppBundle( try recursivelyCodesign(path: "\(clientContentsDir)/PlugIns", identity: codeSignIdentity) try recursivelyCodesign(path: "\(clientContentsDir)/Resources", identity: codeSignIdentity) - print("Code-signing Nextcloud Desktop Client app bundle...") - try codesign(identity: codeSignIdentity, path: clientAppDir) + // Time to fix notarisation issues. + // Multiple components of the app will now have the get-task-allow entitlements. + // We need to strip these out manually. + + print("Code-signing Sparkle autoupdater app (without entitlements)...") + let sparkleFrameworkPath = "\(clientContentsDir)/Frameworks/Sparkle.framework" + try codesign(identity: codeSignIdentity, + path: "\(sparkleFrameworkPath)/Resources/Autoupdate.app/Contents/MacOS/*", + options: "--timestamp --force --verbose=4 --options runtime") + + print("Re-codesigning Sparkle library...") + try codesign(identity: codeSignIdentity, path: "\(sparkleFrameworkPath)/Sparkle") } From be7e3e7c19967d42dc3aba6ff7e150c07881fa27 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 21:55:52 +0800 Subject: [PATCH 4/9] Remove get-task-allow entitlement when code-signing app extensions Signed-off-by: Claudio Cambra --- .../mac-crafter/Sources/Utils/Codesign.swift | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index 61dfe123764d..c4c985b65b09 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -86,4 +86,26 @@ func codesignClientAppBundle( print("Re-codesigning Sparkle library...") try codesign(identity: codeSignIdentity, path: "\(sparkleFrameworkPath)/Sparkle") + + print("Code-signing app extensions (removing get-task-allow entitlements)...") + let fm = FileManager.default + let appExtensionPaths = + try fm.contentsOfDirectory(atPath: "\(clientContentsDir)/PlugIns").filter(isAppExtension) + for appExtension in appExtensionPaths { + let appExtensionPath = "\(clientContentsDir)/PlugIns/\(appExtension)" + let tmpEntitlementXmlPath = + fm.temporaryDirectory.appendingPathComponent(UUID().uuidString).path.appending(".xml") + try saveCodesignEntitlements(target: appExtensionPath, path: tmpEntitlementXmlPath) + // Strip the get-task-allow entitlement from the XML entitlements file + let xmlEntitlements = try String(contentsOfFile: tmpEntitlementXmlPath) + let entitlementKeyValuePair = "com.apple.security.get-task-allow" + let strippedEntitlements = + xmlEntitlements.replacingOccurrences(of: entitlementKeyValuePair, with: "") + try strippedEntitlements.write(toFile: tmpEntitlementXmlPath, + atomically: true, + encoding: .utf8) + try codesign(identity: codeSignIdentity, + path: appExtensionPath, + options: "--timestamp --force --verbose=4 --options runtime --entitlements \(tmpEntitlementXmlPath)") + } } From 0c8ffdb49f028ded8b239ad26b9351b4c9dbf7ce Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 21:56:03 +0800 Subject: [PATCH 5/9] Make sure to code-sign all binaries in app bundle Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/Utils/Codesign.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index c4c985b65b09..ccc6d191f503 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -108,4 +108,8 @@ func codesignClientAppBundle( path: appExtensionPath, options: "--timestamp --force --verbose=4 --options runtime --entitlements \(tmpEntitlementXmlPath)") } + + // Now we do the final codesign bit + print("Code-signing Nextcloud Desktop Client binaries...") + try codesign(identity: codeSignIdentity, path: "\(clientContentsDir)/MacOS/*") } From 12ba22f5c6667bdc21ca59c9a07b3376d7c94bf6 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 23:26:30 +0800 Subject: [PATCH 6/9] Use deep codesigning by default in mac crafter Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/Utils/Codesign.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index ccc6d191f503..4e764d13807d 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -33,7 +33,7 @@ func isAppExtension(_ path: String) -> Bool { func codesign( identity: String, path: String, - options: String = "--timestamp --force --preserve-metadata=entitlements --verbose=4 --options runtime" + options: String = "--timestamp --force --preserve-metadata=entitlements --verbose=4 --options runtime --deep" ) throws { print("Code-signing \(path)...") let command = "codesign -s \"\(identity)\" \(options) \(path)" @@ -82,7 +82,7 @@ func codesignClientAppBundle( let sparkleFrameworkPath = "\(clientContentsDir)/Frameworks/Sparkle.framework" try codesign(identity: codeSignIdentity, path: "\(sparkleFrameworkPath)/Resources/Autoupdate.app/Contents/MacOS/*", - options: "--timestamp --force --verbose=4 --options runtime") + options: "--timestamp --force --verbose=4 --options runtime --deep") print("Re-codesigning Sparkle library...") try codesign(identity: codeSignIdentity, path: "\(sparkleFrameworkPath)/Sparkle") @@ -106,7 +106,7 @@ func codesignClientAppBundle( encoding: .utf8) try codesign(identity: codeSignIdentity, path: appExtensionPath, - options: "--timestamp --force --verbose=4 --options runtime --entitlements \(tmpEntitlementXmlPath)") + options: "--timestamp --force --verbose=4 --options runtime --deep --entitlements \(tmpEntitlementXmlPath)") } // Now we do the final codesign bit From f5ed1f29a7ff5edd800924ef122b3f13cbbea41c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 11 Sep 2024 23:26:51 +0800 Subject: [PATCH 7/9] Fix codesigning of qtwebengine related helper app in mac crafter Signed-off-by: Claudio Cambra --- .../mac-crafter/Sources/Utils/Codesign.swift | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift index 4e764d13807d..696104813218 100644 --- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift +++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift @@ -69,17 +69,27 @@ func codesignClientAppBundle( print("Code-signing Nextcloud Desktop Client libraries, frameworks and plugins...") let clientContentsDir = "\(clientAppDir)/Contents" + let frameworksPath = "\(clientContentsDir)/Frameworks" + let pluginsPath = "\(clientContentsDir)/PlugIns" - try recursivelyCodesign(path: "\(clientContentsDir)/Frameworks", identity: codeSignIdentity) - try recursivelyCodesign(path: "\(clientContentsDir)/PlugIns", identity: codeSignIdentity) + try recursivelyCodesign(path: frameworksPath, identity: codeSignIdentity) + try recursivelyCodesign(path: pluginsPath, identity: codeSignIdentity) try recursivelyCodesign(path: "\(clientContentsDir)/Resources", identity: codeSignIdentity) + print("Code-signing QtWebEngineProcess...") + let qtWebEngineProcessPath = + "\(frameworksPath)/QtWebEngineCore.framework/Versions/A/Helpers/QtWebEngineProcess.app" + try codesign(identity: codeSignIdentity, path: qtWebEngineProcessPath) + + print("Code-signing QtWebEngine...") + try codesign(identity: codeSignIdentity, path: "\(frameworksPath)/QtWebEngineCore.framework") + // Time to fix notarisation issues. // Multiple components of the app will now have the get-task-allow entitlements. // We need to strip these out manually. print("Code-signing Sparkle autoupdater app (without entitlements)...") - let sparkleFrameworkPath = "\(clientContentsDir)/Frameworks/Sparkle.framework" + let sparkleFrameworkPath = "\(frameworksPath)/Sparkle.framework" try codesign(identity: codeSignIdentity, path: "\(sparkleFrameworkPath)/Resources/Autoupdate.app/Contents/MacOS/*", options: "--timestamp --force --verbose=4 --options runtime --deep") @@ -90,9 +100,9 @@ func codesignClientAppBundle( print("Code-signing app extensions (removing get-task-allow entitlements)...") let fm = FileManager.default let appExtensionPaths = - try fm.contentsOfDirectory(atPath: "\(clientContentsDir)/PlugIns").filter(isAppExtension) + try fm.contentsOfDirectory(atPath: pluginsPath).filter(isAppExtension) for appExtension in appExtensionPaths { - let appExtensionPath = "\(clientContentsDir)/PlugIns/\(appExtension)" + let appExtensionPath = "\(pluginsPath)/\(appExtension)" let tmpEntitlementXmlPath = fm.temporaryDirectory.appendingPathComponent(UUID().uuidString).path.appending(".xml") try saveCodesignEntitlements(target: appExtensionPath, path: tmpEntitlementXmlPath) From a5c208a45363cdf979838fda6443b8b1aad22ceb Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 12 Sep 2024 03:48:28 +0800 Subject: [PATCH 8/9] Always move final product to product path in mac crafter Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/main.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/admin/osx/mac-crafter/Sources/main.swift b/admin/osx/mac-crafter/Sources/main.swift index 25e1d824c506..7e92f7878973 100644 --- a/admin/osx/mac-crafter/Sources/main.swift +++ b/admin/osx/mac-crafter/Sources/main.swift @@ -194,14 +194,11 @@ struct MacCrafter: ParsableCommand { throw MacCrafterError.craftError("Error crafting Nextcloud Desktop Client.") } - guard let codeSignIdentity else { - print("Crafted Nextcloud Desktop Client. Not codesigned.") - return - } - - print("Code-signing Nextcloud Desktop Client libraries and frameworks...") let clientAppDir = "\(clientBuildDir)/image-\(buildType)-master/\(appName).app" - try codesignClientAppBundle(at: clientAppDir, withCodeSignIdentity: codeSignIdentity) + if let codeSignIdentity { + print("Code-signing Nextcloud Desktop Client libraries and frameworks...") + try codesignClientAppBundle(at: clientAppDir, withCodeSignIdentity: codeSignIdentity) + } print("Placing Nextcloud Desktop Client in \(productPath)...") if !fm.fileExists(atPath: productPath) { From 7afa3938446a6ec83a5566da751332adc259351c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Thu, 12 Sep 2024 03:54:01 +0800 Subject: [PATCH 9/9] Move building into separate subcommand, add subcommand just for codesigning Signed-off-by: Claudio Cambra --- admin/osx/mac-crafter/Sources/main.swift | 30 ++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/admin/osx/mac-crafter/Sources/main.swift b/admin/osx/mac-crafter/Sources/main.swift index 7e92f7878973..0492d9fc527b 100644 --- a/admin/osx/mac-crafter/Sources/main.swift +++ b/admin/osx/mac-crafter/Sources/main.swift @@ -15,10 +15,8 @@ import ArgumentParser import Foundation -struct MacCrafter: ParsableCommand { - static let configuration = CommandConfiguration( - abstract: "A tool to easily build a fully-functional Nextcloud Desktop Client for macOS." - ) +struct Build: ParsableCommand { + static let configuration = CommandConfiguration(abstract: "Client building script") enum MacCrafterError: Error { case failedEnumeration(String) @@ -215,4 +213,28 @@ struct MacCrafter: ParsableCommand { } } +struct Codesign: ParsableCommand { + static let configuration = CommandConfiguration(abstract: "Codesigning script for the client.") + + @Argument(help: "Path to the Nextcloud Desktop Client app bundle.") + var appBundlePath = "\(FileManager.default.currentDirectoryPath)/product/Nextcloud.app" + + @Option(name: [.short, .long], help: "Code signing identity for desktop client and libs.") + var codeSignIdentity: String + + mutating func run() throws { + try codesignClientAppBundle(at: appBundlePath, withCodeSignIdentity: codeSignIdentity) + } +} + +struct MacCrafter: ParsableCommand { + static let configuration = CommandConfiguration( + abstract: "A tool to easily build a fully-functional Nextcloud Desktop Client for macOS.", + subcommands: [Build.self, Codesign.self], + defaultSubcommand: Build.self + ) + + +} + MacCrafter.main()