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
18 changes: 9 additions & 9 deletions Sources/LucaCore/Core/Installer/Installer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public struct Installer {
let installationDestination = fileManager.toolsFolder
.appending(components: tool.name, tool.version)
let binaryPath = try binaryFinder.findBinary(atPath: installationDestination.path)
let enrichedTool = EnrichedTool(
let resolvedTool = Tool(
name: tool.name,
version: tool.version,
url: tool.url,
Expand All @@ -96,8 +96,8 @@ public struct Installer {
checksum: tool.checksum,
algorithm: tool.algorithm
)
try permissionManager.setExecutablePermission(for: enrichedTool)
let symLink = try symLinker.setSymLink(for: enrichedTool)
try permissionManager.setExecutablePermission(for: resolvedTool)
let symLink = try symLinker.setSymLink(for: resolvedTool)
printer.printFormatted("\(.raw("🔗 Recreated symlink at \(symLink.path)"))")

printer.printFormatted("\(.primary("🙌 Tool \(tool.name) version \(tool.version) installed for the current project."))")
Expand Down Expand Up @@ -151,7 +151,7 @@ public struct Installer {
installationDestination: installationDestination
)

let enrichedTool = EnrichedTool(
let resolvedTool = Tool(
name: tool.name,
version: tool.version,
url: tool.url,
Expand All @@ -160,11 +160,11 @@ public struct Installer {
checksum: tool.checksum,
algorithm: tool.algorithm
)
try permissionManager.setExecutablePermission(for: enrichedTool)
try permissionManager.setExecutablePermission(for: resolvedTool)

printer.printFormatted("\(.raw("💾 Installed \(tool.name) version \(tool.version) at \(installationDestination.path)"))")

let symLink = try symLinker.setSymLink(for: enrichedTool)
let symLink = try symLinker.setSymLink(for: resolvedTool)
printer.printFormatted("\(.raw("🔗 Created symlink to \(symLink.path)"))")
}

Expand All @@ -184,7 +184,7 @@ public struct Installer {
installationDestination: installationDestination
)

let enrichedTool = EnrichedTool(
let resolvedTool = Tool(
name: tool.name,
version: tool.version,
url: tool.url,
Expand All @@ -193,11 +193,11 @@ public struct Installer {
checksum: nil,
algorithm: nil
)
try permissionManager.setExecutablePermission(for: enrichedTool)
try permissionManager.setExecutablePermission(for: resolvedTool)

printer.printFormatted("\(.raw("💾 Installed \(tool.name) version \(tool.version) at \(installationDestination.path)"))")

let symLink = try symLinker.setSymLink(for: enrichedTool)
let symLink = try symLinker.setSymLink(for: resolvedTool)
printer.printFormatted("\(.raw("🔗 Created symlink to \(symLink.path)"))")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ struct PermissionManager: PermissionManaging {
self.fileManager = fileManager
}

func setExecutablePermission(for tool: EnrichedTool) throws {
func setExecutablePermission(for tool: Tool) throws {
let destinationFile = fileManager.toolsFolder
.appending(components: tool.name, tool.version)
.appending(components: tool.desiredBinaryName ?? tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)

guard fileManager.fileExists(atPath: destinationFile.path) else {
throw PermissionManagerError.missingFile(destinationFile.path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import Foundation

protocol PermissionManaging {
func setExecutablePermission(for tool: EnrichedTool) throws
func setExecutablePermission(for tool: Tool) throws
}
8 changes: 4 additions & 4 deletions Sources/LucaCore/Core/SymLinker/SymLinker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ struct SymLinker: SymLinking {
// MARK: - Internal

@discardableResult
func setSymLink(for tool: EnrichedTool) throws -> URL {
func setSymLink(for tool: Tool) throws -> URL {
let symLinkFile = fileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)

let destinationFile = fileManager.toolsFolder
.appending(components: tool.name, tool.version)
.appending(components: tool.desiredBinaryName ?? tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)

if !fileManager.fileExists(atPath: destinationFile.path) {
throw SymLinkerError.missingBinaryFile(binaryName: tool.binaryName, expectedLocation: destinationFile.path)
throw SymLinkerError.missingBinaryFile(binaryName: tool.expectedBinaryName, expectedLocation: destinationFile.path)
}
if symLinkExists(atPath: symLinkFile.path) {
try fileManager.removeItem(at: symLinkFile)
Expand Down
2 changes: 1 addition & 1 deletion Sources/LucaCore/Core/SymLinker/SymLinking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import Foundation

protocol SymLinking {
func setSymLink(for tool: EnrichedTool) throws -> URL
func setSymLink(for tool: Tool) throws -> URL
}
28 changes: 4 additions & 24 deletions Sources/LucaCore/Models/Tool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,30 +28,10 @@ extension Tool {
if let binaryPath { return URL(fileURLWithPath: binaryPath).lastPathComponent }
return name
}
}

struct EnrichedTool: Codable {
/// Logical name of the tool (used for directory hierarchy).
let name: String
/// Version string (used to build folder names and allow side‑by‑side installs).
let version: String
/// Remote URL to an archive containing the tool or an executable file.
let url: URL
/// Path (possibly nested) to the binary inside the unzipped archive.
let binaryPath: String
/// Name of the binary stored locally. Requires `url` to point to an executable file, ignored otherwise.
let desiredBinaryName: String?
/// The checksum hash of asset associated with the tool.
let checksum: String?
/// The algorithm used to generate the checksum.
let algorithm: ChecksumAlgorithm?

/// Basename of the binary derived from `binaryPath` if available, otherwise falls back to `name`.
var binaryName: String {
if let desiredBinaryName {
return desiredBinaryName
}
return URL(fileURLWithPath: binaryPath)
.lastPathComponent
/// Resolves the path to the binary file within the tool's installation directory.
/// Priority: desiredBinaryName > binaryPath > name.
var effectiveBinaryPath: String {
desiredBinaryName ?? binaryPath ?? name
}
}
15 changes: 8 additions & 7 deletions Tests/Core/PermissionManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ struct PermissionManagerTests {
let permissionManagerFileManager = PermissionManagerFileManagerMock(fileManager: fileManager)
let sut = PermissionManager(fileManager: permissionManagerFileManager)

let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -25,7 +25,7 @@ struct PermissionManagerTests {

let binaryPath = permissionManagerFileManager.toolsFolder
.appending(components: tool.name, tool.version)
.appending(component: tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)
try fileManager.createDirectory(at: binaryPath.deletingLastPathComponent(), withIntermediateDirectories: true)
#expect(fileManager.createFile(atPath: binaryPath.path, contents: Data()))

Expand All @@ -41,11 +41,7 @@ struct PermissionManagerTests {
let permissionManagerFileManager = PermissionManagerFileManagerMock(fileManager: fileManager)
let sut = PermissionManager(fileManager: permissionManagerFileManager)

let filePath = permissionManagerFileManager.toolsFolder
.appending(components: "ToggleGen", "1.0.0", "bin/ToggleGen")
.path

let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -55,6 +51,11 @@ struct PermissionManagerTests {
algorithm: nil
)

let filePath = permissionManagerFileManager.toolsFolder
.appending(components: tool.name, tool.version)
.appending(path: tool.effectiveBinaryPath)
.path

#expect(throws: PermissionManager.PermissionManagerError.missingFile(filePath)) {
try sut.setExecutablePermission(for: tool)
}
Expand Down
32 changes: 16 additions & 16 deletions Tests/Core/SymLinkerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ struct SymLinkerTests {

@Test
func createSymLink_toolExists_binaryPathSpecified() throws {
let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -24,14 +24,14 @@ struct SymLinkerTests {

let toolFilePath = symLinkFileManager.toolsFolder
.appending(components: tool.name, "1.0.0")
.appending(components: tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)
try fileManager.createDirectory(at: toolFilePath.deletingLastPathComponent(), withIntermediateDirectories: true)
fileManager.createFile(atPath: toolFilePath.path, contents: Data())

let sut = SymLinker(fileManager: symLinkFileManager)

let symLinkPath = symLinkFileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)
.path

#expect(!symLinkExists(atPath: symLinkPath))
Expand All @@ -44,7 +44,7 @@ struct SymLinkerTests {
@Test
func createSymLink_toolExists_desiredBinaryNameSpecified() throws {
let desiredBinaryName = "togglegen"
let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -58,14 +58,14 @@ struct SymLinkerTests {

let toolFilePath = symLinkFileManager.toolsFolder
.appending(components: tool.name, "1.0.0")
.appending(components: desiredBinaryName)
.appending(component: desiredBinaryName)
try fileManager.createDirectory(at: toolFilePath.deletingLastPathComponent(), withIntermediateDirectories: true)
fileManager.createFile(atPath: toolFilePath.path, contents: Data())

let sut = SymLinker(fileManager: symLinkFileManager)

let symLinkPath = symLinkFileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)
.path

#expect(!symLinkExists(atPath: symLinkPath))
Expand All @@ -77,7 +77,7 @@ struct SymLinkerTests {

@Test
func createSymLink_toolExists_nestedBinaryPathSpecified() throws {
let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -91,14 +91,14 @@ struct SymLinkerTests {

let toolFilePath = symLinkFileManager.toolsFolder
.appending(components: tool.name, "1.0.0")
.appending(components: tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)
try fileManager.createDirectory(at: toolFilePath.deletingLastPathComponent(), withIntermediateDirectories: true)
fileManager.createFile(atPath: toolFilePath.path, contents: Data())

let sut = SymLinker(fileManager: symLinkFileManager)

let symLinkPath = symLinkFileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)
.path

#expect(!symLinkExists(atPath: symLinkPath))
Expand All @@ -110,7 +110,7 @@ struct SymLinkerTests {

@Test
func createSymLink_symLinkExists() throws {
let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -126,13 +126,13 @@ struct SymLinkerTests {
.appending(components: tool.name, "1.0.0")
try fileManager.createDirectory(at: toolFolderPath, withIntermediateDirectories: true)
let toolFilePath = toolFolderPath
.appending(components: tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)
fileManager.createFile(atPath: toolFilePath.path, contents: Data())

let sut = SymLinker(fileManager: symLinkFileManager)

let symLinkPath = symLinkFileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)
.path

try sut.setSymLink(for: tool)
Expand All @@ -143,7 +143,7 @@ struct SymLinkerTests {

@Test
func createSymLink_toolDoesNotExist() throws {
let tool = EnrichedTool(
let tool = Tool(
name: "ToggleGen",
version: "1.0.0",
url: URL(string: "https://example.com")!,
Expand All @@ -158,18 +158,18 @@ struct SymLinkerTests {
let sut = SymLinker(fileManager: symLinkFileManager)

let symLinkPath = symLinkFileManager.activeFolder
.appending(component: tool.binaryName)
.appending(component: tool.expectedBinaryName)
.path

#expect(!symLinkExists(atPath: symLinkPath))

let expectedDestination = symLinkFileManager.toolsFolder
.appending(components: tool.name, tool.version)
.appending(components: tool.binaryPath)
.appending(path: tool.effectiveBinaryPath)

#expect(
throws: SymLinker.SymLinkerError.missingBinaryFile(
binaryName: tool.binaryName,
binaryName: tool.expectedBinaryName,
expectedLocation: expectedDestination.path
)
) {
Expand Down