Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/cli/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,18 @@ Do not load any config files

Can also use `MISE_NO_CONFIG=1`

### `--no-env`

Do not load environment variables from config files

Can also use `MISE_NO_ENV=1`

### `--no-hooks`

Do not execute hooks from config files

Can also use `MISE_NO_HOOKS=1`

### `--output <OUTPUT>`

## Subcommands
Expand Down
34 changes: 34 additions & 0 deletions e2e/cli/test_no_env
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

# Test that --no-env flag prevents loading environment variables from config files

# Create a config file with environment variables
cat <<EOF >mise.toml
[env]
TEST_FOO_VAR = "FOO"
EOF

# Test if it works with env
assert_contains "mise env" "TEST_FOO_VAR"
assert_not_contains "mise --no-env env" "TEST_FOO_VAR"

# Test with exec as well
assert_contains "mise exec -- env | grep TEST_FOO_VAR" "TEST_FOO_VAR=FOO"
assert_not_contains "mise --no-env exec -- env" "TEST_FOO_VAR"

# Test that MISE_NO_ENV=1 environment variable works the same way
assert_not_contains "MISE_NO_ENV=1 mise env" "TEST_FOO_VAR"
assert_not_contains "MISE_NO_ENV=1 mise exec -- env" "TEST_FOO_VAR"

cat <<EOF >mise.toml
[settings]
no_env = true

[env]
TEST_FOO_VAR = "FOO"
EOF

assert_not_contains "mise env" "TEST_FOO_VAR"

# Cleanup
rm -f mise.toml
29 changes: 29 additions & 0 deletions e2e/cli/test_no_hooks
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
[settings]
experimental = true
[tools]
dummy = 'latest'
[hooks]
preinstall = 'echo PREINSTALL'
EOF

assert_contains "mise i dummy 2>&1" "PREINSTALL"
assert_not_contains "mise --no-hooks i dummy 2>&1" "PREINSTALL"

assert_not_contains "MISE_NO_HOOKS=1 mise --no-hooks i dummy 2>&1" "PREINSTALL"

cat <<EOF >mise.toml
[settings]
experimental = true
no_hooks = true
[tools]
dummy = 'latest'
[hooks]
preinstall = 'echo PREINSTALL'
EOF

assert_not_contains "mise i dummy 2>&1" "PREINSTALL"

rm -f mise.toml
10 changes: 10 additions & 0 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ Do not load any config files

Can also use `MISE_NO_CONFIG=1`
.TP
\fB\-\-no\-env\fR
Do not load environment variables from config files

Can also use `MISE_NO_ENV=1`
.TP
\fB\-\-no\-hooks\fR
Do not execute hooks from config files

Can also use `MISE_NO_HOOKS=1`
.TP
\fB\-\-no\-timings\fR
Hides elapsed time after each task completes

