Skip to content

feat(backend): add environment variable override for tool backends#6456

Merged
jdx merged 6 commits intomainfrom
feat/backend-env-vars
Sep 28, 2025
Merged

feat(backend): add environment variable override for tool backends#6456
jdx merged 6 commits intomainfrom
feat/backend-env-vars

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Sep 28, 2025

Summary

  • Add support for MISE_BACKENDS_* environment variables to override tool backends
  • Works for both registry and non-registry tools
  • Uses LazyLock with HashMap cache for efficient caching (avoids memory leaks)
  • Uses heck::ToShoutySnakeCase for consistent case conversion

Motivation

The MISE_BACKENDS_* environment variable pattern was not working but should have been supported. This PR implements that functionality, allowing users to override the backend for any tool via environment variables.

Implementation Details

  1. Registry tools: Modified RegistryTool::backends() in src/registry.rs to check for environment variable overrides
  2. Non-registry tools: Added environment variable checking in BackendArg::full() to support custom tools
  3. Memory management: Uses LazyLock<Mutex<HashMap>> for caching instead of Box::leak
  4. Case conversion: Uses heck::ToShoutySnakeCase for consistent environment variable naming

Test plan

  • Added unit test test_backend_env_override to verify env var override functionality
  • Added e2e test test_backend_env_override for registry tools (graphite example)
  • Added e2e test test_backend_env_override_non_registry for non-registry tools
  • Manually tested with various backends (GitHub, UBI, etc.)

Example usage

# Override graphite to use GitHub backend instead of npm
export MISE_BACKENDS_GRAPHITE='github:withgraphite/homebrew-tap[bin=gt]'
mise install graphite@latest

# Override a custom tool not in the registry
export MISE_BACKENDS_MYCLI='github:BurntSushi/ripgrep[bin=rg]'
mise install mycli@latest

# Works with any backend type
export MISE_BACKENDS_NODE='asdf:mise-plugins/mise-node'
mise install node@latest

Breaking changes

None - this is a new feature that doesn't affect existing behavior.

🤖 Generated with Claude Code


Note

Add support for MISE_BACKENDS_<TOOL> to override backends (registry and non‑registry), with caching, docs, and tests.

  • Backend:
    • Add env override in BackendArg::full() using MISE_BACKENDS_<TOOL> (SHOUTY_SNAKE_CASE via ToShoutySnakeCase).
    • Add env override in RegistryTool::backends() with cached lookups (Lazy<Mutex<HashMap>>).
  • Docs:
    • Update backend selection priority to include env var override in docs/dev-tools/backend_architecture.md.
    • Add "Environment Variable Overrides" section and examples in docs/registry.md and backend architecture docs.
  • Tests:
    • New unit test test_backend_env_override in src/registry.rs.
    • New e2e script e2e/backend/test_backend_env_override covering non‑registry tool override.

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

Allow overriding tool backends via MISE_BACKENDS_* environment variables.
For example, MISE_BACKENDS_GRAPHITE='github:withgraphite/homebrew-tap[exe=gt]'
will override the default backend for graphite.

This enables users to dynamically select backends without modifying
registry.toml or configuration files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings September 28, 2025 10:54
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

Adds support for MISE_BACKENDS_* environment variables to override tool backends dynamically without modifying configuration files. This enables users to specify alternative backends for tools at runtime.

  • Implements environment variable parsing in RegistryTool::backends() to check for overrides
  • Adds comprehensive test coverage with both unit and end-to-end tests
  • Uses memory leak pattern to handle static lifetime requirements for backend strings

Reviewed Changes

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

File Description
src/registry.rs Implements env var override logic and adds unit test for functionality
e2e/backend/test_backend_env_override Adds end-to-end test validating full installation flow with graphite tool

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines 36 to +49
pub fn backends(&self) -> Vec<&'static str> {
// Check for environment variable override first
// e.g., MISE_BACKENDS_GRAPHITE='github:withgraphite/homebrew-tap[exe=gt]'
let env_key = format!(
"MISE_BACKENDS_{}",
self.short.to_uppercase().replace('-', "_")
);
if let Ok(env_value) = env::var(&env_key) {
// Return the environment variable value as a single backend
// We need to leak the string to get a 'static lifetime since the registry expects it
let leaked = Box::leak(env_value.into_boxed_str());
return vec![leaked];
}
Copy link

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

This intentional memory leak could accumulate over time if the same tool's backend is overridden multiple times during the program's lifetime. Consider using a static cache or once_cell::sync::Lazy to store and reuse leaked strings for the same environment variable values.

Suggested change
pub fn backends(&self) -> Vec<&'static str> {
// Check for environment variable override first
// e.g., MISE_BACKENDS_GRAPHITE='github:withgraphite/homebrew-tap[exe=gt]'
let env_key = format!(
"MISE_BACKENDS_{}",
self.short.to_uppercase().replace('-', "_")
);
if let Ok(env_value) = env::var(&env_key) {
// Return the environment variable value as a single backend
// We need to leak the string to get a 'static lifetime since the registry expects it
let leaked = Box::leak(env_value.into_boxed_str());
return vec![leaked];
}

Copilot uses AI. Check for mistakes.
Comment on lines +202 to +214
// SAFETY: This is safe in a test environment
unsafe {
env::set_var("MISE_BACKENDS_NODE", "test:backend");
}
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");

// Clean up
// SAFETY: This is safe in a test environment
unsafe {
env::remove_var("MISE_BACKENDS_NODE");
}
Copy link

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

Using unsafe blocks for env::set_var is unnecessary as this function is not unsafe. The env::set_var and env::remove_var functions are safe to call and don't require unsafe blocks.

Suggested change
// SAFETY: This is safe in a test environment
unsafe {
env::set_var("MISE_BACKENDS_NODE", "test:backend");
}
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");
// Clean up
// SAFETY: This is safe in a test environment
unsafe {
env::remove_var("MISE_BACKENDS_NODE");
}
// Set environment variable for test
env::set_var("MISE_BACKENDS_NODE", "test:backend");
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");
// Clean up
// Clean up environment variable after test
env::remove_var("MISE_BACKENDS_NODE");

Copilot uses AI. Check for mistakes.
Comment on lines +202 to +214
// SAFETY: This is safe in a test environment
unsafe {
env::set_var("MISE_BACKENDS_NODE", "test:backend");
}
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");

// Clean up
// SAFETY: This is safe in a test environment
unsafe {
env::remove_var("MISE_BACKENDS_NODE");
}
Copy link

Copilot AI Sep 28, 2025

Choose a reason for hiding this comment

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

Using unsafe blocks for env::remove_var is unnecessary as this function is not unsafe. The env::set_var and env::remove_var functions are safe to call and don't require unsafe blocks.

Suggested change
// SAFETY: This is safe in a test environment
unsafe {
env::set_var("MISE_BACKENDS_NODE", "test:backend");
}
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");
// Clean up
// SAFETY: This is safe in a test environment
unsafe {
env::remove_var("MISE_BACKENDS_NODE");
}
env::set_var("MISE_BACKENDS_NODE", "test:backend");
let overridden_backends = tool.backends();
assert_eq!(overridden_backends.len(), 1);
assert_eq!(overridden_backends[0], "test:backend");
// Clean up
env::remove_var("MISE_BACKENDS_NODE");

Copilot uses AI. Check for mistakes.
cursor[bot]

This comment was marked as outdated.

jdx and others added 2 commits September 28, 2025 10:59
- Use LazyLock with HashMap cache instead of Box::leak to avoid memory leaks
- Use heck::ToShoutySnakeCase for consistent case conversion
- Support MISE_BACKENDS_* for non-registry tools in BackendArg::full()
- Add e2e test for non-registry tool backend overrides

