Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/update_service.rs
3309 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
use std::{fmt, path::Path};
7
8
use serde::{Deserialize, Serialize};
9
10
use crate::{
11
constants::VSCODE_CLI_UPDATE_ENDPOINT,
12
debug, log, options, spanf,
13
util::{
14
errors::{wrap, AnyError, CodeError, WrappedError},
15
http::{BoxedHttp, SimpleResponse},
16
io::ReportCopyProgress,
17
tar::{self, has_gzip_header},
18
zipper,
19
},
20
};
21
22
/// Implementation of the VS Code Update service for use in the CLI.
23
#[derive(Clone)]
24
pub struct UpdateService {
25
client: BoxedHttp,
26
log: log::Logger,
27
}
28
29
/// Describes a specific release, can be created manually or returned from the update service.
30
#[derive(Clone, Eq, PartialEq)]
31
pub struct Release {
32
pub name: String,
33
pub platform: Platform,
34
pub target: TargetKind,
35
pub quality: options::Quality,
36
pub commit: String,
37
}
38
39
impl std::fmt::Display for Release {
40
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41
write!(f, "{} (commit {})", self.name, self.commit)
42
}
43
}
44
45
#[derive(Deserialize)]
46
struct UpdateServerVersion {
47
pub version: String,
48
pub name: String,
49
}
50
51
fn quality_download_segment(quality: options::Quality) -> &'static str {
52
match quality {
53
options::Quality::Stable => "stable",
54
options::Quality::Insiders => "insider",
55
options::Quality::Exploration => "exploration",
56
}
57
}
58
59
fn get_update_endpoint() -> Result<&'static str, CodeError> {
60
VSCODE_CLI_UPDATE_ENDPOINT.ok_or_else(|| CodeError::UpdatesNotConfigured("no service url"))
61
}
62
63
impl UpdateService {
64
pub fn new(log: log::Logger, http: BoxedHttp) -> Self {
65
UpdateService { client: http, log }
66
}
67
68
pub async fn get_release_by_semver_version(
69
&self,
70
platform: Platform,
71
target: TargetKind,
72
quality: options::Quality,
73
version: &str,
74
) -> Result<Release, AnyError> {
75
let update_endpoint = get_update_endpoint()?;
76
let download_segment = target
77
.download_segment(platform)
78
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
79
let download_url = format!(
80
"{}/api/versions/{}/{}/{}",
81
update_endpoint,
82
version,
83
download_segment,
84
quality_download_segment(quality),
85
);
86
87
let mut response = spanf!(
88
self.log,
89
self.log.span("server.version.resolve"),
90
self.client.make_request("GET", download_url)
91
)?;
92
93
if !response.status_code.is_success() {
94
return Err(response.into_err().await.into());
95
}
96
97
let res = response.json::<UpdateServerVersion>().await?;
98
debug!(self.log, "Resolved version {} to {}", version, res.version);
99
100
Ok(Release {
101
target,
102
platform,
103
quality,
104
name: res.name,
105
commit: res.version,
106
})
107
}
108
109
/// Gets the latest commit for the target of the given quality.
110
pub async fn get_latest_commit(
111
&self,
112
platform: Platform,
113
target: TargetKind,
114
quality: options::Quality,
115
) -> Result<Release, AnyError> {
116
let update_endpoint = get_update_endpoint()?;
117
let download_segment = target
118
.download_segment(platform)
119
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
120
let download_url = format!(
121
"{}/api/latest/{}/{}",
122
update_endpoint,
123
download_segment,
124
quality_download_segment(quality),
125
);
126
127
let mut response = spanf!(
128
self.log,
129
self.log.span("server.version.resolve"),
130
self.client.make_request("GET", download_url)
131
)?;
132
133
if !response.status_code.is_success() {
134
return Err(response.into_err().await.into());
135
}
136
137
let res = response.json::<UpdateServerVersion>().await?;
138
debug!(self.log, "Resolved quality {} to {}", quality, res.version);
139
140
Ok(Release {
141
target,
142
platform,
143
quality,
144
name: res.name,
145
commit: res.version,
146
})
147
}
148
149
/// Gets the download stream for the release.
150
pub async fn get_download_stream(&self, release: &Release) -> Result<SimpleResponse, AnyError> {
151
let update_endpoint = get_update_endpoint()?;
152
let download_segment = release
153
.target
154
.download_segment(release.platform)
155
.ok_or_else(|| CodeError::UnsupportedPlatform(release.platform.to_string()))?;
156
157
let download_url = format!(
158
"{}/commit:{}/{}/{}",
159
update_endpoint,
160
release.commit,
161
download_segment,
162
quality_download_segment(release.quality),
163
);
164
165
let response = self.client.make_request("GET", download_url).await?;
166
if !response.status_code.is_success() {
167
return Err(response.into_err().await.into());
168
}
169
170
Ok(response)
171
}
172
}
173
174
pub fn unzip_downloaded_release<T>(
175
compressed_file: &Path,
176
target_dir: &Path,
177
reporter: T,
178
) -> Result<(), WrappedError>
179
where
180
T: ReportCopyProgress,
181
{
182
match has_gzip_header(compressed_file) {
183
Ok((f, true)) => tar::decompress_tarball(f, target_dir, reporter),
184
Ok((f, false)) => zipper::unzip_file(f, target_dir, reporter),
185
Err(e) => Err(wrap(e, "error checking for gzip header")),
186
}
187
}
188
189
#[derive(Eq, PartialEq, Copy, Clone)]
190
pub enum TargetKind {
191
Server,
192
Archive,
193
Web,
194
Cli,
195
}
196
197
impl TargetKind {
198
fn download_segment(&self, platform: Platform) -> Option<String> {
199
match *self {
200
TargetKind::Server => Some(platform.headless()),
201
TargetKind::Archive => platform.archive(),
202
TargetKind::Web => Some(platform.web()),
203
TargetKind::Cli => Some(platform.cli()),
204
}
205
}
206
}
207
208
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
209
pub enum Platform {
210
LinuxAlpineX64,
211
LinuxAlpineARM64,
212
LinuxX64,
213
LinuxX64Legacy,
214
LinuxARM64,
215
LinuxARM64Legacy,
216
LinuxARM32,
217
LinuxARM32Legacy,
218
DarwinX64,
219
DarwinARM64,
220
WindowsX64,
221
WindowsX86,
222
WindowsARM64,
223
}
224
225
impl Platform {
226
pub fn archive(&self) -> Option<String> {
227
match self {
228
Platform::LinuxX64 => Some("linux-x64".to_owned()),
229
Platform::LinuxARM64 => Some("linux-arm64".to_owned()),
230
Platform::LinuxARM32 => Some("linux-armhf".to_owned()),
231
Platform::DarwinX64 => Some("darwin".to_owned()),
232
Platform::DarwinARM64 => Some("darwin-arm64".to_owned()),
233
Platform::WindowsX64 => Some("win32-x64-archive".to_owned()),
234
Platform::WindowsX86 => Some("win32-archive".to_owned()),
235
Platform::WindowsARM64 => Some("win32-arm64-archive".to_owned()),
236
_ => None,
237
}
238
}
239
pub fn headless(&self) -> String {
240
match self {
241
Platform::LinuxAlpineARM64 => "server-alpine-arm64",
242
Platform::LinuxAlpineX64 => "server-linux-alpine",
243
Platform::LinuxX64 => "server-linux-x64",
244
Platform::LinuxX64Legacy => "server-linux-legacy-x64",
245
Platform::LinuxARM64 => "server-linux-arm64",
246
Platform::LinuxARM64Legacy => "server-linux-legacy-arm64",
247
Platform::LinuxARM32 => "server-linux-armhf",
248
Platform::LinuxARM32Legacy => "server-linux-legacy-armhf",
249
Platform::DarwinX64 => "server-darwin",
250
Platform::DarwinARM64 => "server-darwin-arm64",
251
Platform::WindowsX64 => "server-win32-x64",
252
Platform::WindowsX86 => "server-win32",
253
Platform::WindowsARM64 => "server-win32-arm64",
254
}
255
.to_owned()
256
}
257
258
pub fn cli(&self) -> String {
259
match self {
260
Platform::LinuxAlpineARM64 => "cli-alpine-arm64",
261
Platform::LinuxAlpineX64 => "cli-alpine-x64",
262
Platform::LinuxX64 => "cli-linux-x64",
263
Platform::LinuxX64Legacy => "cli-linux-x64",
264
Platform::LinuxARM64 => "cli-linux-arm64",
265
Platform::LinuxARM64Legacy => "cli-linux-arm64",
266
Platform::LinuxARM32 => "cli-linux-armhf",
267
Platform::LinuxARM32Legacy => "cli-linux-armhf",
268
Platform::DarwinX64 => "cli-darwin-x64",
269
Platform::DarwinARM64 => "cli-darwin-arm64",
270
Platform::WindowsARM64 => "cli-win32-arm64",
271
Platform::WindowsX64 => "cli-win32-x64",
272
Platform::WindowsX86 => "cli-win32",
273
}
274
.to_owned()
275
}
276
277
pub fn web(&self) -> String {
278
format!("{}-web", self.headless())
279
}
280
281
pub fn env_default() -> Option<Platform> {
282
if cfg!(all(
283
target_os = "linux",
284
target_arch = "x86_64",
285
target_env = "musl"
286
)) {
287
Some(Platform::LinuxAlpineX64)
288
} else if cfg!(all(
289
target_os = "linux",
290
target_arch = "aarch64",
291
target_env = "musl"
292
)) {
293
Some(Platform::LinuxAlpineARM64)
294
} else if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
295
Some(Platform::LinuxX64)
296
} else if cfg!(all(target_os = "linux", target_arch = "arm")) {
297
Some(Platform::LinuxARM32)
298
} else if cfg!(all(target_os = "linux", target_arch = "aarch64")) {
299
Some(Platform::LinuxARM64)
300
} else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
301
Some(Platform::DarwinX64)
302
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
303
Some(Platform::DarwinARM64)
304
} else if cfg!(all(target_os = "windows", target_arch = "x86_64")) {
305
Some(Platform::WindowsX64)
306
} else if cfg!(all(target_os = "windows", target_arch = "x86")) {
307
Some(Platform::WindowsX86)
308
} else if cfg!(all(target_os = "windows", target_arch = "aarch64")) {
309
Some(Platform::WindowsARM64)
310
} else {
311
None
312
}
313
}
314
}
315
316
impl fmt::Display for Platform {
317
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
318
f.write_str(match self {
319
Platform::LinuxAlpineARM64 => "LinuxAlpineARM64",
320
Platform::LinuxAlpineX64 => "LinuxAlpineX64",
321
Platform::LinuxX64 => "LinuxX64",
322
Platform::LinuxX64Legacy => "LinuxX64Legacy",
323
Platform::LinuxARM64 => "LinuxARM64",
324
Platform::LinuxARM64Legacy => "LinuxARM64Legacy",
325
Platform::LinuxARM32 => "LinuxARM32",
326
Platform::LinuxARM32Legacy => "LinuxARM32Legacy",
327
Platform::DarwinX64 => "DarwinX64",
328
Platform::DarwinARM64 => "DarwinARM64",
329
Platform::WindowsX64 => "WindowsX64",
330
Platform::WindowsX86 => "WindowsX86",
331
Platform::WindowsARM64 => "WindowsARM64",
332
})
333
}
334
}
335
336