summaryrefslogtreecommitdiff
path: root/cli/tools/upgrade.rs
diff options
context:
space:
mode:
authorBartek Iwańczuk <biwanczuk@gmail.com>2024-08-12 19:32:53 +0100
committerGitHub <noreply@github.com>2024-08-12 18:32:53 +0000
commit3c70b9435a649a53614bd3ae6f434c2a455d575a (patch)
treef9424c6f1d9206c0fb3a6291371294cf830060b8 /cli/tools/upgrade.rs
parent76f4f202e7be3835bd3d95af9e1c485a4b4695fe (diff)
feat: `deno upgrade --rc` (#24905)
This commit adds the "--rc" flag to "deno upgrade" subcommand. This flag allows to upgrade to the latest "release candidate" release. The update checker was also updated to take this into account.
Diffstat (limited to 'cli/tools/upgrade.rs')
-rw-r--r--cli/tools/upgrade.rs584
1 files changed, 468 insertions, 116 deletions
diff --git a/cli/tools/upgrade.rs b/cli/tools/upgrade.rs
index fe5c565f0..8821e92a7 100644
--- a/cli/tools/upgrade.rs
+++ b/cli/tools/upgrade.rs
@@ -93,12 +93,14 @@ trait VersionProvider: Clone {
async fn latest_version(
&self,
release_channel: ReleaseChannel,
- ) -> Result<String, AnyError>;
+ ) -> Result<AvailableVersion, AnyError>;
- // TODO(bartlomieju): what this one actually returns?
+ /// Returns either a semver or git hash. It's up to implementor to
+ /// decide which one is appropriate, but in general only "stable"
+ /// and "lts" versions use semver.
fn current_version(&self) -> Cow<str>;
- // TODO(bartlomieju): update to handle `Lts` and `Rc` channels
+ // TODO(bartlomieju): update to handle `Lts` channel
async fn get_current_exe_release_channel(
&self,
) -> Result<ReleaseChannel, AnyError>;
@@ -127,7 +129,7 @@ impl VersionProvider for RealVersionProvider {
async fn latest_version(
&self,
release_channel: ReleaseChannel,
- ) -> Result<String, AnyError> {
+ ) -> Result<AvailableVersion, AnyError> {
fetch_latest_version(
&self.http_client_provider.get_or_create()?,
release_channel,
@@ -140,12 +142,26 @@ impl VersionProvider for RealVersionProvider {
Cow::Borrowed(version::release_version_or_canary_commit_hash())
}
- // TODO(bartlomieju): update to handle `Lts` and `Rc` channels
+ // TODO(bartlomieju): update to handle `Lts` channel
async fn get_current_exe_release_channel(
&self,
) -> Result<ReleaseChannel, AnyError> {
if version::is_canary() {
- Ok(ReleaseChannel::Canary)
+ let rc_versions = get_rc_versions(
+ &self.http_client_provider.get_or_create()?,
+ self.check_kind,
+ )
+ .await?;
+
+ let is_current_exe_an_rc = rc_versions
+ .iter()
+ .any(|(hash, _)| hash == version::GIT_COMMIT_HASH);
+
+ if is_current_exe_an_rc {
+ Ok(ReleaseChannel::Rc)
+ } else {
+ Ok(ReleaseChannel::Canary)
+ }
} else {
Ok(ReleaseChannel::Stable)
}
@@ -176,20 +192,19 @@ impl<
}
pub fn should_check_for_new_version(&self) -> bool {
- match &self.maybe_file {
- Some(file) => {
- let last_check_age = self
- .env
- .current_time()
- .signed_duration_since(file.last_checked);
- last_check_age > chrono::Duration::hours(UPGRADE_CHECK_INTERVAL)
- }
- None => true,
- }
+ let Some(file) = &self.maybe_file else {
+ return true;
+ };
+
+ let last_check_age = self
+ .env
+ .current_time()
+ .signed_duration_since(file.last_checked);
+ last_check_age > chrono::Duration::hours(UPGRADE_CHECK_INTERVAL)
}
- /// Returns the version if a new one is available and it should be prompted about.
- pub fn should_prompt(&self) -> Option<String> {
+ /// Returns the current exe release channel and a version if a new one is available and it should be prompted about.
+ pub fn should_prompt(&self) -> Option<(ReleaseChannel, String)> {
let file = self.maybe_file.as_ref()?;
// If the current version saved is not the actually current version of the binary
// It means
@@ -217,7 +232,7 @@ impl<
.current_time()
.signed_duration_since(file.last_prompt);
if last_prompt_age > chrono::Duration::hours(UPGRADE_CHECK_INTERVAL) {
- Some(file.latest_version.clone())
+ Some((file.current_release_channel, file.latest_version.clone()))
} else {
None
}
@@ -238,6 +253,9 @@ fn get_minor_version(version: &str) -> &str {
}
fn print_release_notes(current_version: &str, new_version: &str) {
+ // TODO(bartlomieju): we might want to reconsider this one for RC releases.
+ // TODO(bartlomieju): also maybe just parse using `Version::standard` instead
+ // of using `get_minor_version`?
if get_minor_version(current_version) == get_minor_version(new_version) {
return;
}
@@ -274,8 +292,10 @@ pub fn check_for_upgrades(
}
let env = RealUpdateCheckerEnvironment::new(cache_file_path);
- let version_provider =
- RealVersionProvider::new(http_client_provider, UpgradeCheckKind::Execution);
+ let version_provider = RealVersionProvider::new(
+ http_client_provider.clone(),
+ UpgradeCheckKind::Execution,
+ );
let update_checker = UpdateChecker::new(env, version_provider);
if update_checker.should_check_for_new_version() {
@@ -294,16 +314,20 @@ pub fn check_for_upgrades(
});
}
+ // Don't bother doing any more computation if we're not in TTY environment.
+ let should_prompt =
+ log::log_enabled!(log::Level::Info) && std::io::stderr().is_terminal();
+
+ if !should_prompt {
+ return;
+ }
+
// Print a message if an update is available
- if let Some(upgrade_version) = update_checker.should_prompt() {
- if log::log_enabled!(log::Level::Info) && std::io::stderr().is_terminal() {
- if version::is_canary() {
- log::info!(
- "{} {}",
- colors::green("A new canary release of Deno is available."),
- colors::italic_gray("Run `deno upgrade --canary` to install it.")
- );
- } else {
+ if let Some((release_channel, upgrade_version)) =
+ update_checker.should_prompt()
+ {
+ match release_channel {
+ ReleaseChannel::Stable => {
log::info!(
"{} {} → {} {}",
colors::green("A new release of Deno is available:"),
@@ -312,16 +336,31 @@ pub fn check_for_upgrades(
colors::italic_gray("Run `deno upgrade` to install it.")
);
}
-
- update_checker.store_prompted();
+ ReleaseChannel::Canary => {
+ log::info!(
+ "{} {}",
+ colors::green("A new canary release of Deno is available."),
+ colors::italic_gray("Run `deno upgrade --canary` to install it.")
+ );
+ }
+ ReleaseChannel::Rc => {
+ log::info!(
+ "{} {}",
+ colors::green("A new release candidate of Deno is available."),
+ colors::italic_gray("Run `deno upgrade --rc` to install it.")
+ );
+ }
+ // TODO(bartlomieju)
+ ReleaseChannel::Lts => unreachable!(),
}
+
+ update_checker.store_prompted();
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LspVersionUpgradeInfo {
pub latest_version: String,
- // TODO(bartlomieju): use `ReleaseChannel` instead
pub is_canary: bool,
}
@@ -346,34 +385,39 @@ async fn check_for_upgrades_for_lsp_with_provider(
let current_version = version_provider.current_version();
// Nothing to upgrade
- if current_version == latest_version {
+ if current_version == latest_version.version_or_hash {
return Ok(None);
}
match release_channel {
ReleaseChannel::Stable => {
if let Ok(current) = Version::parse_standard(&current_version) {
- if let Ok(latest) = Version::parse_standard(&latest_version) {
+ if let Ok(latest) =
+ Version::parse_standard(&latest_version.version_or_hash)
+ {
if current >= latest {
return Ok(None); // nothing to upgrade
}
}
}
Ok(Some(LspVersionUpgradeInfo {
- latest_version,
+ latest_version: latest_version.display,
is_canary: false,
}))
}
ReleaseChannel::Canary => Ok(Some(LspVersionUpgradeInfo {
- latest_version,
+ latest_version: latest_version.display,
+ is_canary: true,
+ })),
+
+ ReleaseChannel::Rc => Ok(Some(LspVersionUpgradeInfo {
+ latest_version: latest_version.display,
is_canary: true,
})),
// TODO(bartlomieju)
ReleaseChannel::Lts => unreachable!(),
- // TODO(bartlomieju)
- ReleaseChannel::Rc => unreachable!(),
}
}
@@ -396,18 +440,18 @@ async fn fetch_and_store_latest_version<
return;
};
- env.write_check_file(
- &CheckVersionFile {
- // put a date in the past here so that prompt can be shown on next run
- last_prompt: env
- .current_time()
- .sub(chrono::Duration::hours(UPGRADE_CHECK_INTERVAL + 1)),
- last_checked: env.current_time(),
- current_version: version_provider.current_version().to_string(),
- latest_version,
- }
- .serialize(),
- );
+ let version_file = CheckVersionFile {
+ // put a date in the past here so that prompt can be shown on next run
+ last_prompt: env
+ .current_time()
+ .sub(chrono::Duration::hours(UPGRADE_CHECK_INTERVAL + 1)),
+ last_checked: env.current_time(),
+ current_version: version_provider.current_version().to_string(),
+ latest_version: latest_version.version_or_hash,
+ current_release_channel: release_channel,
+ };
+
+ env.write_check_file(&version_file.serialize());
}
pub async fn upgrade(
@@ -437,29 +481,33 @@ pub async fn upgrade(
let requested_version =
RequestedVersion::from_upgrade_flags(upgrade_flags.clone())?;
- let maybe_install_version = match requested_version {
+ let maybe_selected_version_to_upgrade = match &requested_version {
RequestedVersion::Latest(channel) => {
find_latest_version_to_upgrade(
http_client_provider.clone(),
- channel,
+ *channel,
force_selection_of_new_version,
)
.await?
}
RequestedVersion::SpecificVersion(channel, version) => {
select_specific_version_for_upgrade(
- channel,
- version,
+ *channel,
+ version.clone(),
force_selection_of_new_version,
)?
}
};
- let Some(install_version) = maybe_install_version else {
+ let Some(selected_version_to_upgrade) = maybe_selected_version_to_upgrade
+ else {
return Ok(());
};
- let download_url = get_download_url(&install_version, upgrade_flags.canary)?;
+ let download_url = get_download_url(
+ &selected_version_to_upgrade.version_or_hash,
+ requested_version.is_canary(),
+ )?;
log::info!("{}", colors::gray(format!("Downloading {}", &download_url)));
let Some(archive_data) = download_package(&client, download_url).await?
else {
@@ -469,7 +517,10 @@ pub async fn upgrade(
log::info!(
"{}",
- colors::gray(format!("Deno is upgrading to version {}", &install_version))
+ colors::gray(format!(
+ "Deno is upgrading to version {}",
+ &selected_version_to_upgrade.display
+ ))
);
let temp_dir = tempfile::TempDir::new()?;
@@ -486,8 +537,11 @@ pub async fn upgrade(
if upgrade_flags.dry_run {
fs::remove_file(&new_exe_path)?;
log::info!("Upgraded successfully (dry run)");
- if !upgrade_flags.canary {
- print_release_notes(version::deno(), &install_version);
+ if !requested_version.is_canary() {
+ print_release_notes(
+ version::deno(),
+ &selected_version_to_upgrade.version_or_hash,
+ );
}
drop(temp_dir);
return Ok(());
@@ -507,11 +561,11 @@ pub async fn upgrade(
"{}",
colors::green(format!(
"\nUpgraded successfully to Deno v{}\n",
- install_version
+ selected_version_to_upgrade.display
))
);
- if !upgrade_flags.canary {
- print_release_notes(version::deno(), &install_version);
+ if !requested_version.is_canary() {
+ print_release_notes(version::deno(), &selected_version_to_upgrade.display);
}
drop(temp_dir); // delete the temp dir
@@ -526,10 +580,13 @@ enum RequestedVersion {
impl RequestedVersion {
fn from_upgrade_flags(upgrade_flags: UpgradeFlags) -> Result<Self, AnyError> {
let is_canary = upgrade_flags.canary;
+ let is_release_candidate = upgrade_flags.release_candidate;
let Some(passed_version) = upgrade_flags.version else {
let channel = if is_canary {
ReleaseChannel::Canary
+ } else if is_release_candidate {
+ ReleaseChannel::Rc
} else {
ReleaseChannel::Stable
};
@@ -556,13 +613,25 @@ impl RequestedVersion {
Ok(RequestedVersion::SpecificVersion(channel, passed_version))
}
+
+ /// Channels that use Git hashes as versions are considered canary.
+ pub fn is_canary(&self) -> bool {
+ match self {
+ Self::Latest(channel) => {
+ matches!(channel, ReleaseChannel::Canary | ReleaseChannel::Rc)
+ }
+ Self::SpecificVersion(channel, _) => {
+ matches!(channel, ReleaseChannel::Canary | ReleaseChannel::Rc)
+ }
+ }
+ }
}
fn select_specific_version_for_upgrade(
release_channel: ReleaseChannel,
version: String,
force: bool,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<AvailableVersion>, AnyError> {
match release_channel {
ReleaseChannel::Stable => {
let current_is_passed = if !version::is_canary() {
@@ -576,7 +645,10 @@ fn select_specific_version_for_upgrade(
return Ok(None);
}
- Ok(Some(version))
+ Ok(Some(AvailableVersion {
+ version_or_hash: version.to_string(),
+ display: version,
+ }))
}
ReleaseChannel::Canary => {
let current_is_passed = version::GIT_COMMIT_HASH == version;
@@ -585,7 +657,10 @@ fn select_specific_version_for_upgrade(
return Ok(None);
}
- Ok(Some(version))
+ Ok(Some(AvailableVersion {
+ version_or_hash: version.to_string(),
+ display: version,
+ }))
}
// TODO(bartlomieju)
ReleaseChannel::Rc => unreachable!(),
@@ -598,14 +673,14 @@ async fn find_latest_version_to_upgrade(
http_client_provider: Arc<HttpClientProvider>,
release_channel: ReleaseChannel,
force: bool,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<AvailableVersion>, AnyError> {
log::info!(
"{}",
colors::gray(&format!("Looking up {} version", release_channel.name()))
);
let client = http_client_provider.get_or_create()?;
- let latest_version =
+ let latest_version_found =
fetch_latest_version(&client, release_channel, UpgradeCheckKind::Execution)
.await?;
@@ -614,7 +689,9 @@ async fn find_latest_version_to_upgrade(
let current_version = version::deno();
let current_is_most_recent = if !version::is_canary() {
let current = Version::parse_standard(current_version).unwrap();
- let latest = Version::parse_standard(&latest_version).unwrap();
+ let latest =
+ Version::parse_standard(&latest_version_found.version_or_hash)
+ .unwrap();
current >= latest
} else {
false
@@ -623,21 +700,31 @@ async fn find_latest_version_to_upgrade(
if !force && current_is_most_recent {
(None, current_version)
} else {
- (Some(latest_version), current_version)
+ (Some(latest_version_found), current_version)
}
}
ReleaseChannel::Canary => {
let current_version = version::GIT_COMMIT_HASH;
- let current_is_most_recent = current_version == latest_version;
+ let current_is_most_recent =
+ current_version == latest_version_found.version_or_hash;
if !force && current_is_most_recent {
(None, current_version)
} else {
- (Some(latest_version), current_version)
+ (Some(latest_version_found), current_version)
+ }
+ }
+ ReleaseChannel::Rc => {
+ let current_version = version::GIT_COMMIT_HASH;
+ let current_is_most_recent =
+ current_version == latest_version_found.version_or_hash;
+
+ if !force && current_is_most_recent {
+ (None, current_version)
+ } else {
+ (Some(latest_version_found), current_version)
}
}
- // TODO(bartlomieju)
- ReleaseChannel::Rc => unreachable!(),
// TODO(bartlomieju)
ReleaseChannel::Lts => unreachable!(),
};
@@ -648,7 +735,7 @@ async fn find_latest_version_to_upgrade(
"{}",
color_print::cformat!(
"<g>Found latest version {}</>",
- newer_latest_version
+ newer_latest_version.display
)
);
} else {
@@ -665,7 +752,7 @@ async fn find_latest_version_to_upgrade(
Ok(maybe_newer_latest_version)
}
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone, Copy, PartialEq)]
enum ReleaseChannel {
/// Stable version, eg. 1.45.4, 2.0.0, 2.1.0
Stable,
@@ -677,8 +764,7 @@ enum ReleaseChannel {
#[allow(unused)]
Lts,
- /// Release candidate
- #[allow(unused)]
+ /// Release candidate, poiting to a git hash
Rc,
}
@@ -691,26 +777,106 @@ impl ReleaseChannel {
Self::Lts => "LTS (long term support)",
}
}
+
+ fn serialize(&self) -> String {
+ match self {
+ Self::Stable => "stable",
+ Self::Canary => "canary",
+ Self::Rc => "rc",
+ Self::Lts => "lts",
+ }
+ .to_string()
+ }
+
+ fn deserialize(str_: &str) -> Result<Self, AnyError> {
+ Ok(match str_ {
+ "stable" => Self::Stable,
+ "canary" => Self::Canary,
+ "rc" => Self::Rc,
+ "lts" => Self::Lts,
+ unknown => bail!("Unrecognized release channel: {}", unknown),
+ })
+ }
+}
+
+// Return a list of available RC release in format of (<commit_hash>, <version_name>)
+fn parse_rc_versions_text(
+ text: &str,
+) -> Result<Vec<(String, String)>, AnyError> {
+ let lines: Vec<_> = text
+ .split("\n")
+ .map(|s| s.to_string())
+ .filter(|s| !s.is_empty())
+ .collect();
+ if lines.is_empty() {
+ bail!("No release candidates available");
+ }
+
+ let mut parsed = Vec::with_capacity(lines.len());
+
+ for line in lines {
+ let v = line.split(' ').collect::<Vec<_>>();
+ if v.len() != 2 {
+ bail!("Malformed list of RC releases");
+ }
+ parsed.push((v[0].to_string(), v[1].to_string()));
+ }
+
+ Ok(parsed)
+}
+
+async fn get_rc_versions(
+ client: &HttpClient,
+ check_kind: UpgradeCheckKind,
+) -> Result<Vec<(String, String)>, AnyError> {
+ let url =
+ get_latest_version_url(ReleaseChannel::Rc, env!("TARGET"), check_kind);
+ let text = client.download_text(url.parse()?).await?;
+ parse_rc_versions_text(&text)
+}
+
+#[derive(Debug, Clone, PartialEq)]
+struct AvailableVersion {
+ version_or_hash: String,
+ display: String,
}
async fn fetch_latest_version(
client: &HttpClient,
release_channel: ReleaseChannel,
check_kind: UpgradeCheckKind,
-) -> Result<String, AnyError> {
+) -> Result<AvailableVersion, AnyError> {
let url = get_latest_version_url(release_channel, env!("TARGET"), check_kind);
let text = client.download_text(url.parse()?).await?;
- Ok(normalize_version_from_server(release_channel, &text))
+ let version = normalize_version_from_server(release_channel, &text)?;
+ Ok(version)
}
fn normalize_version_from_server(
release_channel: ReleaseChannel,
text: &str,
-) -> String {
+) -> Result<AvailableVersion, AnyError> {
let text = text.trim();
match release_channel {
- ReleaseChannel::Stable => text.trim_start_matches('v').to_string(),
- ReleaseChannel::Canary => text.to_string(),
+ ReleaseChannel::Stable => {
+ let v = text.trim_start_matches('v').to_string();
+ Ok(AvailableVersion {
+ version_or_hash: v.to_string(),
+ display: v.to_string(),
+ })
+ }
+ ReleaseChannel::Canary => Ok(AvailableVersion {
+ version_or_hash: text.to_string(),
+ display: text.to_string(),
+ }),
+ ReleaseChannel::Rc => {
+ let lines = parse_rc_versions_text(text)?;
+ let latest = lines.last().unwrap();
+ Ok(AvailableVersion {
+ version_or_hash: latest.0.to_string(),
+ display: latest.1.to_string(),
+ })
+ }
_ => unreachable!(),
}
}
@@ -725,6 +891,7 @@ fn get_latest_version_url(
ReleaseChannel::Canary => {
Cow::Owned(format!("canary-{target_tuple}-latest.txt"))
}
+ ReleaseChannel::Rc => Cow::Borrowed("release-rc.txt"),
_ => unreachable!(),
};
let query_param = match check_kind {
@@ -864,13 +1031,14 @@ struct CheckVersionFile {
pub last_checked: chrono::DateTime<chrono::Utc>,
pub current_version: String,
pub latest_version: String,
+ pub current_release_channel: ReleaseChannel,
}
impl CheckVersionFile {
pub fn parse(content: String) -> Option<Self> {
let split_content = content.split('!').collect::<Vec<_>>();
- if split_content.len() != 4 {
+ if split_content.len() != 5 {
return None;
}
@@ -882,6 +1050,15 @@ impl CheckVersionFile {
if current_version.is_empty() {
return None;
}
+ let current_release_channel = split_content[4].trim().to_owned();
+ if current_release_channel.is_empty() {
+ return None;
+ }
+ let Ok(current_release_channel) =
+ ReleaseChannel::deserialize(&current_release_channel)
+ else {
+ return None;
+ };
let last_prompt = chrono::DateTime::parse_from_rfc3339(split_content[0])
.map(|dt| dt.with_timezone(&chrono::Utc))
@@ -895,16 +1072,18 @@ impl CheckVersionFile {
last_checked,
current_version,
latest_version,
+ current_release_channel,
})
}
fn serialize(&self) -> String {
format!(
- "{}!{}!{}!{}",
+ "{}!{}!{}!{}!{}",
self.last_prompt.to_rfc3339(),
self.last_checked.to_rfc3339(),
self.latest_version,
self.current_version,
+ self.current_release_channel.serialize(),
)
}
@@ -925,9 +1104,16 @@ mod test {
#[test]
fn test_parse_upgrade_check_file() {
- let file = CheckVersionFile::parse(
+ // NOTE(bartlomieju): pre-1.46 format
+ let maybe_file = CheckVersionFile::parse(
"2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2"
.to_string(),
+ );
+ assert!(maybe_file.is_none());
+ // NOTE(bartlomieju): post-1.46 format
+ let file = CheckVersionFile::parse(
+ "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2!stable"
+ .to_string(),
)
.unwrap();
assert_eq!(
@@ -940,6 +1126,7 @@ mod test {
);
assert_eq!(file.latest_version, "1.2.3".to_string());
assert_eq!(file.current_version, "1.2.2".to_string());
+ assert_eq!(file.current_release_channel, ReleaseChannel::Stable);
let result =
CheckVersionFile::parse("2020-01-01T00:00:00+00:00!".to_string());
@@ -953,8 +1140,45 @@ mod test {
}
#[test]
+ fn test_parse_rc_versions_text() {
+ let rc_versions = parse_rc_versions_text(
+ r#"qwerw3452gbxcvbarwett234 v1.46.0-rc.0
+cvbnfhuertt23523452345 v1.46.0-rc.1
+sdfq3452345egasdfgsdgf v2.0.0-rc.0
+asdf456yegfncbvjwe4523 v2.0.0-rc.1
+hjr6562w34rgzcvh56734a v2.0.0-rc.2
+bdfgtd6wergsdg3243234v v2.0.0-rc.3
+"#,
+ )
+ .unwrap();
+
+ assert_eq!(rc_versions.len(), 6);
+ assert_eq!(rc_versions[3].0, "asdf456yegfncbvjwe4523");
+ assert_eq!(rc_versions[3].1, "v2.0.0-rc.1");
+
+ let rc_versions = parse_rc_versions_text(
+ r#"qwerw3452gbxcvbarwett234 v1.46.0-rc.0
+
+cvbnfhuertt23523452345 v1.46.0-rc.1
+"#,
+ )
+ .unwrap();
+
+ assert_eq!(rc_versions.len(), 2);
+ assert_eq!(rc_versions[1].0, "cvbnfhuertt23523452345");
+ assert_eq!(rc_versions[1].1, "v1.46.0-rc.1");
+
+ let err =
+ parse_rc_versions_text(r#"qwerw3452gbxcvbarwett234 v1.46.0-rc.0"#)
+ .unwrap_err();
+ assert_eq!(err.to_string(), "Malformed list of RC releases");
+ let err = parse_rc_versions_text("").unwrap_err();
+ assert_eq!(err.to_string(), "No release candidates available");
+ }
+
+ #[test]
fn test_serialize_upgrade_check_file() {
- let file = CheckVersionFile {
+ let mut file = CheckVersionFile {
last_prompt: chrono::DateTime::parse_from_rfc3339("2020-01-01T00:00:00Z")
.unwrap()
.with_timezone(&chrono::Utc),
@@ -965,19 +1189,35 @@ mod test {
.with_timezone(&chrono::Utc),
latest_version: "1.2.3".to_string(),
current_version: "1.2.2".to_string(),
+ current_release_channel: ReleaseChannel::Stable,
};
assert_eq!(
file.serialize(),
- "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2"
+ "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2!stable"
+ );
+ file.current_release_channel = ReleaseChannel::Canary;
+ assert_eq!(
+ file.serialize(),
+ "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2!canary"
+ );
+ file.current_release_channel = ReleaseChannel::Rc;
+ assert_eq!(
+ file.serialize(),
+ "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2!rc"
+ );
+ file.current_release_channel = ReleaseChannel::Lts;
+ assert_eq!(
+ file.serialize(),
+ "2020-01-01T00:00:00+00:00!2020-01-01T00:00:00+00:00!1.2.3!1.2.2!lts"
);
}
#[derive(Clone)]
struct TestUpdateCheckerEnvironment {
file_text: Rc<RefCell<String>>,
- is_canary: Rc<RefCell<bool>>,
+ release_channel: Rc<RefCell<ReleaseChannel>>,
current_version: Rc<RefCell<String>>,
- latest_version: Rc<RefCell<Result<String, String>>>,
+ latest_version: Rc<RefCell<Result<AvailableVersion, String>>>,
time: Rc<RefCell<chrono::DateTime<chrono::Utc>>>,
}
@@ -986,8 +1226,11 @@ mod test {
Self {
file_text: Default::default(),
current_version: Default::default(),
- is_canary: Default::default(),
- latest_version: Rc::new(RefCell::new(Ok("".to_string()))),
+ release_channel: Rc::new(RefCell::new(ReleaseChannel::Stable)),
+ latest_version: Rc::new(RefCell::new(Ok(AvailableVersion {
+ version_or_hash: "".to_string(),
+ display: "".to_string(),
+ }))),
time: Rc::new(RefCell::new(chrono::Utc::now())),
}
}
@@ -1008,15 +1251,18 @@ mod test {
}
pub fn set_latest_version(&self, version: &str) {
- *self.latest_version.borrow_mut() = Ok(version.to_string());
+ *self.latest_version.borrow_mut() = Ok(AvailableVersion {
+ version_or_hash: version.to_string(),
+ display: version.to_string(),
+ });
}
pub fn set_latest_version_err(&self, err: &str) {
*self.latest_version.borrow_mut() = Err(err.to_string());
}
- pub fn set_is_canary(&self, is_canary: bool) {
- *self.is_canary.borrow_mut() = is_canary;
+ pub fn set_release_channel(&self, channel: ReleaseChannel) {
+ *self.release_channel.borrow_mut() = channel;
}
}
@@ -1026,7 +1272,7 @@ mod test {
async fn latest_version(
&self,
_release_channel: ReleaseChannel,
- ) -> Result<String, AnyError> {
+ ) -> Result<AvailableVersion, AnyError> {
match self.latest_version.borrow().clone() {
Ok(result) => Ok(result),
Err(err) => bail!("{}", err),
@@ -1037,15 +1283,10 @@ mod test {
Cow::Owned(self.current_version.borrow().clone())
}
- // TODO(bartlomieju): update to handle `Lts` and `Rc` channels
async fn get_current_exe_release_channel(
&self,
) -> Result<ReleaseChannel, AnyError> {
- if *self.is_canary.borrow() {
- Ok(ReleaseChannel::Canary)
- } else {
- Ok(ReleaseChannel::Stable)
- }
+ Ok(*self.release_channel.borrow())
}
}
@@ -1083,25 +1324,37 @@ mod test {
// should not check for latest version because we just did
assert!(!checker.should_check_for_new_version());
// but should prompt
- assert_eq!(checker.should_prompt(), Some("1.1.0".to_string()));
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Stable, "1.1.0".to_string()))
+ );
// fast forward an hour and bump the latest version
env.add_hours(1);
env.set_latest_version("1.2.0");
assert!(!checker.should_check_for_new_version());
- assert_eq!(checker.should_prompt(), Some("1.1.0".to_string()));
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Stable, "1.1.0".to_string()))
+ );
// fast forward again and it should check for a newer version
env.add_hours(UPGRADE_CHECK_INTERVAL);
assert!(checker.should_check_for_new_version());
- assert_eq!(checker.should_prompt(), Some("1.1.0".to_string()));
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Stable, "1.1.0".to_string()))
+ );
fetch_and_store_latest_version(&env, &env).await;
// reload and store that we prompted
let checker = UpdateChecker::new(env.clone(), env.clone());
assert!(!checker.should_check_for_new_version());
- assert_eq!(checker.should_prompt(), Some("1.2.0".to_string()));
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Stable, "1.2.0".to_string()))
+ );
checker.store_prompted();
// reload and it should now say not to prompt
@@ -1112,7 +1365,10 @@ mod test {
// but if we fast forward past the upgrade interval it should prompt again
env.add_hours(UPGRADE_CHECK_INTERVAL + 1);
assert!(checker.should_check_for_new_version());
- assert_eq!(checker.should_prompt(), Some("1.2.0".to_string()));
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Stable, "1.2.0".to_string()))
+ );
// upgrade the version and it should stop prompting
env.set_current_version("1.2.0");
@@ -1128,6 +1384,21 @@ mod test {
fetch_and_store_latest_version(&env, &env).await;
assert!(checker.should_check_for_new_version());
assert_eq!(checker.should_prompt(), None);
+
+ // now switch to RC release
+ env.set_release_channel(ReleaseChannel::Rc);
+ env.set_current_version("1.46.0-rc.0");
+ env.set_latest_version("1.46.0-rc.1");
+ fetch_and_store_latest_version(&env, &env).await;
+ env.add_hours(UPGRADE_CHECK_INTERVAL + 1);
+
+ // We should check for new version and prompt
+ let checker = UpdateChecker::new(env.clone(), env.clone());
+ assert!(checker.should_check_for_new_version());
+ assert_eq!(
+ checker.should_prompt(),
+ Some((ReleaseChannel::Rc, "1.46.0-rc.1".to_string()))
+ );
}
#[tokio::test]
@@ -1140,6 +1411,7 @@ mod test {
last_checked: env.current_time(),
latest_version: "1.26.2".to_string(),
current_version: "1.27.0".to_string(),
+ current_release_channel: ReleaseChannel::Stable,
}
.serialize();
env.write_check_file(&file_content);
@@ -1162,6 +1434,7 @@ mod test {
last_checked: env.current_time(),
latest_version: "1.26.2".to_string(),
current_version: "1.25.0".to_string(),
+ current_release_channel: ReleaseChannel::Stable,
}
.serialize();
env.write_check_file(&file_content);
@@ -1231,11 +1504,43 @@ mod test {
);
assert_eq!(
get_latest_version_url(
- ReleaseChannel::Stable,
+ ReleaseChannel::Rc,
"x86_64-pc-windows-msvc",
UpgradeCheckKind::Lsp
),
- "https://dl.deno.land/release-latest.txt?lsp"
+ "https://dl.deno.land/release-rc.txt?lsp"
+ );
+ assert_eq!(
+ get_latest_version_url(
+ ReleaseChannel::Rc,
+ "aarch64-apple-darwin",
+ UpgradeCheckKind::Execution
+ ),
+ "https://dl.deno.land/release-rc.txt"
+ );
+ assert_eq!(
+ get_latest_version_url(
+ ReleaseChannel::Rc,
+ "aarch64-apple-darwin",
+ UpgradeCheckKind::Lsp
+ ),
+ "https://dl.deno.land/release-rc.txt?lsp"
+ );
+ assert_eq!(
+ get_latest_version_url(
+ ReleaseChannel::Rc,
+ "x86_64-pc-windows-msvc",
+ UpgradeCheckKind::Execution
+ ),
+ "https://dl.deno.land/release-rc.txt"
+ );
+ assert_eq!(
+ get_latest_version_url(
+ ReleaseChannel::Rc,
+ "x86_64-pc-windows-msvc",
+ UpgradeCheckKind::Lsp
+ ),
+ "https://dl.deno.land/release-rc.txt?lsp"
);
}
@@ -1243,24 +1548,46 @@ mod test {
fn test_normalize_version_server() {
// should strip v for stable
assert_eq!(
- normalize_version_from_server(ReleaseChannel::Stable, "v1.0.0"),
- "1.0.0"
+ normalize_version_from_server(ReleaseChannel::Stable, "v1.0.0").unwrap(),
+ AvailableVersion {
+ version_or_hash: "1.0.0".to_string(),
+ display: "1.0.0".to_string()
+ },
);
// should not replace v after start
assert_eq!(
normalize_version_from_server(
ReleaseChannel::Stable,
" v1.0.0-test-v\n\n "
- ),
- "1.0.0-test-v"
+ )
+ .unwrap(),
+ AvailableVersion {
+ version_or_hash: "1.0.0-test-v".to_string(),
+ display: "1.0.0-test-v".to_string()
+ }
);
// should not strip v for canary
assert_eq!(
normalize_version_from_server(
ReleaseChannel::Canary,
" v1452345asdf \n\n "
- ),
- "v1452345asdf"
+ )
+ .unwrap(),
+ AvailableVersion {
+ version_or_hash: "v1452345asdf".to_string(),
+ display: "v1452345asdf".to_string()
+ }
+ );
+ assert_eq!(
+ normalize_version_from_server(
+ ReleaseChannel::Rc,
+ "asdfq345wdfasdfasdf v1.46.0-rc.0\nasdfq345wdfasdfasdf v1.46.0-rc.1\n"
+ )
+ .unwrap(),
+ AvailableVersion {
+ version_or_hash: "asdfq345wdfasdfasdf".to_string(),
+ display: "v1.46.0-rc.1".to_string(),
+ },
);
}
@@ -1303,7 +1630,7 @@ mod test {
{
env.set_current_version("123");
env.set_latest_version("123");
- env.set_is_canary(true);
+ env.set_release_channel(ReleaseChannel::Canary);
let maybe_info = check_for_upgrades_for_lsp_with_provider(&env)
.await
.unwrap();
@@ -1323,5 +1650,30 @@ mod test {
})
);
}
+ // rc equal
+ {
+ env.set_release_channel(ReleaseChannel::Rc);
+ env.set_current_version("1.2.3-rc.0");
+ env.set_latest_version("1.2.3-rc.0");
+ let maybe_info = check_for_upgrades_for_lsp_with_provider(&env)
+ .await
+ .unwrap();
+ assert_eq!(maybe_info, None);
+ }
+ // canary different
+ {
+ env.set_latest_version("1.2.3-rc.0");
+ env.set_latest_version("1.2.3-rc.1");
+ let maybe_info = check_for_upgrades_for_lsp_with_provider(&env)
+ .await
+ .unwrap();
+ assert_eq!(
+ maybe_info,
+ Some(LspVersionUpgradeInfo {
+ latest_version: "1.2.3-rc.1".to_string(),
+ is_canary: true,
+ })
+ );
+ }
}
}