Skip to content

Commit

Permalink
Provide default changelog URL
Browse files Browse the repository at this point in the history
  • Loading branch information
KonishchevDmitry committed Aug 2, 2024
1 parent ede13ae commit a69de16
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 29 deletions.
5 changes: 2 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
use globset::{GlobBuilder, GlobMatcher};
use serde::Deserialize;
use serde::de::{Deserializer, Error};
use url::Url;
use validator::Validate;

use crate::core::GenericResult;
Expand Down Expand Up @@ -45,9 +46,7 @@ pub struct Tool {
#[serde(default, deserialize_with = "deserialize_optional_glob")]
pub binary_matcher: Option<GlobMatcher>,

// FIXME(konishchev): Provide default
// FIXME(konishchev): echo 'Upgrading prometheus: 2.51.2 -> 2.53.1 (see https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md)...'
pub changelog: Option<String>,
pub changelog: Option<Url>,

#[serde(default, deserialize_with = "deserialize_optional_path")]
pub path: Option<PathBuf>,
Expand Down
37 changes: 26 additions & 11 deletions src/github.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct GithubConfig {
pub struct Release {
pub tag: String,
pub assets: Vec<Asset>,
pub changelog: Url,
}

pub struct Asset {
Expand All @@ -32,18 +33,18 @@ pub fn get_release(config: &GithubConfig, project: &str) -> GenericResult<Releas
create_runtime()?.block_on(get_release_async(config, project))
}

async fn get_release_async(config: &GithubConfig, project: &str) -> GenericResult<Release> {
let (owner, repository) = parse_project_name(project)?;
async fn get_release_async(config: &GithubConfig, full_name: &str) -> GenericResult<Release> {
let project = parse_project_name(full_name)?;

let mut builder = OctocrabBuilder::new();
if let Some(token) = config.token.as_ref() {
builder = builder.user_access_token(token.to_owned());
}

let github = builder.build()?;
let repository = github.repos(owner, repository);
let repository = github.repos(project.owner, project.name);

debug!("Getting {project} release info...");
debug!("Getting {full_name} release info...");

let release = repository.releases().get_latest().await
.map(Some)
Expand All @@ -59,15 +60,17 @@ async fn get_release_async(config: &GithubConfig, project: &str) -> GenericResul
None => {
repository.get().await.map_err(|err| {
match err {
Error::GitHub {source, ..} if source.status_code == StatusCode::NOT_FOUND => "The project doesn't exist".into(),
Error::GitHub {source, ..} if source.status_code == StatusCode::NOT_FOUND => {
"The project doesn't exist".into()
},
_ => humanize_error(err),
}
})?;
return Err!("The project has no releases");
},
};

trace!("The latest {project} release:\n{release:#?}");
trace!("The latest {full_name} release:\n{release:#?}");

Ok(Release {
tag: release.tag_name,
Expand All @@ -78,18 +81,30 @@ async fn get_release_async(config: &GithubConfig, project: &str) -> GenericResul
url: asset.browser_download_url,
}
}).collect(),
changelog: project.changelog,
})
}

fn parse_project_name(name: &str) -> GenericResult<(&str, &str)> {
let mut parts = name.split('/');
struct Project {
name: String,
owner: String,
changelog: Url,
}

fn parse_project_name(full_name: &str) -> GenericResult<Project> {
let mut parts = full_name.split('/');

let owner = parts.next();
let repository = parts.next();
let name = parts.next();
let extra = parts.next();
let changelog = Url::parse(&format!("https://github.com/{}/releases", full_name)).ok();

Ok(match (owner, repository, extra) {
(Some(owner), Some(repository), None) => (owner, repository),
Ok(match (owner, name, extra, changelog) {
(Some(owner), Some(name), None, Some(changelog)) => Project {
name: name.to_owned(),
owner: owner.to_owned(),
changelog,
},
_ => {
return Err!("Invalid GitHub project name");
},
Expand Down
27 changes: 12 additions & 15 deletions src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ fn install_tool(name: &str, tool: &Tool, mut mode: Mode, path: &Path, github_con
};

let release_time: SystemTime = asset.time.into();
let changelog = tool.changelog.as_ref().unwrap_or(&release.changelog);
let current_version = current_state.as_ref().and_then(|_|
version::get_binary_version(&install_path));

Expand All @@ -112,8 +113,8 @@ fn install_tool(name: &str, tool: &Tool, mut mode: Mode, path: &Path, github_con
} else {
match current_version {
Some(current_version) => info!(
"Reinstalling {name}: {current_version} -> {release_version}{changelog}...",
changelog=format_changelog(tool.changelog.as_deref(), Some(&current_version), &release_version),
"Reinstalling {name}: {current_version} -> {release_version}{changelog}",
changelog=format_changelog(changelog, Some(&current_version), &release_version),
),

None => info!("Reinstalling {name}..."),
Expand All @@ -132,13 +133,13 @@ fn install_tool(name: &str, tool: &Tool, mut mode: Mode, path: &Path, github_con

match current_version {
Some(current_version) => info!(
"Upgrading {name}: {current_version} -> {release_version}{changelog}...",
changelog=format_changelog(tool.changelog.as_deref(), Some(&current_version), &release_version),
"Upgrading {name}: {current_version} -> {release_version}{changelog}",
changelog=format_changelog(changelog, Some(&current_version), &release_version),
),

None => info!(
"Upgrading {name} to {release_version}{changelog}...",
changelog=format_changelog(tool.changelog.as_deref(), None, &release_version),
"Upgrading {name} to {release_version}{changelog}",
changelog=format_changelog(changelog, None, &release_version),
),
}
},
Expand Down Expand Up @@ -331,14 +332,10 @@ fn format_list<T: Display, I: Iterator<Item = T>>(mut iter: I) -> String {
"\n* ".to_owned() + &iter.join("\n* ")
}

fn format_changelog(changelog: Option<&str>, from: Option<&Version>, to: &ReleaseVersion) -> String {
let Some(changelog) = changelog else {
return String::new();
};

if matches!((from, to), (Some(from), ReleaseVersion::Version(to)) if from == to) {
return String::new();
fn format_changelog(changelog: &Url, from: Option<&Version>, to: &ReleaseVersion) -> String {
match (from, to) {
// We don't place ellipsis after changelog, because at least iTerm2 parses URL improperly in this case
(Some(from), ReleaseVersion::Version(to)) if from == to => "...".to_owned(),
_ => format!(" (see {changelog})")
}

format!(" (see {changelog})")
}

0 comments on commit a69de16

Please sign in to comment.