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
23 changes: 23 additions & 0 deletions e2e/lockfile/test_lockfile_idiomatic_version_file
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@ idiomatic_version_file_enable_tools = ["dummy"]
lockfile = true
EOF

# Keep mise.toml and .mise/config.toml together to verify that an idiomatic
# .dummy-version maps to .mise/mise.lock instead of the root mise.lock.
mkdir -p .mise
cat <<'EOF' >.mise/config.toml
[settings]
idiomatic_version_file_enable_tools = ["dummy"]
lockfile = true
EOF

echo "1" >.dummy-version

output=$(mise lock --dry-run --platform "$PLATFORM" 2>&1)
assert_contains "echo '$output'" ".mise/mise.lock"
assert_not_contains "echo '$output'" "for ~/workdir/mise.lock"

rm -rf mise.toml .mise .dummy-version

cat <<'EOF' >mise.toml
[settings]
idiomatic_version_file_enable_tools = ["dummy"]
lockfile = true
EOF

echo "1" >.dummy-version
touch mise.lock

Expand Down
14 changes: 10 additions & 4 deletions src/lockfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,9 @@ pub fn lockfile_path_for_config(config_path: &Path) -> (PathBuf, bool) {
///
/// Idiomatic version files are not config files themselves, so their lock entries
/// belong to the nearest active mise config root that contains the version file.
/// If multiple base configs share that root, the later entry in config_files wins
/// so colocated configs have a deterministic lockfile target, such as
/// .mise/config.toml mapping .dummy-version to .mise/mise.lock instead of mise.lock.
pub fn lockfile_path_for_tool_source(
config: &Config,
source: &ToolSource,
Expand All @@ -907,21 +910,24 @@ pub fn lockfile_path_for_tool_source(
ToolSource::IdiomaticVersionFile(path) => config
.config_files
.iter()
.filter(|(_, cf)| cf.source().is_mise_toml())
.filter_map(|(config_path, cf)| {
.enumerate()
.filter(|(_, (_, cf))| cf.source().is_mise_toml())
.filter_map(|(idx, (config_path, cf))| {
let root = cf.project_root().unwrap_or_else(|| cf.config_root());
let is_base = !is_local_config(config_path)
&& extract_env_from_config_path(config_path).is_none();
path.starts_with(&root).then(|| {
(
root.components().count(),
is_base,
// Tie-break same-root base configs by config_files order.
idx,
lockfile_path_for_config(config_path),
)
})
})
.max_by_key(|(root_depth, is_base, _)| (*root_depth, *is_base))
.map(|(_, _, lockfile)| lockfile),
.max_by_key(|(root_depth, is_base, idx, _)| (*root_depth, *is_base, *idx))
.map(|(_, _, _, lockfile)| lockfile),
_ => None,
}
}
Expand Down
Loading