8000 feat(cli, next): support pre-release versions by augustoccesar · Pull Request #204 · mentimeter/linkup · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(cli, next): support pre-release versions #204

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 24 additions & 16 deletions linkup-cli/src/release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,17 @@ pub async fn available_update(current_version: &Version) -> Option<Update> {
let latest_release = match cached_latest_release().await {
Some(cached_latest_release) => cached_latest_release.release,
None => {
let release = match fetch_latest_release().await {
Ok(release) => release,
let release = if current_version.is_beta() {
fetch_beta_release().await
} else {
fetch_stable_release().await
};

let release = match release {
Ok(Some(release)) => release,
Ok(None) => return None,
Err(error) => {
log::error!("Failed to fetch the latest release: {}", error);

return None;
}
};
Expand Down Expand Up @@ -187,7 +193,7 @@ pub async fn available_update(current_version: &Version) -> Option<Update> {
Some(Update { linkup })
}

async fn fetch_latest_release() -> Result<Release, reqwest::Error> {
async fn fetch_stable_release() -> Result<Option<Release>, reqwest::Error> {
let url: Url = "https://api.github.com/repos/mentimeter/linkup/releases/latest"
.parse()
.unwrap();
Expand All @@ -209,19 +215,15 @@ async fn fetch_latest_release() -> Result<Release, reqwest::Error> {
.build()
.unwrap();

client.execute(req).await?.json().await
}
let release = client.execute(req).await?.json().await?;

#[allow(dead_code)]
pub async fn fetch_release(version: &Version) -> Result<Option<Release>, reqwest::Error> {
let tag = version.to_string();
Ok(Some(release))
}

let url: Url = format!(
"https://api.github.com/repos/mentimeter/linkup/releases/tags/{}",
&tag
)
.parse()
.unwrap();
pub async fn fetch_beta_release() -> Result<Option<Release>, reqwest::Error> {
let url: Url = "https://api.github.com/repos/mentimeter/linkup/releases"
.parse()
.unwrap();

let mut req = reqwest::Request::new(reqwest::Method::GET, url);
let headers = req.headers_mut();
Expand All @@ -240,7 +242,13 @@ pub async fn fetch_release(version: &Version) -> Result<Option<Release>, reqwest
.build()
.unwrap();

client.execute(req).await?.json().await
let releases: Vec<Release> = client.execute(req).await?.json().await?;

let beta_release = releases
.into_iter()
.find(|release| release.version.starts_with("0.0.0-next-"));

Ok(beta_release)
}

async fn cached_latest_release() -> Option<CachedLatestRelease> {
Expand Down
102 changes: 86 additions & 16 deletions linkup/src/versioning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,48 +8,86 @@ pub enum VersionError {

#[derive(Debug, Clone)]
pub struct Version {
major: u16,
minor: u16,
patch: u16,
pub major: u16,
pub minor: u16,
pub patch: u16,
pub pre_release: Option<String>,
}

impl Version {
pub fn is_beta(&self) -> bool {
if let Some(pre_release) = &self.pre_release {
return pre_release.starts_with("next-");
}

false
}
}

impl PartialEq for Version {
fn eq(&self, other: &Self) -> bool {
self.major == other.major && self.minor == other.minor && self.patch == other.patch
self.major == other.major
&& self.minor == other.minor
&& self.patch == other.patch
&& self.pre_release == other.pre_release
}
}

impl PartialOrd for Version {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match (
self.major.cmp(&other.major),
self.minor.cmp(&other.minor),
self.patch.cmp(&other.patch),
) {
(std::cmp::Ordering::Equal, std::cmp::Ordering::Equal, ord) => Some(ord),
(std::cmp::Ordering::Equal, ord, _) => Some(ord),
(ord, _, _) => Some(ord),
match (&self.pre_release, &other.pre_release) {
(Some(a), Some(b)) => a.cmp(b).into(),
(Some(_), None) => {
// pre-release is always lower than stable
Some(std::cmp::Ordering::Less)
}
(None, Some(_)) => {
// stable is always higher than pre-release
Some(std::cmp::Ordering::Greater)
}
Comment on lines +40 to +47
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is more to protect against any accidental updates from beta channel into stable, and the other way around.
It shouldn't happen, since the available_update fetches from the same channel. But might be good to have.

(None, None) => {
match (
self.major.cmp(&other.major),
self.minor.cmp(&other.minor),
self.patch.cmp(&other.patch),
) {
(std::cmp::Ordering::Equal, std::cmp::Ordering::Equal, ord) => Some(ord),
(std::cmp::Ordering::Equal, ord, _) => Some(ord),
(ord, _, _) => Some(ord),
}
}
}
}
}

impl Display for Version {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)?;

Ok(())
if let Some(pre) = &self.pre_release {
write!(f, "{}.{}.{}-{}", self.major, self.minor, self.patch, pre)
} else {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
}

impl TryFrom<&str> for Version {
type Error = VersionError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
let (major, minor, patch) = match value.split('.').collect::<Vec<&str>>()[..] {
let parts: Vec<&str> = value.split('-').collect();
let version_part = parts[0];

let (major, minor, patch) = match version_part.split('.').collect::<Vec<&str>>()[..] {
[major, minor, patch] => (major, minor, patch),
_ => return Err(VersionError::Parsing(value.to_string())),
};

let pre_release = if parts.len() > 1 {
Some(parts[1..].join("-"))
} else {
None
};

Ok(Self {
major: major
.parse::<u16>()
Expand All @@ -60,6 +98,7 @@ impl TryFrom<&str> for Version {
patch: patch
.parse::<u16>()
.map_err(|_| VersionError::Parsing(value.to_string()))?,
pre_release,
})
}
}
Expand Down Expand Up @@ -136,14 +175,45 @@ mod tests {
assert!(newer_version <= version);
}

#[test]
fn test_pre_release_vs_stable() {
let pre_release_version = Version::try_from("0.0.0-next-20250317-abc123").unwrap();
let stable_version = Version::try_from("1.2.3").unwrap();

assert!(stable_version > pre_release_version);
assert!(stable_version >= pre_release_version);
}

#[test]
fn test_stable_vs_pre_release() {
let stable_version = Version::try_from("1.2.3").unwrap();
let pre_release_version = Version::try_from("0.0.0-next-20250317-abc123").unwrap();

assert!(pre_release_version <= stable_version);
assert!(pre_release_version < stable_version);
}

#[test]
fn test_display() {
let version = Version {
major: 1,
minor: 2,
patch: 3,
pre_release: None,
};

assert_eq!(version.to_string(), "1.2.3");
}

#[test]
fn test_display_pre_release() {
let version = Version {
major: 1,
minor: 2,
patch: 3,
pre_release: Some("next-20250317-abc123".into()),
};

assert_eq!(version.to_string(), "1.2.3-next-20250317-abc123");
}
}
0