mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
feat(update): prompt for new version once per day (#16375)
<!-- Before submitting a PR, please read http://deno.land/manual/contributing 1. Give the PR a descriptive title. Examples of good title: - fix(std/http): Fix race condition in server - docs(console): Update docstrings - feat(doc): Handle nested reexports Examples of bad title: - fix #7123 - update docs - fix bugs 2. Ensure there is a related issue and it is referenced in the PR text. 3. Ensure there are tests that cover the changes. 4. Ensure `cargo test` passes. 5. Ensure `./tools/format.js` passes without changing files. 6. Ensure `./tools/lint.js` passes. -->
This commit is contained in:
parent
e825022c82
commit
4b6168d5e3
2 changed files with 92 additions and 43 deletions
29
cli/main.rs
29
cli/main.rs
|
@ -807,29 +807,6 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
fn check_for_upgrades(cache_dir: PathBuf) {
|
||||
// Run a background task that checks for available upgrades. If an earlier
|
||||
// run of this background task found a new version of Deno, the new version
|
||||
// number (or commit hash for canary builds) is returned.
|
||||
let maybe_upgrade_version = tools::upgrade::check_for_upgrades(cache_dir);
|
||||
|
||||
// Print a message if an update is available, unless:
|
||||
// * stderr is not a tty
|
||||
// * we're already running the 'deno upgrade' command.
|
||||
if let Some(upgrade_version) = maybe_upgrade_version {
|
||||
if atty::is(atty::Stream::Stderr) {
|
||||
eprint!(
|
||||
"{} ",
|
||||
colors::green(format!("Deno {upgrade_version} has been released."))
|
||||
);
|
||||
eprintln!(
|
||||
"{}",
|
||||
colors::italic_gray("Run `deno upgrade` to install it.")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_command(
|
||||
flags: Flags,
|
||||
run_flags: RunFlags,
|
||||
|
@ -847,7 +824,11 @@ async fn run_command(
|
|||
// map specified and bare specifier is used on the command line - this should
|
||||
// probably call `ProcState::resolve` instead
|
||||
let ps = ProcState::build(flags).await?;
|
||||
check_for_upgrades(ps.dir.root.clone());
|
||||
|
||||
// Run a background task that checks for available upgrades. If an earlier
|
||||
// run of this background task found a new version of Deno.
|
||||
tools::upgrade::check_for_upgrades(ps.dir.root.clone());
|
||||
|
||||
let main_module = if NpmPackageReference::from_str(&run_flags.script).is_ok()
|
||||
{
|
||||
ModuleSpecifier::parse(&run_flags.script)?
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
//! This module provides feature to upgrade deno executable
|
||||
|
||||
use crate::args::UpgradeFlags;
|
||||
use crate::colors;
|
||||
use crate::version;
|
||||
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::StreamExt;
|
||||
|
@ -29,9 +31,9 @@ const UPGRADE_CHECK_FILE_NAME: &str = "latest.txt";
|
|||
|
||||
const UPGRADE_CHECK_FETCH_DELAY: Duration = Duration::from_millis(500);
|
||||
|
||||
pub fn check_for_upgrades(cache_dir: PathBuf) -> Option<String> {
|
||||
pub fn check_for_upgrades(cache_dir: PathBuf) {
|
||||
if env::var("DENO_NO_UPDATE_CHECK").is_ok() {
|
||||
return None;
|
||||
return;
|
||||
}
|
||||
|
||||
let p = cache_dir.join(UPGRADE_CHECK_FILE_NAME);
|
||||
|
@ -52,7 +54,8 @@ pub fn check_for_upgrades(cache_dir: PathBuf) -> Option<String> {
|
|||
};
|
||||
|
||||
if should_check {
|
||||
tokio::spawn(async move {
|
||||
let cache_dir = cache_dir.clone();
|
||||
tokio::spawn(async {
|
||||
// Sleep for a small amount of time to not unnecessarily impact startup
|
||||
// time.
|
||||
tokio::time::sleep(UPGRADE_CHECK_FETCH_DELAY).await;
|
||||
|
@ -71,21 +74,53 @@ pub fn check_for_upgrades(cache_dir: PathBuf) -> Option<String> {
|
|||
Err(_) => return,
|
||||
};
|
||||
|
||||
let contents = CheckVersionFile {
|
||||
let file = CheckVersionFile {
|
||||
last_prompt: chrono::Utc::now(),
|
||||
last_checked: chrono::Utc::now(),
|
||||
latest_version,
|
||||
}
|
||||
.serialize();
|
||||
let _ = std::fs::write(cache_dir.join(UPGRADE_CHECK_FILE_NAME), contents);
|
||||
};
|
||||
file.save(cache_dir);
|
||||
});
|
||||
}
|
||||
|
||||
// Return `Some(version)` if a new version is available, `None` otherwise.
|
||||
maybe_file
|
||||
.map(|f| f.latest_version)
|
||||
let new_version_available = maybe_file
|
||||
.as_ref()
|
||||
.map(|f| f.latest_version.to_string())
|
||||
.filter(|latest_version| {
|
||||
latest_version != version::release_version_or_canary_commit_hash()
|
||||
})
|
||||
});
|
||||
|
||||
let should_prompt = match &maybe_file {
|
||||
Some(file) => {
|
||||
let last_prompt_age =
|
||||
chrono::Utc::now().signed_duration_since(file.last_prompt);
|
||||
last_prompt_age > chrono::Duration::hours(UPGRADE_CHECK_INTERVAL)
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
|
||||
// Print a message if an update is available, unless:
|
||||
// * stderr is not a tty
|
||||
// * we're already running the 'deno upgrade' command.
|
||||
if should_prompt {
|
||||
if let Some(upgrade_version) = new_version_available {
|
||||
if atty::is(atty::Stream::Stderr) {
|
||||
eprint!(
|
||||
"{} ",
|
||||
colors::green(format!("Deno {upgrade_version} has been released."))
|
||||
);
|
||||
eprintln!(
|
||||
"{}",
|
||||
colors::italic_gray("Run `deno upgrade` to install it.")
|
||||
);
|
||||
|
||||
if let Some(file) = maybe_file {
|
||||
file.with_last_prompt(chrono::Utc::now()).save(cache_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn upgrade(upgrade_flags: UpgradeFlags) -> Result<(), AnyError> {
|
||||
|
@ -385,6 +420,7 @@ fn check_exe(exe_path: &Path) -> Result<(), AnyError> {
|
|||
}
|
||||
|
||||
struct CheckVersionFile {
|
||||
pub last_prompt: chrono::DateTime<chrono::Utc>,
|
||||
pub last_checked: chrono::DateTime<chrono::Utc>,
|
||||
pub latest_version: String,
|
||||
}
|
||||
|
@ -393,27 +429,48 @@ impl CheckVersionFile {
|
|||
pub fn parse(content: String) -> Option<Self> {
|
||||
let split_content = content.split('!').collect::<Vec<_>>();
|
||||
|
||||
if split_content.len() != 2 {
|
||||
if split_content.len() != 3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let latest_version = split_content[1].trim().to_owned();
|
||||
let latest_version = split_content[2].trim().to_owned();
|
||||
if latest_version.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let last_checked = chrono::DateTime::parse_from_rfc3339(split_content[0])
|
||||
let last_prompt = chrono::DateTime::parse_from_rfc3339(split_content[0])
|
||||
.map(|dt| dt.with_timezone(&chrono::Utc))
|
||||
.ok()?;
|
||||
let last_checked = chrono::DateTime::parse_from_rfc3339(split_content[1])
|
||||
.map(|dt| dt.with_timezone(&chrono::Utc))
|
||||
.ok()?;
|
||||
|
||||
Some(CheckVersionFile {
|
||||
last_prompt,
|
||||
last_checked,
|
||||
latest_version,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> String {
|
||||
format!("{}!{}", self.last_checked.to_rfc3339(), self.latest_version)
|
||||
fn serialize(&self) -> String {
|
||||
format!(
|
||||
"{}!{}!{}",
|
||||
self.last_prompt.to_rfc3339(),
|
||||
self.last_checked.to_rfc3339(),
|
||||
self.latest_version
|
||||
)
|
||||
}
|
||||
|
||||
fn with_last_prompt(self, dt: chrono::DateTime<chrono::Utc>) -> Self {
|
||||
Self {
|
||||
last_prompt: dt,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
fn save(&self, cache_dir: PathBuf) {
|
||||
let _ =
|
||||
std::fs::write(cache_dir.join(UPGRADE_CHECK_FILE_NAME), self.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -423,9 +480,14 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_parse_upgrade_check_file() {
|
||||
let file =
|
||||
CheckVersionFile::parse("2020-01-01T00:00:00+00:00!1.2.3".to_string())
|
||||
.unwrap();
|
||||
let file = CheckVersionFile::parse(
|
||||
"2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3".to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
file.last_prompt.to_rfc3339(),
|
||||
"2020-01-01T00:00:00+00:00".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
file.last_checked.to_rfc3339(),
|
||||
"2020-01-01T00:00:00+00:00".to_string()
|
||||
|
@ -446,6 +508,9 @@ mod test {
|
|||
#[test]
|
||||
fn test_serialize_upgrade_check_file() {
|
||||
let file = CheckVersionFile {
|
||||
last_prompt: chrono::DateTime::parse_from_rfc3339("2020-01-01T00:00:00Z")
|
||||
.unwrap()
|
||||
.with_timezone(&chrono::Utc),
|
||||
last_checked: chrono::DateTime::parse_from_rfc3339(
|
||||
"2020-01-01T00:00:00Z",
|
||||
)
|
||||
|
@ -453,6 +518,9 @@ mod test {
|
|||
.with_timezone(&chrono::Utc),
|
||||
latest_version: "1.2.3".to_string(),
|
||||
};
|
||||
assert_eq!(file.serialize(), "2020-01-01T00:00:00+00:00!1.2.3");
|
||||
assert_eq!(
|
||||
file.serialize(),
|
||||
"2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue