add detection for integrated cli, verify

This commit is contained in:
Connor Peet
2022-09-26 07:22:58 +02:00
parent 07453efc00
commit 889fbd2f1b
10 changed files with 52 additions and 157 deletions

View File

@@ -14,6 +14,7 @@ use cli::{
update_service::UpdateService,
util::{
errors::{wrap, AnyError},
is_integrated_cli,
prereqs::PreReqChecker,
},
};
@@ -26,10 +27,15 @@ use log::{Level, Metadata, Record};
#[tokio::main]
async fn main() -> Result<(), std::convert::Infallible> {
let raw_args = std::env::args_os().collect::<Vec<_>>();
// todo: only parse to the standalone CLI if not integrated
let parsed = try_parse_legacy(&raw_args)
.map(|core| args::AnyCli::Integrated(args::IntegratedCli { core }))
.unwrap_or_else(|| args::AnyCli::Standalone(args::StandaloneCli::parse_from(&raw_args)));
.unwrap_or_else(|| {
if let Ok(true) = is_integrated_cli() {
args::AnyCli::Integrated(args::IntegratedCli::parse_from(&raw_args))
} else {
args::AnyCli::Standalone(args::StandaloneCli::parse_from(&raw_args))
}
});
let core = parsed.core();
let context = CommandContext {

View File

@@ -74,7 +74,7 @@ impl TryFrom<&str> for Quality {
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"stable" => Ok(Quality::Stable),
"insiders" => Ok(Quality::Insiders),
"insiders" | "insider" => Ok(Quality::Insiders),
"exploration" => Ok(Quality::Exploration),
_ => Err(format!(
"Unknown quality: {}. Must be one of stable, insiders, or exploration.",

View File

@@ -3,7 +3,10 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use std::{fs::rename, path::Path};
use std::{
fs::{rename, set_permissions},
path::Path,
};
use tempfile::tempdir;
use crate::{
@@ -91,8 +94,6 @@ impl<'a> SelfUpdate<'a> {
#[cfg(target_os = "windows")]
fn copy_file_metadata(from: &Path, to: &Path) -> Result<(), std::io::Error> {
use std::fs::set_permissions;
let permissions = from.metadata()?.permissions();
set_permissions(&to, permissions)?;
Ok(())

View File

@@ -1,120 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use crate::constants::{VSCODE_CLI_ASSET_NAME, VSCODE_CLI_VERSION};
use crate::util::{errors, http, io::SilentCopyProgress};
use serde::Deserialize;
use std::{
fs::{rename, set_permissions},
path::Path,
};
pub struct Update {
client: reqwest::Client,
}
const LATEST_URL: &str = "https://aka.ms/vscode-server-launcher/update";
impl Default for Update {
fn default() -> Self {
Self::new()
}
}
impl Update {
// Creates a new Update instance without authentication
pub fn new() -> Update {
Update {
client: reqwest::Client::new(),
}
}
// Gets the asset to update to, or None if the current launcher is up to date.
pub async fn get_latest_release(&self) -> Result<LauncherRelease, errors::AnyError> {
let res = self
.client
.get(LATEST_URL)
.header(
"User-Agent",
format!(
"vscode-server-launcher/{}",
VSCODE_CLI_VERSION.unwrap_or("dev")
),
)
.send()
.await?;
if !res.status().is_success() {
return Err(errors::StatusError::from_res(res).await?.into());
}
Ok(res.json::<LauncherRelease>().await?)
}
pub async fn switch_to_release(
&self,
update: &LauncherRelease,
target_path: &Path,
) -> Result<(), errors::AnyError> {
let mut staging_path = target_path.to_owned();
staging_path.set_file_name(format!(
"{}.next",
target_path.file_name().unwrap().to_string_lossy()
));
let an = VSCODE_CLI_ASSET_NAME.unwrap();
let mut url = format!("{}/{}/{}", update.url, an, an);
if cfg!(target_os = "windows") {
url += ".exe";
}
let res = self.client.get(url).send().await?;
if !res.status().is_success() {
return Err(errors::StatusError::from_res(res).await?.into());
}
http::download_into_file(&staging_path, SilentCopyProgress(), res).await?;
copy_file_metadata(target_path, &staging_path)
.map_err(|e| errors::wrap(e, "failed to set file permissions"))?;
rename(&staging_path, &target_path)
.map_err(|e| errors::wrap(e, "failed to copy new launcher version"))?;
Ok(())
}
}
#[derive(Deserialize, Clone)]
pub struct LauncherRelease {
pub version: String,
pub url: String,
pub released_at: u64,
}
#[cfg(target_os = "windows")]
fn copy_file_metadata(from: &Path, to: &Path) -> Result<(), std::io::Error> {
let permissions = from.metadata()?.permissions();
set_permissions(&to, permissions)?;
Ok(())
}
#[cfg(not(target_os = "windows"))]
fn copy_file_metadata(from: &Path, to: &Path) -> Result<(), std::io::Error> {
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
let metadata = from.metadata()?;
set_permissions(&to, metadata.permissions())?;
// based on coreutils' chown https://github.com/uutils/coreutils/blob/72b4629916abe0852ad27286f4e307fbca546b6e/src/chown/chown.rs#L266-L281
let s = std::ffi::CString::new(to.as_os_str().as_bytes()).unwrap();
let ret = unsafe { libc::chown(s.as_ptr(), metadata.uid(), metadata.gid()) };
if ret != 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}

View File

@@ -3,6 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
mod is_integrated;
pub mod command;
pub mod errors;
pub mod http;
@@ -11,6 +13,7 @@ pub mod io;
pub mod machine;
pub mod prereqs;
pub mod sync;
pub use is_integrated::*;
#[cfg(target_os = "linux")]
pub mod tar;

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use std::{env, io};
use crate::constants::VSCODE_CLI_QUALITY;
pub fn is_integrated_cli() -> io::Result<bool> {
let exe = env::current_exe()?;
let parent = match exe.parent().and_then(|p| p.parent()) {
Some(p) => p,
None => return Ok(false),
};
let expected_file = if cfg!(windows) {
match VSCODE_CLI_QUALITY {
Some("insider") => "Code - Insiders.exe",
Some("exploration") => "Code - Exploration.exe",
_ => "Code.exe",
}
} else {
match VSCODE_CLI_QUALITY {
Some("insider") => "code-insiders",
Some("exploration") => "code-exploration",
_ => "code",
}
};
Ok(parent.join(expected_file).exists())
}

View File

@@ -2,7 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use crate::util::errors;
use std::path::Path;
use sysinfo::{Pid, PidExt, ProcessExt, System, SystemExt};
@@ -49,30 +49,3 @@ pub fn find_running_process(name: &Path) -> Option<u32> {
}
None
}
#[cfg(not(target_family = "unix"))]
pub async fn set_executable_permission<P: AsRef<std::path::Path>>(
_file: P,
) -> Result<(), errors::WrappedError> {
Ok(())
}
#[cfg(target_family = "unix")]
pub async fn set_executable_permission<P: AsRef<std::path::Path>>(
file: P,
) -> Result<(), errors::WrappedError> {
use std::os::unix::prelude::PermissionsExt;
let mut permissions = tokio::fs::metadata(&file)
.await
.map_err(|e| errors::wrap(e, "failed to read executable file metadata"))?
.permissions();
permissions.set_mode(0o750);
tokio::fs::set_permissions(&file, permissions)
.await
.map_err(|e| errors::wrap(e, "failed to set executable permissions"))?;
Ok(())
}