Skip to content
Draft
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 .github/renovate-tracked-deps.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"actionlint",
"aqua:jonwiggins/xmloxide",
"aqua:owenlamont/ryl",
"aube",
"biome",
"dotnet",
"editorconfig-checker",
Expand Down
3 changes: 2 additions & 1 deletion mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
FLINT_CONFIG_DIR = ".github/config"

[tools]
aube = "latest"
dotnet = "10.0.201"
go = "1.26.3"
node = "24.16.0"
Expand All @@ -19,7 +20,7 @@ google-java-format = "1.35.0"
hadolint = "2.14.0"
ktlint = "1.8.0"
lychee = "0.24.2"
"npm:renovate" = "43.150.0"
"npm:renovate" = { version = "43.150.0", allow_builds = ["re2"] }
ruff = "0.15.12"
rumdl = "0.1.78"
shellcheck = "0.11.0"
Expand Down
5 changes: 3 additions & 2 deletions src/init/generation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use std::process::Command;
use super::LinterGroup;

pub(super) use super::mise_tools::{
apply_changes, ensure_flint_self_pin, ensure_node_for_npm, remove_tool_keys,
apply_changes, ensure_aube_for_renovate, ensure_flint_self_pin, ensure_node_for_npm,
remove_tool_keys,
};
pub(crate) use super::mise_tools::{
needs_node_for_npm, normalize_tools_section, replace_obsolete_keys,
needs_aube_for_renovate, needs_node_for_npm, normalize_tools_section, replace_obsolete_keys,
tools_section_needs_normalization,
};

Expand Down
13 changes: 11 additions & 2 deletions src/init/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ use super::config_files::{
};
use super::detection::parse_tool_keys;
use super::generation;
use super::generation::needs_node_for_npm;
use super::{ensure_node_for_npm, install_key, remove_tool_keys};
use super::generation::{needs_aube_for_renovate, needs_node_for_npm};
use super::{ensure_aube_for_renovate, ensure_node_for_npm, install_key, remove_tool_keys};

pub(super) struct RepoMigrationSummary {
replaced_obsolete: Vec<(String, String)>,
removed_unsupported: Vec<String>,
node_added: bool,
aube_added: bool,
legacy_files_removed: Vec<String>,
stale_md013_comments_removed: Vec<String>,
stale_editorconfig_checker_comments_removed: Vec<String>,
Expand All @@ -34,6 +35,7 @@ impl RepoMigrationSummary {
self.replaced_obsolete.is_empty()
&& self.removed_unsupported.is_empty()
&& !self.node_added
&& !self.aube_added
&& self.legacy_files_removed.is_empty()
&& self.stale_md013_comments_removed.is_empty()
&& self.stale_editorconfig_checker_comments_removed.is_empty()
Expand All @@ -50,6 +52,9 @@ impl RepoMigrationSummary {
if self.node_added {
println!(" added node (LTS) — required by npm: backend tools");
}
if self.aube_added {
println!(" added aube — npm:renovate needs allow_builds for re2 native binary");
}
for rel in &self.legacy_files_removed {
println!(" removed <REPO>/{rel} (legacy flint file)");
}
Expand Down Expand Up @@ -220,10 +225,12 @@ fn detect_setup_migrations_with_keys(
.map(|(old_key, _)| (*old_key).to_string())
.collect();
let node_added = needs_node_for_npm(mise_content);
let aube_added = needs_aube_for_renovate(mise_content);
RepoMigrationSummary {
replaced_obsolete,
removed_unsupported,
node_added,
aube_added,
legacy_files_removed: vec![],
stale_md013_comments_removed: vec![],
stale_editorconfig_checker_comments_removed: vec![],
Expand All @@ -248,6 +255,7 @@ fn apply_repo_migrations_with_keys(
.collect::<Vec<_>>(),
)?;
let node_added = ensure_node_for_npm(project_root)?;
let aube_added = ensure_aube_for_renovate(project_root)?;
let legacy_files_removed = if include_repo_cleanup {
remove_legacy_lint_files(project_root)?
} else {
Expand All @@ -271,6 +279,7 @@ fn apply_repo_migrations_with_keys(
replaced_obsolete,
removed_unsupported,
node_added,
aube_added,
legacy_files_removed,
stale_md013_comments_removed,
stale_editorconfig_checker_comments_removed,
Expand Down
169 changes: 167 additions & 2 deletions src/init/mise_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,104 @@ fn ensure_node_for_npm_with(
Ok(true)
}

/// True when `[tools]` contains `npm:renovate` but either `aube` is absent or
/// `npm:renovate` lacks an `allow_builds` key. Mise's npm backend runs with
/// `--ignore-scripts=true`; renovate's `re2` dependency needs its postinstall
/// script to download the native binary. `aube` (picked up automatically by mise
/// when present) supports per-tool `allow_builds` allowlisting.
pub(crate) fn needs_aube_for_renovate(content: &str) -> bool {
let Ok(doc) = content.parse::<toml_edit::DocumentMut>() else {
return false;
};
let Some(tools) = doc.get("tools").and_then(|t| t.as_table()) else {
return false;
};
if !tools.contains_key("npm:renovate") {
return false;
}
!tools.contains_key("aube") || !renovate_has_allow_builds(tools)
}

fn renovate_has_allow_builds(tools: &toml_edit::Table) -> bool {
tools
.get("npm:renovate")
.and_then(|item| item.as_value())
.and_then(|v| {
if let toml_edit::Value::InlineTable(tbl) = v {
Some(tbl)
} else {
None
}
})
.is_some_and(|tbl| tbl.contains_key("allow_builds"))
}

/// Ensures `aube` is present and `npm:renovate` has `allow_builds = ["re2"]`
/// when renovate is a configured tool. Prefers `mise use --pin aube@latest` for
/// the aube pin; falls back to writing `aube = "latest"` directly when mise
/// isn't available.
///
/// Returns `true` if any change was written.
pub(crate) fn ensure_aube_for_renovate(project_root: &Path) -> Result<bool> {
ensure_aube_for_renovate_with(project_root, run_mise_use)
}

fn ensure_aube_for_renovate_with(
project_root: &Path,
mut runner: impl FnMut(&Path, &str, &str),
) -> Result<bool> {
let mise_path = project_root.join("mise.toml");
let content = std::fs::read_to_string(&mise_path).unwrap_or_default();
if !needs_aube_for_renovate(&content) {
return Ok(false);
}

// Add aube if missing.
let content = std::fs::read_to_string(&mise_path).unwrap_or_default();
let doc: toml_edit::DocumentMut = content.parse().context("failed to parse mise.toml")?;
let has_aube = doc
.get("tools")
.and_then(|t| t.as_table())
.is_some_and(|tools| tools.contains_key("aube"));

if !has_aube && !pin_tool_via_mise_with(project_root, "aube", "latest", &mut runner) {
let content = std::fs::read_to_string(&mise_path).unwrap_or_default();
let mut doc: toml_edit::DocumentMut =
content.parse().context("failed to parse mise.toml")?;
let tools = doc["tools"]
.as_table_mut()
.context("[tools] is not a table")?;
tools.insert("aube", toml_edit::value("latest"));
std::fs::write(&mise_path, doc.to_string())?;
}

// Update npm:renovate to add allow_builds = ["re2"].
let content = std::fs::read_to_string(&mise_path).context("failed to read mise.toml")?;
let mut doc: toml_edit::DocumentMut = content.parse().context("failed to parse mise.toml")?;
let tools = doc["tools"]
.as_table_mut()
.context("[tools] is not a table")?;
if !renovate_has_allow_builds(tools) {
let version = tool_version(tools, "npm:renovate").unwrap_or_else(|| "latest".to_string());
insert_renovate_with_allow_builds(tools, &version);
std::fs::write(&mise_path, doc.to_string())?;
}

Ok(true)
}

fn insert_renovate_with_allow_builds(tools: &mut toml_edit::Table, version: &str) {
let mut tbl = toml_edit::InlineTable::new();
tbl.insert("version", toml_edit::Value::from(version));
let mut arr = toml_edit::Array::new();
arr.push("re2");
tbl.insert("allow_builds", toml_edit::Value::Array(arr));
tools.insert(
"npm:renovate",
toml_edit::Item::Value(toml_edit::Value::InlineTable(tbl)),
);
}

/// Pins `aqua:grafana/flint` in mise.toml at the calling binary's version so
/// contributors all run the same flint release. Skips when the key already
/// exists (any pin — never overwrite the user's explicit choice). Pre-release
Expand Down Expand Up @@ -519,8 +617,9 @@ fn sort_and_group_tools(tools: &mut toml_edit::Table, original: &str) {
#[cfg(test)]
mod tests {
use super::{
apply_changes_with, ensure_flint_self_pin, ensure_flint_self_pin_with, ensure_node_for_npm,
ensure_node_for_npm_with, needs_node_for_npm, pin_tool_via_mise_with, remove_tool_keys,
apply_changes_with, ensure_aube_for_renovate_with, ensure_flint_self_pin,
ensure_flint_self_pin_with, ensure_node_for_npm, ensure_node_for_npm_with,
needs_aube_for_renovate, needs_node_for_npm, pin_tool_via_mise_with, remove_tool_keys,
replace_obsolete_keys, replace_obsolete_keys_with,
};

Expand Down Expand Up @@ -549,6 +648,72 @@ mod tests {
assert_eq!(std::fs::read_to_string(&path).unwrap(), original);
}

#[test]
fn needs_aube_when_renovate_without_aube() {
let content = "[tools]\nnode = \"24\"\n\"npm:renovate\" = \"43.0.0\"\n";
assert!(needs_aube_for_renovate(content));
}

#[test]
fn needs_aube_when_renovate_has_aube_but_no_allow_builds() {
let content = "[tools]\naube = \"1.0.0\"\nnode = \"24\"\n\"npm:renovate\" = \"43.0.0\"\n";
assert!(needs_aube_for_renovate(content));
}

#[test]
fn no_aube_needed_when_fully_configured() {
let content = "[tools]\naube = \"1.0.0\"\nnode = \"24\"\n\"npm:renovate\" = { version = \"43.0.0\", allow_builds = [\"re2\"] }\n";
assert!(!needs_aube_for_renovate(content));
}

#[test]
fn no_aube_needed_when_no_renovate() {
let content = "[tools]\nnode = \"24\"\nshellcheck = \"v0.11.0\"\n";
assert!(!needs_aube_for_renovate(content));
}

#[test]
fn ensure_aube_adds_aube_and_allow_builds() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("mise.toml");
std::fs::write(
&path,
"[tools]\nnode = \"24\"\n\"npm:renovate\" = \"43.100.0\"\n",
)
.unwrap();

let added = ensure_aube_for_renovate_with(dir.path(), |project_root, key, _| {
let content = std::fs::read_to_string(project_root.join("mise.toml")).unwrap();
let mut doc: toml_edit::DocumentMut = content.parse().unwrap();
doc["tools"]
.as_table_mut()
.unwrap()
.insert(key, toml_edit::value("1.2.3"));
std::fs::write(project_root.join("mise.toml"), doc.to_string()).unwrap();
})
.unwrap();

let result = std::fs::read_to_string(&path).unwrap();
assert!(added);
assert!(result.contains("aube"));
assert!(result.contains("allow_builds"));
assert!(result.contains("re2"));
assert!(result.contains("43.100.0"));
}

#[test]
fn ensure_aube_noop_when_already_configured() {
let original = "[tools]\naube = \"1.0.0\"\nnode = \"24\"\n\"npm:renovate\" = { version = \"43.0.0\", allow_builds = [\"re2\"] }\n";
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("mise.toml");
std::fs::write(&path, original).unwrap();

let added = ensure_aube_for_renovate_with(dir.path(), |_, _, _| {}).unwrap();

assert!(!added);
assert_eq!(std::fs::read_to_string(&path).unwrap(), original);
}

#[test]
fn pin_tool_via_mise_detects_successful_write() {
let dir = tempfile::tempdir().unwrap();
Expand Down
11 changes: 8 additions & 3 deletions src/init/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ use detection::{
build_linter_groups, detect_obsolete_keys, detect_present_patterns, parse_tool_keys,
};
use generation::{
apply_changes, detect_base_branch, ensure_flint_self_pin, ensure_node_for_npm,
get_existing_config_dir, has_slow_selected, normalize_tools_section, prompt_config_dir,
remove_tool_keys,
apply_changes, detect_base_branch, ensure_aube_for_renovate, ensure_flint_self_pin,
ensure_node_for_npm, get_existing_config_dir, has_slow_selected, normalize_tools_section,
prompt_config_dir, remove_tool_keys,
};
use migrations::{
apply_repo_migrations, selected_editorconfig_cleanup_sections,
Expand Down Expand Up @@ -369,6 +369,10 @@ Add and stage your source files before running init so the detection is accurate
if node_added {
println!(" added node (LTS) — required by npm: backend tools");
}
let aube_added = ensure_aube_for_renovate(project_root)?;
if aube_added {
println!(" added aube — npm:renovate needs allow_builds for re2 native binary");
}
let tools_normalized = normalize_tools_section(&mise_path)?;

interactive_note(yes, "\nScaffolding flint config and workflow...");
Expand Down Expand Up @@ -413,6 +417,7 @@ Add and stage your source files before running init so the detection is accurate
&& !flint_pinned
&& !meta_changed
&& !node_added
&& !aube_added
&& !tools_normalized
&& !toml_generated
&& !workflow_generated
Expand Down
12 changes: 10 additions & 2 deletions src/linters/flint_setup.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::Path;
use std::path::PathBuf;

use crate::init::generation::needs_node_for_npm;
use crate::init::generation::{needs_aube_for_renovate, needs_node_for_npm};
use crate::init::generation::{normalize_tools_section, tools_section_needs_normalization};
use crate::linters::LinterOutput;
use crate::registry::{
Expand Down Expand Up @@ -71,6 +71,14 @@ pub async fn run(fix: bool, project_root: &Path, config_dir: &Path) -> LinterOut
setup_outcome = setup_outcome.at_least(SetupOutcome::Blocking);
errors.push("mise.toml is missing `node` for npm: backend tools.".to_string());
}
if needs_aube_for_renovate(&mise_content) {
setup_outcome = setup_outcome.at_least(SetupOutcome::Blocking);
errors.push(
"mise.toml has npm:renovate but is missing `aube` or `allow_builds = [\"re2\"]` — \
run `flint run --fix flint-setup` to add them."
.to_string(),
);
}

match tools_section_needs_normalization(&path) {
Ok(true) => {
Expand Down Expand Up @@ -203,7 +211,7 @@ mod tests {
let tmp = tempfile::TempDir::new().unwrap();
std::fs::write(
tmp.path().join("mise.toml"),
"[tools]\nnode = \"24\"\n\n# Linters\n\"npm:renovate\" = \"latest\"\n",
"[tools]\naube = \"1.0.0\"\nnode = \"24\"\n\n# Linters\n\"npm:renovate\" = { version = \"latest\", allow_builds = [\"re2\"] }\n",
)
.unwrap();
std::fs::write(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[tools]
aube = "latest"
node = "24.0.0"

# Linters
"npm:renovate" = "43.136.3"
"npm:renovate" = { version = "43.136.3", allow_builds = ["re2"] }
shellcheck = "0.10.0"
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[tools]
aube = "latest"
node = "22.0.0"

# Linters
"npm:renovate" = "43.136.3"
"npm:renovate" = { version = "43.136.3", allow_builds = ["re2"] }
shellcheck = "0.10.0"
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[tools]
aube = "latest"
node = "22.0.0"

# Linters
"npm:renovate" = "43.136.3"
"npm:renovate" = { version = "43.136.3", allow_builds = ["re2"] }
3 changes: 2 additions & 1 deletion tests/cases/renovate-deps/irrelevant/files/mise.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[tools]
aube = "latest"
node = "22.0.0"

# Linters
"npm:renovate" = "43.136.3"
"npm:renovate" = { version = "43.136.3", allow_builds = ["re2"] }
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[tools]
aube = "latest"
node = "22.0.0"

# Linters
"npm:renovate" = "43.136.3"
"npm:renovate" = { version = "43.136.3", allow_builds = ["re2"] }
Loading
Loading