Skip to content

fix(env): parse --env=VALUE and -E=VALUE flag forms correctly#8889

Merged
jdx merged 5 commits intomainfrom
fix/env-equals-sign-parsing
Apr 4, 2026
Merged

fix(env): parse --env=VALUE and -E=VALUE flag forms correctly#8889
jdx merged 5 commits intomainfrom
fix/env-equals-sign-parsing

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Apr 4, 2026

Summary

  • Fix --env=production and -E=production (equals-sign form) being silently ignored, causing fallback to the default environment from .miserc.toml
  • The environment() function in src/env.rs parsed args using windows(2) which only matched space-separated flag/value pairs (--env production), not the =-joined form
  • Now handles both --env production and --env=production (and likewise -E, -P, --profile)

Fixes #8883

Test plan

  • mise --env=production env produces same output as mise --env production env
  • mise -E=production env produces same output as mise -E production env
  • mise --env production env still works as before
  • mise -E production env still works as before
  • Comma-separated values work: mise --env=dev,staging env

🤖 Generated with Claude Code


Note

Medium Risk
Adjusts CLI argument parsing for environment selection, which can change which config/environment is applied at runtime; scope is localized and covered by new e2e tests.

Overview
Fixes environment/profile flag parsing so --env=dev, -E=dev, --profile=dev, and -P=dev are recognized (in addition to space-separated forms), including comma-separated multi-env values.

Updates environment() scanning to stop treating flags after mise run <task> as global env selectors, and adds an e2e test validating equals-sign, space-separated, comma-separated, and mise run ordering behavior.

Reviewed by Cursor Bugbot for commit 5c3df3b. Bugbot is set up for automated code reviews on this repo. Configure here.

The environment() function only handled space-separated flag/value pairs
(e.g., `--env production`) but silently ignored the equals-sign form
(e.g., `--env=production`), causing it to fall through to the default
environment from .miserc.toml.

Fixes #8883

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the argument parsing logic in src/env.rs to support both space-separated (e.g., --env production) and equals-separated (e.g., --env=production) flags. A logic flaw was identified where space-separated values are not skipped, potentially leading to double-processing of arguments. A more efficient implementation using an iterator and split_once('=') was suggested to resolve this and simplify the code.

