Skip to content

fix(prepare): handle freshness check for auto-created venvs#7770

Merged
jdx merged 3 commits into
mainfrom
fix/prepare-freshness-venv
Jan 19, 2026
Merged

fix(prepare): handle freshness check for auto-created venvs#7770
jdx merged 3 commits into
mainfrom
fix/prepare-freshness-venv

Conversation

@jdx

@jdx jdx commented Jan 19, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fixes issue where mise prepare incorrectly skips dependency installation when a venv is auto-created
  • When venv is auto-created during config loading, its timestamp is newer than the lock file, causing the freshness check to incorrectly conclude dependencies are "fresh"
  • Adds in-memory tracking of directories created during the current session
  • When check_freshness() runs, outputs created this session are considered stale

Fixes: #7762

Test plan

  • Create a test project with [env._.python.venv] and [prepare.uv] configured
  • Run mise prepare on first entry - should run uv sync
  • Run mise prepare again - should report "fresh" (skip sync)
  • Touch uv.lock and run mise prepare - should run uv sync again

🤖 Generated with Claude Code


Note

Ensures prepare runs when outputs like .venv are auto-created during env resolution, preventing false "fresh" detections.

  • Add in-memory tracking of session-created outputs via mark_output_stale, is_output_stale, and clear_output_stale in src/prepare/mod.rs
  • Update check_freshness() to consider is_output_stale(output) before mtime comparison
  • After venv creation, call crate::prepare::mark_output_stale(venv) in src/config/env_directive/venv.rs
  • Execute providers in parallel and return outputs; upon all success, clear flags with clear_output_stale in src/prepare/engine.rs
  • Extend e2e tests to validate directory output freshness behavior (.venv vs requirements.txt)

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

When a venv is auto-created during config loading but before prepare runs,
the timestamp comparison incorrectly concluded dependencies were "fresh"
because the venv directory was newer than the lock file.

This fix adds in-memory tracking of directories created during the current
session. When check_freshness() runs, it checks if any output directory was
created this session - if so, it's considered stale and prepare will run.

Fixes: #7762

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 19, 2026 22:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes an issue where mise prepare incorrectly skips dependency installation when a Python virtual environment is auto-created during config loading. The problem occurs because the newly created venv has a timestamp newer than the lock file, causing the freshness check to incorrectly conclude that dependencies are already installed.

Changes:

  • Adds in-memory tracking of directories created during the current session using a static LazyLock<Mutex<HashSet<PathBuf>>>
  • Modifies freshness check logic to treat outputs created during the current session as stale
  • Marks auto-created venvs as stale and clears this status after successful prepare execution

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/prepare/mod.rs Adds global state tracking and helper functions for managing stale output paths
src/prepare/engine.rs Updates prepare execution flow to check stale status and clear it after successful runs
src/config/env_directive/venv.rs Marks newly created venvs as stale for prepare freshness checks
Comments suppressed due to low confidence (1)

src/prepare/mod.rs:1

  • Silent failure on lock poisoning could mask issues. Consider logging a warning or error when the mutex lock fails, similar to the concern in mark_output_stale.
use std::collections::{BTreeMap, HashSet};

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

Comment thread src/prepare/mod.rs
Comment on lines +120 to +136
if let Ok(mut set) = STALE_OUTPUTS.lock() {
set.insert(path);
}
}

/// Check if a directory was created this session
pub fn is_output_stale(path: &PathBuf) -> bool {
STALE_OUTPUTS
.lock()
.map(|set| set.contains(path))
.unwrap_or(false)
}

/// Clear stale status for a path (after prepare runs successfully)
pub fn clear_output_stale(path: &PathBuf) {
if let Ok(mut set) = STALE_OUTPUTS.lock() {
set.remove(path);

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

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

Silent failure on lock poisoning could mask issues. Consider logging a warning or error when the mutex lock fails, as this would indicate a serious problem with the stale tracking mechanism.

Suggested change
if let Ok(mut set) = STALE_OUTPUTS.lock() {
set.insert(path);
}
}
/// Check if a directory was created this session
pub fn is_output_stale(path: &PathBuf) -> bool {
STALE_OUTPUTS
.lock()
.map(|set| set.contains(path))
.unwrap_or(false)
}
/// Clear stale status for a path (after prepare runs successfully)
pub fn clear_output_stale(path: &PathBuf) {
if let Ok(mut set) = STALE_OUTPUTS.lock() {
set.remove(path);
match STALE_OUTPUTS.lock() {
Ok(mut set) => {
set.insert(path);
}
Err(err) => {
warn!("prepare: failed to mark output as stale due to poisoned mutex: {err}");
}
}
}
/// Check if a directory was created this session
pub fn is_output_stale(path: &PathBuf) -> bool {
match STALE_OUTPUTS.lock() {
Ok(set) => set.contains(path),
Err(err) => {
warn!("prepare: failed to check stale output due to poisoned mutex: {err}");
false
}
}
}
/// Clear stale status for a path (after prepare runs successfully)
pub fn clear_output_stale(path: &PathBuf) {
match STALE_OUTPUTS.lock() {
Ok(mut set) => {
set.remove(path);
}
Err(err) => {
warn!("prepare: failed to clear stale output due to poisoned mutex: {err}");
}

Copilot uses AI. Check for mistakes.
Comment thread src/prepare/engine.rs Outdated
Comment on lines +276 to +278
for output in &outputs {
super::clear_output_stale(output);
}

Copilot AI Jan 19, 2026

Copy link

Choose a reason for hiding this comment

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

Clearing stale status happens after prepare succeeds, but if multiple providers share the same output path and one fails while another succeeds, the output might be incorrectly marked as non-stale. Consider whether clearing should only happen after all prepare steps complete successfully.

Copilot uses AI. Check for mistakes.
@github-actions

github-actions Bot commented Jan 19, 2026

Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.5 x -- echo 19.7 ± 0.5 19.1 24.8 1.00
mise x -- echo 19.8 ± 0.3 19.1 23.2 1.00 ± 0.03

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.5 env 19.1 ± 0.4 18.6 25.2 1.00
mise env 19.5 ± 0.6 18.6 26.2 1.02 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.5 hook-env 19.3 ± 0.2 18.7 20.2 1.00
mise hook-env 19.5 ± 0.3 18.9 20.6 1.01 ± 0.02

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.5 ls 17.3 ± 0.4 16.6 18.6 1.00
mise ls 17.3 ± 0.3 16.7 21.7 1.00 ± 0.03

xtasks/test/perf

Command mise-2026.1.5 mise Variance
install (cached) 109ms 108ms +0%
ls (cached) 66ms 71ms -7%
bin-paths (cached) 70ms 70ms +0%
task-ls (cached) 282ms 283ms +0%

jdx and others added 2 commits January 19, 2026 16:49
Tests the basic timestamp-based freshness logic:
1. No output directory exists → stale
2. Output newer than sources → fresh
3. Sources newer than output → stale

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
If multiple providers share the same output path and one fails while
another succeeds, the output could be incorrectly marked as non-stale.
Now stale status is only cleared after ALL prepare steps complete
successfully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jdx jdx merged commit 26a1ef0 into main Jan 19, 2026
34 checks passed
@jdx jdx deleted the fix/prepare-freshness-venv branch January 19, 2026 23:07
jdx pushed a commit that referenced this pull request Jan 20, 2026
### 🚀 Features

- **(config)** add miette diagnostics for TOML parsing errors by @jdx in
[#7764](#7764)
- **(env)** add environment caching with module cacheability support by
@jdx in [#7761](#7761)

### 🐛 Bug Fixes

- **(prepare)** handle freshness check for auto-created venvs by @jdx in
[#7770](#7770)
- **(release)** use colon separator in release titles by @jdx in
[#7765](#7765)
- **(release)** drop Fedora 41 from COPR build (EOL) by @TobiX in
[#7771](#7771)

### 🚜 Refactor

- improve filetask field parsing tests and parser by @makp0 in
[#7751](#7751)

### 📚 Documentation

- improve CLAUDE.md with additional development guidance by @jdx in
[#7763](#7763)
- drop architecture from Debian sources.list by @TobiX in
[#7772](#7772)

### 📦 Registry

- use aqua for zprint by @scop in
[#7767](#7767)

### Security

- remove insecure registry-comment workflow by @jdx in
[#7769](#7769)

## 📦 Aqua Registry Updates

#### New Packages (2)

-
[`cameron-martin/bazel-lsp`](https://github.com/cameron-martin/bazel-lsp)
- [`micro-editor/micro`](https://github.com/micro-editor/micro)
jdx pushed a commit that referenced this pull request Feb 2, 2026
## Context
With the current `python.uv_venv_auto` behavior, there are two annoying
things:
1. it always creates venvs if they not exist
This is not needed for `uv`, since that usually creates/updates it's
venvs transparently (when doing `uv run` or `uv sync`, ...).
And it breaks `mise prepare` for `uv` since it creates the `.venv`
folder but does not install any deps. So `mise prepare` thinks
everything is up-to-date when it isn't. (the uv-native venv creation
appears to not have been covered by #7770)
2. it exports the mise python version to the env var `UV_PYTHON`, which
forces uv to use that _version_.

## Changes
- Upfront, some reworking of the schema of `settings.toml`, specifically
the `type` and `enum` definitions.
Added the `BooleanOrString` type needed for this and also made the
`enum` definitions more flexible (e.g. with mixed types)
- Introducing new values for `python.uv_venv_auto`
  - `"source"` only sources existing venvs
- `"create|source"` creates if not exists and sources venvs (but does
not set UV_PYTHON anymore)
  - `false` does nothing – like now
- `true` (legacy, will be deprecated in 6 months) – like now, also sets
UV_PYTHON
- Unified the code creating/sourcing venvs between
`settings.python.uv_venv_auto` and `env._.python.venv`, which should now
also mark these as stale at creation.

I think with this changes, `python.uv_venv_auto` becomes much more
useful.
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Context
With the current `python.uv_venv_auto` behavior, there are two annoying
things:
1. it always creates venvs if they not exist
This is not needed for `uv`, since that usually creates/updates it's
venvs transparently (when doing `uv run` or `uv sync`, ...).
And it breaks `mise prepare` for `uv` since it creates the `.venv`
folder but does not install any deps. So `mise prepare` thinks
everything is up-to-date when it isn't. (the uv-native venv creation
appears to not have been covered by jdx#7770)
2. it exports the mise python version to the env var `UV_PYTHON`, which
forces uv to use that _version_.

## Changes
- Upfront, some reworking of the schema of `settings.toml`, specifically
the `type` and `enum` definitions.
Added the `BooleanOrString` type needed for this and also made the
`enum` definitions more flexible (e.g. with mixed types)
- Introducing new values for `python.uv_venv_auto`
  - `"source"` only sources existing venvs
- `"create|source"` creates if not exists and sources venvs (but does
not set UV_PYTHON anymore)
  - `false` does nothing – like now
- `true` (legacy, will be deprecated in 6 months) – like now, also sets
UV_PYTHON
- Unified the code creating/sourcing venvs between
`settings.python.uv_venv_auto` and `env._.python.venv`, which should now
also mark these as stale at creation.

I think with this changes, `python.uv_venv_auto` becomes much more
useful.
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.

2 participants