Skip to content

Commit 1a1e770

Browse files
committed
Add GitHubCopilotInstallationManager
1 parent 9eb9ff7 commit 1a1e770

1 file changed

Lines changed: 97 additions & 0 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import Foundation
2+
import Terminal
3+
4+
public struct GitHubCopilotInstallationManager {
5+
private static var isInstalling = false
6+
7+
public init() {}
8+
9+
public enum InstallationStatus {
10+
case notInstalled
11+
case installed
12+
}
13+
14+
public func checkInstallation() -> InstallationStatus {
15+
guard let urls = try? GitHubCopilotBaseService.createFoldersIfNeeded()
16+
else { return .notInstalled }
17+
let executableFolderURL = urls.executableURL
18+
let binaryURL = executableFolderURL.appendingPathComponent("copilot")
19+
20+
if !FileManager.default.fileExists(atPath: binaryURL.path) {
21+
return .notInstalled
22+
}
23+
24+
return .installed
25+
}
26+
27+
public enum InstallationStep {
28+
case downloading
29+
case uninstalling
30+
case decompressing
31+
case done
32+
}
33+
34+
public enum Error: Swift.Error, LocalizedError {
35+
case isInstalling
36+
case failedToFindLanguageServer
37+
38+
public var errorDescription: String? {
39+
switch self {
40+
case .isInstalling:
41+
return "Language server is installing."
42+
case .failedToFindLanguageServer:
43+
return "Failed to find language server. Please open an issue on GitHub."
44+
}
45+
}
46+
}
47+
48+
public func installLatestVersion() -> AsyncThrowingStream<InstallationStep, Swift.Error> {
49+
AsyncThrowingStream<InstallationStep, Swift.Error> { continuation in
50+
Task {
51+
guard !GitHubCopilotInstallationManager.isInstalling else {
52+
continuation.finish(throwing: Error.isInstalling)
53+
return
54+
}
55+
GitHubCopilotInstallationManager.isInstalling = true
56+
defer { GitHubCopilotInstallationManager.isInstalling = false }
57+
do {
58+
continuation.yield(.downloading)
59+
let urls = try GitHubCopilotBaseService.createFoldersIfNeeded()
60+
let executable = Bundle.main.bundleURL.appendingPathComponent("Contents/Applications/CopilotForXcodeExtensionService.app/Contents/Resources/copilot")
61+
print(executable)
62+
guard FileManager.default.fileExists(atPath: executable.path) else {
63+
throw Error.failedToFindLanguageServer
64+
}
65+
66+
let targetURL = urls.executableURL.appendingPathComponent("copilot")
67+
68+
try FileManager.default.copyItem(
69+
at: executable,
70+
to: targetURL
71+
)
72+
73+
// update permission 755
74+
try FileManager.default.setAttributes(
75+
[.posixPermissions: 0o755],
76+
ofItemAtPath: targetURL.path
77+
)
78+
79+
continuation.yield(.done)
80+
continuation.finish()
81+
} catch {
82+
continuation.finish(throwing: error)
83+
}
84+
}
85+
}
86+
}
87+
88+
public func uninstall() async throws {
89+
guard let urls = try? GitHubCopilotBaseService.createFoldersIfNeeded()
90+
else { return }
91+
let executableFolderURL = urls.executableURL
92+
let binaryURL = executableFolderURL.appendingPathComponent("copilot")
93+
if FileManager.default.fileExists(atPath: binaryURL.path) {
94+
try FileManager.default.removeItem(at: binaryURL)
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)