fix(http): store tool opts as native TOML to fix platform switching#8448
fix(http): store tool opts as native TOML to fix platform switching#8448
Conversation
Summary of ChangesHello, 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 resolves a critical issue in the HTTP backend configuration where stale cached options could incorrectly override new, platform-specific URL definitions. Previously, changing from a single URL to a 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 pull request addresses a bug that occurred when switching a tool's configuration from a single URL to platform-specific URLs. The issue was caused by stale, mangled platform data in the cache shadowing the new configuration. The fix correctly filters out these stale platforms or platform keys from cached options when a new platform definition is present in the configuration. A new e2e test has been added to verify this fix and prevent regressions. The changes are well-implemented and effectively solve the problem.
Greptile SummaryThis PR changes how tool options are persisted in Key changes:
Confidence Score: 4/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[mise install / use] --> B[Read mise.toml config]
B --> C[Build ToolVersionOptions\nopts: IndexMap<String, toml::Value>]
C --> D{Has bracket opts\nin full string?}
D -- Yes --> E[parse_tool_options\nTry TOML inline-table first\nFallback to manual comma-split]
D -- No --> F[Use opts from struct directly]
E --> G[Merge into BackendArg.opts]
F --> G
G --> H[Backend uses opts]
H --> I[write_backend_meta]
I --> J[.mise-installs.toml]
J --> J1[full = 'http:hello'\nwithout brackets]
J --> J2["[hello.opts]\nurl = '...'\nbin_path = 'bin'"]
K[Next mise run: read manifest] --> L{Old format?\nopts empty +\nbrackets in full}
L -- Yes --> M[Migration: extract opts\nfrom full brackets\nrewrite manifest]
L -- No --> N[Use native opts map]
M --> O[InstallStateTool\nfull = stripped\nopts = parsed]
N --> O
O --> P[From<InstallStateTool> for BackendArg\nmanifest opts inserted with or_insert\nconfig opts take priority]
|
2be05b4 to
17b32ba
Compare
17b32ba to
b4da894
Compare
When a tool is installed with a single `url` and later switched to platform-specific URLs, the cached options in the install manifest were mangled by the comma-based parser, causing "Http backend requires 'url' option" errors. Fix: parse_tool_options() now tries TOML inline-table parsing first (handling nested structures correctly), with a legacy manual fallback for old unquoted manifests. full_with_opts() now quotes string values to produce valid TOML. Fixes discussion #7034. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b4da894 to
fd2bc60
Compare
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 x -- echo |
25.8 ± 0.6 | 24.3 | 30.9 | 1.01 ± 0.07 |
mise x -- echo |
25.7 ± 1.7 | 23.7 | 57.1 | 1.00 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 env |
25.5 ± 0.6 | 23.7 | 30.5 | 1.01 ± 0.03 |
mise env |
25.2 ± 0.6 | 23.4 | 26.8 | 1.00 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 hook-env |
26.4 ± 0.6 | 24.7 | 29.2 | 1.01 ± 0.03 |
mise hook-env |
26.0 ± 0.5 | 24.3 | 27.8 | 1.00 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.3.3 ls |
25.4 ± 0.9 | 23.7 | 35.3 | 1.01 ± 0.04 |
mise ls |
25.2 ± 0.6 | 23.5 | 27.8 | 1.00 |
xtasks/test/perf
| Command | mise-2026.3.3 | mise | Variance |
|---|---|---|---|
| install (cached) | 159ms | 157ms | +1% |
| ls (cached) | 87ms | 87ms | +0% |
| bin-paths (cached) | 91ms | 89ms | +2% |
| task-ls (cached) | 870ms | 852ms | +2% |
Instead of encoding opts inside brackets in the manifest's `full` field
(e.g. `http:hello[url=..., platforms={...}]`), store them as a separate
native TOML `[tool.opts]` table. This avoids round-trip mangling of
nested structures like platform-specific URLs.
Includes backward compat: old manifests with bracketed opts in `full`
are automatically migrated on read.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace 9 duplicated regex patterns for parsing bracketed tool options (e.g. `http:hello[url=...]`) with two reusable helper functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
bugbot run |
|
@greptileai review |
Use ba.opts (the field) instead of ba.opts() (the method) in write_backend_meta to avoid persisting registry default options. This prevents stale stored values from overriding updated registry defaults. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n test - Extract duplicated string-to-toml::Value conversion into str_to_toml_value() helper - Fix e2e test to use darwin-x64 (not darwin-amd64) matching mise's arch normalization (x86_64 → x64) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add manifest_opts field to BackendArg to carry native toml::Value opts from the install manifest directly, avoiding lossy conversion through ToolVersionOptions string intermediary. Tables no longer degrade to escaped strings on re-write. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use full_without_opts() instead of full() in write_backend_meta() to prevent storing opts in both the full field (as brackets) and the separate opts map. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Start with manifest_opts, override with user opts (matching the read path in opts()), and always filter EPHEMERAL_OPT_KEYS. This fixes two issues: manifest_opts bypassing ephemeral key filtering, and manifest_opts shadowing updated user opts during write. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Manifest opts are user-specified options stored in native TOML format. Use insert() instead of entry().or_insert_with() so they override registry defaults, matching the precedence of user_opts parsed from brackets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move manifest_opts merge before user_opts merge in opts() so the precedence is: registry defaults < manifest opts (cached) < user opts (fresh config). Previously manifest opts were merged last, silently overriding user-provided values with stale cached values. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change ToolVersionOptions.opts from IndexMap<String, String> to IndexMap<String, toml::Value> so nested structures like platform-specific URLs survive round-trips without lossy string conversion. This eliminates the manifest_opts field on BackendArg and the str_to_toml_value helper, since opts are now natively TOML throughout the entire pipeline: config parsing → BackendArg → manifest storage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
bugbot run |
Integer and boolean TOML values (e.g. strip_components = 1) were stored as native toml::Value::Integer, but opts.get() only returns string values via as_str(), causing these options to silently return None. Convert scalar non-string/non-table/non-array values to strings at parse time. Tables and arrays remain as native TOML to preserve nested structures. Also removes unused get_value method. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
| // Fall back to manual parsing for legacy formats with unquoted values | ||
| parse_tool_options_manual(s) | ||
| } |
There was a problem hiding this comment.
TOML-first parsing silently changes whitespace and value semantics
Medium Severity
parse_tool_options now tries TOML parsing first via try_parse_as_toml. For legacy bracket-encoded option strings where values happen to be valid TOML literals (e.g. locked=true, strip_components=1), the TOML parser succeeds and strips whitespace from values. Previously the manual parser preserved raw values including surrounding whitespace. For example, " exe = true " would yield value " true " (with spaces) via the manual parser, but now the TOML parser succeeds and yields "true" (trimmed). This also means values like 0x2A (hex literal) become "42" after TOML integer parsing and string conversion, changing semantics from the legacy format.
| opts.opts | ||
| .iter() | ||
| .map(|(k, v)| (k.clone(), v.as_str().unwrap_or_default().to_string())) | ||
| .collect(), |
There was a problem hiding this comment.
vfox env_keys receives changed opts type silently
Low Severity
For non-backend vfox plugins, vfox.env_keys is called with &opts.opts which is now IndexMap<String, toml::Value> instead of the previous IndexMap<String, String>. While the generic T: Serialize signature compiles, the Lua serialization of toml::Value::Table or toml::Value::Array produces nested structures rather than the TOML string representation the plugins may expect. The backend-plugin path at lines 303-306 explicitly converts to IndexMap<String, String>, but this non-backend path was not updated.
Tool stubs were serializing TOML tables to strings then wrapping them back as toml::Value::String, causing lookup_platform_key to fail since it expects toml::Value::Table for nested platform structures. Keep tables/arrays as native toml::Value throughout the tool stub pipeline, matching the approach in mise_toml.rs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
bugbot run |
- Fix Hash/Eq contract violation: recursively sort Table entries before hashing so structurally equal Tables with different insertion orders produce identical hashes - Skip Table/Array values in full_with_opts bracket serialization since they can't round-trip through bracket notation - Fix non-string TOML values silently becoming empty strings in asdf, external_plugin_cache, and vfox backends - Add comment explaining why install_env is in EPHEMERAL_OPT_KEYS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
… conversion Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### 🚀 Features - **(github)** keep exe extensions on Windows by @iki in [#8424](#8424) - **(task)** add `interactive` field for exclusive terminal access by @jdx in [#8491](#8491) - add header comment to generated lockfiles by @ivy in [#8481](#8481) - runtime musl/glibc detection for correct libc variant selection by @jdx in [#8490](#8490) ### 🐛 Bug Fixes - **(github)** use registry platform options during install by @jdx in [#8492](#8492) - **(http)** store tool opts as native TOML to fix platform switching by @jdx in [#8448](#8448) - **(installer)** error if MISE_INSTALL_PATH is a directory by @jdx in [#8468](#8468) - **(prepare)** resolve sources/outputs relative to `dir` when set by @jdx in [#8472](#8472) - **(ruby)** fetch precompiled binary by release tag instead of listing all releases by @jdx in [#8488](#8488) - **(schema)** support structured objects in task depends by @risu729 in [#8463](#8463) - **(task)** replace println!/eprintln! with calm_io in task output macros by @vmaleze in [#8485](#8485) - handle scoped npm package names without backend prefix by @jdx in [#8477](#8477) ### 📦️ Dependency Updates - update ghcr.io/jdx/mise:copr docker digest to c485c4c by @renovate[bot] in [#8484](#8484) - update ghcr.io/jdx/mise:alpine docker digest to 8118bc7 by @renovate[bot] in [#8483](#8483) ### 📦 Registry - disable sd version test by @jdx in [#8489](#8489) ### New Contributors - @ivy made their first contribution in [#8481](#8481) - @iki made their first contribution in [#8424](#8424) ## 📦 Aqua Registry Updates #### New Packages (5) - [`datadog-labs/pup`](https://github.com/datadog-labs/pup) - [`k1LoW/mo`](https://github.com/k1LoW/mo) - [`rtk-ai/rtk`](https://github.com/rtk-ai/rtk) - [`suzuki-shunsuke/docfresh`](https://github.com/suzuki-shunsuke/docfresh) - [`yashikota/exiftool-go`](https://github.com/yashikota/exiftool-go) #### Updated Packages (6) - [`cloudflare/cloudflared`](https://github.com/cloudflare/cloudflared) - [`mozilla/sccache`](https://github.com/mozilla/sccache) - [`owenlamont/ryl`](https://github.com/owenlamont/ryl) - [`spinel-coop/rv`](https://github.com/spinel-coop/rv) - [`technicalpickles/envsense`](https://github.com/technicalpickles/envsense) - [`weaviate/weaviate`](https://github.com/weaviate/weaviate)


