Skip to content

Commit

Permalink
feat: handle relative symbolic links (#98)
Browse files Browse the repository at this point in the history
* add func to create symbolic link with RelativePath as destination

* add failing test with relative sym link

* fix handling of relative symbolic link

* formatting

* add createSymbolicLink with RelativePath to FileSysteming protocol

* add private helper func for creating symbolic links with absolute and relative path

* formatting

* Make authentication optional

---------

Co-authored-by: fortmarek <[email protected]>
  • Loading branch information
KaiOelfke and fortmarek authored Jan 9, 2025
1 parent 95fccd7 commit c60a236
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 13 deletions.
20 changes: 17 additions & 3 deletions Sources/FileSystem/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,12 @@ public protocol FileSysteming {
/// - to: The path the symlink points to.
func createSymbolicLink(from: AbsolutePath, to: AbsolutePath) async throws

/// Creates a relative symlink.
/// - Parameters:
/// - from: The path where the symlink is created.
/// - to: The relative path the symlink points to.
func createSymbolicLink(from: AbsolutePath, to: RelativePath) async throws

/// Given a symlink, it resolves it returning the path to the file or directory the symlink is pointing to.
/// - Parameter symlinkPath: The absolute path to the symlink.
/// - Returns: The resolved path.
Expand Down Expand Up @@ -542,10 +548,18 @@ public struct FileSystem: FileSysteming, Sendable {
}

public func createSymbolicLink(from: AbsolutePath, to: AbsolutePath) async throws {
logger?.debug("Creating symbolic link from \(from.pathString) to \(to.pathString).")
try await createSymbolicLink(fromPathString: from.pathString, toPathString: to.pathString)
}

public func createSymbolicLink(from: AbsolutePath, to: RelativePath) async throws {
try await createSymbolicLink(fromPathString: from.pathString, toPathString: to.pathString)
}

private func createSymbolicLink(fromPathString: String, toPathString: String) async throws {
logger?.debug("Creating symbolic link from \(fromPathString) to \(toPathString).")
try await NIOFileSystem.FileSystem.shared.createSymbolicLink(
at: FilePath(from.pathString),
withDestination: FilePath(to.pathString)
at: FilePath(fromPathString),
withDestination: FilePath(toPathString)
)
}

Expand Down
16 changes: 7 additions & 9 deletions Sources/Glob/GlobSearch.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,15 @@ public func search(
}

let path = baseURL.absoluteString.removingPercentEncoding ?? baseURL.absoluteString
let symbolicLinkDestination: URL?
if let symbolicLinkDestinationAbsoluteString = try? FileManager.default
.destinationOfSymbolicLink(atPath: path)
{
symbolicLinkDestination = URL(string: symbolicLinkDestinationAbsoluteString)
} else {
symbolicLinkDestination = nil
}
let symbolicLinkDestination = URL(filePath: path).resolvingSymlinksInPath()
var isDirectory: ObjCBool = false

let symbolicLinkDestinationPath: String = symbolicLinkDestination
.path()
.removingPercentEncoding ?? symbolicLinkDestination.path()

guard FileManager.default.fileExists(
atPath: symbolicLinkDestination?.absoluteString ?? path,
atPath: symbolicLinkDestinationPath,
isDirectory: &isDirectory
),
isDirectory.boolValue
Expand Down
34 changes: 34 additions & 0 deletions Tests/FileSystemTests/FileSystemTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,40 @@ final class FileSystemTests: XCTestCase, @unchecked Sendable {
}
}

func test_glob_with_relative_symlink() async throws {
try await subject.runInTemporaryDirectory(prefix: "FileSystem") { temporaryDirectory in
// Given
let frameworkDir = temporaryDirectory.appending(component: "Framework")
let sourceDir = frameworkDir.appending(component: "Source")
let spmResourcesDir = sourceDir.appending(component: "SwiftPackageResources")
let modelSymLinkPath = spmResourcesDir.appending(component: "MyModel.xcdatamodeld")

let actualResourcesDir = frameworkDir.appending(component: "Resources")
let actualModelPath = actualResourcesDir.appending(component: "MyModel.xcdatamodeld")
let versionPath = actualModelPath.appending(component: "MyModel_0.xcdatamodel")

try await subject.makeDirectory(at: spmResourcesDir)
try await subject.makeDirectory(at: actualResourcesDir)
try await subject.makeDirectory(at: actualModelPath)
try await subject.touch(versionPath)

let relativeActualModelPath = try RelativePath(validating: "../../Resources/MyModel.xcdatamodeld")
try await subject.createSymbolicLink(from: modelSymLinkPath, to: relativeActualModelPath)

// When
let got = try await subject.glob(
directory: modelSymLinkPath,
include: ["*.xcdatamodel"]
)
.collect()
.sorted()

// Then
XCTAssertEqual(got.count, 1)
XCTAssertEqual(got.map(\.basename), [versionPath.basename])
}
}

func test_glob_with_double_directory_wildcard() async throws {
try await subject.runInTemporaryDirectory(prefix: "FileSystem") { temporaryDirectory in
// Given
Expand Down
9 changes: 8 additions & 1 deletion Tuist.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import ProjectDescription

let tuist = Tuist(fullHandle: "tuist/FileSystem")
let tuist = Tuist(
fullHandle: "tuist/FileSystem",
project: .tuist(
generationOptions: .options(
optionalAuthentication: true
)
)
)

0 comments on commit c60a236

Please sign in to comment.