mirror of
https://github.com/xtool-org/xtool.git
synced 2026-02-04 11:53:30 +01:00
TemporaryDirectoryRoot
This commit is contained in:
@@ -214,7 +214,7 @@ public struct Planner: Sendable {
|
||||
}
|
||||
|
||||
private func dumpDependencies() async throws -> PackageDependency {
|
||||
let tempDir = try TemporaryDirectory(name: "xtool-dump-\(UUID().uuidString)")
|
||||
let tempDir = try TemporaryDirectory(name: "xtool-dump")
|
||||
let tempFileURL = tempDir.url.appendingPathComponent("dump.json")
|
||||
|
||||
// SwiftPM sometimes prints extraneous data to stdout, so ask
|
||||
|
||||
@@ -2,6 +2,7 @@ import Foundation
|
||||
import SwiftyMobileDevice
|
||||
import ConcurrencyExtras
|
||||
import Dependencies
|
||||
import XUtils
|
||||
|
||||
extension LockdownClient {
|
||||
static let installerLabel = "xtool"
|
||||
@@ -54,9 +55,6 @@ public actor IntegratedInstaller {
|
||||
|
||||
private var appInstaller: AppInstaller?
|
||||
|
||||
private let tempDir = FileManager.default.temporaryDirectoryShim
|
||||
.appendingPathComponent("sh.xtool.Staging")
|
||||
|
||||
private var stage: String?
|
||||
|
||||
private nonisolated let updateTask = LockIsolated<Task<Void, Never>?>(nil)
|
||||
@@ -206,12 +204,9 @@ public actor IntegratedInstaller {
|
||||
public func install(app: URL) async throws -> String {
|
||||
try await self.updateStage(to: "Unpacking app", initialProgress: nil)
|
||||
|
||||
if FileManager.default.fileExists(atPath: tempDir.path) {
|
||||
try? FileManager.default.removeItem(at: tempDir)
|
||||
}
|
||||
|
||||
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
|
||||
defer { try? FileManager.default.removeItem(at: tempDir) }
|
||||
let _tempDir = try TemporaryDirectory(name: "staging")
|
||||
let tempDir = _tempDir.url
|
||||
defer { withExtendedLifetime(_tempDir) {} }
|
||||
|
||||
switch app.pathExtension {
|
||||
case "ipa":
|
||||
@@ -235,7 +230,7 @@ public actor IntegratedInstaller {
|
||||
|
||||
try await self.updateProgress(to: 1)
|
||||
|
||||
let payload = self.tempDir.appendingPathComponent("Payload")
|
||||
let payload = tempDir.appendingPathComponent("Payload")
|
||||
guard let appDir = payload.implicitContents.first(where: { $0.pathExtension == "app" })
|
||||
else { throw Error.appExtractionFailed }
|
||||
|
||||
|
||||
@@ -1,36 +1,5 @@
|
||||
import Foundation
|
||||
|
||||
package struct TemporaryDirectory: ~Copyable {
|
||||
private var shouldDelete = true
|
||||
|
||||
package let url: URL
|
||||
|
||||
package init(name: String) throws {
|
||||
self.url = FileManager.default.temporaryDirectory.appendingPathComponent(name, isDirectory: true)
|
||||
_delete()
|
||||
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
||||
}
|
||||
|
||||
private func _delete() {
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
}
|
||||
|
||||
package consuming func persist() -> URL {
|
||||
shouldDelete = false
|
||||
return url
|
||||
}
|
||||
|
||||
package consuming func persist(at location: URL) throws {
|
||||
try FileManager.default.moveItem(at: url, to: location)
|
||||
// we do this after moving, so that if the move fails we clean up
|
||||
shouldDelete = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
if shouldDelete { _delete() }
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
// AsyncBytes is Darwin-only :/
|
||||
|
||||
|
||||
86
Sources/XUtils/TemporaryDirectory.swift
Normal file
86
Sources/XUtils/TemporaryDirectory.swift
Normal file
@@ -0,0 +1,86 @@
|
||||
import Foundation
|
||||
|
||||
package struct TemporaryDirectory: ~Copyable {
|
||||
private static let debugTmp = ProcessInfo.processInfo.environment["XTL_DEBUG_TMP"] != nil
|
||||
|
||||
private var shouldDelete: Bool
|
||||
package let url: URL
|
||||
|
||||
package init(name: String) throws {
|
||||
do {
|
||||
let basename = name.replacingOccurrences(of: ".", with: "_")
|
||||
self.url = try TemporaryDirectoryRoot.shared.url
|
||||
// ensures uniqueness
|
||||
.appendingPathComponent("tmp-\(basename)-\(UUID().uuidString)")
|
||||
.appendingPathComponent(name, isDirectory: true)
|
||||
self.shouldDelete = true
|
||||
} catch {
|
||||
// non-copyable types can't be partially initialized so we need a stub value
|
||||
self.url = URL(fileURLWithPath: "")
|
||||
self.shouldDelete = false
|
||||
throw error
|
||||
}
|
||||
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
||||
|
||||
if Self.debugTmp {
|
||||
stderrPrint("Created TemporaryDirectory: \(url.path)")
|
||||
}
|
||||
}
|
||||
|
||||
private func _delete() {
|
||||
guard !Self.debugTmp else { return }
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
}
|
||||
|
||||
package consuming func persist(at location: URL) throws {
|
||||
try FileManager.default.moveItem(at: url, to: location)
|
||||
// we do this after moving, so that if the move fails we clean up
|
||||
shouldDelete = false
|
||||
}
|
||||
|
||||
deinit {
|
||||
if shouldDelete { _delete() }
|
||||
}
|
||||
}
|
||||
|
||||
private struct TemporaryDirectoryRoot {
|
||||
static let shared = TemporaryDirectoryRoot()
|
||||
|
||||
private let _url: Result<URL, Errors>
|
||||
var url: URL {
|
||||
get throws(Errors) {
|
||||
try _url.get()
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
let base: URL
|
||||
let env = ProcessInfo.processInfo.environment
|
||||
if let tmpdir = env["XTL_TMPDIR"] ?? env["TMPDIR"] {
|
||||
base = URL(fileURLWithPath: tmpdir)
|
||||
} else {
|
||||
base = FileManager.default.temporaryDirectory
|
||||
}
|
||||
|
||||
let url = base.appendingPathComponent("sh.xtool")
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
self._url = .failure(Errors.tmpdirCreationFailed(url, error))
|
||||
return
|
||||
}
|
||||
self._url = .success(url)
|
||||
}
|
||||
|
||||
enum Errors: Error, CustomStringConvertible {
|
||||
case tmpdirCreationFailed(URL, Error)
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case let .tmpdirCreationFailed(url, error):
|
||||
"Could not create temporary directory at '\(url.path)': \(error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user