Summary
[tool.opts]) in.mise-installs.tomlinstead of encoding them inside brackets in thefullstringparse_tool_options()to try TOML inline-table parsing first, correctly handling nested structures likeplatforms={linux-x64={url="..."}}split_bracketed_opts()/strip_opts()helpers andEPHEMERAL_OPT_KEYSconstant to reduce duplicationfullare automatically migrated on readProblem
When a tool is installed with a single
urland later switched to platform-specific URLs ([tools."http:X".platforms]), the cached options in the install manifest were mangled during round-tripping. Options were encoded as a string inside brackets in the manifest'sfullfield, but nested TOML structures were destroyed by comma-based parsing and re-serialization.Before:
After:
Fixes discussion #7034.
Test plan
test_http_platform_switchvalidates the full scenario (single URL → platform-specific URLs)test_httpe2e tests passcargo testpasses🤖 Generated with Claude Code
Note
Medium Risk
Touches core tool option parsing and install-state persistence/migration; bugs could affect installs/lockfiles across multiple backends, though changes are largely mechanical with added regression coverage.
Overview
Fixes a regression where switching an
http:tool from a singleurlto platform-specificplatforms.*.urlcould fail due to stale/mangled cached options shadowing new config values.This changes
ToolVersionOptionsto store values astoml::Value(preserving nested tables/arrays), updates option parsing to prefer TOML inline-table parsing, and adjusts backends/callers to read string values viaget()/opts_as_strings().Install state now writes
.mise-installs.tomlwithfullwithout bracketed opts and persists non-ephemeral options in a dedicatedoptsmap, automatically migrating legacyfull[...]entries on read. Adds an e2e regression test for the platform-switch scenario and updates related unit tests/snapshots.Written by Cursor Bugbot for commit eabd6e9. This will update automatically on new commits. Configure here.