This allows users to override backends for any tool, even those not in
the registry, enabling maximum flexibility for custom tool configurations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use [bin=just] and [bin=rg] instead of [exe=...] for consistency
with mise's backend option syntax.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jdx jdx force-pushed the feat/backend-env-vars branch from 355d44e to f8a827f Compare September 28, 2025 11:06
jdx and others added 3 commits September 28, 2025 11:11
Document the new environment variable override feature that allows users
to dynamically override tool backends using MISE_BACKENDS_<TOOL> pattern.

Added documentation in:
- backend_architecture.md: Detailed explanation with examples
- registry.md: Quick reference for users

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Rearranged the order of backend selection criteria in the documentation to clarify the priority of using an explicit backend over environment variable overrides. Added an example for using the vfox backend for PHP, enhancing the guidance for users on dynamic backend selection.

Changes made in:
- backend_architecture.md: Updated priority list and added new example for PHP backend.
…usage

Revised examples in the registry documentation to demonstrate the use of environment variables for backend overrides. Specifically, added an example for using the vfox backend for PHP, enhancing clarity for users on dynamic backend configuration.

Changes made in:
- registry.md: Updated examples for backend environment variable usage.
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.9.21 x -- echo 20.2 ± 0.6 18.8 23.1 1.02 ± 0.04
mise x -- echo 19.8 ± 0.5 19.2 25.0 1.00

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.9.21 env 18.6 ± 0.3 18.0 20.2 1.00
mise env 19.1 ± 0.3 18.6 20.4 1.03 ± 0.02

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.9.21 hook-env 19.7 ± 0.7 18.3 27.8 1.00
mise hook-env 21.2 ± 0.8 19.3 26.3 1.07 ± 0.06

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2025.9.21 ls 17.8 ± 0.5 16.5 19.2 1.00
mise ls 18.9 ± 0.7 17.2 20.8 1.06 ± 0.05

xtasks/test/perf

Command mise-2025.9.21 mise Variance
install (cached) 170ms ✅ 106ms +60%
ls (cached) 64ms 64ms +0%
bin-paths (cached) 70ms 70ms +0%
task-ls (cached) 482ms 482ms +0%

✅ Performance improvement: install cached is 60%

@jdx jdx merged commit 27f07bf into main Sep 28, 2025
26 checks passed
@jdx jdx deleted the feat/backend-env-vars branch September 28, 2025 13:36
@jdx jdx mentioned this pull request Sep 28, 2025
jdx added a commit that referenced this pull request Sep 28, 2025
### 📦 Registry

- re-enable tests by @risu729 in
[#6454](#6454)
- restore comments and tests by @risu729 in
[#6378](#6378)
- add github backend for graphite by @jdx in
[#6455](#6455)

### 🚀 Features

- **(backend)** add environment variable override for tool backends by
@jdx in [#6456](#6456)
- add a http_retries setting to define number of retry attempts by
@roele in [#6444](#6444)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> Bump version to 2025.9.22 with updated completions cache keys,
registry tweak for CycloneDX assets, doc star count update, and
usage-cli tool bump.
> 
> - **Release/versioning**
> - Bump `mise` to `2025.9.22` in `Cargo.toml`, `Cargo.lock`,
`default.nix`, `packaging/rpm/mise.spec`, and update version in
`README.md`.
>   - Add `CHANGELOG.md` entry for `2025.9.22`.
> - **Completions**
> - Update cached usage spec keys in `completions/_mise`,
`completions/mise.bash`, and `completions/mise.fish` to `2025_9_22`.
> - **Registry**
> - Adjust CycloneDX `cyclonedx-cli` assets in
`crates/.../cyclonedx-cli/registry.yaml` (remove linux musl overrides in
version overrides).
> - **Tooling/lockfile**
>   - Bump `cargo:usage-cli` to `2.3.0` in `mise.lock`.
> - **Docs**
>   - Update stars count in `docs/.vitepress/stars.data.ts` to `19.6k`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5b3be09. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: mise-en-dev <release@mise.jdx.dev>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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