feat(set): add --stdin flag for multiline environment variables#8110
feat(set): add --stdin flag for multiline environment variables#8110
Conversation
Allow reading environment variable values from stdin for multiline or binary-safe input. This enables use cases like storing SSH keys or certificates via `cat key.pem | mise set --stdin MY_KEY`. Strips a single trailing newline to match `gh secret set` behavior. Closes #6878 Co-Authored-By: Claude Opus 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 enhances the 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.
Pull request overview
Adds a --stdin flag to mise set to support setting multiline environment variable values by reading from stdin, including trimming a single trailing newline and validating correct usage.
Changes:
- Added
--stdinCLI flag and implementation for reading value from stdin until EOF. - Prevented
--stdinfrom triggering “get” behavior and added usage validation/error paths. - Added E2E coverage for multiline input, trailing newline stripping, and invalid invocations.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/cli/set.rs | Implements --stdin flag behavior, validation, and help text examples |
| e2e/cli/test_set | Adds E2E tests for multiline stdin input, trailing newline stripping, and failure cases |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/cli/set.rs
Outdated
| /// Read the value from stdin (for multiline or binary-safe input) | ||
| /// | ||
| /// When using --stdin, provide a single key without a value. | ||
| /// The value will be read from stdin until EOF. |
There was a problem hiding this comment.
The help text claims “binary-safe input”, but the implementation reads into a String (UTF-8), which is not binary-safe and will fail on non-UTF-8 bytes. Either adjust the wording to match reality (e.g., “multiline / UTF-8 text input”) or change the implementation to read raw bytes and explicitly handle UTF-8 decoding/validation (and document what happens on invalid UTF-8).
| /// Read the value from stdin (for multiline or binary-safe input) | |
| /// | |
| /// When using --stdin, provide a single key without a value. | |
| /// The value will be read from stdin until EOF. | |
| /// Read the value from stdin (for multiline / UTF-8 text input) | |
| /// | |
| /// When using --stdin, provide a single key without a value. | |
| /// The value will be read from stdin until EOF and must be valid UTF-8. |
| let mut value = String::new(); | ||
| std::io::stdin().read_to_string(&mut value)?; |
There was a problem hiding this comment.
read_to_string requires valid UTF-8; if stdin contains non-UTF-8 bytes, this will error even though the flag help suggests binary-safe behavior. If binary input is truly a requirement, read into a Vec<u8> and then decide on a supported encoding/representation (or explicitly error with a clearer message that stdin must be UTF-8).
| let mut value = String::new(); | |
| std::io::stdin().read_to_string(&mut value)?; | |
| let mut buf = Vec::new(); | |
| std::io::stdin().read_to_end(&mut buf)?; | |
| let mut value = String::from_utf8(buf) | |
| .map_err(|e| anyhow::anyhow!("stdin must be valid UTF-8: {}", e))?; |
| // Read value from stdin if requested | ||
| if self.stdin { | ||
| if env_vars.len() != 1 { | ||
| bail!("--stdin requires exactly one environment variable key"); |
There was a problem hiding this comment.
The error message would be more actionable if it included the expected invocation format. Consider something like: "--stdin expects exactly one KEY (no KEY=VALUE). Usage: mise set --stdin KEY".
| bail!("--stdin requires exactly one environment variable key"); | |
| bail!("--stdin expects exactly one KEY (no KEY=VALUE). Usage: mise set --stdin KEY"); |
There was a problem hiding this comment.
Code Review
This pull request introduces a --stdin flag to mise set, allowing for multiline environment variables to be set from standard input. The changes include adding the new flag, implementing the logic to read from stdin, and stripping a single trailing newline to align with gh secret set's behavior. The implementation is well-tested with new e2e tests covering multiline values, newline stripping, and error cases. The feedback provided focuses on improving the readability of the newline stripping logic.
src/cli/set.rs
Outdated
| if value.ends_with('\n') { | ||
| value.pop(); | ||
| if value.ends_with('\r') { | ||
| value.pop(); | ||
| } | ||
| } |
There was a problem hiding this comment.
The logic for stripping the trailing newline is correct, but it could be made more explicit and readable. Using truncate with ends_with for both \r\n and \n makes the intent clearer and is just as efficient.
I'd suggest replacing this block with:
if value.ends_with("\r\n") {
value.truncate(value.len() - 2);
} else if value.ends_with('\n') {
value.truncate(value.len() - 1);
}if value.ends_with("\r\n") {
value.truncate(value.len() - 2);
} else if value.ends_with('\n') {
value.truncate(value.len() - 1);
}|
bugbot run |
- Remove "binary-safe" from doc comment since read_to_string requires UTF-8 (Copilot feedback) - Use explicit \r\n / \n check with truncate for clearer newline stripping logic (Gemini feedback) - Regenerate docs/completions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the stdin field after remove in the Set struct to maintain alphabetical ordering of long-only flags, and add requires = "env_vars" so --stdin without a key gives a clear error instead of silently listing env vars. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 x -- echo |
23.3 ± 0.5 | 22.2 | 28.9 | 1.00 |
mise x -- echo |
24.2 ± 0.5 | 22.9 | 30.1 | 1.04 ± 0.03 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 env |
22.8 ± 0.5 | 21.8 | 26.2 | 1.00 |
mise env |
23.5 ± 0.6 | 22.5 | 33.2 | 1.03 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 hook-env |
23.5 ± 0.4 | 22.5 | 25.9 | 1.00 |
mise hook-env |
24.5 ± 0.5 | 23.1 | 27.6 | 1.04 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 ls |
21.6 ± 0.5 | 20.5 | 28.0 | 1.00 |
mise ls |
22.6 ± 0.5 | 21.4 | 25.0 | 1.04 ± 0.03 |
xtasks/test/perf
| Command | mise-2026.2.9 | mise | Variance |
|---|---|---|---|
| install (cached) | 123ms | 124ms | +0% |
| ls (cached) | 76ms | 76ms | +0% |
| bin-paths (cached) | 81ms | 81ms | +0% |
| task-ls (cached) | 556ms | 557ms | +0% |
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [#8106](#8106) - **(env)** add `tools` variable to tera template context by @jdx in [#8108](#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [#8110](#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [#8087](#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [#8100](#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [#8109](#8109) - **(github)** skip v prefix for "latest" version by @jdx in [#8105](#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [#8084](#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [#8081](#8081) - **(registry)** add Linux support for tuist by @fortmarek in [#8102](#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [#8086](#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [#8099](#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [#8101](#8101) - SLSA for in-toto statement with no signatures by @gerhard in [#8094](#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [#8035](#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [#8112](#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [#8080](#8080) - add conda backends for 10 asdf-only tools by @jdx in [#8083](#8083) - added podman-tui by @tony-sol in [#8098](#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [#8111](#8111) ### New Contributors - @ctaintor made their first contribution in [#8106](#8106) - @rileychh made their first contribution in [#8112](#8112) - @fortmarek made their first contribution in [#8102](#8102) - @pose made their first contribution in [#8035](#8035) - @gerhard made their first contribution in [#8094](#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [#8106](#8106) - **(env)** add `tools` variable to tera template context by @jdx in [#8108](#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [#8110](#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [#8087](#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [#8100](#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [#8109](#8109) - **(github)** skip v prefix for "latest" version by @jdx in [#8105](#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [#8084](#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [#8081](#8081) - **(registry)** add Linux support for tuist by @fortmarek in [#8102](#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [#8086](#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [#8099](#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [#8101](#8101) - SLSA for in-toto statement with no signatures by @gerhard in [#8094](#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [#8035](#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [#8112](#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [#8080](#8080) - add conda backends for 10 asdf-only tools by @jdx in [#8083](#8083) - added podman-tui by @tony-sol in [#8098](#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [#8111](#8111) ### New Contributors - @ctaintor made their first contribution in [#8106](#8106) - @rileychh made their first contribution in [#8112](#8112) - @fortmarek made their first contribution in [#8102](#8102) - @pose made their first contribution in [#8035](#8035) - @gerhard made their first contribution in [#8094](#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
…8110) ## Summary - Adds `--stdin` flag to `mise set` that reads the value from stdin until EOF, enabling multiline environment variables (e.g., SSH keys, certificates) - Strips a single trailing newline to match `gh secret set` behavior (`echo "val" | mise set --stdin KEY` stores `"val"`) - Validates exactly one key is provided without `KEY=VALUE` syntax Closes jdx#6878 ## Test plan - [x] `printf 'line1\nline2' | mise set --stdin MULTI_LINE` stores multiline value correctly - [x] `printf 'hello\n' | mise set --stdin TRAILING_NL` strips trailing newline - [x] `mise set --stdin KEY1 KEY2` fails with error - [x] `mise set --stdin KEY1=value` fails with error - [x] E2E tests pass: `mise run test:e2e test_set` 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: adds an opt-in `--stdin` path to `mise set` plus validations; existing `set/get/prompt` behavior is unchanged unless the new flag is used. > > **Overview** > Adds `mise set --stdin` to read an env var value from stdin until EOF, enabling multiline values, and strips a single trailing newline for parity with `gh secret set`. > > Enforces `--stdin` usage rules (exactly one key, no `KEY=VALUE` syntax, and conflicts with `--prompt`), and updates CLI docs/man page/completions plus e2e coverage for multiline input and error cases. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2a27930. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [jdx#8106](jdx#8106) - **(env)** add `tools` variable to tera template context by @jdx in [jdx#8108](jdx#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [jdx#8110](jdx#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [jdx#8087](jdx#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [jdx#8100](jdx#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [jdx#8109](jdx#8109) - **(github)** skip v prefix for "latest" version by @jdx in [jdx#8105](jdx#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [jdx#8084](jdx#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [jdx#8081](jdx#8081) - **(registry)** add Linux support for tuist by @fortmarek in [jdx#8102](jdx#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [jdx#8086](jdx#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [jdx#8099](jdx#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [jdx#8101](jdx#8101) - SLSA for in-toto statement with no signatures by @gerhard in [jdx#8094](jdx#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [jdx#8035](jdx#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [jdx#8112](jdx#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [jdx#8080](jdx#8080) - add conda backends for 10 asdf-only tools by @jdx in [jdx#8083](jdx#8083) - added podman-tui by @tony-sol in [jdx#8098](jdx#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [jdx#8111](jdx#8111) ### New Contributors - @ctaintor made their first contribution in [jdx#8106](jdx#8106) - @rileychh made their first contribution in [jdx#8112](jdx#8112) - @fortmarek made their first contribution in [jdx#8102](jdx#8102) - @pose made their first contribution in [jdx#8035](jdx#8035) - @gerhard made their first contribution in [jdx#8094](jdx#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [jdx#8106](jdx#8106) - **(env)** add `tools` variable to tera template context by @jdx in [jdx#8108](jdx#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [jdx#8110](jdx#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [jdx#8087](jdx#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [jdx#8100](jdx#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [jdx#8109](jdx#8109) - **(github)** skip v prefix for "latest" version by @jdx in [jdx#8105](jdx#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [jdx#8084](jdx#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [jdx#8081](jdx#8081) - **(registry)** add Linux support for tuist by @fortmarek in [jdx#8102](jdx#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [jdx#8086](jdx#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [jdx#8099](jdx#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [jdx#8101](jdx#8101) - SLSA for in-toto statement with no signatures by @gerhard in [jdx#8094](jdx#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [jdx#8035](jdx#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [jdx#8112](jdx#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [jdx#8080](jdx#8080) - add conda backends for 10 asdf-only tools by @jdx in [jdx#8083](jdx#8083) - added podman-tui by @tony-sol in [jdx#8098](jdx#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [jdx#8111](jdx#8111) ### New Contributors - @ctaintor made their first contribution in [jdx#8106](jdx#8106) - @rileychh made their first contribution in [jdx#8112](jdx#8112) - @fortmarek made their first contribution in [jdx#8102](jdx#8102) - @pose made their first contribution in [jdx#8035](jdx#8035) - @gerhard made their first contribution in [jdx#8094](jdx#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
Summary
--stdinflag tomise setthat reads the value from stdin until EOF, enabling multiline environment variables (e.g., SSH keys, certificates)gh secret setbehavior (echo "val" | mise set --stdin KEYstores"val")KEY=VALUEsyntaxCloses #6878
Test plan
printf 'line1\nline2' | mise set --stdin MULTI_LINEstores multiline value correctlyprintf 'hello\n' | mise set --stdin TRAILING_NLstrips trailing newlinemise set --stdin KEY1 KEY2fails with errormise set --stdin KEY1=valuefails with errormise run test:e2e test_set🤖 Generated with Claude Code
Note
Low Risk
Low risk: adds an opt-in
--stdinpath tomise setplus validations; existingset/get/promptbehavior is unchanged unless the new flag is used.Overview
Adds
mise set --stdinto read an env var value from stdin until EOF, enabling multiline values, and strips a single trailing newline for parity withgh secret set.Enforces
--stdinusage rules (exactly one key, noKEY=VALUEsyntax, and conflicts with--prompt), and updates CLI docs/man page/completions plus e2e coverage for multiline input and error cases.Written by Cursor Bugbot for commit 2a27930. This will update automatically on new commits. Configure here.