-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix(lock): lock idiomatic version file tools #10309
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| export MISE_LOCKFILE=1 | ||
|
|
||
| detect_platform | ||
| PLATFORM="$MISE_PLATFORM" | ||
|
|
||
| cat <<'EOF' >mise.toml | ||
| [settings] | ||
| idiomatic_version_file_enable_tools = ["node"] | ||
| lockfile = true | ||
| EOF | ||
|
|
||
| echo "24" >.node-version | ||
|
|
||
| assert_contains "mise ls --current node" ".node-version" | ||
|
|
||
| mise lock --platform "$PLATFORM" | ||
|
|
||
| assert_contains "cat mise.lock" '[[tools.node]]' | ||
| assert_contains "cat mise.lock" 'backend = "core:node"' | ||
| assert_contains "cat mise.lock" "\"platforms.$PLATFORM\"" | ||
|
|
||
| assert_contains "mise install --locked --dry-run node 2>&1" "would install" | ||
|
|
||
| rm -f mise.toml mise.lock .node-version | ||
|
|
||
| cat <<'EOF' >mise.toml | ||
| [settings] | ||
| idiomatic_version_file_enable_tools = ["dummy"] | ||
| lockfile = true | ||
|
|
||
| [tools] | ||
| dummy = "2" | ||
| EOF | ||
|
|
||
| echo "1" >.dummy-version | ||
|
|
||
| output=$(mise lock --dry-run --platform "$PLATFORM" 2>&1) | ||
| assert_contains "echo '$output'" "dummy@1.1.0" | ||
| assert_contains "echo '$output'" "dummy@2.0.0" | ||
|
|
||
| rm -f mise.toml .dummy-version | ||
|
|
||
| cat <<'EOF' >mise.toml | ||
| [settings] | ||
| idiomatic_version_file_enable_tools = ["dummy"] | ||
| lockfile = true | ||
| EOF | ||
|
|
||
| echo "1" >.dummy-version | ||
| touch mise.lock | ||
|
|
||
| mise install dummy | ||
|
|
||
| assert_contains "cat mise.lock" '[[tools.dummy]]' | ||
| assert_contains "cat mise.lock" 'backend = "asdf:dummy"' | ||
| assert "mise install --locked --dry-run dummy >/dev/null 2>&1" | ||
|
|
||
| rm -f mise.toml mise.lock .dummy-version | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -894,6 +894,38 @@ pub fn lockfile_path_for_config(config_path: &Path) -> (PathBuf, bool) { | |
| (lockfile_dir.join(lockfile_name), is_local) | ||
| } | ||
|
|
||
| /// Determines the lockfile path for a tool source. | ||
| /// | ||
| /// 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. | ||
| pub fn lockfile_path_for_tool_source( | ||
| config: &Config, | ||
| source: &ToolSource, | ||
| ) -> Option<(PathBuf, bool)> { | ||
| match source { | ||
| ToolSource::MiseToml(path) => Some(lockfile_path_for_config(path)), | ||
| ToolSource::IdiomaticVersionFile(path) => config | ||
| .config_files | ||
| .iter() | ||
| .filter(|(_, cf)| cf.source().is_mise_toml()) | ||
| .filter_map(|(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, | ||
| lockfile_path_for_config(config_path), | ||
| ) | ||
| }) | ||
| }) | ||
| .max_by_key(|(root_depth, is_base, _)| (*root_depth, *is_base)) | ||
| .map(|(_, _, lockfile)| lockfile), | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ambiguous idiomatic lockfile mappingMedium Severity When several base Reviewed by Cursor Bugbot for commit 4af9d2e. Configure here. |
||
| _ => None, | ||
| } | ||
| } | ||
|
|
||
| /// Checks if a config path is a "local" config (should go to mise.local.lock) | ||
| fn is_local_config(path: &Path) -> bool { | ||
| let filename = path | ||
|
|
@@ -997,18 +1029,12 @@ pub fn update_lockfiles( | |
| // Process each lockfile, deferring provenance errors until all lockfiles are saved. | ||
| let mut provenance_errors: Vec<String> = Vec::new(); | ||
|
|
||
| for (lockfile_path, configs) in lockfile_configs { | ||
| for (lockfile_path, _configs) in lockfile_configs { | ||
| // Only update existing lockfiles - creation is done elsewhere (e.g., by `mise lock`) | ||
| if !lockfile_path.exists() { | ||
| continue; | ||
| } | ||
|
|
||
| trace!( | ||
| "updating lockfile {} from {} config files", | ||
| display_path(&lockfile_path), | ||
| configs.len() | ||
| ); | ||
|
|
||
| let mut existing_lockfile = Lockfile::read(&lockfile_path) | ||
| .unwrap_or_else(|err| handle_lockfile_read_error(err, &lockfile_path)); | ||
|
|
||
|
|
@@ -1017,10 +1043,14 @@ pub fn update_lockfiles( | |
| // fuzzy request may still resolve through the old lockfile entry until | ||
| // this update is written. | ||
| let mut tool_versions_by_short: HashMap<String, Vec<ToolVersion>> = HashMap::new(); | ||
| let mut contributing_sources = 0; | ||
|
|
||
| for config_path in &configs { | ||
| let tool_source = ToolSource::MiseToml(config_path.clone()); | ||
| if let Some(tools) = tools_by_source.get(&tool_source) { | ||
| for (source, tools) in &tools_by_source { | ||
| let Some((source_lockfile, _)) = lockfile_path_for_tool_source(config, source) else { | ||
| continue; | ||
| }; | ||
| if source_lockfile == lockfile_path { | ||
| contributing_sources += 1; | ||
| for (short, tvl) in tools { | ||
| tool_versions_by_short | ||
| .entry(short.clone()) | ||
|
|
@@ -1030,9 +1060,17 @@ pub fn update_lockfiles( | |
| } | ||
| } | ||
|
|
||
| trace!( | ||
| "updating lockfile {} from {} source(s)", | ||
| display_path(&lockfile_path), | ||
| contributing_sources | ||
| ); | ||
|
|
||
| for new_version in new_versions { | ||
| if let Some(source_path) = new_version.request.source().path() { | ||
| if !configs.iter().any(|config| config == source_path) { | ||
| if let Some((source_lockfile, _)) = | ||
| lockfile_path_for_tool_source(config, new_version.request.source()) | ||
| { | ||
| if source_lockfile != lockfile_path { | ||
| continue; | ||
| } | ||
|
|
||
|
|
@@ -1232,17 +1270,15 @@ fn check_provenance_regression( | |
| /// lockfile (existing entries are authoritative) apart from a fresh one whose only | ||
| /// current-platform entry was just added by this install. | ||
| pub fn snapshot_pre_install_platforms( | ||
| config: &Config, | ||
| new_versions: &[ToolVersion], | ||
| ) -> HashMap<PathBuf, BTreeSet<String>> { | ||
| let mut result: HashMap<PathBuf, BTreeSet<String>> = HashMap::new(); | ||
| for tv in new_versions { | ||
| if !tv.request.source().is_mise_toml() { | ||
| continue; | ||
| } | ||
| let Some(source_path) = tv.request.source().path() else { | ||
| let Some((lockfile_path, _)) = lockfile_path_for_tool_source(config, tv.request.source()) | ||
| else { | ||
| continue; | ||
| }; | ||
| let (lockfile_path, _) = lockfile_path_for_config(source_path); | ||
| if result.contains_key(&lockfile_path) { | ||
| continue; | ||
| } | ||
|
|
@@ -1331,6 +1367,7 @@ pub fn determine_existing_platforms(lockfile_path: &Path) -> Result<Vec<Platform | |
| /// so the lockfile is complete and doesn't change when other developers on different | ||
| /// platforms run `mise install`. | ||
| pub async fn auto_lock_new_versions( | ||
| config: &Config, | ||
| new_versions: &[ToolVersion], | ||
| pre_install_platforms: &HashMap<PathBuf, BTreeSet<String>>, | ||
| mode: LockfileUpdateMode, | ||
|
|
@@ -1342,14 +1379,11 @@ pub async fn auto_lock_new_versions( | |
| return Ok(()); | ||
| } | ||
|
|
||
| // Group new_versions by lockfile path (only mise.toml sources, matching update_lockfiles) | ||
| // Group new_versions by lockfile path, matching update_lockfiles. | ||
| let mut versions_by_lockfile: HashMap<PathBuf, Vec<&ToolVersion>> = HashMap::new(); | ||
| for tv in new_versions { | ||
| if !tv.request.source().is_mise_toml() { | ||
| continue; | ||
| } | ||
| if let Some(source_path) = tv.request.source().path() { | ||
| let (lockfile_path, _) = lockfile_path_for_config(source_path); | ||
| if let Some((lockfile_path, _)) = lockfile_path_for_tool_source(config, tv.request.source()) | ||
| { | ||
| versions_by_lockfile | ||
| .entry(lockfile_path) | ||
| .or_default() | ||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.