From b441e409d926f893e532a8a3ff48ef97d54b8096 Mon Sep 17 00:00:00 2001
From: Claudio Cambra <claudio.cambra@nextcloud.com>
Date: Thu, 21 Nov 2024 18:27:52 +0800
Subject: [PATCH 1/3] Allow using an external task for running commands in
 mac-crafter run command

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
---
 .../osx/mac-crafter/Sources/Utils/Shell.swift | 31 +++++++++----------
 1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/admin/osx/mac-crafter/Sources/Utils/Shell.swift b/admin/osx/mac-crafter/Sources/Utils/Shell.swift
index 1d7a35522c411..8c1570c5e8f51 100644
--- a/admin/osx/mac-crafter/Sources/Utils/Shell.swift
+++ b/admin/osx/mac-crafter/Sources/Utils/Shell.swift
@@ -14,40 +14,39 @@
 
 import Foundation
 
-var task: Process?
+weak var globalTaskRef: Process?
 
 @discardableResult
 func run(
     _ launchPath: String,
     _ args: [String],
     env: [String: String]? = nil,
-    quiet: Bool = false
+    quiet: Bool = false,
+    task: Process = Process()
 ) -> Int32 {
-    defer { task = nil }
-    task = Process()
-
+    globalTaskRef = task
     signal(SIGINT) { _ in
-        task?.terminate()  // Send terminate signal to the task
-        exit(0)            // Exit the script after cleanup
+        globalTaskRef?.terminate()  // Send terminate signal to the task
+        exit(0)           // Exit the script after cleanup
     }
 
-    task?.launchPath = launchPath
-    task?.arguments = args
+    task.launchPath = launchPath
+    task.arguments = args
 
     if let env,
-       let combinedEnv = task?.environment?.merging(env, uniquingKeysWith: { (_, new) in new })
+       let combinedEnv = task.environment?.merging(env, uniquingKeysWith: { (_, new) in new })
     {
-        task?.environment = combinedEnv
+        task.environment = combinedEnv
     }
 
     if quiet {
-        task?.standardOutput = nil
-        task?.standardError = nil
+        task.standardOutput = nil
+        task.standardError = nil
     }
 
-    task?.launch()
-    task?.waitUntilExit()
-    return task?.terminationStatus ?? 1
+    task.launch()
+    task.waitUntilExit()
+    return task.terminationStatus
 }
 
 func run(

From 68531f62893c5823670d7c7a6fd8c1948b59857a Mon Sep 17 00:00:00 2001
From: Claudio Cambra <claudio.cambra@nextcloud.com>
Date: Thu, 21 Nov 2024 18:28:19 +0800
Subject: [PATCH 2/3] Do not rely on FileManager's isExecutableFile, check
 manually for Mach-O executable type

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
---
 .../mac-crafter/Sources/Utils/Codesign.swift  | 30 ++++++++++++-------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
index 139a1e802449c..bb8ded8ffeb6e 100644
--- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
+++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
@@ -32,11 +32,21 @@ func isAppExtension(_ path: String) -> Bool {
     path.hasSuffix(".appex")
 }
 
-func isExecutable(_ path: String) -> Bool {
-    let fm = FileManager.default
-    var isDir: ObjCBool = false
-    let exists = fm.fileExists(atPath: path, isDirectory: &isDir)
-    return fm.isExecutableFile(atPath: path) && !isDir.boolValue && exists
+func isExecutable(_ path: String) throws -> Bool {
+    let outPipe = Pipe()
+    let errPipe = Pipe()
+    let task = Process()
+    task.standardOutput = outPipe
+    task.standardError = errPipe
+
+    let command = "file \"\(path)\""
+    guard run("/bin/zsh", ["-c", command], task: task) == 0 else {
+        throw CodeSigningError.failedToCodeSign("Failed to determine if \(path) is an executable.")
+    }
+
+    let outputData = outPipe.fileHandleForReading.readDataToEndOfFile()
+    let output = String(data: outputData, encoding: .utf8) ?? ""
+    return output.contains("Mach-O 64-bit executable")
 }
 
 func codesign(identity: String, path: String, options: String = defaultCodesignOptions) throws {
@@ -60,11 +70,11 @@ func recursivelyCodesign(
     }
 
     for case let enumeratedItem as String in pathEnumerator {
-        guard isLibrary(enumeratedItem) ||
-              isAppExtension(enumeratedItem) ||
-              isExecutable(enumeratedItem)
-        else { continue }
-        try codesign(identity: identity, path: "\(path)/\(enumeratedItem)")
+        let isExecutableFile = try isExecutable(fm.currentDirectoryPath + "/" + path + "/" + enumeratedItem)
+        guard isLibrary(enumeratedItem) || isAppExtension(enumeratedItem) || isExecutableFile else {
+            continue
+        }
+        try codesign(identity: identity, path: "\(path)/\(enumeratedItem)", options: options)
     }
 }
 

From ef3fa81e3095791dc9a9fcf2ab0ee06ea3afb8c8 Mon Sep 17 00:00:00 2001
From: Claudio Cambra <claudio.cambra@nextcloud.com>
Date: Thu, 21 Nov 2024 18:43:58 +0800
Subject: [PATCH 3/3] Do not do final bundle codesign

Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
---
 admin/osx/mac-crafter/Sources/Utils/Codesign.swift | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
index bb8ded8ffeb6e..426d3ce05a4d6 100644
--- a/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
+++ b/admin/osx/mac-crafter/Sources/Utils/Codesign.swift
@@ -146,7 +146,4 @@ func codesignClientAppBundle(
     // Now we do the final codesign bit
     print("Code-signing Nextcloud Desktop Client binaries...")
     try recursivelyCodesign(path: "\(clientContentsDir)/MacOS/", identity: codeSignIdentity)
-
-    print("Code-signing Nextcloud Desktop Client app bundle...")
-    try codesign(identity: codeSignIdentity, path: clientAppDir)
 }