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
1 change: 1 addition & 0 deletions registry/oc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ full = "http:oc"

[backends.options]
bin = "oc"
version_expr = 'sortVersions(versions)'
version_list_url = "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/"
version_regex = 'href="(\d+\.\d+\.\d+)/"'

Expand Down
1 change: 1 addition & 0 deletions registry/openshift-install.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ full = "http:openshift-install"

[backends.options]
bin = "openshift-install"
version_expr = 'sortVersions(versions)'
version_list_url = "https://mirror.openshift.com/pub/openshift-v4/clients/ocp/"
version_regex = 'href="(\d+\.\d+\.\d+)/"'

Expand Down
38 changes: 30 additions & 8 deletions src/backend/version_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,9 @@ pub fn parse_version_list(
let mut versions = Vec::new();
let trimmed = content.trim();

// If an expr is provided, use it to evaluate and extract versions
// Fail hard if the expression is invalid - don't silently fall through
if let Some(expr_str) = version_expr {
versions = eval_version_expr(expr_str, trimmed)?;
}
// If a JSON path is provided (like ".[].version" or ".versions"), try to use it
// but fall back to text parsing if JSON parsing fails
else if let Some(json_path) = version_json_path {
if let Some(json_path) = version_json_path {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(trimmed)
&& let Ok(extracted) = jq::extract(&json, json_path)
{
Expand Down Expand Up @@ -119,16 +114,29 @@ pub fn parse_version_list(

// DO NOT sort versions here - the backend/upstream determines version order.
// Sorting is handled elsewhere (e.g., versions host, resolve logic).
// When a registry entry needs explicit sorting, it can use version_expr
// (e.g. sortVersions) to post-process the extracted versions.
// When no other extraction method produced results, the expr operates on
// `body` alone (the original behavior).
if let Some(expr_str) = version_expr {
versions = eval_version_expr(expr_str, trimmed, &versions)?;
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

Ok(versions)
}

/// Evaluate a version expression using expr-lang
fn eval_version_expr(expr_str: &str, body: &str) -> Result<Vec<String>> {
/// Evaluate a version expression using expr-lang.
/// The previously extracted version list is always injected as a `versions`
/// variable so the expression can post-process it (e.g. sortVersions).
fn eval_version_expr(expr_str: &str, body: &str, versions: &[String]) -> Result<Vec<String>> {
use versions::Versioning;

let mut ctx = Context::default();
ctx.insert("body".to_string(), Value::String(body.to_string()));
ctx.insert(
"versions".to_string(),
Value::Array(versions.iter().map(|v| Value::String(v.clone())).collect()),
);

// expr-lang 1.0+ has built-in fromJSON, keys, values, len, toJSON functions
let mut env = Environment::new();
Expand Down Expand Up @@ -410,4 +418,18 @@ mod tests {
.unwrap();
assert_eq!(versions, vec!["1.0.0", "1.1.0", "1.2.0-rc1"]);
}

#[test]
fn test_parse_with_regex_and_version_expr_pipeline() {
let content =
r#"<a href="4.9.9/">4.9.9</a><a href="4.21.3/">4.21.3</a><a href="4.10.1/">4.10.1</a>"#;
let versions = parse_version_list(
content,
Some(r#"href="(\d+\.\d+\.\d+)/""#),
None,
Some("sortVersions(versions)"),
)
.unwrap();
assert_eq!(versions, vec!["4.9.9", "4.10.1", "4.21.3"]);
}
}
Loading