Skip to content

Commit d7f2566

Browse files
committed
cleaned up run() function
1 parent 99e9a39 commit d7f2566

1 file changed

Lines changed: 126 additions & 121 deletions

File tree

Sources/quickpkg/QuickPkg.swift

Lines changed: 126 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -87,21 +87,10 @@ struct QuickPkg: AsyncParsableCommand {
8787
mutating func run() async throws {
8888
let logger = Logger(verbosity: verbose)
8989

90-
// Normalize path
91-
var path = itemPath
92-
if path.hasPrefix("~") {
93-
path = NSString(string: path).expandingTildeInPath
94-
}
95-
path = (path as NSString).standardizingPath
96-
97-
// Remove trailing slash
98-
if path.hasSuffix("/") {
99-
path = String(path.dropLast())
100-
}
101-
90+
// Normalize path and determine input type
91+
let path = normalizePath(itemPath)
10292
let url = URL(filePath: path)
10393

104-
// Determine input type
10594
guard let inputType = InputType.from(path: path) else {
10695
throw QuickPkgError.unsupportedExtension(url.pathExtension)
10796
}
@@ -126,67 +115,21 @@ struct QuickPkg: AsyncParsableCommand {
126115
// Find the application
127116
let appURL: URL
128117
do {
129-
switch inputType {
130-
case .app:
131-
guard FileManager.default.fileExists(atPath: path) else {
132-
throw QuickPkgError.fileNotFound(path)
133-
}
134-
appURL = url
135-
136-
case .dmg:
137-
guard FileManager.default.fileExists(atPath: path) else {
138-
throw QuickPkgError.fileNotFound(path)
139-
}
140-
let mountPoints = try await dmgManager.attach(url)
141-
let apps = findApplications(in: mountPoints)
142-
guard !apps.isEmpty else {
143-
throw QuickPkgError.noApplicationFound
144-
}
145-
guard apps.count == 1 else {
146-
throw QuickPkgError.multipleApplicationsFound(apps.map(\.path))
147-
}
148-
appURL = apps[0]
149-
150-
case .zip:
151-
guard FileManager.default.fileExists(atPath: path) else {
152-
throw QuickPkgError.fileNotFound(path)
153-
}
154-
let extractDir = tempDir.path.appendingPathComponent("unarchive")
155-
try FileManager.default.createDirectory(at: extractDir, withIntermediateDirectories: true)
156-
try await archiveExtractor.extractZip(url, to: extractDir)
157-
let apps = findApplications(in: [extractDir])
158-
guard !apps.isEmpty else {
159-
throw QuickPkgError.noApplicationFound
160-
}
161-
guard apps.count == 1 else {
162-
throw QuickPkgError.multipleApplicationsFound(apps.map(\.path))
163-
}
164-
appURL = apps[0]
165-
166-
case .xip:
167-
guard FileManager.default.fileExists(atPath: path) else {
168-
throw QuickPkgError.fileNotFound(path)
169-
}
170-
let extractDir = tempDir.path.appendingPathComponent("unarchive")
171-
try FileManager.default.createDirectory(at: extractDir, withIntermediateDirectories: true)
172-
try await archiveExtractor.extractXip(url, to: extractDir)
173-
let apps = findApplications(in: [extractDir])
174-
guard !apps.isEmpty else {
175-
throw QuickPkgError.noApplicationFound
176-
}
177-
guard apps.count == 1 else {
178-
throw QuickPkgError.multipleApplicationsFound(apps.map(\.path))
179-
}
180-
appURL = apps[0]
181-
}
118+
appURL = try await findApplication(
119+
at: url,
120+
inputType: inputType,
121+
tempDir: tempDir,
122+
dmgManager: dmgManager,
123+
archiveExtractor: archiveExtractor
124+
)
182125
} catch {
183126
if shouldClean { await dmgManager.detachAll() }
184127
throw error
185128
}
186129

187130
logger.log("Found application: \(appURL.path)", level: 1)
188131

189-
// Copy app to payload directory (needed for dmg/zip/xip, and for apps to avoid modifying original)
132+
// Copy app to payload directory
190133
let payloadDir = tempDir.path.appendingPathComponent("payload")
191134
try FileManager.default.createDirectory(at: payloadDir, withIntermediateDirectories: true)
192135
let payloadAppURL = payloadDir.appendingPathComponent(appURL.lastPathComponent)
@@ -203,63 +146,10 @@ struct QuickPkg: AsyncParsableCommand {
203146
}
204147

205148
// Prepare scripts if needed
206-
var scriptsDir: URL?
207-
if let scriptsPath = scripts {
208-
let scriptsURL = URL(filePath: scriptsPath)
209-
guard FileManager.default.fileExists(atPath: scriptsPath) else {
210-
throw QuickPkgError.scriptNotFound(scriptsPath)
211-
}
212-
scriptsDir = scriptsURL
213-
}
214-
215-
if preinstall != nil || postinstall != nil {
216-
let tmpScriptsDir = tempDir.path.appendingPathComponent("scripts")
217-
try FileManager.default.createDirectory(at: tmpScriptsDir, withIntermediateDirectories: true)
218-
219-
// Copy existing scripts folder if provided
220-
if let existingScripts = scriptsDir {
221-
for item in try FileManager.default.contentsOfDirectory(at: existingScripts, includingPropertiesForKeys: nil) {
222-
try FileManager.default.copyItem(at: item, to: tmpScriptsDir.appendingPathComponent(item.lastPathComponent))
223-
}
224-
}
225-
226-
// Add preinstall script
227-
if let preinstallPath = preinstall {
228-
let preinstallURL = URL(filePath: preinstallPath)
229-
guard FileManager.default.fileExists(atPath: preinstallPath) else {
230-
throw QuickPkgError.scriptNotFound(preinstallPath)
231-
}
232-
let destURL = tmpScriptsDir.appendingPathComponent("preinstall")
233-
if FileManager.default.fileExists(atPath: destURL.path) {
234-
throw QuickPkgError.scriptConflict("preinstall script already exists in scripts folder")
235-
}
236-
try FileManager.default.copyItem(at: preinstallURL, to: destURL)
237-
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: destURL.path)
238-
logger.log("Copied preinstall script to \(destURL.path)", level: 1)
239-
}
240-
241-
// Add postinstall script
242-
if let postinstallPath = postinstall {
243-
let postinstallURL = URL(filePath: postinstallPath)
244-
guard FileManager.default.fileExists(atPath: postinstallPath) else {
245-
throw QuickPkgError.scriptNotFound(postinstallPath)
246-
}
247-
let destURL = tmpScriptsDir.appendingPathComponent("postinstall")
248-
if FileManager.default.fileExists(atPath: destURL.path) {
249-
throw QuickPkgError.scriptConflict("postinstall script already exists in scripts folder")
250-
}
251-
try FileManager.default.copyItem(at: postinstallURL, to: destURL)
252-
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: destURL.path)
253-
logger.log("Copied postinstall script to \(destURL.path)", level: 1)
254-
}
255-
256-
scriptsDir = tmpScriptsDir
257-
}
149+
let scriptsDir = try prepareScripts(tempDir: tempDir, logger: logger)
258150

259151
// Build the package
260152
let packageBuilder = PackageBuilder(executor: executor, logger: logger)
261-
262-
// Determine output path
263153
let outputPath = determineOutputPath(
264154
output: output,
265155
name: metadata.name,
@@ -291,6 +181,20 @@ struct QuickPkg: AsyncParsableCommand {
291181

292182
// MARK: - Helpers
293183

184+
/// Normalize a file path (expand tilde, standardize, remove trailing slash)
185+
private func normalizePath(_ path: String) -> String {
186+
var result = path
187+
if result.hasPrefix("~") {
188+
result = NSString(string: result).expandingTildeInPath
189+
}
190+
result = (result as NSString).standardizingPath
191+
if result.hasSuffix("/") {
192+
result = String(result.dropLast())
193+
}
194+
return result
195+
}
196+
197+
/// Find applications in the given directories
294198
private func findApplications(in directories: [URL]) -> [URL] {
295199
var apps: [URL] = []
296200
let fm = FileManager.default
@@ -309,6 +213,107 @@ struct QuickPkg: AsyncParsableCommand {
309213
return apps
310214
}
311215

216+
/// Validate exactly one application exists and return it
217+
private func validateSingleApplication(in directories: [URL]) throws -> URL {
218+
let apps = findApplications(in: directories)
219+
guard !apps.isEmpty else {
220+
throw QuickPkgError.noApplicationFound
221+
}
222+
guard apps.count == 1 else {
223+
throw QuickPkgError.multipleApplicationsFound(apps.map(\.path))
224+
}
225+
return apps[0]
226+
}
227+
228+
/// Find the application from the input source
229+
private func findApplication(
230+
at url: URL,
231+
inputType: InputType,
232+
tempDir: TempDirectory,
233+
dmgManager: DMGManager,
234+
archiveExtractor: ArchiveExtractor
235+
) async throws -> URL {
236+
guard FileManager.default.fileExists(atPath: url.path) else {
237+
throw QuickPkgError.fileNotFound(url.path)
238+
}
239+
240+
switch inputType {
241+
case .app:
242+
return url
243+
244+
case .dmg:
245+
let mountPoints = try await dmgManager.attach(url)
246+
return try validateSingleApplication(in: mountPoints)
247+
248+
case .zip:
249+
let extractDir = tempDir.path.appendingPathComponent("unarchive")
250+
try FileManager.default.createDirectory(at: extractDir, withIntermediateDirectories: true)
251+
try await archiveExtractor.extractZip(url, to: extractDir)
252+
return try validateSingleApplication(in: [extractDir])
253+
254+
case .xip:
255+
let extractDir = tempDir.path.appendingPathComponent("unarchive")
256+
try FileManager.default.createDirectory(at: extractDir, withIntermediateDirectories: true)
257+
try await archiveExtractor.extractXip(url, to: extractDir)
258+
return try validateSingleApplication(in: [extractDir])
259+
}
260+
}
261+
262+
/// Prepare the scripts directory, merging --scripts with --preinstall/--postinstall if needed
263+
private func prepareScripts(tempDir: TempDirectory, logger: Logger) throws -> URL? {
264+
var scriptsDir: URL?
265+
266+
if let scriptsPath = scripts {
267+
let scriptsURL = URL(filePath: scriptsPath)
268+
guard FileManager.default.fileExists(atPath: scriptsPath) else {
269+
throw QuickPkgError.scriptNotFound(scriptsPath)
270+
}
271+
scriptsDir = scriptsURL
272+
}
273+
274+
guard preinstall != nil || postinstall != nil else {
275+
return scriptsDir
276+
}
277+
278+
let tmpScriptsDir = tempDir.path.appendingPathComponent("scripts")
279+
try FileManager.default.createDirectory(at: tmpScriptsDir, withIntermediateDirectories: true)
280+
281+
// Copy existing scripts folder if provided
282+
if let existingScripts = scriptsDir {
283+
for item in try FileManager.default.contentsOfDirectory(at: existingScripts, includingPropertiesForKeys: nil) {
284+
try FileManager.default.copyItem(at: item, to: tmpScriptsDir.appendingPathComponent(item.lastPathComponent))
285+
}
286+
}
287+
288+
// Add preinstall script
289+
if let preinstallPath = preinstall {
290+
try copyScript(from: preinstallPath, to: tmpScriptsDir, name: "preinstall", logger: logger)
291+
}
292+
293+
// Add postinstall script
294+
if let postinstallPath = postinstall {
295+
try copyScript(from: postinstallPath, to: tmpScriptsDir, name: "postinstall", logger: logger)
296+
}
297+
298+
return tmpScriptsDir
299+
}
300+
301+
/// Copy a script to the scripts directory with proper permissions
302+
private func copyScript(from sourcePath: String, to scriptsDir: URL, name: String, logger: Logger) throws {
303+
let sourceURL = URL(filePath: sourcePath)
304+
guard FileManager.default.fileExists(atPath: sourcePath) else {
305+
throw QuickPkgError.scriptNotFound(sourcePath)
306+
}
307+
let destURL = scriptsDir.appendingPathComponent(name)
308+
if FileManager.default.fileExists(atPath: destURL.path) {
309+
throw QuickPkgError.scriptConflict("\(name) script already exists in scripts folder")
310+
}
311+
try FileManager.default.copyItem(at: sourceURL, to: destURL)
312+
try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: destURL.path)
313+
logger.log("Copied \(name) script to \(destURL.path)", level: 1)
314+
}
315+
316+
/// Determine the output path for the package
312317
private func determineOutputPath(output: String?, name: String, version: String, identifier: String) -> String {
313318
let defaultName = "{name}-{version}.pkg"
314319
var path: String

0 commit comments

Comments
 (0)