refactor(config): consolidate flat task_* settings into nested task.*#8239
refactor(config): consolidate flat task_* settings into nested task.*#8239
Conversation
… settings Add 9 new nested task.* settings (task.disable_paths, task.output, task.remote_no_cache, task.run_auto_install, task.show_full_cmd, task.skip, task.skip_depends, task.timeout, task.timings) and deprecate the corresponding flat task_* settings with version-gated warnings (deprecated_warn_at="2026.8.0", deprecated_remove_at="2027.2.0"). Extend SettingsMeta and build.rs codegen to support deprecation fields, add warn_deprecated() helper, and migrate all code references from settings.task_xxx to settings.task.xxx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary of ChangesHello @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 significantly refactors the configuration settings related to tasks by moving them from a flat Highlights
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
Code Review
This is a solid refactoring that consolidates task-related settings into a task.* namespace, improving configuration organization. The implementation is thorough, covering documentation, tests, and schemas, and includes a well-designed migration path with deprecation warnings for backward compatibility. The suggestion to improve the maintainability of the settings migration logic by reducing code duplication is valid and has been kept.
| // Migrate task_* settings to task.* (must run before auto_install override below) | ||
| if let Some(v) = self.task_disable_paths.take() { | ||
| if !v.is_empty() { | ||
| warn_deprecated("task_disable_paths"); | ||
| self.task.disable_paths.extend(v); | ||
| } | ||
| } | ||
| if let Some(v) = self.task_output.take() { | ||
| warn_deprecated("task_output"); | ||
| self.task.output = Some(v); | ||
| } | ||
| if let Some(v) = self.task_remote_no_cache { | ||
| warn_deprecated("task_remote_no_cache"); | ||
| self.task.remote_no_cache = Some(v); | ||
| } | ||
| if let Some(v) = self.task_run_auto_install { | ||
| warn_deprecated("task_run_auto_install"); | ||
| self.task.run_auto_install = v; | ||
| } | ||
| if let Some(v) = self.task_show_full_cmd { | ||
| warn_deprecated("task_show_full_cmd"); | ||
| self.task.show_full_cmd = v; | ||
| } | ||
| if let Some(v) = self.task_skip.take() { | ||
| if !v.is_empty() { | ||
| warn_deprecated("task_skip"); | ||
| self.task.skip.extend(v); | ||
| } | ||
| } | ||
| if let Some(v) = self.task_skip_depends { | ||
| warn_deprecated("task_skip_depends"); | ||
| self.task.skip_depends = v; | ||
| } | ||
| if let Some(v) = self.task_timeout.take() { | ||
| warn_deprecated("task_timeout"); | ||
| self.task.timeout = Some(v); | ||
| } | ||
| if let Some(v) = self.task_timings { | ||
| warn_deprecated("task_timings"); | ||
| self.task.timings = Some(v); | ||
| } |
There was a problem hiding this comment.
The migration logic for each of the task_* settings is very repetitive. To improve maintainability and reduce boilerplate, you could use a macro to handle the common pattern of checking for a value, warning about deprecation, and assigning it to the new setting.
macro_rules! migrate_task_setting {
// For settings that are moved, e.g. Option<T> -> Option<T> where T is not Copy
($s:ident, $old:ident, $new:ident) => {
if let Some(v) = $s.$old.take() {
warn_deprecated(stringify!($old));
$s.task.$new = Some(v);
}
};
// For settings that are moved and are Copy, e.g. Option<bool> -> Option<bool>
($s:ident, copy $old:ident, $new:ident) => {
if let Some(v) = $s.$old {
warn_deprecated(stringify!($old));
$s.task.$new = Some(v);
}
};
// For bool settings, e.g. Option<bool> -> bool
($s:ident, bool $old:ident, $new:ident) => {
if let Some(v) = $s.$old {
warn_deprecated(stringify!($old));
$s.task.$new = v;
}
};
// For collection settings
($s:ident, extend $old:ident, $new:ident) => {
if let Some(v) = $s.$old.take() {
if !v.is_empty() {
warn_deprecated(stringify!($old));
$s.task.$new.extend(v);
}
}
};
}
// Migrate task_* settings to task.* (must run before auto_install override below)
migrate_task_setting!(self, extend task_disable_paths, disable_paths);
migrate_task_setting!(self, task_output, output);
migrate_task_setting!(self, copy task_remote_no_cache, remote_no_cache);
migrate_task_setting!(self, bool task_run_auto_install, run_auto_install);
migrate_task_setting!(self, bool task_show_full_cmd, show_full_cmd);
migrate_task_setting!(self, extend task_skip, skip);
migrate_task_setting!(self, bool task_skip_depends, skip_depends);
migrate_task_setting!(self, task_timeout, timeout);
migrate_task_setting!(self, copy task_timings, timings);There was a problem hiding this comment.
Pull request overview
Refactors the configuration surface by moving legacy flat task_* settings into a nested task.* namespace, while preserving backwards compatibility through migration logic and adding version-gated deprecation metadata to the settings schema/tooling.
Changes:
- Migrates 9 legacy
task_*settings totask.*, updating Rust call sites accordingly. - Adds TOML-declared deprecation metadata (
deprecated_warn_at/deprecated_remove_at) and emits version-gated warnings when deprecated keys are used. - Updates settings/schema/docs/usage/man/e2e configs to prefer the new
task.*keys.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/task/task_tool_installer.rs | Switches auto-install behavior to read task.run_auto_install. |
| src/task/task_output_handler.rs | Reads output mode from task.output instead of task_output. |
| src/task/task_output.rs | Uses task.show_full_cmd for truncation behavior. |
| src/task/task_fetcher.rs | Uses task.remote_no_cache for remote task caching. |
| src/task/task_executor.rs | Uses task.timings and task.skip. |
| src/config/settings.rs | Adds deprecation metadata + warning helper; migrates deprecated task_* keys into task.*. |
| src/config/mod.rs | Uses task.disable_paths / task.remote_no_cache during task discovery/fetching. |
| src/cli/watch.rs | Updates help text references to task.output. |
| src/cli/run.rs | Updates help text and uses task.skip_depends. |
| settings.toml | Defines new task.* settings and marks task_* keys as deprecated/hidden with version gates. |
| schema/mise.json | Adds nested task properties and marks legacy task_* keys deprecated. |
| schema/mise-settings.json | Extends schema to allow deprecation version fields. |
| mise.usage.kdl | Updates CLI help text references to task.output. |
| man/man1/mise.1 | Updates manpage references to task.output. |
| e2e/tasks/test_task_skip_deps | Updates test config to task.skip_depends. |
| e2e/tasks/test_task_raw_output | Updates test config to task.output. |
| e2e/tasks/test_task_file_auto_install | Updates test config to task.run_auto_install. |
| e2e/config/test_auto_install_false | Updates test config to task.run_auto_install. |
| docs/tasks/running-tasks.md | Updates docs to reference task.output. |
| docs/configuration.md | Updates example config to task.output. |
| build.rs | Emits deprecation metadata into generated SETTINGS_META. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/config/settings.rs
Outdated
| fn warn_deprecated(key: &str) { | ||
| if let Some(meta) = SETTINGS_META.get(key) | ||
| && let (Some(msg), Some(warn_at), Some(remove_at)) = ( | ||
| meta.deprecated, | ||
| meta.deprecated_warn_at, | ||
| meta.deprecated_remove_at, | ||
| ) { | ||
| use versions::Versioning; | ||
| let warn_version = Versioning::new(warn_at).unwrap(); | ||
| let remove_version = Versioning::new(remove_at).unwrap(); | ||
| debug_assert!( | ||
| *crate::cli::version::V < remove_version, | ||
| "Deprecated setting [{key}] should have been removed in {remove_at}. Please remove this deprecated setting.", | ||
| ); | ||
| if *crate::cli::version::V >= warn_version { | ||
| let id = Box::leak(format!("setting.{key}").into_boxed_str()); | ||
| if crate::output::DEPRECATED.lock().unwrap().insert(id) { | ||
| warn!( |
There was a problem hiding this comment.
warn_deprecated leaks memory by Box::leak-ing a newly formatted string every time a deprecated key is processed (even when the warning has already been emitted). Since all current call sites pass string literals, consider making key an &'static str and using that directly as the deprecation id (or reuse the existing deprecated_at! macro) so the warning can still be deduped without allocating/leaking per call.
| if let Some(v) = self.task_disable_paths.take() | ||
| && !v.is_empty() { | ||
| warn_deprecated("task_disable_paths"); | ||
| self.task.disable_paths.extend(v); | ||
| } | ||
| if let Some(v) = self.task_output.take() { | ||
| warn_deprecated("task_output"); | ||
| self.task.output = Some(v); | ||
| } | ||
| if let Some(v) = self.task_remote_no_cache { | ||
| warn_deprecated("task_remote_no_cache"); | ||
| self.task.remote_no_cache = Some(v); | ||
| } | ||
| if let Some(v) = self.task_run_auto_install { | ||
| warn_deprecated("task_run_auto_install"); | ||
| self.task.run_auto_install = v; | ||
| } | ||
| if let Some(v) = self.task_show_full_cmd { | ||
| warn_deprecated("task_show_full_cmd"); | ||
| self.task.show_full_cmd = v; | ||
| } | ||
| if let Some(v) = self.task_skip.take() | ||
| && !v.is_empty() { | ||
| warn_deprecated("task_skip"); | ||
| self.task.skip.extend(v); | ||
| } | ||
| if let Some(v) = self.task_skip_depends { | ||
| warn_deprecated("task_skip_depends"); | ||
| self.task.skip_depends = v; | ||
| } | ||
| if let Some(v) = self.task_timeout.take() { | ||
| warn_deprecated("task_timeout"); | ||
| self.task.timeout = Some(v); | ||
| } | ||
| if let Some(v) = self.task_timings { | ||
| warn_deprecated("task_timings"); | ||
| self.task.timings = Some(v); | ||
| } | ||
| if !self.auto_install { | ||
| self.exec_auto_install = false; | ||
| self.not_found_auto_install = false; |
There was a problem hiding this comment.
The PR introduces backwards-compat behavior (migrating task_* to task.*) and version-gated deprecation warnings, but there are no automated checks asserting that: (1) old keys still apply correctly, and (2) MISE_TASK_OUTPUT/other env vars still map to the new keys. Adding an e2e (or unit) test that sets the deprecated keys/env and asserts the observed behavior would prevent regressions when these migrations are refactored later.
| "task_output": { | ||
| "description": "Change output style when executing tasks.", | ||
| "type": "string", | ||
| "enum": [ | ||
| "prefix", | ||
| "interleave", | ||
| "keep-order", | ||
| "replacing", | ||
| "timed", | ||
| "quiet", | ||
| "silent" | ||
| ] | ||
| "deprecated": true | ||
| }, | ||
| "task_remote_no_cache": { | ||
| "description": "Mise will always fetch the latest tasks from the remote, by default the cache is used.", | ||
| "type": "boolean" | ||
| "type": "boolean", | ||
| "deprecated": true |
There was a problem hiding this comment.
In the JSON schema, task_output is still supported (deprecated) but its enum constraints were removed. This makes the schema accept values that the runtime parser will still reject, which is a validation/IDE-assistance regression. Consider keeping the original enum (and other constraints/defaults where applicable) while also marking the property as deprecated.
|
bugbot run |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.16 x -- echo |
25.7 ± 0.6 | 24.2 | 30.2 | 1.00 |
mise x -- echo |
25.7 ± 0.8 | 24.4 | 32.6 | 1.00 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.16 env |
25.3 ± 0.8 | 23.6 | 28.9 | 1.00 ± 0.04 |
mise env |
25.3 ± 0.5 | 23.7 | 27.7 | 1.00 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.16 hook-env |
25.9 ± 0.6 | 24.2 | 28.4 | 1.00 |
mise hook-env |
26.2 ± 0.5 | 24.7 | 27.6 | 1.01 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.16 ls |
23.5 ± 0.4 | 21.8 | 24.9 | 1.01 ± 0.03 |
mise ls |
23.2 ± 0.6 | 21.8 | 26.2 | 1.00 |
xtasks/test/perf
| Command | mise-2026.2.16 | mise | Variance |
|---|---|---|---|
| install (cached) | 136ms | 136ms | +0% |
| ls (cached) | 83ms | 83ms | +0% |
| bin-paths (cached) | 89ms | 88ms | +1% |
| task-ls (cached) | 848ms | 835ms | +1% |
### 🐛 Bug Fixes - **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [#8234](#8234) - **(use)** write to config.toml instead of config.local.toml by @jdx in [#8240](#8240) - default legacy .mise.backend installs to non-explicit by @jean-humann in [#8245](#8245) ### 🚜 Refactor - **(config)** consolidate flat task_* settings into nested task.* by @jdx in [#8239](#8239)
## [2026.2.18](https://github.com/jdx/mise/compare/v2026.2.17..v2026.2.18) - 2026-02-21 ### 🚀 Features - **(install)** auto-lock all platforms after tool installation by @jdx in [#8277](jdx/mise#8277) ### 🐛 Bug Fixes - **(config)** respect --yes flag for config trust prompts by @jdx in [#8288](jdx/mise#8288) - **(exec)** strip shims from PATH on Unix to prevent infinite recursion by @jdx in [#8276](jdx/mise#8276) - **(install)** validate --locked before --dry-run short-circuit by @altendky in [#8290](jdx/mise#8290) - **(release)** refresh PATH after mise up in release-plz by @jdx in [#8292](jdx/mise#8292) - **(schema)** replace unevaluatedProperties with additionalProperties by @jdx in [#8285](jdx/mise#8285) - **(task)** avoid duplicated stderr on task failure in replacing mode by @jdx in [#8275](jdx/mise#8275) - **(task)** use process groups to kill child process trees on Unix by @jdx in [#8279](jdx/mise#8279) - **(task)** run depends_post tasks even when parent task fails by @jdx in [#8274](jdx/mise#8274) - **(task)** suggest similar commands when mistyping a CLI subcommand by @jdx in [#8286](jdx/mise#8286) - **(task)** execute monorepo subdirectory prepare steps from root by @jdx in [#8291](jdx/mise#8291) - **(upgrade)** don't force-reinstall already installed versions by @jdx in [#8282](jdx/mise#8282) - **(watch)** restore terminal state after watchexec exits by @jdx in [#8273](jdx/mise#8273) ### 📚 Documentation - clarify that MISE_CEILING_PATHS excludes the ceiling directory itself by @jdx in [#8283](jdx/mise#8283) ### Chore - replace gen-release-notes script with communique by @jdx in [#8289](jdx/mise#8289) ### New Contributors - @altendky made their first contribution in [#8290](jdx/mise#8290) ### 📦 Aqua Registry Updates #### New Packages (4) - [`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml) - [`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases) - [`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs) - [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds) #### Updated Packages (2) - [`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local) - [`k1LoW/runn`](https://github.com/k1LoW/runn) ## [2026.2.17](https://github.com/jdx/mise/compare/v2026.2.16..v2026.2.17) - 2026-02-19 ### 🚀 Features - **(prepare)** update mtime of outputs after command is run by @halms in [#8243](jdx/mise#8243) ### 🐛 Bug Fixes - **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [#8234](jdx/mise#8234) - **(use)** write to config.toml instead of config.local.toml by @jdx in [#8240](jdx/mise#8240) - default legacy .mise.backend installs to non-explicit by @jean-humann in [#8245](jdx/mise#8245) ### 🚜 Refactor - **(config)** consolidate flat task_* settings into nested task.* by @jdx in [#8239](jdx/mise#8239) ### Chore - **(prepare)** refactor common code into ProviderBase by @halms in [#8246](jdx/mise#8246) ### 📦 Aqua Registry Updates #### Updated Packages (1) - [`namespacelabs/foundation/nsc`](https://github.com/namespacelabs/foundation/nsc)
…jdx#8239) ## Summary - Moves 9 flat \`task_*\` settings to the \`task.*\` namespace (e.g. \`task_output\` → \`task.output\`) - Old names remain fully functional via migration in \`set_hidden_configs()\` — no breaking changes - Deprecation warnings will appear starting at mise \`2026.8.0\`; \`debug_assert!\` fires at \`2027.2.0\` to remind devs to remove the old names - Extends \`settings.toml\` schema to support \`deprecated_warn_at\` / \`deprecated_remove_at\` fields, enabling version-gated deprecation to be declared in TOML rather than scattered in code **Settings migrated:** | Old | New | |---|---| | \`task_disable_paths\` | \`task.disable_paths\` | | \`task_output\` | \`task.output\` | | \`task_remote_no_cache\` | \`task.remote_no_cache\` | | \`task_run_auto_install\` | \`task.run_auto_install\` | | \`task_show_full_cmd\` | \`task.show_full_cmd\` | | \`task_skip\` | \`task.skip\` | | \`task_skip_depends\` | \`task.skip_depends\` | | \`task_timeout\` | \`task.timeout\` | | \`task_timings\` | \`task.timings\` | ## Test plan - [x] \`mise run build\` — compiles cleanly - [x] \`mise run test:unit\` — 481 tests pass - [x] \`mise run test:e2e test_task_raw_output test_task_skip_deps test_task_file_auto_install test_auto_install_false\` — all pass - [ ] Old setting names still work (backwards compat via migration) - [ ] \`MISE_TASK_OUTPUT=prefix\` env var routes to new \`task.output\` setting 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches config parsing/migration and task execution behavior via renamed settings, so mistakes could silently change task defaults or deprecation timing; scope is contained and largely refactor/migration-driven. > > **Overview** > **Consolidates task-related configuration under `task.*`** by adding a new nested `task` settings namespace (e.g. `task.output`, `task.run_auto_install`, `task.skip_depends`) and updating runtime/task code paths to read from the nested struct. > > Maintains backward compatibility by migrating legacy `task_*` keys in `Settings::set_hidden_configs()` while introducing version-gated deprecation metadata (`deprecated_warn_at`/`deprecated_remove_at`) and warnings/assertions driven from `settings.toml`/generated `SETTINGS_META`. Documentation, manpage/usage text, schemas, and E2E tests are updated to use the new `task.*` keys, and legacy keys are marked deprecated/hidden in the schema and settings definitions. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 95b5fc6. 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> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### 🐛 Bug Fixes - **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [jdx#8234](jdx#8234) - **(use)** write to config.toml instead of config.local.toml by @jdx in [jdx#8240](jdx#8240) - default legacy .mise.backend installs to non-explicit by @jean-humann in [jdx#8245](jdx#8245) ### 🚜 Refactor - **(config)** consolidate flat task_* settings into nested task.* by @jdx in [jdx#8239](jdx#8239)
Summary
Settings migrated:
Test plan
🤖 Generated with Claude Code
Note
Medium Risk
Touches config parsing/migration and task execution behavior via renamed settings, so mistakes could silently change task defaults or deprecation timing; scope is contained and largely refactor/migration-driven.
Overview
Consolidates task-related configuration under
task.*by adding a new nestedtasksettings namespace (e.g.task.output,task.run_auto_install,task.skip_depends) and updating runtime/task code paths to read from the nested struct.Maintains backward compatibility by migrating legacy
task_*keys inSettings::set_hidden_configs()while introducing version-gated deprecation metadata (deprecated_warn_at/deprecated_remove_at) and warnings/assertions driven fromsettings.toml/generatedSETTINGS_META. Documentation, manpage/usage text, schemas, and E2E tests are updated to use the newtask.*keys, and legacy keys are marked deprecated/hidden in the schema and settings definitions.Written by Cursor Bugbot for commit 95b5fc6. This will update automatically on new commits. Configure here.