forked from intitni/CopilotForXcode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCodeiumInstallationManager.swift
More file actions
132 lines (114 loc) · 5.15 KB
/
CodeiumInstallationManager.swift
File metadata and controls
132 lines (114 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import Foundation
import Terminal
public struct CodeiumInstallationManager {
private static var isInstalling = false
static let latestSupportedVersion = "1.8.5"
public init() {}
public enum InstallationStatus {
case notInstalled
case installed(String)
case outdated(current: String, latest: String)
case unsupported(current: String, latest: String)
}
public func checkInstallation() -> InstallationStatus {
guard let urls = try? CodeiumSuggestionService.createFoldersIfNeeded()
else { return .notInstalled }
let executableFolderURL = urls.executableURL
let binaryURL = executableFolderURL.appendingPathComponent("language_server")
let versionFileURL = executableFolderURL.appendingPathComponent("version")
if !FileManager.default.fileExists(atPath: binaryURL.path) {
return .notInstalled
}
if FileManager.default.fileExists(atPath: versionFileURL.path),
let versionData = try? Data(contentsOf: versionFileURL),
let version = String(data: versionData, encoding: .utf8)
{
switch version.compare(Self.latestSupportedVersion) {
case .orderedAscending:
return .outdated(current: version, latest: Self.latestSupportedVersion)
case .orderedSame:
return .installed(version)
case .orderedDescending:
return .unsupported(current: version, latest: Self.latestSupportedVersion)
}
}
return .outdated(current: "Unknown", latest: Self.latestSupportedVersion)
}
public enum InstallationStep {
case downloading
case uninstalling
case decompressing
case done
}
public func installLatestVersion() -> AsyncThrowingStream<InstallationStep, Error> {
AsyncThrowingStream<InstallationStep, Error> { continuation in
Task {
guard !CodeiumInstallationManager.isInstalling else {
continuation.finish(throwing: CodeiumError.languageServiceIsInstalling)
return
}
CodeiumInstallationManager.isInstalling = true
defer { CodeiumInstallationManager.isInstalling = false }
do {
continuation.yield(.downloading)
let urls = try CodeiumSuggestionService.createFoldersIfNeeded()
let urlString =
"https://github.com/Exafunction/codeium/releases/download/language-server-v\(Self.latestSupportedVersion)/language_server_macos_\(isAppleSilicon() ? "arm" : "x64").gz"
guard let url = URL(string: urlString) else { return }
// download
let (fileURL, _) = try await URLSession.shared.download(from: url)
let targetURL = urls.executableURL.appendingPathComponent("language_server")
.appendingPathExtension("gz")
try FileManager.default.copyItem(at: fileURL, to: targetURL)
defer { try? FileManager.default.removeItem(at: targetURL) }
// uninstall
continuation.yield(.uninstalling)
try await uninstall()
// extract file
continuation.yield(.decompressing)
let terminal = Terminal()
_ = try await terminal.runCommand(
"/usr/bin/gunzip",
arguments: [targetURL.path],
environment: [:]
)
// update permission 755
try FileManager.default.setAttributes(
[.posixPermissions: 0o755],
ofItemAtPath: targetURL.deletingPathExtension().path
)
// create version file
let data = Self.latestSupportedVersion.data(using: .utf8)
FileManager.default.createFile(
atPath: urls.executableURL.appendingPathComponent("version").path,
contents: data
)
continuation.yield(.done)
continuation.finish()
} catch {
continuation.finish(throwing: error)
}
}
}
}
public func uninstall() async throws {
guard let urls = try? CodeiumSuggestionService.createFoldersIfNeeded()
else { return }
let executableFolderURL = urls.executableURL
let binaryURL = executableFolderURL.appendingPathComponent("language_server")
let versionFileURL = executableFolderURL.appendingPathComponent("version")
if FileManager.default.fileExists(atPath: binaryURL.path) {
try FileManager.default.removeItem(at: binaryURL)
}
if FileManager.default.fileExists(atPath: versionFileURL.path) {
try FileManager.default.removeItem(at: versionFileURL)
}
}
}
func isAppleSilicon() -> Bool {
var result = false
#if arch(arm64)
result = true
#endif
return result
}