From e1df119bf3abc786babbd9d5f5378afd4dd31bb0 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 d4d49d389c2b0..25e1d824c5067 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 76fbf012ea2115b85de61139fdf732605c41350c 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 16da33eb363e0..027d99c32cb5e 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 b0c4f650fe6567b4815dc1f79067ae81d5166290 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 027d99c32cb5e..61dfe123764df 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 d51def663839e8f585023e654e362b54854e8050 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 61dfe123764df..c4c985b65b096 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 9126b66ad4ee023fcd0af4ab16c7e50e67375c38 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 c4c985b65b096..ccc6d191f503a 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 ad428c759f784bbe8073c27efa21a0b23f746b5d 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 ccc6d191f503a..4e764d13807d6 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 dcd640a504dd07ded0ad493f7bd4cada2664ba32 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 4e764d13807d6..6961048132184 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 8620ff5127b5d97b9c3a7fb4e527c888244d914a 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 25e1d824c5067..7e92f78789731 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 7f3b13718ad477ae376d2297150178d1f7c2bda0 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 7e92f78789731..0492d9fc527b8 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()