cli: implement better self-updating

- 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.
This commit is contained in:
Connor Peet
2022-09-23 17:44:22 -07:00
parent a9bcb15b75
commit 07453efc00
22 changed files with 381 additions and 102 deletions

View File

@@ -24,7 +24,14 @@ const TEMPLATE: &str = "
name = "Visual Studio Code CLI",
version = match constants::VSCODE_CLI_VERSION { Some(v) => v, None => "dev" },
)]
pub struct Cli {
pub struct IntegratedCli {
#[clap(flatten)]
pub core: CliCore,
}
/// Common CLI shared between intergated and standalone interfaces.
#[derive(Args, Debug, Default, Clone)]
pub struct CliCore {
/// One or more files, folders, or URIs to open.
#[clap(name = "paths")]
pub open_paths: Vec<String>,
@@ -42,7 +49,36 @@ pub struct Cli {
pub subcommand: Option<Commands>,
}
impl Cli {
#[derive(Parser, Debug, Default)]
#[clap(
help_template = TEMPLATE,
long_about = None,
name = "Visual Studio Code CLI",
version = match constants::VSCODE_CLI_VERSION { Some(v) => v, None => "dev" },
)]
pub struct StandaloneCli {
#[clap(flatten)]
pub core: CliCore,
#[clap(subcommand)]
pub subcommand: Option<StandaloneCommands>,
}
pub enum AnyCli {
Integrated(IntegratedCli),
Standalone(StandaloneCli),
}
impl AnyCli {
pub fn core(&self) -> &CliCore {
match self {
AnyCli::Integrated(cli) => &cli.core,
AnyCli::Standalone(cli) => &cli.core,
}
}
}
impl CliCore {
pub fn get_base_code_args(&self) -> Vec<String> {
let mut args = self.open_paths.clone();
self.editor_options.add_code_args(&mut args);
@@ -52,8 +88,8 @@ impl Cli {
}
}
impl<'a> From<&'a Cli> for CodeServerArgs {
fn from(cli: &'a Cli) -> Self {
impl<'a> From<&'a CliCore> for CodeServerArgs {
fn from(cli: &'a CliCore) -> Self {
let mut args = CodeServerArgs {
log: cli.global_options.log,
accept_server_license_terms: true,
@@ -77,6 +113,19 @@ impl<'a> From<&'a Cli> for CodeServerArgs {
}
}
#[derive(Subcommand, Debug, Clone)]
pub enum StandaloneCommands {
/// Updates the VS Code CLI.
Update(StandaloneUpdateArgs),
}
#[derive(Args, Debug, Clone)]
pub struct StandaloneUpdateArgs {
/// Only check for updates, without actually updating the CLI.
#[clap(long)]
pub check: bool,
}
#[derive(Subcommand, Debug, Clone)]
pub enum Commands {
@@ -234,7 +283,7 @@ pub struct UninstallVersionArgs {
pub name: String,
}
#[derive(Args, Debug, Default)]
#[derive(Args, Debug, Default, Clone)]
pub struct EditorOptions {
/// Compare two files with each other.
#[clap(short, long, value_names = &["file", "file"])]
@@ -348,7 +397,7 @@ impl DesktopCodeOptions {
}
}
#[derive(Args, Debug, Default)]
#[derive(Args, Debug, Default, Clone)]
pub struct GlobalOptions {
/// Directory where CLI metadata, such as VS Code installations, should be stored.
#[clap(long, env = "VSCODE_CLI_DATA_DIR", global = true)]
@@ -389,7 +438,7 @@ impl GlobalOptions {
}
}
#[derive(Args, Debug, Default)]
#[derive(Args, Debug, Default, Clone)]
pub struct EditorTroubleshooting {
/// Run CPU profiler during startup.
#[clap(long)]

View File

@@ -5,11 +5,11 @@
use crate::{log, state::LauncherPaths};
use super::args::Cli;
use super::args::CliCore;
pub struct CommandContext {
pub log: log::Logger,
pub paths: LauncherPaths,
pub args: Cli,
pub args: CliCore,
pub http: reqwest::Client,
}

View File

@@ -10,7 +10,7 @@ use tokio::sync::oneshot;
use super::{
args::{
AuthProvider, Cli, ExistingTunnelArgs, TunnelRenameArgs, TunnelServeArgs,
AuthProvider, CliCore, ExistingTunnelArgs, TunnelRenameArgs, TunnelServeArgs,
TunnelServiceSubCommands, TunnelUserSubCommands,
},
CommandContext,
@@ -57,11 +57,11 @@ impl From<ExistingTunnelArgs> for Option<dev_tunnels::ExistingTunnel> {
}
struct TunnelServiceContainer {
args: Cli,
args: CliCore,
}
impl TunnelServiceContainer {
fn new(args: Cli) -> Self {
fn new(args: CliCore) -> Self {
Self { args }
}
}

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use indicatif::ProgressBar;
use crate::{
self_update::SelfUpdate,
update_service::UpdateService,
util::{errors::AnyError, input::ProgressBarReporter},
};
use super::{args::StandaloneUpdateArgs, CommandContext};
pub async fn update(ctx: CommandContext, args: StandaloneUpdateArgs) -> Result<i32, AnyError> {
let update_service = UpdateService::new(ctx.log.clone(), ctx.http.clone());
let update_service = SelfUpdate::new(&update_service)?;
let current_version = update_service.get_current_release().await?;
if update_service.is_up_to_date_with(&current_version) {
ctx.log.result(format!(
"VS Code is already to to date ({})",
current_version.commit
));
return Ok(1);
}
if args.check {
ctx.log
.result(format!("Update to {} is available", current_version));
return Ok(0);
}
let pb = ProgressBar::new(1);
pb.set_message("Downloading...");
update_service
.do_update(&current_version, ProgressBarReporter::from(pb))
.await?;
ctx.log
.result(format!("Successfully updated to {}", current_version));
Ok(0)
}