Skip to content
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## `Unreleased`

### Added

- Allow `x.y` versioning scheme in the config file ([#86])

### Changed

- Modified the `rokit install` command to check if Rokit is in the user's PATH after installation, and provide appropriate instructions based on the user's operating system and shell ([#92])
Expand Down
5 changes: 3 additions & 2 deletions lib/discovery/foreman.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{collections::HashMap, str::FromStr};
use semver::Version;
use toml_edit::{DocumentMut, InlineTable, Table};

use crate::tool::{ToolAlias, ToolId, ToolSpec};
use crate::tool::{util::to_xyz_version, ToolAlias, ToolId, ToolSpec};

use super::Manifest;

Expand Down Expand Up @@ -71,7 +71,8 @@ fn parse_foreman_tool_definition(map: SpecType) -> Option<ToolSpec> {
let version = map.get("version").and_then(|t| t.as_str()).and_then(|v| {
// TODO: Support real version requirements instead of just exact/min versions
let without_prefix = v.trim_start_matches('=').trim_start_matches('^');
without_prefix.parse::<Version>().ok()
let version_str = to_xyz_version(without_prefix);
version_str.parse::<Version>().ok()
})?;
// TODO: Support gitlab tool ids
let github_tool_id = map
Expand Down
74 changes: 41 additions & 33 deletions lib/sources/github/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use reqwest::{
StatusCode,
};

use crate::tool::{ToolId, ToolSpec};
use crate::tool::{util::to_xyz_version, ToolId, ToolSpec};

use super::{client::create_client, Artifact, ArtifactProvider, Release};

Expand Down Expand Up @@ -144,9 +144,9 @@ impl GithubProvider {
Ok(r) => r,
};

let version = release
.tag_name
.trim_start_matches('v')
let version_str = release.tag_name.trim_start_matches('v');
let version_str_xyz = to_xyz_version(version_str);
let version = version_str_xyz
.parse::<Version>()
.map_err(|e| GithubError::Other(e.to_string()))?;

Expand All @@ -163,36 +163,44 @@ impl GithubProvider {
#[instrument(skip(self), fields(%tool_spec), level = "debug")]
pub async fn get_specific_release(&self, tool_spec: &ToolSpec) -> GithubResult<Release> {
debug!(spec = %tool_spec, "fetching release for tool");

let url_with_prefix = format!(
"{BASE_URL}/repos/{owner}/{repo}/releases/tags/v{tag}",
owner = tool_spec.author(),
repo = tool_spec.name(),
tag = tool_spec.version(),
);
let url_without_prefix = format!(
"{BASE_URL}/repos/{owner}/{repo}/releases/tags/{tag}",
owner = tool_spec.author(),
repo = tool_spec.name(),
tag = tool_spec.version(),
);

let release: GithubRelease = match self.get_json(&url_with_prefix).await {
Err(e) if is_404(&e) => match self.get_json(&url_without_prefix).await {
Err(e) if is_404(&e) => {
return Err(GithubError::ReleaseNotFound(tool_spec.clone().into()));
let mut tags_to_try = vec![tool_spec.version().to_string()];
let version = tool_spec.version();
if version.patch == 0 && version.pre.is_empty() && version.build.is_empty() {
tags_to_try.push(format!("{}.{}", version.major, version.minor));
}
for tag in tags_to_try {
let url_with_prefix = format!(
"{BASE_URL}/repos/{owner}/{repo}/releases/tags/v{tag}",
owner = tool_spec.author(),
repo = tool_spec.name(),
);
let url_without_prefix = format!(
"{BASE_URL}/repos/{owner}/{repo}/releases/tags/{tag}",
owner = tool_spec.author(),
repo = tool_spec.name(),
);
match self.get_json::<GithubRelease>(&url_with_prefix).await {
Ok(release) => {
return Ok(Release {
changelog: release.changelog.clone(),
artifacts: artifacts_from_release(&release, tool_spec),
});
}
Err(e) => return Err(e),
Ok(r) => r,
},
Err(e) => return Err(e),
Ok(r) => r,
};

Ok(Release {
changelog: release.changelog.clone(),
artifacts: artifacts_from_release(&release, tool_spec),
})
Err(e) if !is_404(&e) => return Err(e),
_ => {}
}
match self.get_json::<GithubRelease>(&url_without_prefix).await {
Ok(release) => {
return Ok(Release {
changelog: release.changelog.clone(),
artifacts: artifacts_from_release(&release, tool_spec),
});
}
Err(e) if !is_404(&e) => return Err(e),
_ => {}
}
}
Err(GithubError::ReleaseNotFound(tool_spec.clone().into()))
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/tool/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
mod alias;
mod id;
mod spec;
mod util;
pub(crate) mod spec;
pub(crate) mod util;

pub use self::alias::{ToolAlias, ToolAliasParseError};
pub use self::id::{ToolId, ToolIdParseError};
Expand Down
10 changes: 8 additions & 2 deletions lib/tool/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use thiserror::Error;

use crate::sources::ArtifactProvider;

use super::{util::is_invalid_identifier, ToolId, ToolIdParseError};
use super::{util::is_invalid_identifier, util::to_xyz_version, ToolId, ToolIdParseError};

/**
Error type representing the possible errors that can occur when parsing a `ToolSpec`.
Expand Down Expand Up @@ -97,7 +97,8 @@ impl FromStr for ToolSpec {
return Err(ToolSpecParseError::InvalidVersion(after.to_string()));
}

let version = match after.parse::<Version>() {
let version_str = to_xyz_version(after);
let version = match version_str.parse::<Version>() {
Ok(version) => version,
Err(e) => {
return match after.parse::<VersionReq>() {
Expand Down Expand Up @@ -147,6 +148,7 @@ mod tests {
// Basic strings should parse ok
assert!("a/b@0.0.0".parse::<ToolSpec>().is_ok());
assert!("author/name@1.2.3".parse::<ToolSpec>().is_ok());
assert!("author/name@6.9".parse::<ToolSpec>().is_ok());
assert!("123abc456/78de90@11.22.33".parse::<ToolSpec>().is_ok());
// The parsed ToolSpec should match the input
assert_eq!(
Expand All @@ -157,6 +159,10 @@ mod tests {
"author/name@1.2.3".parse::<ToolSpec>().unwrap(),
new_spec("author", "name", "1.2.3"),
);
assert_eq!(
"author/name@6.9".parse::<ToolSpec>().unwrap(),
new_spec("author", "name", "6.9.0"),
);
assert_eq!(
"123abc456/78de90@11.22.33".parse::<ToolSpec>().unwrap(),
new_spec("123abc456", "78de90", "11.22.33"),
Expand Down
17 changes: 17 additions & 0 deletions lib/tool/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
use std::borrow::Cow;

pub(crate) fn to_xyz_version(v_str: &str) -> Cow<'_, str> {
let (version_num_part, rest) = v_str
.find(['-', '+'])
.map_or((v_str, ""), |i| v_str.split_at(i));

let num_dots = version_num_part.matches('.').count();

if num_dots == 1 {
// x.y
return Cow::Owned(format!("{version_num_part}.0{rest}"));
}

Cow::Borrowed(v_str)
}

pub fn is_invalid_identifier(s: &str) -> bool {
s.is_empty() // Must not be empty
|| s.chars().all(char::is_whitespace) // Must contain some information
Expand Down
Loading