Skip to content

Commit

Permalink
Merge pull request #7103 from nextcloud/bugfix/mac-crafter-codesign-n…
Browse files Browse the repository at this point in the history
…otarisation

Code-sign client via mac-crafter so it may pass notarisation
  • Loading branch information
tobiasKaminsky authored Sep 12, 2024
2 parents 3facce2 + 7f3b137 commit 9ffd0d4
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 16 deletions.
63 changes: 58 additions & 5 deletions admin/osx/mac-crafter/Sources/Utils/Codesign.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand All @@ -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 = "<key>com.apple.security.get-task-allow</key><true/>"
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/*")
}
44 changes: 33 additions & 11 deletions admin/osx/mac-crafter/Sources/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -194,25 +192,49 @@ 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) {
try fm.createDirectory(
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()

0 comments on commit 9ffd0d4

Please sign in to comment.