Expand Down
6 changes: 6 additions & 0 deletions mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ flag --log-level hide=#true global=#true {
flag --no-config help="Do not load any config files" {
long_help "Do not load any config files\n\nCan also use `MISE_NO_CONFIG=1`"
}
flag --no-env help="Do not load environment variables from config files" {
long_help "Do not load environment variables from config files\n\nCan also use `MISE_NO_ENV=1`"
}
flag --no-hooks help="Do not execute hooks from config files" {
long_help "Do not execute hooks from config files\n\nCan also use `MISE_NO_HOOKS=1`"
}
flag --no-timings help="Hides elapsed time after each task completes" hide=#true {
long_help "Hides elapsed time after each task completes\n\nDefault to always hide with `MISE_TASK_TIMINGS=0`"
}
Expand Down
8 changes: 8 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,14 @@
"description": "Path to the netrc file to use for HTTP Basic authentication.",
"type": "string"
},
"no_env": {
"description": "Do not load environment variables from config files.",
"type": "boolean"
},
"no_hooks": {
"description": "Do not execute hooks from config files.",
"type": "boolean"
},
"node": {
"type": "object",
"additionalProperties": false,
Expand Down
14 changes: 13 additions & 1 deletion settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -861,6 +861,18 @@ env = "MISE_NETRC_FILE"
optional = true
type = "Path"

[no_env]
description = "Do not load environment variables from config files."
env = "MISE_NO_ENV"
optional = true
type = "Bool"

[no_hooks]
description = "Do not execute hooks from config files."
env = "MISE_NO_HOOKS"
optional = true
type = "Bool"

[node.compile]
description = "Compile node from source."
env = "MISE_NODE_COMPILE"
Expand Down Expand Up @@ -1752,7 +1764,7 @@ type = "Bool"
default = false
description = "Opt out of parsing task run scripts to infer the usage spec (arguments and flags). When enabled, mise will derive the usage spec only from the `usage` field, ignoring any `arg()`, `option()`, or `flag()` templates used in run scripts. This can restore previous behavior and avoid the extra template pass over run scripts when collecting specs."
docs = """
When enabled, `arg()`, `option()`, and `flag()` Tera functions in run scripts will not contribute
When enabled, `arg()`, `option()`, and `flag()` Tera functions in run scripts will not contribute
to the task's usage spec—only the explicit `usage` field is used.

This is useful for:
Expand Down
10 changes: 10 additions & 0 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ pub struct Cli {
/// Can also use `MISE_NO_CONFIG=1`
#[clap(long)]
pub no_config: bool,
/// Do not load environment variables from config files
///
/// Can also use `MISE_NO_ENV=1`
#[clap(long)]
pub no_env: bool,
/// Do not execute hooks from config files
///
/// Can also use `MISE_NO_HOOKS=1`
#[clap(long)]
pub no_hooks: bool,
/// Hides elapsed time after each task completes
///
/// Default to always hide with `MISE_TASK_TIMINGS=0`
Expand Down
3 changes: 3 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,9 @@ impl Config {
}

async fn load_env(self: &Arc<Self>) -> Result<EnvResults> {
if Settings::no_env() || Settings::get().no_env.unwrap_or(false) {
return Ok(EnvResults::default());
}
time!("load_env start");
let entries = self
.config_files
Expand Down
22 changes: 22 additions & 0 deletions src/config/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,28 @@ impl Settings {
.take_while(|a| *a != "--")
.any(|a| a == "--no-config")
}

pub fn no_env() -> bool {
*env::MISE_NO_ENV
|| !*crate::env::IS_RUNNING_AS_SHIM
&& env::ARGS
.read()
.unwrap()
.iter()
.take_while(|a| *a != "--")
.any(|a| a == "--no-env")
}

pub fn no_hooks() -> bool {
*env::MISE_NO_HOOKS
|| !*crate::env::IS_RUNNING_AS_SHIM
&& env::ARGS
.read()
.unwrap()
.iter()
.take_while(|a| *a != "--")
.any(|a| a == "--no-hooks")
}
Comment thread
cursor[bot] marked this conversation as resolved.
}

impl Display for Settings {
Expand Down
2 changes: 2 additions & 0 deletions src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ pub static MISE_FRIENDLY_ERROR: Lazy<bool> = Lazy::new(|| {
pub static MISE_TOOL_STUB: Lazy<bool> =
Lazy::new(|| ARGS.read().unwrap().get(1).map(|s| s.as_str()) == Some("tool-stub"));
pub static MISE_NO_CONFIG: Lazy<bool> = Lazy::new(|| var_is_true("MISE_NO_CONFIG"));
pub static MISE_NO_ENV: Lazy<bool> = Lazy::new(|| var_is_true("MISE_NO_ENV"));
pub static MISE_NO_HOOKS: Lazy<bool> = Lazy::new(|| var_is_true("MISE_NO_HOOKS"));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

why is this not done in settings.toml?

Copy link
Copy Markdown
Contributor Author

@aacebedo aacebedo Jan 5, 2026

Choose a reason for hiding this comment

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

I was thinking that is shall be just CLI flags to be used dynamically.
if I want to disable it from config, I'll simply remove the hooks and the env directives.

However If I put it in the settings It could still work dynamically using the env variables.
Do you prefer I remove the CLI flags and put this in the config file then? Maybe it makes more sense as CLI flags are global to all commands anyway.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I think it should be both global flags and settings. If you put it into settings.toml it will be documented as a setting

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added the options in the settings file

pub static MISE_PROGRESS_TRACE: Lazy<bool> = Lazy::new(|| var_is_true("MISE_PROGRESS_TRACE"));
pub static MISE_CACHE_DIR: Lazy<PathBuf> =
Lazy::new(|| var_path("MISE_CACHE_DIR").unwrap_or_else(|| XDG_CACHE_HOME.join("mise")));
Expand Down
6 changes: 6 additions & 0 deletions src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub fn schedule_hook(hook: Hooks) {
}

pub async fn run_all_hooks(config: &Arc<Config>, ts: &Toolset, shell: &dyn Shell) {
if Settings::no_hooks() || Settings::get().no_hooks.unwrap_or(false) {
return;
}
let hooks = {
let mut mu = SCHEDULED_HOOKS.lock().unwrap();
mu.drain(..).collect::<Vec<_>>()
Expand Down Expand Up @@ -113,6 +116,9 @@ pub async fn run_one_hook_with_context(
shell: Option<&dyn Shell>,
installed_tools: Option<&[InstalledToolInfo]>,
) {
if Settings::no_hooks() || Settings::get().no_hooks.unwrap_or(false) {
return;
}
for (root, h) in all_hooks(config).await {
if hook != h.hook || (h.shell.is_some() && h.shell != shell.map(|s| s.to_string())) {
continue;
Expand Down
5 changes: 4 additions & 1 deletion src/toolset/toolset_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::sync::Arc;

use eyre::Result;

use crate::config::Config;
use crate::config::env_directive::{EnvResolveOptions, EnvResults, ToolsFilter};
use crate::config::{Config, Settings};
use crate::env::{PATH_KEY, WARN_ON_MISSING_REQUIRED_ENV};
use crate::env_diff::EnvMap;
use crate::path_env::PathEnv;
Expand Down Expand Up @@ -134,6 +134,9 @@ impl Toolset {
ctx: tera::Context,
env: &EnvMap,
) -> Result<EnvResults> {
if Settings::no_env() || Settings::get().no_env.unwrap_or(false) {
return Ok(EnvResults::default());
}
let entries = config
.config_files
.iter()
Expand Down
10 changes: 10 additions & 0 deletions xtasks/fig/src/mise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3844,6 +3844,16 @@ const completionSpec: Fig.Spec = {
description: "Do not load any config files",
isRepeatable: false,
},
{
name: "--no-env",
description: "Do not load environment variables from config files",
isRepeatable: false,
},
{
name: "--no-hooks",
description: "Do not execute hooks from config files",
isRepeatable: false,
},
{
name: "--output",
isRepeatable: false,
Expand Down
Loading