Skip to content

fix(lockfile): prevent lockfile writes when --locked is set#8308

Merged
jdx merged 1 commit intomainfrom
fix/locked-prevents-lockfile-writes
Feb 22, 2026
Merged

fix(lockfile): prevent lockfile writes when --locked is set#8308
jdx merged 1 commit intomainfrom
fix/locked-prevents-lockfile-writes

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 22, 2026

Summary

  • When mise install --locked is run, the mise.lock file should not be modified
  • Adds early returns in update_lockfiles and auto_lock_new_versions when Settings::get().locked is true
  • Consistent with how cargo install --locked and uv pip install --locked behave: the lock file is treated as authoritative and must not be updated

Changes Made

  • update_lockfiles(): guard changed from if !Settings::get().lockfile to if !Settings::get().lockfile || Settings::get().locked
  • auto_lock_new_versions(): guard changed from if !Settings::get().lockfile || new_versions.is_empty() to if !Settings::get().lockfile || Settings::get().locked || new_versions.is_empty()

Testing

  • Existing lockfile tests continue to pass
  • The --locked flag now prevents any writes to mise.lock during install

Related

  • Spec: /home/jdx/src/mise/src/lockfile.rs

Note

Low Risk
Small conditional-guard change that only disables lockfile writes in locked mode; minimal behavioral surface area and no security/data-handling impact.

Overview
When Settings::get().locked (e.g. mise install --locked) is enabled, lockfile update paths now short-circuit to avoid modifying mise.lock.

Specifically, update_lockfiles and auto_lock_new_versions add an early return when locked mode is active, preventing post-install lockfile refresh/auto-lock behavior.

Written by Cursor Bugbot for commit fdccfd2. This will update automatically on new commits. Configure here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 22, 2026 18:42
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a crucial safeguard to prevent unintended modifications to the mise.lock file when the --locked flag is used during installation. By adding checks within key lockfile management functions, the system now respects the --locked directive, ensuring that the existing lock file remains unchanged. This enhancement improves consistency with established package management practices and contributes to more reproducible build environments.

Highlights

  • Prevent Lockfile Writes: The mise.lock file will no longer be modified when mise install --locked is executed, ensuring the lock file is treated as authoritative.
  • Early Return Guards: Added early return conditions in update_lockfiles and auto_lock_new_versions functions to prevent writes when the locked setting is true.
  • Consistency with Other Tools: This change aligns mise's --locked behavior with that of cargo install --locked and uv pip install --locked.
Changelog
  • src/lockfile.rs
    • Modified the guard condition in update_lockfiles() to include Settings::get().locked.
    • Modified the guard condition in auto_lock_new_versions() to include Settings::get().locked.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly prevents lockfile modifications when the --locked flag is used by adding checks in update_lockfiles and auto_lock_new_versions. The changes are straightforward and align with the intended behavior. The suggestions to refactor the code by fetching the settings object once per function have been kept as they improve readability and avoid redundant calls to Settings::get().


