Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/cli/src/update_service.rs
5220 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<String, CodeError> {
60
if let Ok(url) = std::env::var("VSCODE_CLI_UPDATE_URL") {
61
if !url.is_empty() {
62
return Ok(url);
63
}
64
}
65
VSCODE_CLI_UPDATE_ENDPOINT
66
.map(|s| s.to_string())
67
.ok_or_else(|| CodeError::UpdatesNotConfigured("no service url"))
68
}
69
70
impl UpdateService {
71
pub fn new(log: log::Logger, http: BoxedHttp) -> Self {
72
UpdateService { client: http, log }
73
}
74
75
pub async fn get_release_by_semver_version(
76
&self,
77
platform: Platform,
78
target: TargetKind,
79
quality: options::Quality,
80
version: &str,
81
) -> Result<Release, AnyError> {
82
let update_endpoint = get_update_endpoint()?;
83
let download_segment = target
84
.download_segment(platform)
85
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
86
let download_url = format!(
87
"{}/api/versions/{}/{}/{}",
88
&update_endpoint,
89
version,
90
download_segment,
91
quality_download_segment(quality),
92
);
93
94
let mut response = spanf!(
95
self.log,
96
self.log.span("server.version.resolve"),
97
self.client.make_request("GET", download_url)
98
)?;
99
100
if !response.status_code.is_success() {
101
return Err(response.into_err().await.into());
102
}
103
104
let res = response.json::<UpdateServerVersion>().await?;
105
debug!(self.log, "Resolved version {} to {}", version, res.version);
106
107
Ok(Release {
108
target,
109
platform,
110
quality,
111
name: res.name,
112
commit: res.version,
113
})
114
}
115
116
/// Gets the latest commit for the target of the given quality.
117
pub async fn get_latest_commit(
118
&self,
119
platform: Platform,
120
target: TargetKind,
121
quality: options::Quality,
122
) -> Result<Release, AnyError> {
123
let update_endpoint = get_update_endpoint()?;
124
let download_segment = target
125
.download_segment(platform)
126
.ok_or_else(|| CodeError::UnsupportedPlatform(platform.to_string()))?;
127
let download_url = format!(
128
"{}/api/latest/{}/{}",
129
&update_endpoint,
130
download_segment,
131
quality_download_segment(quality),
132
);
133
134
let mut response = spanf!(
135
self.log,
136
self.log.span("server.version.resolve"),
137
self.client.make_request("GET", download_url)
138
)?;
139
140
if !response.status_code.is_success() {
141
return Err(response.into_err().await.into());
142
}
143
144
let res = response.json::<UpdateServerVersion>().await?;
145
debug!(self.log, "Resolved quality {} to {}", quality, res.version);
146
147
Ok(Release {
148
target,
149
platform,
150
quality,
151
name: res.name,
152
commit: res.version,
153
})
154
}
155
156
/// Gets the download stream for the release.
157
pub async fn get_download_stream(&self, release: &Release) -> Result<SimpleResponse, AnyError> {
158
let update_endpoint = get_update_endpoint()?;
159
let download_segment = release
160
.target
161
.download_segment(release.platform)
162
.ok_or_else(|| CodeError::UnsupportedPlatform(release.platform.to_string()))?;
163
164
let download_url = format!(
165
"{}/commit:{}/{}/{}",
166
&update_endpoint,
167
release.commit,
168
download_segment,
169
quality_download_segment(release.quality),
170
);
171
172
let response = self.client.make_request("GET", download_url).await?;
173
if !response.status_code.is_success() {
174
return Err(response.into_err().await.into());
175
}
176
177
Ok(response)
178
}
179
}
180
181
pub fn unzip_downloaded_release<T>(
182
compressed_file: &Path,
183
target_dir: &Path,
184
reporter: T,
185
) -> Result<(), WrappedError>
186
where
187
T: ReportCopyProgress,
188
{
189
match has_gzip_header(compressed_file) {
190
Ok((f, true)) => tar::decompress_tarball(f, target_dir, reporter),
191
Ok((f, false)) => zipper::unzip_file(f, target_dir, reporter),
192
Err(e) => Err(wrap(e, "error checking for gzip header")),
193
}
194
}
195
196
#[derive(Eq, PartialEq, Copy, Clone)]
197
pub enum TargetKind {
198
Server,
199
Archive,
200
Web,
201
Cli,
202
}
203
204
impl TargetKind {
205
fn download_segment(&self, platform: Platform) -> Option<String> {
206
match *self {
207
TargetKind::Server => Some(platform.headless()),
208
TargetKind::Archive => platform.archive(),
209
TargetKind::Web => Some(platform.web()),
210
TargetKind::Cli => Some(platform.cli()),
211
}
212
}
213
}
214
215
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
216
pub enum Platform {
217
LinuxAlpineX64,
218
LinuxAlpineARM64,
219
LinuxX64,
220
LinuxX64Legacy,
221
LinuxARM64,
222
LinuxARM64Legacy,
223
LinuxARM32,
224
LinuxARM32Legacy,
225
DarwinX64,
226
DarwinARM64,
227
WindowsX64,
228
WindowsX86,
229
WindowsARM64,
230
}
231
232
impl Platform {
233
pub fn archive(&self) -> Option<String> {
234
match self {
235
Platform::LinuxX64 => Some("linux-x64".to_owned()),
236
Platform::LinuxARM64 => Some("linux-arm64".to_owned()),
237
Platform::LinuxARM32 => Some("linux-armhf".to_owned()),
238
Platform::DarwinX64 => Some("darwin".to_owned()),
239
Platform::DarwinARM64 => Some("darwin-arm64".to_owned()),
240
Platform::WindowsX64 => Some("win32-x64-archive".to_owned()),
241
Platform::WindowsX86 => Some("win32-archive".to_owned()),
242
Platform::WindowsARM64 => Some("win32-arm64-archive".to_owned()),
243
_ => None,
244
}
245
}
246
pub fn headless(&self) -> String {
247
match self {
248
Platform::LinuxAlpineARM64 => "server-alpine-arm64",
249
Platform::LinuxAlpineX64 => "server-linux-alpine",
250
Platform::LinuxX64 => "server-linux-x64",
251
Platform::LinuxX64Legacy => "server-linux-legacy-x64",
252
Platform::LinuxARM64 => "server-linux-arm64",
253
Platform::LinuxARM64Legacy => "server-linux-legacy-arm64",
254
Platform::LinuxARM32 => "server-linux-armhf",
255
Platform::LinuxARM32Legacy => "server-linux-legacy-armhf",
256
Platform::DarwinX64 => "server-darwin",
257
Platform::DarwinARM64 => "server-darwin-arm64",
258
Platform::WindowsX64 => "server-win32-x64",
259
Platform::WindowsX86 => "server-win32",
260
Platform::WindowsARM64 => "server-win32-arm64",
261
}
262
.to_owned()
263
}
264
265
pub fn cli(&self) -> String {
266
match self {
267
Platform::LinuxAlpineARM64 => "cli-alpine-arm64",
268
Platform::LinuxAlpineX64 => "cli-alpine-x64",
269
Platform::LinuxX64 => "cli-linux-x64",
270
Platform::LinuxX64Legacy => "cli-linux-x64",
271
Platform::LinuxARM64 => "cli-linux-arm64",
272
Platform::LinuxARM64Legacy => "cli-linux-arm64",
273
Platform::LinuxARM32 => "cli-linux-armhf",
274
Platform::LinuxARM32Legacy => "cli-linux-armhf",
275
Platform::DarwinX64 => "cli-darwin-x64",
276
Platform::DarwinARM64 => "cli-darwin-arm64",
277
Platform::WindowsARM64 => "cli-win32-arm64",
278
Platform::WindowsX64 => "cli-win32-x64",
279
Platform::WindowsX86 => "cli-win32",
280
}
281
.to_owned()
282
}
283
284
pub fn web(&self) -> String {
285
format!("{}-web", self.headless())
286
}
287
288
pub fn env_default() -> Option<Platform> {
289
if cfg!(all(
290
target_os = "linux",
291
target_arch = "x86_64",
292
target_env = "musl"
293
)) {
294
Some(Platform::LinuxAlpineX64)
295
} else if cfg!(all(
296
target_os = "linux",
297
target_arch = "aarch64",
298
target_env = "musl"
299
)) {
300
Some(Platform::LinuxAlpineARM64)
301
} else if cfg!(all(target_os = "linux", target_arch = "x86_64")) {
302
Some(Platform::LinuxX64)
303
} else if cfg!(all(target_os = "linux", target_arch = "arm")) {
304
Some(Platform::LinuxARM32)
305
} else if cfg!(all(target_os = "linux", target_arch = "aarch64")) {
306
Some(Platform::LinuxARM64)
307
} else if cfg!(all(target_os = "macos", target_arch = "x86_64")) {
308
Some(Platform::DarwinX64)
309
} else if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
310
Some(Platform::DarwinARM64)
311
} else if cfg!(all(target_os = "windows", target_arch = "x86_64")) {
312
Some(Platform::WindowsX64)
313
} else if cfg!(all(target_os = "windows", target_arch = "x86")) {
314
Some(Platform::WindowsX86)
315
} else if cfg!(all(target_os = "windows", target_arch = "aarch64")) {
316
Some(Platform::WindowsARM64)
317
} else {
318
None
319
}
320
}
321
}
322
323
impl fmt::Display for Platform {
324
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325
f.write_str(match self {
326
Platform::LinuxAlpineARM64 => "LinuxAlpineARM64",
327
Platform::LinuxAlpineX64 => "LinuxAlpineX64",
328
Platform::LinuxX64 => "LinuxX64",
329
Platform::LinuxX64Legacy => "LinuxX64Legacy",
330
Platform::LinuxARM64 => "LinuxARM64",
331
Platform::LinuxARM64Legacy => "LinuxARM64Legacy",
332
Platform::LinuxARM32 => "LinuxARM32",
333
Platform::LinuxARM32Legacy => "LinuxARM32Legacy",
334
Platform::DarwinX64 => "DarwinX64",
335
Platform::DarwinARM64 => "DarwinARM64",
336
Platform::WindowsX64 => "WindowsX64",
337
Platform::WindowsX86 => "WindowsX86",
338
Platform::WindowsARM64 => "WindowsARM64",
339
})
340
}
341
}
342
343