diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
index 16da33eb363e..696104813218 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)"
@@ -56,17 +56,70 @@ 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 {
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 Nextcloud Desktop Client app bundle...")
- try codesign(identity: codeSignIdentity, path: clientAppDir)
+ 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 = "\(frameworksPath)/Sparkle.framework"
+ try codesign(identity: codeSignIdentity,
+ path: "\(sparkleFrameworkPath)/Resources/Autoupdate.app/Contents/MacOS/*",
+ options: "--timestamp --force --verbose=4 --options runtime --deep")
+
+ 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: pluginsPath).filter(isAppExtension)
+ for appExtension in appExtensionPaths {
+ let appExtensionPath = "\(pluginsPath)/\(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 --deep --entitlements \(tmpEntitlementXmlPath)")
+ }
+
+ // Now we do the final codesign bit
+ print("Code-signing Nextcloud Desktop Client binaries...")
+ try codesign(identity: codeSignIdentity, path: "\(clientContentsDir)/MacOS/*")
}
diff --git a/admin/osx/mac-crafter/Sources/main.swift b/admin/osx/mac-crafter/Sources/main.swift
index d4d49d389c2b..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)
@@ -194,14 +192,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) {
@@ -209,10 +204,37 @@ 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!")
}
}
+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()