src/env.rs Outdated
Comment on lines +649 to +662
let arg_prefixes: Vec<String> = arg_defs.iter().map(|a| format!("{a}=")).collect();
// Try to get from command line args first
args.windows(2)
.take_while(|window| !window.iter().any(|a| a == "--"))
.filter_map(|window| {
if arg_defs.contains(&*window[0]) {
Some(window[1].clone())
} else {
None
// Handles both `--env production` (separate args) and `--env=production` (joined with =)
let mut values = Vec::new();
let args_before_dashdash: Vec<_> = args.iter().take_while(|a| a.as_str() != "--").collect();
for (i, arg) in args_before_dashdash.iter().enumerate() {
if let Some(prefix) = arg_prefixes.iter().find(|p| arg.starts_with(p.as_str())) {
values.push(arg[prefix.len()..].to_string());
} else if arg_defs.contains(arg.as_str()) {
if let Some(next) = args_before_dashdash.get(i + 1) {
values.push(next.to_string());
}
})
}
}
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.

high

The current implementation of the argument parsing loop has a logic flaw: it does not skip the next argument when it is consumed as a value for a space-separated flag (e.g., --env production). This causes the value to be processed again as a potential flag in the next iteration. For example, with mise --env --env=prod, the current code would add both --env=prod and prod to the values list.

Additionally, the logic can be simplified and made more efficient by using an iterator and split_once('='), which avoids the need to pre-allocate arg_prefixes and the intermediate args_before_dashdash vector.

Suggested change
let arg_prefixes: Vec<String> = arg_defs.iter().map(|a| format!("{a}=")).collect();
// Try to get from command line args first
args.windows(2)
.take_while(|window| !window.iter().any(|a| a == "--"))
.filter_map(|window| {
if arg_defs.contains(&*window[0]) {
Some(window[1].clone())
} else {
None
// Handles both `--env production` (separate args) and `--env=production` (joined with =)
let mut values = Vec::new();
let args_before_dashdash: Vec<_> = args.iter().take_while(|a| a.as_str() != "--").collect();
for (i, arg) in args_before_dashdash.iter().enumerate() {
if let Some(prefix) = arg_prefixes.iter().find(|p| arg.starts_with(p.as_str())) {
values.push(arg[prefix.len()..].to_string());
} else if arg_defs.contains(arg.as_str()) {
if let Some(next) = args_before_dashdash.get(i + 1) {
values.push(next.to_string());
}
})
}
}
let mut values = Vec::new();
let mut it = args.iter().take_while(|a| a.as_str() != "--");
while let Some(arg) = it.next() {
if let Some((flag, value)) = arg.split_once('=') {
if arg_defs.contains(flag) {
values.push(value.to_string());
}
} else if arg_defs.contains(arg.as_str()) {
if let Some(next) = it.next() {
values.push(next.to_string());
}
}
}
values

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 4, 2026

Greptile Summary

This PR fixes a silent bug where --env=VALUE and -E=VALUE (equals-sign forms) were ignored by environment() in src/env.rs, causing fallback to the default environment. The root cause was that windows(2) only matched space-separated flag/value pairs.

The fix replaces the windows(2) approach with an explicit iterator loop that handles both forms, and adds an in_run_subcommand guard so that -E/--env appearing after the task name in mise run <task> is correctly treated as a task argument rather than a global flag.

Key changes:

  • src/env.rs — new iterator-based arg scanner handles --env=VALUE, -E=VALUE, --profile=VALUE, -P=VALUE, comma-separated values, and the mise run positional-arg boundary correctly.
  • e2e/cli/test_env_flag_forms — comprehensive new test covering the fixed forms, regression cases for space-separated forms, and mise run interaction — however line 53 contains a typo (mise run --env=dev run show-env has a spurious extra run that will cause the test to fail since no task named run is defined in the fixture).

Confidence Score: 4/5

Safe to merge after fixing the typo on line 53 of the e2e test — the core env.rs logic is correct and well-reasoned.

The src/env.rs implementation is sound: it correctly handles all four flag forms, comma-separated values, and the mise run positional-arg boundary. One P1 defect exists in the new e2e test — line 53 uses mise run --env=dev run show-env (with a spurious extra run), which will fail at runtime because the task named run does not exist in the fixture. This test would either fail CI or pass only by accident; it needs a one-word fix before merging.

e2e/cli/test_env_flag_forms line 53 — spurious extra run in the command string causes the test to attempt running a non-existent task.

Important Files Changed

Filename Overview
src/env.rs Replaces windows(2)-based arg scanning with an explicit iterator loop that handles both space-separated (--env prod) and equals-joined (--env=prod) forms; adds in_run_subcommand guard to stop scanning after the task name in mise run.
e2e/cli/test_env_flag_forms New e2e test covering equals-sign, space-separated, comma-separated, and run-subcommand interaction forms — but line 53 has mise run --env=dev run show-env (duplicate run) which will fail because no task named run exists in the test fixture.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["environment(args)"] --> B{IS_RUNNING_AS_SHIM?}
    B -- Yes --> C[return vec! empty]
    B -- No --> D[iter over args, stop at --]
    D --> E{next arg?}
    E -- None --> F[collect values]
    E -- Some arg --> G{arg contains '='?}
    G -- Yes --> H{flag part in arg_defs?}
    H -- Yes --> I[push value part] --> E
    H -- No --> E
    G -- No --> J{arg in arg_defs?}
    J -- Yes --> K[it.next as next_arg
push next_arg] --> E
    J -- No --> L{arg starts with '-'?}
    L -- Yes --> E
    L -- No --> M{in_run_subcommand?}
    M -- Yes --> N[BREAK]
    M -- No --> O{arg in run_subcommands?}
    O -- Yes --> P[in_run_subcommand = true] --> E
    O -- No --> E
    F --> Q[flat_map: split each value on comma]
    Q --> R{from_args empty?}
    R -- No --> S[return from_args]
    R -- Yes --> T[check MISE_ENV / MISE_PROFILE / .miserc.toml]
Loading

Fix All in Claude Code

Reviews (4): Last reviewed commit: "refactor(env): simplify flag parsing wit..." | Re-trigger Greptile

autofix-ci bot and others added 3 commits April 4, 2026 13:33
…rated values

When parsing `--env production`, the value arg was revisited on the next
loop iteration and could be double-processed if it happened to match a
flag name (e.g., `--env -E`).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When parsing raw args for MISE_ENV, the environment() function scanned
all args before `--`. This meant `mise run show-env -E dev` incorrectly
set the environment to "dev", even though -E after the task name is a
task argument, not a global flag.

Now stops scanning for env flags once a positional arg (the task name)
is seen after `run`/`r`. Also adds comprehensive e2e tests for all -E
flag forms (=, space-separated, commas, before/after run).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2cb67b9. Configure here.

}
if run_subcommands.contains(arg.as_str()) {
in_run_subcommand = true;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

False run-subcommand detection from other flags' values

Low Severity

The in_run_subcommand heuristic doesn't account for values of other global flags being misidentified as positional args. Since environment() only knows about --env/-E/--profile/-P, the values of other value-taking flags like --cd/-C fall through to the positional-arg branch. If that value happens to be "run" or "r" (e.g., mise -C run env -E dev), in_run_subcommand is falsely set to true, causing subsequent -E flags to be silently ignored. The old windows(2) code didn't have this issue since it had no in_run_subcommand guard. Having a directory named run is plausible in real projects.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 2cb67b9. Configure here.

Use an iterator with split_once('=') instead of index-based loop with
arg_prefixes and skip_next. The iterator naturally handles consumed
values via it.next(), and split_once cleanly handles the =VALUE form.
Also adds --profile=VALUE and -P=VALUE e2e test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 4, 2026

Hyperfine Performance

xtasks/test/perf

Command mise-2026.4.3 mise Variance
install (cached) 116ms 116ms +0%
ls (cached) 66ms 66ms +0%
bin-paths (cached) 68ms 68ms +0%
task-ls (cached) 719ms ⚠️ 2636ms -72%

⚠️ Warning: task-ls cached performance variance is -72%

@jdx jdx merged commit c0f6e0f into main Apr 4, 2026
48 of 52 checks passed
@jdx jdx deleted the fix/env-equals-sign-parsing branch April 4, 2026 14:54
jdx pushed a commit that referenced this pull request Apr 5, 2026
### 🚀 Features

- **(ci)** auto-convert external PRs to draft mode by @jdx in
[#8896](#8896)
- **(deps)** add `depends` field for user-specified tool dependencies by
@cprecioso in [#8776](#8776)
- **(dotnet)** support runtime-only installs by @fragon10 in
[#8524](#8524)
- **(npm)** apply install_before to transitive dependencies by @risu729
in [#8851](#8851)
- **(task)** allow passing arguments to task dependencies via
{{usage.*}} templates by @jdx in
[#8893](#8893)
- add options field to BackendListVersionsCtx by @esteve in
[#8875](#8875)

### 🐛 Bug Fixes

- **(backend)** filter PEP 440 .dev versions in fuzzy version matching
by @richardthe3rd in [#8849](#8849)
- **(ci)** update COPR BuildRequires rust version to match MSRV 1.88 by
@jdx in [#8911](#8911)
- **(ci)** add Ruby build dependencies to e2e Docker image by @jdx in
[#8910](#8910)
- **(ci)** add missing build dependencies to e2e Docker image by @jdx in
[#8912](#8912)
- **(ci)** add missing build dependencies to e2e Docker image by @jdx in
[#8914](#8914)
- **(ci)** use Node 24 LTS for corepack e2e test by @jdx in
[#8915](#8915)
- **(ci)** add libxml2 and pkg-config to e2e Docker image by @jdx in
[#8917](#8917)
- **(ci)** add libxml2-dev to e2e image and disable Swift SPM tests by
@jdx in [#8918](#8918)
- **(docs)** use sans-serif font for badges by @jdx in
[#8887](#8887)
- **(env)** parse --env=VALUE and -E=VALUE flag forms correctly by @jdx
in [#8889](#8889)
- **(exec)** use i64::from() for seccomp syscall numbers to survive
autofix by @jdx in [#8882](#8882)
- **(github)** preserve tool options like filter_bins when version
specified via CLI by @jdx in
[#8888](#8888)
- **(github)** use alias-specific options when tool_alias has its own
config by @jdx in [#8892](#8892)
- **(install)** add locked_verify_provenance setting and detect github
attestations at lock time by @jdx in
[#8901](#8901)
- **(lock)** prune stale version entries during filtered `mise lock
<tool>` runs by @altendky in
[#8599](#8599)
- **(python)** use lockfile URL for precompiled installs by @hehaoqian
in [#8750](#8750)
- **(release)** verify all build targets succeed before releasing by
@jdx in [#8886](#8886)
- **(ruby)** support build revisions for precompiled binaries in
mise.lock by @jdx in [#8900](#8900)
- **(swift)** fall back to Ubuntu 24.04 for unsupported Ubuntu versions
by @jdx in [#8916](#8916)
- **(zsh)** avoid duplicate trust warning after cd by @timothysparg in
[#8898](#8898)
- update flake.lock and add fix for rust-bindgen to default.nix by
@esteve in [#8874](#8874)
- when direnv diff is empty, do not try to parse it by @yaleman in
[#8857](#8857)
- skip trust check for plain .tool-versions in task list by @dportalesr
in [#8876](#8876)

### 🚜 Refactor

- **(go)** rename go_* settings to go.* namespace by @jdbruijn in
[#8598](#8598)

### 📚 Documentation

- **(tasks)** clarify task_config.includes behavior by @risu729 in
[#8905](#8905)

### 🧪 Testing

- **(ci)** run e2e tests inside Docker containers by @jdx in
[#8899](#8899)

### 📦️ Dependency Updates

- bump ubi from 0.8 to 0.9 by @jdx in
[#8906](#8906)
- bump zip from 3 to 8 by @jdx in
[#8908](#8908)
- update lockfile deps (hold back rattler) by @jdx in
[#8909](#8909)
- update bun.lock by @jdx in
[#8913](#8913)

### 📦 Registry

- add turso
([github:tursodatabase/turso-cli](https://github.com/tursodatabase/turso-cli))
by @kenn in [#8884](#8884)
- remove carp test by @jdx in
[#8894](#8894)

### Chore

- **(ci)** add workflow to warn PRs modifying vendored aqua-registry by
@jdx in [#8897](#8897)
- **(ci)** use github.token for draft conversion in auto-draft workflow
by @jdx in [#8903](#8903)
- remove deprecated settings older than 12 months by @jdx in
[#8904](#8904)

### New Contributors

- @dportalesr made their first contribution in
[#8876](#8876)
- @timothysparg made their first contribution in
[#8898](#8898)
- @hehaoqian made their first contribution in
[#8750](#8750)
- @jdbruijn made their first contribution in
[#8598](#8598)
- @cprecioso made their first contribution in
[#8776](#8776)
- @yaleman made their first contribution in
[#8857](#8857)
- @kenn made their first contribution in
[#8884](#8884)
- @fragon10 made their first contribution in
[#8524](#8524)

## 📦 Aqua Registry Updates

#### New Packages (6)

- [`ahkohd/oyo`](https://github.com/ahkohd/oyo)
- [`bellicose100xp/jiq`](https://github.com/bellicose100xp/jiq)
- [`kurama/dealve-tui`](https://github.com/kurama/dealve-tui)
- [`micahkepe/jsongrep`](https://github.com/micahkepe/jsongrep)
- [`textfuel/lazyjira`](https://github.com/textfuel/lazyjira)
- [`ubugeeei/vize`](https://github.com/ubugeeei/vize)

#### Updated Packages (1)

- [`sigstore/cosign`](https://github.com/sigstore/cosign)
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.

1 participant