11import Foundation
2+ import Subprocess
3+
4+ #if canImport(System)
5+ import System
6+ #else
7+ import SystemPackage
8+ #endif
29
310actor DMGManager {
4- private let executor : ShellExecutor
511 private let logger : Logger
612 private var wasMounted : [ URL : Bool ] = [ : ]
713 private var mountedVolumes : [ URL ] = [ ]
814
9- init (
10- executor: ShellExecutor ,
11- logger: Logger
12- ) {
13- self . executor = executor
15+ init ( logger: Logger ) {
1416 self . logger = logger
1517 }
1618
1719 /// Check if a DMG has a Software License Agreement
1820 func hasSLA( at path: URL ) async throws -> Bool {
19- let result = try await executor. runOrThrow ( [
20- " /usr/bin/hdiutil " , " imageinfo " , path. path, " -plist "
21- ] )
21+ let arguments = [ " /usr/bin/hdiutil " , " imageinfo " , path. path, " -plist " ]
22+ logger. log ( " Executing: \( arguments. joined ( separator: " " ) ) " , level: 3 )
23+
24+ let result = try await Subprocess . run (
25+ . path( FilePath ( arguments [ 0 ] ) ) ,
26+ arguments: Arguments ( Array ( arguments. dropFirst ( ) ) ) ,
27+ output: . string( limit: . max) ,
28+ error: . string( limit: . max)
29+ )
30+
31+ guard result. terminationStatus. isSuccess else {
32+ throw QuickPkgError . dmgMountFailed ( result. standardError ?? " hdiutil imageinfo failed " )
33+ }
2234
23- let plist = try PlistHandler . parse ( Data ( result. stdout . utf8) )
35+ let plist = try PlistHandler . parse ( Data ( ( result. standardOutput ?? " " ) . utf8) )
2436 if let properties = plist [ " Properties " ] as? [ String : Any ] ,
2537 let hasSLA = properties [ " Software License Agreement " ] as? Bool {
2638 return hasSLA
@@ -30,9 +42,21 @@ actor DMGManager {
3042
3143 /// Check if DMG is already mounted and return mount points
3244 func existingMountPoints( for dmgPath: URL ) async throws -> [ URL ] ? {
33- let result = try await executor. runOrThrow ( [ " /usr/bin/hdiutil " , " info " , " -plist " ] )
45+ let arguments = [ " /usr/bin/hdiutil " , " info " , " -plist " ]
46+ logger. log ( " Executing: \( arguments. joined ( separator: " " ) ) " , level: 3 )
47+
48+ let result = try await Subprocess . run (
49+ . path( FilePath ( arguments [ 0 ] ) ) ,
50+ arguments: Arguments ( Array ( arguments. dropFirst ( ) ) ) ,
51+ output: . string( limit: . max) ,
52+ error: . string( limit: . max)
53+ )
54+
55+ guard result. terminationStatus. isSuccess else {
56+ throw QuickPkgError . dmgMountFailed ( result. standardError ?? " hdiutil info failed " )
57+ }
3458
35- let plistData = try PlistHandler . extractFirstPlist ( from: Data ( result. stdout . utf8) )
59+ let plistData = try PlistHandler . extractFirstPlist ( from: Data ( ( result. standardOutput ?? " " ) . utf8) )
3660 let info = try PlistHandler . parse ( plistData)
3761
3862 guard let images = info [ " images " ] as? [ [ String : Any ] ] else {
@@ -93,14 +117,41 @@ actor DMGManager {
93117 " -nobrowse "
94118 ]
95119
96- let result = try await executor . run ( arguments , input : sla ? " Y \n " : nil )
120+ logger . log ( " Executing: \( arguments . joined ( separator : " " ) ) " , level : 3 )
97121
98- guard result. exitCode == 0 else {
99- throw QuickPkgError . dmgMountFailed ( " ( \( result. exitCode) ) \( result. stderr) " )
122+ let terminationStatus: TerminationStatus
123+ let standardOutput: String ?
124+ let standardError: String ?
125+
126+ if sla {
127+ let result = try await Subprocess . run (
128+ . path( FilePath ( arguments [ 0 ] ) ) ,
129+ arguments: Arguments ( Array ( arguments. dropFirst ( ) ) ) ,
130+ input: . string( " Y \n " ) ,
131+ output: . string( limit: . max) ,
132+ error: . string( limit: . max)
133+ )
134+ terminationStatus = result. terminationStatus
135+ standardOutput = result. standardOutput
136+ standardError = result. standardError
137+ } else {
138+ let result = try await Subprocess . run (
139+ . path( FilePath ( arguments [ 0 ] ) ) ,
140+ arguments: Arguments ( Array ( arguments. dropFirst ( ) ) ) ,
141+ output: . string( limit: . max) ,
142+ error: . string( limit: . max)
143+ )
144+ terminationStatus = result. terminationStatus
145+ standardOutput = result. standardOutput
146+ standardError = result. standardError
147+ }
148+
149+ guard terminationStatus. isSuccess else {
150+ throw QuickPkgError . dmgMountFailed ( standardError ?? " hdiutil attach failed " )
100151 }
101152
102153 // Parse the plist output to get mount points
103- let plistData = try PlistHandler . extractFirstPlist ( from: Data ( result . stdout . utf8) )
154+ let plistData = try PlistHandler . extractFirstPlist ( from: Data ( ( standardOutput ?? " " ) . utf8) )
104155 let attachResult = try PlistHandler . parse ( plistData)
105156
106157 var mountPoints: [ URL] = [ ]
@@ -133,10 +184,18 @@ actor DMGManager {
133184
134185 guard mountPoint. fileExists else { return }
135186
136- let result = try await executor. run ( [ " /usr/bin/hdiutil " , " detach " , mountPoint. path] )
187+ let arguments = [ " /usr/bin/hdiutil " , " detach " , mountPoint. path]
188+ logger. log ( " Executing: \( arguments. joined ( separator: " " ) ) " , level: 3 )
189+
190+ let result = try await Subprocess . run (
191+ . path( FilePath ( arguments [ 0 ] ) ) ,
192+ arguments: Arguments ( Array ( arguments. dropFirst ( ) ) ) ,
193+ output: . string( limit: . max) ,
194+ error: . string( limit: . max)
195+ )
137196
138- if result. exitCode != 0 {
139- logger. log ( " Warning: Failed to detach \( mountPoint. path) : \( result. stderr ) " , level: 1 )
197+ if ! result. terminationStatus . isSuccess {
198+ logger. log ( " Warning: Failed to detach \( mountPoint. path) : \( result. standardError ?? " " ) " , level: 1 )
140199 } else {
141200 logger. log ( " Detached: \( mountPoint. path) " , level: 2 )
142201 }
0 commit comments