pub fn update_lockfiles(config: &Config, ts: &Toolset, new_versions: &[ToolVersion]) -> Result<()> {
if !Settings::get().lockfile {
if !Settings::get().lockfile || Settings::get().locked {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve readability and avoid multiple calls to Settings::get(), consider fetching the settings once at the beginning of the function and using the variable in the if condition.

For example:

pub fn update_lockfiles(config: &Config, ts: &Toolset, new_versions: &[ToolVersion]) -> Result<()> {
    let settings = Settings::get();
    if !settings.lockfile || settings.locked {
        return Ok(());
    }
    // ...
}

/// platforms run `mise install`.
pub async fn auto_lock_new_versions(_config: &Config, new_versions: &[ToolVersion]) -> Result<()> {
if !Settings::get().lockfile || new_versions.is_empty() {
if !Settings::get().lockfile || Settings::get().locked || new_versions.is_empty() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve readability and avoid multiple calls to Settings::get(), consider fetching the settings once at the beginning of the function. This also allows you to remove the redundant Settings::get() call on line 699.

For example:

pub async fn auto_lock_new_versions(_config: &Config, new_versions: &[ToolVersion]) -> Result<()> {
    let settings = Settings::get();
    if !settings.lockfile || settings.locked || new_versions.is_empty() {
        return Ok(());
    }
    // ...
    let jobs = settings.jobs;
    // ...
}

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates lockfile behavior so that running mise install --locked will not modify mise.lock, aligning with the intent that locked mode should treat existing lockfile content as authoritative during installation.

Changes:

  • Skip update_lockfiles() when Settings::get().locked is enabled.
  • Skip auto_lock_new_versions() when Settings::get().locked is enabled (in addition to existing guards).
Comments suppressed due to low confidence (1)

src/lockfile.rs:701

  • The guard calls Settings::get() multiple times and then fetches let settings = Settings::get(); again a few lines later (for jobs). Consider hoisting let settings = Settings::get(); before the guard and reusing it for lockfile, locked, and jobs to avoid redundant lookups.
pub async fn auto_lock_new_versions(_config: &Config, new_versions: &[ToolVersion]) -> Result<()> {
    if !Settings::get().lockfile || Settings::get().locked || new_versions.is_empty() {
        return Ok(());
    }

    // Group new_versions by lockfile path (only mise.toml sources, 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);
            versions_by_lockfile
                .entry(lockfile_path)
                .or_default()
                .push(tv);
        }
    }

    let settings = Settings::get();
    let jobs = settings.jobs;


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


pub fn update_lockfiles(config: &Config, ts: &Toolset, new_versions: &[ToolVersion]) -> Result<()> {
if !Settings::get().lockfile {
if !Settings::get().lockfile || Settings::get().locked {
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings::get() is called twice in the guard, which means two Arc clones and two RwLock reads. Consider storing let settings = Settings::get(); and using settings.lockfile/settings.locked for the early-return to keep this consistent with other code and avoid repeated lookups.

Suggested change
if !Settings::get().lockfile || Settings::get().locked {
let settings = Settings::get();
if !settings.lockfile || settings.locked {

Copilot uses AI. Check for mistakes.
@jdx jdx merged commit ae74963 into main Feb 22, 2026
39 of 41 checks passed
@jdx jdx deleted the fix/locked-prevents-lockfile-writes branch February 22, 2026 18:56
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.19 x -- echo 17.4 ± 0.4 16.6 18.6 1.00
mise x -- echo 17.7 ± 0.4 16.7 21.5 1.02 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.19 env 16.8 ± 0.4 15.9 20.7 1.00
mise env 16.9 ± 0.3 16.1 18.0 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.19 hook-env 17.1 ± 0.3 16.2 19.1 1.00
mise hook-env 17.4 ± 0.3 16.5 18.8 1.01 ± 0.03

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.19 ls 15.9 ± 0.4 15.0 18.2 1.00
mise ls 16.2 ± 0.3 15.3 17.6 1.02 ± 0.03

xtasks/test/perf

Command mise-2026.2.19 mise Variance
install (cached) 89ms 89ms +0%
ls (cached) 59ms 59ms +0%
bin-paths (cached) 61ms 61ms +0%
task-ls (cached) 680ms 675ms +0%

@carlosjgp
Copy link

I think this PR did break mise install

sudo apt update -y && sudo apt install -y gpg sudo wget curl
sudo install -dm 755 /etc/apt/keyrings
wget -qO - https://mise.jdx.dev/gpg-key.pub | gpg --dearmor | sudo tee /etc/apt/keyrings/mise-archive-keyring.gpg 1> /dev/null
echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.gpg arch=$ARCH] https://mise.jdx.dev/deb stable main" | sudo tee /etc/apt/sources.list.d/mise.list
sudo apt update
sudo apt install -y mise

mise settings always_keep_download=true
mise settings lockfile=true
mise settings experimental=true
# Only allow installation of packages that exist in the lockfile
mise settings locked=true

mise install

output

mise hint use multiple versions simultaneously with mise use python@3.12 python@3.11
mise 2026.2.19 by @jdx                                                   [16/16]
awscli@2.33.28                                                                 ◜
kubectl@1.35.1                                                                 ◜
jq@1.8.1                                                                       ◜
terraform@1.14.5                                                               ◜
python@3.14.3                                                                  ◜
uv@0.10.4                                                                      ◜
helm@4.1.1                                                                     ◜
terragrunt@0.99.4                                                              ◜
gh@2.87.2                                                                      ◜
shellcheck@0.11.0                                                              ◜
tf-summarize@0.3.16                                                            ◜
tflint@0.61.0                                                                  ◜
talosctl@1.12.4                                                                ◜
temporal@1.29.3                                                                ◜
prek@0.3.3                                                                     ◜
trivy@0.69.1                                                                   ◜
mise ERROR Failed to install tools: aqua:aquasecurity/trivy@0.69.1, aqua:astral-sh/uv@0.10.4, aqua:aws/aws-cli@2.33.28, aqua:cli/cli@2.87.2, aqua:dineshba/tf-summarize@0.3.16, aqua:gruntwork-io/terragrunt@0.99.4, aqua:hashicorp/terraform@1.14.5, aqua:helm/helm@4.1.1, aqua:j178/prek@latest, aqua:jqlang/jq@1.8.1, aqua:koalaman/shellcheck@0.11.0, aqua:kubernetes/kubernetes/kubectl@1.35.1, aqua:siderolabs/talos@1.12.4, aqua:temporalio/temporal@latest, aqua:terraform-linters/tflint@latest, core:python@3.14.3

aqua:aquasecurity/trivy@0.69.1: No lockfile URL found for trivy@0.69.1 on platform linux-x64 (--locked mode)
hint: Run `mise lock` to generate lockfile URLs, or disable locked mode
....

mise.lock exists and has all the platforms' URLs

mise-en-dev added a commit that referenced this pull request Feb 25, 2026
### 🚀 Features

- **(conda)** replace custom backend with rattler crates by @jdx in
[#8325](#8325)
- **(task)** enforce per-task timeout configuration by @tvararu in
[#8250](#8250)
- **(vsix)** added vsix archives to http backend by @sosumappu in
[#8306](#8306)
- add core dotnet plugin for .NET SDK management by @jdx in
[#8326](#8326)

### 🐛 Bug Fixes

- **(conda)** preserve conda_packages on locked install and fix temp
file race by @jdx in [#8335](#8335)
- **(conda)** deduplicate repodata records to fix solver error on Linux
by @jdx in [#8337](#8337)
- **(env)** include watch_files in fast-path early exit check by @jdx in
[#8317](#8317)
- **(env)** clear fish completions when setting/unsetting shell aliases
by @jdx in [#8324](#8324)
- **(lockfile)** prevent lockfile writes when --locked is set by @jdx in
[#8308](#8308)
- **(lockfile)** prune orphan tool entries on mise lock by @mackwic in
[#8265](#8265)
- **(lockfile)** error on contradictory locked=true + lockfile=false
config by @jdx in [#8329](#8329)
- **(regal)** Update package location by @charlieegan3 in
[#8315](#8315)
- **(release)** strip markdown heading prefix from communique release
title by @jdx in [#8303](#8303)
- **(schema)** enforce additionalProperties constraint for env by
@adamliang0 in [#8328](#8328)

### 📚 Documentation

- Remove incorrect oh-my-zsh plugin ordering comment by @bvosk in
[#8323](#8323)
- require AI disclosure on GitHub comments by @jdx in
[#8330](#8330)

### 📦 Registry

- add `oxfmt` by @taoufik07 in
[#8316](#8316)

### New Contributors

- @adamliang0 made their first contribution in
[#8328](#8328)
- @tvararu made their first contribution in
[#8250](#8250)
- @bvosk made their first contribution in
[#8323](#8323)
- @taoufik07 made their first contribution in
[#8316](#8316)
- @charlieegan3 made their first contribution in
[#8315](#8315)
- @sosumappu made their first contribution in
[#8306](#8306)

## 📦 Aqua Registry Updates

#### New Packages (3)

- [`Tyrrrz/FFmpegBin`](https://github.com/Tyrrrz/FFmpegBin)
- [`elixir-lang/expert`](https://github.com/elixir-lang/expert)
- [`erikjuhani/basalt`](https://github.com/erikjuhani/basalt)

#### Updated Packages (5)

- [`caarlos0/fork-cleaner`](https://github.com/caarlos0/fork-cleaner)
-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`jackchuka/mdschema`](https://github.com/jackchuka/mdschema)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
- [`peco/peco`](https://github.com/peco/peco)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
## Summary

- When `mise install --locked` is run, the `mise.lock` file should not
be modified
- Adds early returns in `update_lockfiles` and `auto_lock_new_versions`
when `Settings::get().locked` is true
- Consistent with how `cargo install --locked` and `uv pip install
--locked` behave: the lock file is treated as authoritative and must not
be updated

## Changes Made

- `update_lockfiles()`: guard changed from `if
!Settings::get().lockfile` to `if !Settings::get().lockfile ||
Settings::get().locked`
- `auto_lock_new_versions()`: guard changed from `if
!Settings::get().lockfile || new_versions.is_empty()` to `if
!Settings::get().lockfile || Settings::get().locked ||
new_versions.is_empty()`

## Testing

- Existing lockfile tests continue to pass
- The `--locked` flag now prevents any writes to `mise.lock` during
install

## Related

- Spec: `/home/jdx/src/mise/src/lockfile.rs`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Small conditional-guard change that only disables lockfile writes in
locked mode; minimal behavioral surface area and no
security/data-handling impact.
> 
> **Overview**
> When `Settings::get().locked` (e.g. `mise install --locked`) is
enabled, lockfile update paths now short-circuit to avoid modifying
`mise.lock`.
> 
> Specifically, `update_lockfiles` and `auto_lock_new_versions` add an
early return when locked mode is active, preventing post-install
lockfile refresh/auto-lock behavior.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
fdccfd2. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(conda)** replace custom backend with rattler crates by @jdx in
[jdx#8325](jdx#8325)
- **(task)** enforce per-task timeout configuration by @tvararu in
[jdx#8250](jdx#8250)
- **(vsix)** added vsix archives to http backend by @sosumappu in
[jdx#8306](jdx#8306)
- add core dotnet plugin for .NET SDK management by @jdx in
[jdx#8326](jdx#8326)

### 🐛 Bug Fixes

- **(conda)** preserve conda_packages on locked install and fix temp
file race by @jdx in [jdx#8335](jdx#8335)
- **(conda)** deduplicate repodata records to fix solver error on Linux
by @jdx in [jdx#8337](jdx#8337)
- **(env)** include watch_files in fast-path early exit check by @jdx in
[jdx#8317](jdx#8317)
- **(env)** clear fish completions when setting/unsetting shell aliases
by @jdx in [jdx#8324](jdx#8324)
- **(lockfile)** prevent lockfile writes when --locked is set by @jdx in
[jdx#8308](jdx#8308)
- **(lockfile)** prune orphan tool entries on mise lock by @mackwic in
[jdx#8265](jdx#8265)
- **(lockfile)** error on contradictory locked=true + lockfile=false
config by @jdx in [jdx#8329](jdx#8329)
- **(regal)** Update package location by @charlieegan3 in
[jdx#8315](jdx#8315)
- **(release)** strip markdown heading prefix from communique release
title by @jdx in [jdx#8303](jdx#8303)
- **(schema)** enforce additionalProperties constraint for env by
@adamliang0 in [jdx#8328](jdx#8328)

### 📚 Documentation

- Remove incorrect oh-my-zsh plugin ordering comment by @bvosk in
[jdx#8323](jdx#8323)
- require AI disclosure on GitHub comments by @jdx in
[jdx#8330](jdx#8330)

### 📦 Registry

- add `oxfmt` by @taoufik07 in
[jdx#8316](jdx#8316)

### New Contributors

- @adamliang0 made their first contribution in
[jdx#8328](jdx#8328)
- @tvararu made their first contribution in
[jdx#8250](jdx#8250)
- @bvosk made their first contribution in
[jdx#8323](jdx#8323)
- @taoufik07 made their first contribution in
[jdx#8316](jdx#8316)
- @charlieegan3 made their first contribution in
[jdx#8315](jdx#8315)
- @sosumappu made their first contribution in
[jdx#8306](jdx#8306)

## 📦 Aqua Registry Updates

#### New Packages (3)

- [`Tyrrrz/FFmpegBin`](https://github.com/Tyrrrz/FFmpegBin)
- [`elixir-lang/expert`](https://github.com/elixir-lang/expert)
- [`erikjuhani/basalt`](https://github.com/erikjuhani/basalt)

#### Updated Packages (5)

- [`caarlos0/fork-cleaner`](https://github.com/caarlos0/fork-cleaner)
-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`jackchuka/mdschema`](https://github.com/jackchuka/mdschema)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
- [`peco/peco`](https://github.com/peco/peco)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants