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
6 changes: 3 additions & 3 deletions src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,15 @@ fn collect_changed_names(
let mut names: std::collections::BTreeSet<String> = Default::default();

// Committed changes in the range.
for line in git_diff_names(project_root, &["--diff-filter=d", &range])? {
for line in git_diff_names(project_root, &[&range])? {
names.insert(line);
}
// Unstaged changes.
for line in git_diff_names(project_root, &["--diff-filter=d"])? {
for line in git_diff_names(project_root, &[])? {
names.insert(line);
}
// Staged changes.
for line in git_diff_names(project_root, &["--cached", "--diff-filter=d"])? {
for line in git_diff_names(project_root, &["--cached"])? {
names.insert(line);
}

Expand Down
76 changes: 63 additions & 13 deletions src/linters/renovate_deps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,7 @@ pub(crate) fn is_relevant(file_list: &FileList, project_root: &Path) -> bool {
return true;
}

let changed: HashSet<String> = file_list
.files
.iter()
.filter_map(|path| {
path.strip_prefix(project_root)
.ok()
.map(|rel| rel.to_string_lossy().into_owned())
})
.collect();
let changed = changed_rel_paths(file_list, project_root);

if changed.is_empty() {
return false;
Expand Down Expand Up @@ -173,6 +165,34 @@ pub(crate) fn is_relevant(file_list: &FileList, project_root: &Path) -> bool {
committed.keys().any(|path| changed.contains(path))
}

fn changed_rel_paths(file_list: &FileList, project_root: &Path) -> HashSet<String> {
if !file_list.changed_paths.is_empty() {
return file_list
.changed_paths
.iter()
.map(|path| {
let path = Path::new(path);
path.strip_prefix(project_root).unwrap_or(path)
})
.map(normalize_path)
.collect();
}

file_list
.files
.iter()
.filter_map(|path| path.strip_prefix(project_root).ok())
.map(normalize_path)
.collect()
}

fn normalize_path(path: &Path) -> String {
path.components()
.map(|component| component.as_os_str().to_string_lossy())
.collect::<Vec<_>>()
.join("/")
}
Comment thread
zeitlinger marked this conversation as resolved.

pub(crate) fn adaptive_relevance(ctx: &dyn AdaptiveRelevanceContext) -> bool {
is_relevant(ctx.file_list(), ctx.project_root())
}
Expand Down Expand Up @@ -514,10 +534,7 @@ fn committed_path_for_config(config_path: &Path) -> PathBuf {
}

fn display_path(project_root: &Path, path: &Path) -> String {
path.strip_prefix(project_root)
.unwrap_or(path)
.to_string_lossy()
.into_owned()
normalize_path(path.strip_prefix(project_root).unwrap_or(path))
}

/// Parses Renovate's NDJSON log and returns the dep map.
Expand Down Expand Up @@ -967,6 +984,19 @@ mod tests {
assert!(diff.contains("renovate-tracked-deps.json"));
}

#[test]
fn display_path_normalizes_separators() {
let dir = tempfile::tempdir().unwrap();
let path = dir
.path()
.join(".github")
.join("renovate-tracked-deps.json");
assert_eq!(
display_path(dir.path(), &path),
".github/renovate-tracked-deps.json"
);
}

#[test]
fn resolves_supported_renovate_config_file() {
let dir = tempfile::tempdir().unwrap();
Expand Down Expand Up @@ -1071,6 +1101,26 @@ mod tests {
));
}

#[test]
fn relevant_when_tracked_manifest_was_deleted() {
let dir = tempfile::tempdir().unwrap();
std::fs::create_dir_all(dir.path().join(".github")).unwrap();
write_snapshot(
&dir.path().join(".github/renovate-tracked-deps.json"),
&dep_map(&[("package.json", &[("npm", &["express"])])]),
)
.unwrap();

let file_list = FileList {
files: vec![],
changed_paths: vec!["package.json".to_string()],
merge_base: Some("base".to_string()),
full: false,
};

assert!(is_relevant(&file_list, dir.path()));
}

#[test]
fn not_relevant_for_untracked_change() {
let dir = tempfile::tempdir().unwrap();
Expand Down
111 changes: 111 additions & 0 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,115 @@ fn cases() {
}
}

// Unix-only: this e2e test creates a fake linter as a POSIX shell script and
// marks it executable with Unix permissions.
#[cfg(unix)]
#[test]
fn renovate_deps_fast_only_runs_for_deleted_tracked_file() {
let repo = git_repo();

std::fs::create_dir_all(repo.path().join(".github")).unwrap();
std::fs::write(
repo.path().join("mise.toml"),
r#"[tools]
node = "22.0.0"

# Linters
"npm:renovate" = "43.136.3"
"#,
)
.unwrap();
std::fs::write(repo.path().join(".github/renovate.json5"), "{}\n").unwrap();
std::fs::write(repo.path().join("package.json"), "{}\n").unwrap();
std::fs::write(
repo.path().join(".github/renovate-tracked-deps.json"),
r#"{
"package.json": {
"npm": [
"express"
]
}
}
"#,
)
.unwrap();

let out = Command::new("git")
.args(["add", "-A"])
.current_dir(repo.path())
.output()
.expect("failed to spawn git add");
assert!(
out.status.success(),
"git add failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let out = Command::new("git")
.args(["commit", "-q", "-m", "init"])
.current_dir(repo.path())
.output()
.expect("failed to spawn git commit");
assert!(
out.status.success(),
"git commit failed: {}",
String::from_utf8_lossy(&out.stderr)
);

let out = Command::new("git")
.args(["rm", "-q", "package.json"])
.current_dir(repo.path())
.output()
.expect("failed to spawn git rm");
assert!(
out.status.success(),
"git rm failed: {}",
String::from_utf8_lossy(&out.stderr)
);

let fake_bin_dir = tempfile::tempdir().expect("fake_bin tempdir");
let renovate = fake_bin_dir.path().join("renovate");
std::fs::write(
&renovate,
r#"#!/bin/sh
set -eu

touch .renovate-ran
printf '%s\n' '{"msg":"Extracted dependencies","packageFiles":{"mise":[{"packageFile":"mise.toml","deps":[{"depName":"npm:renovate"}]}]}}'
"#,
)
.unwrap();

use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&renovate, std::fs::Permissions::from_mode(0o755)).unwrap();

let fake_path = format!(
"{}:{}",
fake_bin_dir.path().display(),
std::env::var("PATH").unwrap_or_default()
);
let out = flint_with_env(
&["run", "--fast-only"],
repo.path(),
&[("PATH", &fake_path)],
);

assert!(
repo.path().join(".renovate-ran").exists(),
"renovate-deps was skipped; stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
assert_eq!(
out.status.code(),
Some(1),
"expected stale renovate snapshot failure; stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr)
);
}

// Unix-only: this e2e test creates fake linters as POSIX shell scripts and
// marks them executable with Unix permissions.
#[cfg(unix)]
#[test]
fn markdown_tool_ignores_biome_owned_jsonc() {
Expand Down Expand Up @@ -336,6 +445,8 @@ exit 1
);
}

// Unix-only: this e2e test creates a fake linter as a POSIX shell script and
// marks it executable with Unix permissions.
#[cfg(unix)]
#[test]
fn rumdl_fix_hides_success_noise_when_another_file_fails() {
Expand Down
Loading