Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "binary-dependencies-manager",
platforms: [
.macOS(.v11),
.macOS(.v14),
],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
Expand Down
26 changes: 14 additions & 12 deletions Sources/BinaryDependencyManager/DepeneciesResolverRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public struct DependenciesResolverRunner {
repo: dependency.repo,
tag: dependency.tag,
pattern: asset.pattern,
outputFilePath: downloadFileURL.filePath
outputFilePath: downloadFileURL.path(percentEncoded: false)
)

let checksum = try runThrowable("Calculating checksum") { try calculateChecksum(fileURL: downloadFileURL) }
Expand Down Expand Up @@ -123,7 +123,7 @@ public struct DependenciesResolverRunner {
guard try shouldResolve(dependency, asset: asset) else { return }
let tempRootDirURL = fileManager.privateDownloadsDirectoryURL

let tempDir = tempRootDirURL.appending(pathComponents: uuidString, isDirectory: true)
let tempDir = tempRootDirURL.appending(path: uuidString, directoryHint: .isDirectory)
try createDirectoryIfNeeded(at: tempDir)

defer {
Expand All @@ -132,10 +132,10 @@ public struct DependenciesResolverRunner {

// Unpack downloaded file to the temporary dir, so we can traverse through the contents and copy only specified `asset.contents`.
let downloadedFileURL = downloadURL(for: dependency, asset: asset)
try unarchiver.unzip(archivePath: downloadedFileURL.filePath, outputFilePath: tempDir.filePath)
try unarchiver.unzip(archivePath: downloadedFileURL.path(percentEncoded: false), outputFilePath: tempDir.path(percentEncoded: false))

// if we have additional contents directory from dependency, we should use it
let contentsDirectoryURL = tempDir.appending(pathComponents: asset.contents ?? "", isDirectory: true)
let contentsDirectoryURL = tempDir.appending(path: asset.contents ?? "", directoryHint: .isDirectory)

let contents = try fileManager.contentsOfDirectory(at: contentsDirectoryURL)

Expand All @@ -144,8 +144,8 @@ public struct DependenciesResolverRunner {
try createDirectoryIfNeeded(at: outputDirectory)

for item in contents {
let downloadedFileURL = contentsDirectoryURL.appending(pathComponents: item, isDirectory: false)
let destinationFileURL = outputDirectory.appending(pathComponents: item, isDirectory: false)
let downloadedFileURL = contentsDirectoryURL.appending(path: item, directoryHint: .notDirectory)
let destinationFileURL = outputDirectory.appending(path: item, directoryHint: .notDirectory)
if fileManager.fileExists(at: destinationFileURL) {
Logger.log("[Unzip] Removing \(destinationFileURL.relativeFilePath).")
try fileManager.removeItem(at: destinationFileURL)
Expand Down Expand Up @@ -182,17 +182,18 @@ public struct DependenciesResolverRunner {
/// Location of directory where the downloaded dependency will be placed
func downloadDirectoryURL(for dependency: Dependency, asset: Dependency.Asset) -> URL {
downloadsDirectoryURL
.appending(pathComponents: dependency.repo, dependency.tag, asset.outputDirectory ?? "", isDirectory: true)
.appending(components: dependency.repo, dependency.tag, asset.outputDirectory ?? "", directoryHint: .isDirectory)
}

/// Location of the file where the downloaded dependency will be placed
func downloadURL(for dependency: Dependency, asset: Dependency.Asset) -> URL {
downloadDirectoryURL(for: dependency, asset: asset)
.appending(pathComponents: asset.checksum + ".zip", isDirectory: false)
.appending(path: asset.checksum + ".zip", directoryHint: .notDirectory)
}

func outputDirectoryURL(for dependency: Dependency, asset: Dependency.Asset) -> URL {
outputDirectoryURL.appending(pathComponents: dependency.repo, asset.outputDirectory ?? "", isDirectory: true)
outputDirectoryURL
.appending(components: dependency.repo, asset.outputDirectory ?? "", directoryHint: .isDirectory)
}

func outputDirectoryHashFile(for dependency: Dependency, asset: Dependency.Asset) throws -> URL {
Expand All @@ -209,11 +210,12 @@ public struct DependenciesResolverRunner {

filename = ".\(filename).hash"

return outputDirectoryURL(for: dependency, asset: asset).appending(pathComponents: filename, isDirectory: false)
return outputDirectoryURL(for: dependency, asset: asset)
.appending(path: filename, directoryHint: .notDirectory)
}

var downloadsDirectoryURL: URL {
cacheDirectoryURL.appending(pathComponents: ".downloads", isDirectory: true)
cacheDirectoryURL.appending(path: ".downloads", directoryHint: .isDirectory)
}

func createDirectoryIfNeeded(at url: URL) throws {
Expand Down Expand Up @@ -241,6 +243,6 @@ public struct DependenciesResolverRunner {

extension FileManagerProtocol {
var privateDownloadsDirectoryURL: URL {
temporaryDirectory.appending(pathComponents: "PrivateDownloads", isDirectory: true)
temporaryDirectory.appending(path: "PrivateDownloads", directoryHint: .isDirectory)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,19 @@ extension FileManagerProtocol {
}

public func fileExists(at url: URL) -> Bool {
fileExists(atPath: url.filePath)
fileExists(atPath: url.path(percentEncoded: false))
}

public func contents(at url: URL) -> Data? {
contents(atPath: url.filePath)
contents(atPath: url.path(percentEncoded: false))
}

public func contentsOfDirectory(at url: URL) throws -> [String] {
try contentsOfDirectory(atPath: url.filePath)
try contentsOfDirectory(atPath: url.path(percentEncoded: false))
}

func createFile(at url: URL, contents data: Data?) -> Bool {
createFile(atPath: url.filePath, contents: data, attributes: .none)
createFile(atPath: url.path(percentEncoded: false), contents: data, attributes: .none)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/CommandLine/Commands/CleanCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ struct CleanCommand: ParsableCommand {

outputDirectoryPath = configurationReader
.resolveOutputDirectoryURL(outputDirectoryPath)
.filePath
.path(percentEncoded: false)
cacheDirectoryPath = configurationReader
.resolveCacheDirectoryURL(cacheDirectoryPath)
.filePath
.path(percentEncoded: false)
}

func run() throws {
Expand Down
4 changes: 2 additions & 2 deletions Sources/CommandLine/Commands/ResolveCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ struct ResolveCommand: ParsableCommand {
// Paths from CLI arguments take precedence over those from the configuration file.
outputDirectoryPath = configurationReader
.resolveOutputDirectoryURL(outputDirectoryPath ?? configuration.outputDirectory)
.filePath
.path(percentEncoded: false)
cacheDirectoryPath = configurationReader
.resolveCacheDirectoryURL(cacheDirectoryPath ?? configuration.cacheDirectory)
.filePath
.path(percentEncoded: false)

}

Expand Down
8 changes: 4 additions & 4 deletions Sources/Utils/CLI/CLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public enum CLI {
/// - currentDirectoryURL: A working directory URL where executable will be launched.
@discardableResult
public static func run(executableURL: URL, arguments: [String], currentDirectoryURL: URL? = .none) throws -> String {
Logger.log("[Run] \(executableURL.filePath) \(arguments.joined(separator: " "))")
Logger.log("[Run] \(executableURL.path(percentEncoded: false)) \(arguments.joined(separator: " "))")

let stdoutPipe = Pipe()
let stderrPipe = Pipe()
Expand All @@ -39,16 +39,16 @@ public enum CLI {

let stdoutData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
guard let stdout = String(data: stdoutData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {
throw GenericError("Can't parse stdout output from the \(executableURL.filePath)")
throw GenericError("Can't parse stdout output from the \(executableURL.path(percentEncoded: false))")
}

let stderrData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
guard let stderr = String(data: stderrData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) else {
throw GenericError("Can't parse stderr output from the \(executableURL.filePath)")
throw GenericError("Can't parse stderr output from the \(executableURL.path(percentEncoded: false))")
}

guard process.terminationStatus == 0 else {
throw GenericError("Error running \(executableURL.filePath) with arguments \(arguments.joined(separator: " ")). Output:\n\(stdout).\nError:\n\(stderr).")
throw GenericError("Error running \(executableURL.path(percentEncoded: false)) with arguments \(arguments.joined(separator: " ")). Output:\n\(stdout).\nError:\n\(stderr).")
}

return stdout
Expand Down
7 changes: 1 addition & 6 deletions Sources/Utils/String+fileURL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ extension String {
///
/// - Note: Used to avoid deprecation warnings.
public var asFileURL: URL {
let url = if #available(macOS 13.0, *) {
URL(filePath: self)
} else {
URL(fileURLWithPath: self)
}
return url.standardizedFileURL.absoluteURL
URL(filePath: self).standardizedFileURL.absoluteURL
}
}
42 changes: 1 addition & 41 deletions Sources/Utils/URL+filePath.swift
Original file line number Diff line number Diff line change
@@ -1,51 +1,11 @@
import Foundation

extension URL {
/// Returns a URL constructed by appending the given variadic list of path components to self.
///
/// - Note: Used to avoid deprecation warning for newer macOS.
///
/// - Parameters:
/// - pathComponents: The list of the path components to add.
/// - isDirectory: A `Bool` flag to whether this URL will point to a directory.
///
/// - Returns: The URL with the appended path components.
public func appending(pathComponents: String..., isDirectory: Bool) -> URL {
var result = self

// Remove empty path components to avoid double slashes.
let pathComponents = pathComponents.filter { !$0.isEmpty }

if #available(macOS 13.0, *) {
for path in pathComponents[0..<max(pathComponents.count - 1, 0)] {
result.append(path: path, directoryHint: .isDirectory)
}
if let lastComponent = pathComponents.last {
result.append(path: lastComponent, directoryHint: isDirectory ? .isDirectory : .inferFromPath)
}
}
else {
result.appendPathComponent(pathComponents.joined(separator: "/"), isDirectory: isDirectory)
}
return result
}

/// Returns the path component of the URL, removing any percent-encoding.
///
/// - Note: Used to avoid deprecation warning for newer macOS.
public var filePath: String {
if #available(macOS 13.0, *) {
path(percentEncoded: false)
}
else {
path
}
}

/// Returns a relative path to the file from the current working directory.
///
/// - Note: Used in the logs to simplify output.
public var relativeFilePath: String {
let filePath = path(percentEncoded: false)
guard filePath.hasPrefix(FileManager.default.currentDirectoryPath + "/") else {
return filePath
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ final class BinaryDependenciesConfigurationReaderTests: XCTestCase {
let sut = makeReader(withFiles: ["/the/path/.binary-dependencies.yaml"])
let url = try sut.resolveConfigurationFileURL("/the/path/.binary-dependencies.yaml")
XCTAssertEqual(url.path, "/the/path/.binary-dependencies.yaml")
XCTAssertEqual(url.filePath, "/the/path/.binary-dependencies.yaml")
XCTAssertEqual(url.path(percentEncoded: false), "/the/path/.binary-dependencies.yaml")
}

func test_resolveConfigurationFileURL_fallbackToDefault() throws {
let sut = makeReader(withFiles: [".binary-dependencies.yaml".asFileURL.filePath])
let sut = makeReader(withFiles: [".binary-dependencies.yaml".asFileURL.path(percentEncoded: false)])
let url = try sut.resolveConfigurationFileURL(nil)
XCTAssertEqual(url.lastPathComponent, ".binary-dependencies.yaml")
}
Expand Down Expand Up @@ -55,7 +55,7 @@ final class BinaryDependenciesConfigurationReaderTests: XCTestCase {
pattern: pattern1
checksum: "check1"
"""
let filePath = ".binary-dependencies.yaml".asFileURL.filePath
let filePath = ".binary-dependencies.yaml".asFileURL.path(percentEncoded: false)
let data = Data(yamlString.utf8)
let mockFileManager = FileManagerProtocolMock()
mockFileManager.existingFiles = [filePath]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct DependenciesResolverRunnerDownloadTests {

let tempDir: URL = {
FileManager.default.temporaryDirectory
.appending(pathComponents: "binary-dependency-manager-tests", UUID().uuidString, isDirectory: true)
.appending(components: "binary-dependency-manager-tests", UUID().uuidString, directoryHint: .isDirectory)
}()

func makeRunner(
Expand All @@ -31,8 +31,8 @@ struct DependenciesResolverRunnerDownloadTests {
) -> DependenciesResolverRunner {
DependenciesResolverRunner.mock(
dependencies: dependencies ?? [sampleDependency],
outputDirectoryURL: tempDir.appending(pathComponents: "output", isDirectory: true),
cacheDirectoryURL: tempDir.appending(pathComponents: "cache", isDirectory: true),
outputDirectoryURL: tempDir.appending(path: "output", directoryHint: .isDirectory),
cacheDirectoryURL: tempDir.appending(path: "cache", directoryHint: .isDirectory),
fileManager: fileManager,
uuidString: "mock-uuid",
dependenciesDownloader: downloaderMock,
Expand Down Expand Up @@ -71,7 +71,7 @@ struct DependenciesResolverRunnerDownloadTests {
#expect(downloadCall.repo == sampleDependency.repo)
#expect(downloadCall.tag == sampleDependency.tag)
#expect(downloadCall.pattern == sampleAsset.pattern)
#expect(downloadCall.outputFilePath == downloadURL.filePath)
#expect(downloadCall.outputFilePath == downloadURL.path(percentEncoded: false))

// Verify checksum was calculated
#expect(checksumCalculatorMock.checksumCalls.contains(downloadURL))
Expand All @@ -92,7 +92,7 @@ struct DependenciesResolverRunnerDownloadTests {
let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset)

// File already exists
fileManager.existingFiles.insert(downloadURL.filePath)
fileManager.existingFiles.insert(downloadURL.path(percentEncoded: false))
// Checksum matches
checksumCalculatorMock.checksums[downloadURL] = sampleAsset.checksum

Expand Down Expand Up @@ -145,7 +145,7 @@ struct DependenciesResolverRunnerDownloadTests {
let downloadURL = runner.downloadURL(for: sampleDependency, asset: sampleAsset)

// File already exists
fileManager.existingFiles.insert(downloadURL.filePath)
fileManager.existingFiles.insert(downloadURL.path(percentEncoded: false))
// First checksum call returns wrong checksum, second returns correct
checksumCalculatorMock.checksums[downloadURL] = "wrong_checksum"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ struct DependenciesResolverRunnerFileURLTests {
cacheDirectoryURL: cacheRoot
)
let downloadsDir = runner.downloadsDirectoryURL
#expect(downloadsDir.filePath.hasSuffix("/cache/.downloads/"))
#expect(downloadsDir.filePath.hasPrefix(FileManager.default.currentDirectoryPath))
#expect(downloadsDir.path(percentEncoded: false).hasSuffix("/cache/.downloads/"))
#expect(downloadsDir.path(percentEncoded: false).hasPrefix(FileManager.default.currentDirectoryPath))
#expect(
downloadsDir == FileManager.default.currentDirectoryPath.asFileURL
.appending(pathComponents: "cache", ".downloads", isDirectory: true)
.appending(components: "cache", ".downloads", directoryHint: .isDirectory)
)
}

Expand All @@ -37,8 +37,8 @@ struct DependenciesResolverRunnerFileURLTests {
cacheDirectoryURL: "/cache".asFileURL
)
let downloadsDir = runner.downloadsDirectoryURL
#expect(downloadsDir.filePath.hasSuffix("/cache/.downloads/"))
#expect(!downloadsDir.filePath.hasPrefix(FileManager.default.currentDirectoryPath))
#expect(downloadsDir.path(percentEncoded: false).hasSuffix("/cache/.downloads/"))
#expect(!downloadsDir.path(percentEncoded: false).hasPrefix(FileManager.default.currentDirectoryPath))
#expect(downloadsDir == "/cache/.downloads/".asFileURL)
#expect(downloadsDir.path == "/cache/.downloads")
}
Expand All @@ -51,7 +51,7 @@ struct DependenciesResolverRunnerFileURLTests {
cacheDirectoryURL: cacheRoot
)
let url = runner.downloadDirectoryURL(for: dependency, asset: asset)
#expect(url.filePath.hasSuffix("/cache/.downloads/owner/repo/1.0.0/Lib/"))
#expect(url.path(percentEncoded: false).hasSuffix("/cache/.downloads/owner/repo/1.0.0/Lib/"))
let downloadURL = runner.downloadURL(for: dependency, asset: asset)
#expect(downloadURL.lastPathComponent == "abc123.zip")
}
Expand All @@ -64,7 +64,7 @@ struct DependenciesResolverRunnerFileURLTests {
cacheDirectoryURL: cacheRoot
)
let out = runner.outputDirectoryURL(for: dependency, asset: asset)
#expect(out.filePath.hasSuffix("/output/owner/repo/Lib/"))
#expect(out.path(percentEncoded: false).hasSuffix("/output/owner/repo/Lib/"))
}

@Test
Expand All @@ -75,7 +75,7 @@ struct DependenciesResolverRunnerFileURLTests {
cacheDirectoryURL: cacheRoot
)
let hashURL = try runner.outputDirectoryHashFile(for: dependency, asset: asset)
#expect(hashURL.filePath.hasSuffix("/output/owner/repo/Lib/.lib_zip_Frameworks_Lib_xcframework.hash"))
#expect(hashURL.path(percentEncoded: false).hasSuffix("/output/owner/repo/Lib/.lib_zip_Frameworks_Lib_xcframework.hash"))
}

@Test
Expand All @@ -97,7 +97,7 @@ struct DependenciesResolverRunnerFileURLTests {
func test_createDirectoryIfNeeded_skips_if_exists() async throws {
let mock = FileManagerProtocolMock()
let url = URL(fileURLWithPath: "/exists/dir")
mock.existingFiles.insert(url.filePath)
mock.existingFiles.insert(url.path(percentEncoded: false))
let runner = DependenciesResolverRunner.mock(
dependencies: [dependency],
outputDirectoryURL: outputRoot,
Expand Down
Loading