mirror of
https://github.com/wez/wezterm.git
synced 2024-09-11 14:25:57 +03:00
wezterm: check for new releases
This commit adds some plumbing that will use the github API to probe the currently released version of wezterm, and if it is newer than the running version, show a window with some release information and links to the changelog and download page. The checks can be disabled in the config (but require a restart to take effect!) and default to checking every 24 hours. If running an AppImage on linux, links directly to the appimage download. In the future I'd like to have the download button use zsync to apply the update to the local image.
This commit is contained in:
parent
407e4f855c
commit
d39c16c406
83
Cargo.lock
generated
83
Cargo.lock
generated
@ -1208,6 +1208,18 @@ dependencies = [
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http_req"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335f373129a4d09664e1ba2b2c6e43e01625aec1eb076e486da4891372c02f14"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"unicase",
|
||||
"webpki",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "1.3.0"
|
||||
@ -1302,6 +1314,12 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.21"
|
||||
@ -2584,6 +2602,19 @@ dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"log",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusttype"
|
||||
version = "0.7.9"
|
||||
@ -2637,6 +2668,16 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "sct"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
@ -2672,6 +2713,17 @@ dependencies = [
|
||||
"syn 1.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial"
|
||||
version = "0.4.0"
|
||||
@ -3221,6 +3273,15 @@ dependencies = [
|
||||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
|
||||
dependencies = [
|
||||
"version_check 0.9.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
@ -3534,6 +3595,25 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.21.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1f50e1972865d6b1adb54167d1c8ed48606004c2c9d0ea5f1eeb34d95e863ef"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91cd5736df7f12a964a5067a12c62fa38e1bd8080aff1f80bc29be7c80d19ab4"
|
||||
dependencies = [
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wezterm"
|
||||
version = "0.1.0"
|
||||
@ -3567,6 +3647,7 @@ dependencies = [
|
||||
"harfbuzz",
|
||||
"hdrhistogram",
|
||||
"hostname",
|
||||
"http_req",
|
||||
"image",
|
||||
"lazy_static",
|
||||
"leb128",
|
||||
@ -3586,7 +3667,9 @@ dependencies = [
|
||||
"rangeset",
|
||||
"ratelimit_meter",
|
||||
"rcgen",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial",
|
||||
"shared_library",
|
||||
"ssh2",
|
||||
|
@ -52,7 +52,10 @@ portable-pty = { path = "pty", features = ["serde_support", "ssh"]}
|
||||
promise = { path = "promise" }
|
||||
ratelimit_meter = "5.0"
|
||||
rcgen = "0.7"
|
||||
http_req = {version="0.6", default-features=false, features=["rust-tls"]}
|
||||
regex = "1"
|
||||
serde = {version="1.0", features = ["rc", "derive"]}
|
||||
serde_json = "1.0"
|
||||
serial = "0.4"
|
||||
ssh2 = "0.8"
|
||||
structopt = "0.3"
|
||||
|
@ -45,7 +45,7 @@ pub use unix::*;
|
||||
lazy_static! {
|
||||
pub static ref HOME_DIR: PathBuf = dirs::home_dir().expect("can't find HOME dir");
|
||||
pub static ref CONFIG_DIR: PathBuf = xdg_config_home();
|
||||
static ref RUNTIME_DIR: PathBuf = compute_runtime_dir().unwrap();
|
||||
pub static ref RUNTIME_DIR: PathBuf = compute_runtime_dir().unwrap();
|
||||
static ref CONFIG: Configuration = Configuration::new();
|
||||
}
|
||||
|
||||
@ -501,6 +501,16 @@ pub struct Config {
|
||||
|
||||
#[serde(default)]
|
||||
pub launch_menu: Vec<SpawnCommand>,
|
||||
|
||||
#[serde(default = "default_true")]
|
||||
pub check_for_updates: bool,
|
||||
|
||||
#[serde(default = "default_update_interval")]
|
||||
pub check_for_updates_interval_seconds: u64,
|
||||
}
|
||||
|
||||
fn default_update_interval() -> u64 {
|
||||
86400
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Copy, Debug)]
|
||||
|
@ -227,30 +227,40 @@ pub struct ConnectionUI {
|
||||
impl ConnectionUI {
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = bounded(16);
|
||||
promise::spawn::spawn_into_main_thread(termwiztermtab::run(80, 24, move |term| {
|
||||
let mut ui = ConnectionUIImpl { term, rx };
|
||||
if let Err(e) = ui.run() {
|
||||
log::error!("while running ConnectionUI loop: {:?}", e);
|
||||
}
|
||||
ui.sleep(
|
||||
"(this window will close automatically)",
|
||||
Duration::new(10, 0),
|
||||
)
|
||||
.ok();
|
||||
Ok(())
|
||||
}));
|
||||
promise::spawn::spawn_into_main_thread(termwiztermtab::run(
|
||||
80,
|
||||
24,
|
||||
move |term| {
|
||||
let mut ui = ConnectionUIImpl { term, rx };
|
||||
if let Err(e) = ui.run() {
|
||||
log::error!("while running ConnectionUI loop: {:?}", e);
|
||||
}
|
||||
ui.sleep(
|
||||
"(this window will close automatically)",
|
||||
Duration::new(10, 0),
|
||||
)
|
||||
.ok();
|
||||
Ok(())
|
||||
},
|
||||
false,
|
||||
));
|
||||
Self { tx }
|
||||
}
|
||||
|
||||
pub fn new_with_no_close_delay() -> Self {
|
||||
let (tx, rx) = bounded(16);
|
||||
promise::spawn::spawn_into_main_thread(termwiztermtab::run(80, 24, move |term| {
|
||||
let mut ui = ConnectionUIImpl { term, rx };
|
||||
if let Err(e) = ui.run() {
|
||||
log::error!("while running ConnectionUI loop: {:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
}));
|
||||
promise::spawn::spawn_into_main_thread(termwiztermtab::run(
|
||||
80,
|
||||
24,
|
||||
move |term| {
|
||||
let mut ui = ConnectionUIImpl { term, rx };
|
||||
if let Err(e) = ui.run() {
|
||||
log::error!("while running ConnectionUI loop: {:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
false,
|
||||
));
|
||||
Self { tx }
|
||||
}
|
||||
|
||||
|
@ -593,6 +593,8 @@ impl TermWindow {
|
||||
|
||||
let cloned_window = window.clone();
|
||||
|
||||
crate::update::start_update_checker();
|
||||
|
||||
Connection::get()
|
||||
.unwrap()
|
||||
.schedule_timer(std::time::Duration::from_millis(35), {
|
||||
|
@ -32,6 +32,7 @@ mod server;
|
||||
mod ssh;
|
||||
mod stats;
|
||||
mod termwiztermtab;
|
||||
mod update;
|
||||
|
||||
use crate::frontend::activity::Activity;
|
||||
use crate::frontend::{front_end, FrontEndSelection};
|
||||
|
@ -193,6 +193,7 @@ pub struct TermWizTerminalTab {
|
||||
renderable: RefCell<RenderableState>,
|
||||
writer: RefCell<RenderableWriter>,
|
||||
reader: Pipe,
|
||||
mouse_grabbed: bool,
|
||||
}
|
||||
|
||||
impl TermWizTerminalTab {
|
||||
@ -224,8 +225,13 @@ impl TermWizTerminalTab {
|
||||
renderable,
|
||||
writer: RefCell::new(RenderableWriter { input_tx }),
|
||||
reader,
|
||||
mouse_grabbed: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mouse_grabbed(&mut self, grabbed: bool) {
|
||||
self.mouse_grabbed = grabbed;
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab for TermWizTerminalTab {
|
||||
@ -314,7 +320,7 @@ impl Tab for TermWizTerminalTab {
|
||||
}
|
||||
|
||||
fn advance_bytes(&self, _buf: &[u8], _host: &mut dyn TerminalHost) {
|
||||
panic!("advance_bytes is undefed for TermWizTerminalTab");
|
||||
panic!("advance_bytes is undefined for TermWizTerminalTab");
|
||||
}
|
||||
|
||||
fn is_dead(&self) -> bool {
|
||||
@ -343,7 +349,7 @@ impl Tab for TermWizTerminalTab {
|
||||
}
|
||||
|
||||
fn is_mouse_grabbed(&self) -> bool {
|
||||
true
|
||||
self.mouse_grabbed
|
||||
}
|
||||
|
||||
fn get_current_working_dir(&self) -> Option<Url> {
|
||||
@ -461,6 +467,7 @@ pub async fn run<
|
||||
width: usize,
|
||||
height: usize,
|
||||
f: F,
|
||||
mouse_grabbed: bool,
|
||||
) -> anyhow::Result<T> {
|
||||
let (render_tx, render_rx) = channel();
|
||||
let (input_tx, input_rx) = channel();
|
||||
@ -481,6 +488,7 @@ pub async fn run<
|
||||
render_rx: Receiver<Vec<Change>>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
mouse_grabbed: bool,
|
||||
) -> anyhow::Result<WindowId> {
|
||||
let mux = Mux::get().unwrap();
|
||||
|
||||
@ -490,13 +498,10 @@ pub async fn run<
|
||||
|
||||
let window_id = mux.new_empty_window();
|
||||
|
||||
let tab: Rc<dyn Tab> = Rc::new(TermWizTerminalTab::new(
|
||||
domain.domain_id(),
|
||||
width,
|
||||
height,
|
||||
input_tx,
|
||||
render_rx,
|
||||
));
|
||||
let mut tab =
|
||||
TermWizTerminalTab::new(domain.domain_id(), width, height, input_tx, render_rx);
|
||||
tab.set_mouse_grabbed(mouse_grabbed);
|
||||
let tab: Rc<dyn Tab> = Rc::new(tab);
|
||||
|
||||
mux.add_tab(&tab)?;
|
||||
mux.add_tab_to_window(&tab, window_id)?;
|
||||
@ -510,7 +515,7 @@ pub async fn run<
|
||||
}
|
||||
|
||||
let window_id: WindowId = promise::spawn::spawn_into_main_thread(async move {
|
||||
register_tab(input_tx, render_rx, width, height).await
|
||||
register_tab(input_tx, render_rx, width, height, mouse_grabbed).await
|
||||
})
|
||||
.await
|
||||
.unwrap_or_else(|| bail!("task panicked or was cancelled"))?;
|
||||
@ -537,19 +542,24 @@ pub fn message_box_ok(message: &str) {
|
||||
let title = "wezterm";
|
||||
let message = message.to_string();
|
||||
|
||||
promise::spawn::block_on(run(60, 10, move |mut term| {
|
||||
term.render(&[
|
||||
Change::Title(title.to_string()),
|
||||
Change::Text(message.to_string()),
|
||||
])
|
||||
.map_err(Error::msg)?;
|
||||
promise::spawn::block_on(run(
|
||||
60,
|
||||
10,
|
||||
move |mut term| {
|
||||
term.render(&[
|
||||
Change::Title(title.to_string()),
|
||||
Change::Text(message.to_string()),
|
||||
])
|
||||
.map_err(Error::msg)?;
|
||||
|
||||
let mut editor = LineEditor::new(&mut term);
|
||||
editor.set_prompt("press enter to continue.");
|
||||
let mut editor = LineEditor::new(&mut term);
|
||||
editor.set_prompt("press enter to continue.");
|
||||
|
||||
let mut host = NopLineEditorHost::default();
|
||||
editor.read_line(&mut host).ok();
|
||||
Ok(())
|
||||
}))
|
||||
let mut host = NopLineEditorHost::default();
|
||||
editor.read_line(&mut host).ok();
|
||||
Ok(())
|
||||
},
|
||||
false,
|
||||
))
|
||||
.ok();
|
||||
}
|
||||
|
346
src/update.rs
Normal file
346
src/update.rs
Normal file
@ -0,0 +1,346 @@
|
||||
use crate::config::configuration;
|
||||
use crate::connui::ConnectionUI;
|
||||
use crate::wezterm_version;
|
||||
use anyhow::anyhow;
|
||||
use http_req::request::{HttpVersion, Request};
|
||||
use http_req::uri::Uri;
|
||||
use regex::Regex;
|
||||
use serde::*;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use termwiz::cell::{AttributeChange, Hyperlink, Underline};
|
||||
use termwiz::surface::{Change, CursorShape};
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Release {
|
||||
pub url: String,
|
||||
pub body: String,
|
||||
pub html_url: String,
|
||||
pub tag_name: String,
|
||||
pub assets: Vec<Asset>,
|
||||
}
|
||||
|
||||
impl Release {
|
||||
pub fn classify_assets(&self) -> HashMap<AssetKind, Asset> {
|
||||
let mut map = HashMap::new();
|
||||
for asset in &self.assets {
|
||||
let kind = classify_asset_name(&asset.name);
|
||||
map.insert(kind, asset.clone());
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Asset {
|
||||
pub name: String,
|
||||
pub size: usize,
|
||||
pub url: String,
|
||||
pub browser_download_url: String,
|
||||
}
|
||||
|
||||
pub type DistVers = String;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum AssetKind {
|
||||
SourceCode,
|
||||
AppImage,
|
||||
AppImageZSync,
|
||||
DebianDeb(DistVers),
|
||||
UbuntuDeb(DistVers),
|
||||
CentOSRpm(DistVers),
|
||||
FedoraRpm(DistVers),
|
||||
MacOSZip,
|
||||
WindowsZip,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn classify_asset_name(name: &str) -> AssetKind {
|
||||
let winzip = Regex::new(r"WezTerm-windows-.*\.zip$").unwrap();
|
||||
let maczip = Regex::new(r"WezTerm-macos-.*\.zip$").unwrap();
|
||||
let appimage = Regex::new(r"WezTerm-.*\.AppImage$").unwrap();
|
||||
let appimage_zsync = Regex::new(r"WezTerm-.*\.AppImage\.zsync$").unwrap();
|
||||
let source = Regex::new(r"wezterm-.*src\.tar\.gz$").unwrap();
|
||||
|
||||
let rpm = Regex::new(r"wezterm-.*-1\.([a-z]+)(\d+)\.x86_64\.rpm$").unwrap();
|
||||
for cap in rpm.captures_iter(name) {
|
||||
match &cap[1] {
|
||||
"fc" => return AssetKind::FedoraRpm(cap[2].to_string()),
|
||||
"el" => return AssetKind::CentOSRpm(cap[2].to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let nightly_rpm = Regex::new(r"wezterm-nightly-(fedora|centos)(\d+)\.rpm$").unwrap();
|
||||
for cap in nightly_rpm.captures_iter(name) {
|
||||
match &cap[1] {
|
||||
"fedora" => return AssetKind::FedoraRpm(cap[2].to_string()),
|
||||
"centos" => return AssetKind::CentOSRpm(cap[2].to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let dot_deb = Regex::new(r"wezterm-.*\.(Ubuntu|Debian)([0-9.]+)\.deb$").unwrap();
|
||||
for cap in dot_deb.captures_iter(name) {
|
||||
match &cap[1] {
|
||||
"Ubuntu" => return AssetKind::UbuntuDeb(cap[2].to_string()),
|
||||
"Debian" => return AssetKind::DebianDeb(cap[2].to_string()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if winzip.is_match(name) {
|
||||
AssetKind::WindowsZip
|
||||
} else if maczip.is_match(name) {
|
||||
AssetKind::MacOSZip
|
||||
} else if appimage.is_match(name) {
|
||||
AssetKind::AppImage
|
||||
} else if appimage_zsync.is_match(name) {
|
||||
AssetKind::AppImageZSync
|
||||
} else if source.is_match(name) {
|
||||
AssetKind::SourceCode
|
||||
} else {
|
||||
AssetKind::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
fn get_github_release_info(uri: &str) -> anyhow::Result<Release> {
|
||||
let uri = uri.parse::<Uri>().expect("URL to be valid!?");
|
||||
|
||||
let mut latest = Vec::new();
|
||||
let _res = Request::new(&uri)
|
||||
.version(HttpVersion::Http10)
|
||||
.header("User-Agent", &format!("wez/wezterm-{}", wezterm_version()))
|
||||
.send(&mut latest)
|
||||
.map_err(|e| anyhow!("failed to query github releases: {}", e))?;
|
||||
|
||||
/*
|
||||
println!("Status: {} {}", _res.status_code(), _res.reason());
|
||||
println!("{}", String::from_utf8_lossy(&latest));
|
||||
*/
|
||||
|
||||
let latest: Release = serde_json::from_slice(&latest)?;
|
||||
Ok(latest)
|
||||
}
|
||||
|
||||
pub fn get_latest_release_info() -> anyhow::Result<Release> {
|
||||
get_github_release_info("https://api.github.com/repos/wez/wezterm/releases/latest")
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn get_nightly_release_info() -> anyhow::Result<Release> {
|
||||
get_github_release_info("https://api.github.com/repos/wez/wezterm/releases/tags/nightly")
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref UPDATER_WINDOW: Mutex<Option<ConnectionUI>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
fn show_update_available(release: Release) {
|
||||
let mut updater = UPDATER_WINDOW.lock().unwrap();
|
||||
|
||||
let ui = ConnectionUI::new_with_no_close_delay();
|
||||
ui.title("WezTerm Update Available");
|
||||
|
||||
let install = "https://wezfurlong.org/wezterm/installation.html";
|
||||
let change_log = "https://wezfurlong.org/wezterm/changelog.html";
|
||||
|
||||
let brief_blurb = release
|
||||
.body
|
||||
// The default for the release body is a series of newlines.
|
||||
// Trim that so that it doesn't make the window look weird
|
||||
.trim_end()
|
||||
// Normalize any dos line endings that might have wound
|
||||
// up in the body field...
|
||||
.replace("\r\n", "\n")
|
||||
// ... and then canonicalize the line endings for the terminal
|
||||
.replace("\n", "\r\n");
|
||||
|
||||
ui.output(vec![
|
||||
Change::CursorShape(CursorShape::Hidden),
|
||||
Change::Attribute(AttributeChange::Hyperlink(Some(Arc::new(Hyperlink::new(
|
||||
install,
|
||||
))))),
|
||||
format!("Version {} is now available!\r\n", release.tag_name).into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(None)),
|
||||
format!("(this is version {})\r\n", wezterm_version()).into(),
|
||||
format!("{}\r\n", brief_blurb).into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(Some(Arc::new(Hyperlink::new(
|
||||
change_log,
|
||||
))))),
|
||||
Change::Attribute(AttributeChange::Underline(Underline::Single)),
|
||||
"View Change Log\r\n".into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(None)),
|
||||
]);
|
||||
|
||||
let assets = release.classify_assets();
|
||||
let appimage = assets.get(&AssetKind::AppImage);
|
||||
|
||||
if cfg!(target_os = "linux") && std::env::var_os("APPIMAGE").is_some() && appimage.is_some() {
|
||||
ui.output(vec![
|
||||
Change::Attribute(AttributeChange::Hyperlink(Some(Arc::new(Hyperlink::new(
|
||||
&appimage.unwrap().browser_download_url,
|
||||
))))),
|
||||
Change::Attribute(AttributeChange::Underline(Underline::Single)),
|
||||
format!("Download {}\r\n", appimage.unwrap().name).into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(None)),
|
||||
]);
|
||||
} else {
|
||||
ui.output(vec![
|
||||
Change::Attribute(AttributeChange::Hyperlink(Some(Arc::new(Hyperlink::new(
|
||||
install,
|
||||
))))),
|
||||
Change::Attribute(AttributeChange::Underline(Underline::Single)),
|
||||
"Open Download Page\r\n".into(),
|
||||
Change::Attribute(AttributeChange::Hyperlink(None)),
|
||||
]);
|
||||
}
|
||||
|
||||
updater.replace(ui);
|
||||
}
|
||||
|
||||
fn update_checker() {
|
||||
// Compute how long we should sleep for;
|
||||
// if we've never checked, give it a few seconds after the first
|
||||
// launch, otherwise compute the interval based on the time of
|
||||
// the last check.
|
||||
let config = configuration();
|
||||
let update_interval = Duration::new(config.check_for_updates_interval_seconds, 0);
|
||||
let initial_interval = Duration::new(10, 0);
|
||||
|
||||
let update_file_name = crate::config::RUNTIME_DIR.join("check_update");
|
||||
let delay = update_file_name
|
||||
.metadata()
|
||||
.and_then(|metadata| metadata.modified())
|
||||
.map_err(|_| ())
|
||||
.and_then(|systime| {
|
||||
let elapsed = systime.elapsed().unwrap_or(Duration::new(0, 0));
|
||||
update_interval.checked_sub(elapsed).ok_or(())
|
||||
})
|
||||
.unwrap_or(initial_interval);
|
||||
|
||||
std::thread::sleep(delay);
|
||||
|
||||
loop {
|
||||
if let Ok(latest) = get_latest_release_info() {
|
||||
let current = crate::wezterm_version();
|
||||
if latest.tag_name.as_str() > current {
|
||||
log::info!(
|
||||
"latest release {} is newer than current build {}",
|
||||
latest.tag_name,
|
||||
current
|
||||
);
|
||||
show_update_available(latest.clone());
|
||||
}
|
||||
}
|
||||
|
||||
crate::create_user_owned_dirs(update_file_name.parent().unwrap()).ok();
|
||||
|
||||
// Record the time of this check
|
||||
if let Ok(mut f) = std::fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(&update_file_name)
|
||||
{
|
||||
f.write(b"_").ok();
|
||||
}
|
||||
|
||||
std::thread::sleep(update_interval);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_update_checker() {
|
||||
static CHECKER_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
if crate::frontend::has_gui_front_end() && configuration().check_for_updates {
|
||||
if CHECKER_STARTED.compare_and_swap(false, true, Ordering::Relaxed) == false {
|
||||
std::thread::Builder::new()
|
||||
.name("update_checker".into())
|
||||
.spawn(update_checker)
|
||||
.expect("failed to spawn update checker thread");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn classify_names() {
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-windows-20200505-090057-31c6155f.zip"),
|
||||
AssetKind::WindowsZip
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-windows-nightly.zip"),
|
||||
AssetKind::WindowsZip
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-macos-20200505-090057-31c6155f.zip"),
|
||||
AssetKind::MacOSZip
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-macos-nightly.zip"),
|
||||
AssetKind::MacOSZip
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505_090057_31c6155f-1.fc32.x86_64.rpm"),
|
||||
AssetKind::FedoraRpm("32".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-nightly-fedora32.rpm"),
|
||||
AssetKind::FedoraRpm("32".into())
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505_090057_31c6155f-1.fc31.x86_64.rpm"),
|
||||
AssetKind::FedoraRpm("31".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505_090057_31c6155f-1.el8.x86_64.rpm"),
|
||||
AssetKind::CentOSRpm("8".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505_090057_31c6155f-1.el7.x86_64.rpm"),
|
||||
AssetKind::CentOSRpm("7".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f.Ubuntu20.04.tar.xz"),
|
||||
AssetKind::Unknown
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f.Ubuntu20.04.deb"),
|
||||
AssetKind::UbuntuDeb("20.04".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f.Ubuntu19.10.deb"),
|
||||
AssetKind::UbuntuDeb("19.10".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f.Debian9.12.deb"),
|
||||
AssetKind::DebianDeb("9.12".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f.Debian10.deb"),
|
||||
AssetKind::DebianDeb("10".into())
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-20200505-090057-31c6155f-Ubuntu16.04.AppImage.zsync"),
|
||||
AssetKind::AppImageZSync
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("WezTerm-20200505-090057-31c6155f-Ubuntu16.04.AppImage"),
|
||||
AssetKind::AppImage
|
||||
);
|
||||
assert_eq!(
|
||||
classify_asset_name("wezterm-20200505-090057-31c6155f-src.tar.gz"),
|
||||
AssetKind::SourceCode
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user