mirror of
https://github.com/microsoft/vscode.git
synced 2026-02-23 11:16:01 +00:00
- Start separating a "standalone" CLI. This is a little awkward with clap- derive, but I got it working. Detection of whether the CLI _is_ standalone is still todo. - Remove the old ad-hoc update code for code-server, and use the update service instead. - Fix some of the "permission denied" errors people got while updating before. We need to rename the old running binary, not just overwrite it.
184 lines
5.7 KiB
Rust
184 lines
5.7 KiB
Rust
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
mod legacy_args;
|
|
|
|
use std::process::Command;
|
|
|
|
use clap::Parser;
|
|
use cli::{
|
|
commands::{args, tunnels, update, version, CommandContext},
|
|
desktop, log as own_log,
|
|
state::LauncherPaths,
|
|
update_service::UpdateService,
|
|
util::{
|
|
errors::{wrap, AnyError},
|
|
prereqs::PreReqChecker,
|
|
},
|
|
};
|
|
use legacy_args::try_parse_legacy;
|
|
use opentelemetry::sdk::trace::TracerProvider as SdkTracerProvider;
|
|
use opentelemetry::trace::TracerProvider;
|
|
|
|
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)));
|
|
|
|
let core = parsed.core();
|
|
let context = CommandContext {
|
|
http: reqwest::Client::new(),
|
|
paths: LauncherPaths::new(&core.global_options.cli_data_dir).unwrap(),
|
|
log: own_log::Logger::new(
|
|
SdkTracerProvider::builder().build().tracer("codecli"),
|
|
if core.global_options.verbose {
|
|
own_log::Level::Trace
|
|
} else {
|
|
core.global_options.log.unwrap_or(own_log::Level::Info)
|
|
},
|
|
),
|
|
args: core.clone(),
|
|
};
|
|
|
|
log::set_logger(Box::leak(Box::new(RustyLogger(context.log.clone()))))
|
|
.map(|()| log::set_max_level(log::LevelFilter::Debug))
|
|
.expect("expected to make logger");
|
|
|
|
let result = match parsed {
|
|
args::AnyCli::Standalone(args::StandaloneCli {
|
|
subcommand: Some(cmd),
|
|
..
|
|
}) => match cmd {
|
|
args::StandaloneCommands::Update(args) => update::update(context, args).await,
|
|
},
|
|
args::AnyCli::Standalone(args::StandaloneCli { core: c, .. })
|
|
| args::AnyCli::Integrated(args::IntegratedCli { core: c, .. }) => match c.subcommand {
|
|
None => {
|
|
let ca = context.args.get_base_code_args();
|
|
start_code(context, ca).await
|
|
}
|
|
|
|
Some(args::Commands::Extension(extension_args)) => {
|
|
let mut ca = context.args.get_base_code_args();
|
|
extension_args.add_code_args(&mut ca);
|
|
start_code(context, ca).await
|
|
}
|
|
|
|
Some(args::Commands::Status) => {
|
|
let mut ca = context.args.get_base_code_args();
|
|
ca.push("--status".to_string());
|
|
start_code(context, ca).await
|
|
}
|
|
|
|
Some(args::Commands::Version(version_args)) => match version_args.subcommand {
|
|
args::VersionSubcommand::Use(use_version_args) => {
|
|
version::switch_to(context, use_version_args).await
|
|
}
|
|
args::VersionSubcommand::Uninstall(uninstall_version_args) => {
|
|
version::uninstall(context, uninstall_version_args).await
|
|
}
|
|
args::VersionSubcommand::List(list_version_args) => {
|
|
version::list(context, list_version_args).await
|
|
}
|
|
},
|
|
|
|
Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand {
|
|
Some(args::TunnelSubcommand::Prune) => tunnels::prune(context).await,
|
|
Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context).await,
|
|
Some(args::TunnelSubcommand::Rename(rename_args)) => {
|
|
tunnels::rename(context, rename_args).await
|
|
}
|
|
Some(args::TunnelSubcommand::User(user_command)) => {
|
|
tunnels::user(context, user_command).await
|
|
}
|
|
Some(args::TunnelSubcommand::Service(service_args)) => {
|
|
tunnels::service(context, service_args).await
|
|
}
|
|
None => tunnels::serve(context, tunnel_args.serve_args).await,
|
|
},
|
|
},
|
|
};
|
|
|
|
match result {
|
|
Err(e) => print_and_exit(e),
|
|
Ok(code) => std::process::exit(code),
|
|
}
|
|
}
|
|
|
|
fn print_and_exit<E>(err: E) -> !
|
|
where
|
|
E: std::fmt::Display,
|
|
{
|
|
own_log::emit(own_log::Level::Error, "", &format!("{}", err));
|
|
std::process::exit(1);
|
|
}
|
|
|
|
async fn start_code(context: CommandContext, args: Vec<String>) -> Result<i32, AnyError> {
|
|
let platform = PreReqChecker::new().verify().await?;
|
|
let version_manager = desktop::CodeVersionManager::new(&context.paths, platform);
|
|
let update_service = UpdateService::new(context.log.clone(), context.http.clone());
|
|
let version = match &context.args.editor_options.code_options.use_version {
|
|
Some(v) => desktop::RequestedVersion::try_from(v.as_str())?,
|
|
None => version_manager.get_preferred_version(),
|
|
};
|
|
|
|
let binary = match version_manager.try_get_entrypoint(&version).await {
|
|
Some(ep) => ep,
|
|
None => {
|
|
desktop::prompt_to_install(&version)?;
|
|
version_manager.install(&update_service, &version).await?
|
|
}
|
|
};
|
|
|
|
let code = Command::new(binary)
|
|
.args(args)
|
|
.status()
|
|
.map(|s| s.code().unwrap_or(1))
|
|
.map_err(|e| wrap(e, "error running VS Code"))?;
|
|
|
|
Ok(code)
|
|
}
|
|
|
|
/// Logger that uses the common rust "log" crate and directs back to one of
|
|
/// our managed loggers.
|
|
struct RustyLogger(own_log::Logger);
|
|
|
|
impl log::Log for RustyLogger {
|
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
|
metadata.level() <= Level::Debug
|
|
}
|
|
|
|
fn log(&self, record: &Record) {
|
|
if !self.enabled(record.metadata()) {
|
|
return;
|
|
}
|
|
|
|
// exclude noisy log modules:
|
|
let src = match record.module_path() {
|
|
Some("russh::cipher") => return,
|
|
Some("russh::negotiation") => return,
|
|
Some(s) => s,
|
|
None => "<unknown>",
|
|
};
|
|
|
|
self.0.emit(
|
|
match record.level() {
|
|
log::Level::Debug => own_log::Level::Debug,
|
|
log::Level::Error => own_log::Level::Error,
|
|
log::Level::Info => own_log::Level::Info,
|
|
log::Level::Trace => own_log::Level::Trace,
|
|
log::Level::Warn => own_log::Level::Warn,
|
|
},
|
|
&format!("[{}] {}", src, record.args()),
|
|
);
|
|
}
|
|
|
|
fn flush(&self) {}
|
|
}
|