From f386503d54bd32726e9ded773360abd5d8d00ab8 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:01:32 -0600 Subject: [PATCH] env-vars: improvements (#1435) * Use ui::table for better formatting * Allow viewing a single env var with `mise ev FOO` * Added --global flag See #1432 --- completions/_mise | 39 ++-- completions/mise.bash | 169 ++++++++++++------ completions/mise.fish | 20 ++- docs/cli-reference.md | 85 ++++++--- e2e/test_poetry | 4 + man/man1/mise.1 | 9 +- src/cli/args/env_var.rs | 61 +++---- src/cli/mod.rs | 9 +- src/cli/{env_vars.rs => set.rs} | 96 +++++++--- ...__cli__env_vars__tests__show_env_vars.snap | 5 - ...=> mise__cli__set__tests__env_vars-3.snap} | 2 +- ...=> mise__cli__set__tests__env_vars-5.snap} | 2 +- ...__cli__set__tests__env_vars_remove-3.snap} | 2 +- ...__cli__set__tests__env_vars_remove-6.snap} | 2 +- .../mise__cli__set__tests__show_env_vars.snap | 5 + ...se__cli__unset__tests__unset_remove-4.snap | 6 + src/cli/unset.rs | 80 +++++++++ 17 files changed, 410 insertions(+), 186 deletions(-) rename src/cli/{env_vars.rs => set.rs} (54%) delete mode 100644 src/cli/snapshots/mise__cli__env_vars__tests__show_env_vars.snap rename src/cli/snapshots/{mise__cli__env_vars__tests__env_vars-2.snap => mise__cli__set__tests__env_vars-3.snap} (74%) rename src/cli/snapshots/{mise__cli__env_vars__tests__env_vars.snap => mise__cli__set__tests__env_vars-5.snap} (74%) rename src/cli/snapshots/{mise__cli__env_vars__tests__env_vars_remove.snap => mise__cli__set__tests__env_vars_remove-3.snap} (70%) rename src/cli/snapshots/{mise__cli__env_vars__tests__env_vars_remove-2.snap => mise__cli__set__tests__env_vars_remove-6.snap} (70%) create mode 100644 src/cli/snapshots/mise__cli__set__tests__show_env_vars.snap create mode 100644 src/cli/snapshots/mise__cli__unset__tests__unset_remove-4.snap create mode 100644 src/cli/unset.rs diff --git a/completions/_mise b/completions/_mise index 5e1ff2fbe..90e1ec931 100644 --- a/completions/_mise +++ b/completions/_mise @@ -28,7 +28,6 @@ _mise() { (direnv) __mise_direnv_cmd && ret=0 ;; (doctor) __mise_doctor_cmd && ret=0 ;; (e|env) __mise_env_cmd && ret=0 ;; - (ev|env-vars) __mise_env_vars_cmd && ret=0 ;; (x|exec) __mise_exec_cmd && ret=0 ;; (g|global) __mise_global_cmd && ret=0 ;; (hook-env) __mise_hook_env_cmd && ret=0 ;; @@ -46,12 +45,14 @@ _mise() { (reshim) __mise_reshim_cmd && ret=0 ;; (r|run) __mise_run_cmd && ret=0 ;; (self-update) __mise_self_update_cmd && ret=0 ;; + (env-vars|ev|set) __mise_set_cmd && ret=0 ;; (settings) __mise_settings_cmd && ret=0 ;; (sh|shell) __mise_shell_cmd && ret=0 ;; (sync) __mise_sync_cmd && ret=0 ;; (t|tasks|task) __mise_task_cmd && ret=0 ;; (trust) __mise_trust_cmd && ret=0 ;; (remove|rm|uninstall) __mise_uninstall_cmd && ret=0 ;; + (unset) __mise_unset_cmd && ret=0 ;; (up|upgrade) __mise_upgrade_cmd && ret=0 ;; (u|use) __mise_use_cmd && ret=0 ;; (v|version) __mise_version_cmd && ret=0 ;; @@ -321,17 +322,6 @@ __mise_env_cmd() { '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ '(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]' } -(( $+functions[__mise_env_vars_cmd] )) || -__mise_env_vars_cmd() { - _arguments -s -S \ - '--file=[The TOML file to update]:file:_files' \ - '*--remove=[Remove the environment variable from config file]:remove:' \ - '*::env_vars:' \ - '(-C --cd)'{-C,--cd}'=[Change directory before running command]:cd:_directories' \ - '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ - '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ - '(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]' -} (( $+functions[__mise_exec_cmd] )) || __mise_exec_cmd() { _arguments -s -S \ @@ -610,6 +600,17 @@ __mise_self_update_cmd() { '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' } +(( $+functions[__mise_set_cmd] )) || +__mise_set_cmd() { + _arguments -s -S \ + '--file=[The TOML file to update]:file:_files' \ + '(-g --global)'{-g,--global}'[Set the environment variable in the global config file]' \ + '*::env_vars:' \ + '(-C --cd)'{-C,--cd}'=[Change directory before running command]:cd:_directories' \ + '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ + '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ + '(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]' +} (( $+functions[__mise_settings_cmd] )) || __mise_settings_cmd() { _arguments -s -S \ @@ -808,6 +809,17 @@ __mise_uninstall_cmd() { '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ '(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]' } +(( $+functions[__mise_unset_cmd] )) || +__mise_unset_cmd() { + _arguments -s -S \ + '*::keys:' \ + '(-f --file)'{-f,--file}'=[Specify a file to use instead of ".mise.toml"]:file:_files' \ + '(-g --global)'{-g,--global}'[Use the global config file]' \ + '(-C --cd)'{-C,--cd}'=[Change directory before running command]:cd:_directories' \ + '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ + '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ + '(-y --yes)'{-y,--yes}'[Answer yes to all confirmation prompts]' +} (( $+functions[__mise_upgrade_cmd] )) || __mise_upgrade_cmd() { _arguments -s -S \ @@ -893,7 +905,6 @@ __mise_cmds() { 'direnv:Output direnv function to use mise inside direnv' 'doctor:Check mise installation for possible problems.' {e,env}':Exports env vars to activate mise a single time' - {ev,env-vars}':Manage environment variables' {x,exec}':Execute a command with tool(s) set' 'implode:Removes mise CLI and all related data' {i,install}':Install a tool version' @@ -907,12 +918,14 @@ __mise_cmds() { 'reshim:rebuilds the shim farm' {r,run}':\[experimental\] Run a task' 'self-update:Updates mise itself' + 'set:Manage environment variables' 'settings:Manage settings' {sh,shell}':Sets a tool version for the current shell session' 'sync:Add tool versions from external tools to mise' {t,task}':\[experimental\] Manage tasks' 'trust:Marks a config file as trusted' {remove,rm,uninstall}':Removes runtime versions' + 'unset:Remove environment variable(s) from the config file' {up,upgrade}':Upgrades outdated tool versions' {u,use}':Change the active version of a tool locally or globally.' 'version:Show mise version' diff --git a/completions/mise.bash b/completions/mise.bash index fc4aa580f..8c0a1b971 100644 --- a/completions/mise.bash +++ b/completions/mise.bash @@ -57,12 +57,6 @@ _mise() { mise,env) cmd="mise__env" ;; - mise,env-vars) - cmd="mise__env__vars" - ;; - mise,ev) - cmd="mise__env__vars" - ;; mise,exec) cmd="mise__exec" ;; @@ -147,6 +141,9 @@ _mise() { mise,self-update) cmd="mise__self__update" ;; + mise,set) + cmd="mise__set" + ;; mise,settings) cmd="mise__settings" ;; @@ -174,6 +171,9 @@ _mise() { mise,uninstall) cmd="mise__uninstall" ;; + mise,unset) + cmd="mise__unset" + ;; mise,up) cmd="mise__upgrade" ;; @@ -348,9 +348,6 @@ _mise() { mise__help,env) cmd="mise__help__env" ;; - mise__help,env-vars) - cmd="mise__help__env__vars" - ;; mise__help,exec) cmd="mise__help__exec" ;; @@ -414,6 +411,9 @@ _mise() { mise__help,self-update) cmd="mise__help__self__update" ;; + mise__help,set) + cmd="mise__help__set" + ;; mise__help,settings) cmd="mise__help__settings" ;; @@ -432,6 +432,9 @@ _mise() { mise__help,uninstall) cmd="mise__help__uninstall" ;; + mise__help,unset) + cmd="mise__help__unset" + ;; mise__help,upgrade) cmd="mise__help__upgrade" ;; @@ -700,7 +703,7 @@ _mise() { case "${cmd}" in mise) - opts="-C -q -v -y -h -V --debug --log-level --trace --cd --quiet --verbose --yes --help --version activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env hook-not-found implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which render-completion render-help render-mangen help" + opts="-C -q -v -y -h -V --debug --log-level --trace --cd --quiet --verbose --yes --help --version activate alias asdf bin-paths cache completion config current deactivate direnv doctor env exec global hook-env hook-not-found implode install latest link local ls ls-remote outdated plugins prune reshim run self-update set settings shell sync task trust uninstall upgrade unset use version watch where which render-completion render-help render-mangen help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1589,40 +1592,6 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - mise__env__vars) - opts="-C -q -v -y -h --file --remove --debug --log-level --trace --cd --quiet --verbose --yes --help [ENV_VARS]..." - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --file) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --remove) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --log-level) - COMPREPLY=($(compgen -W "error warn info debug trace" -- "${cur}")) - return 0 - ;; - --cd) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -C) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; mise__exec) opts="-c -j -C -q -v -y -h --command --jobs --raw --debug --log-level --trace --cd --quiet --verbose --yes --help [TOOL@VERSION]... [COMMAND]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -1696,7 +1665,7 @@ _mise() { return 0 ;; mise__help) - opts="activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env hook-not-found implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which render-completion render-help render-mangen help" + opts="activate alias asdf bin-paths cache completion config current deactivate direnv doctor env exec global hook-env hook-not-found implode install latest link local ls ls-remote outdated plugins prune reshim run self-update set settings shell sync task trust uninstall upgrade unset use version watch where which render-completion render-help render-mangen help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2017,20 +1986,6 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; - mise__help__env__vars) - opts="" - if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; mise__help__exec) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -2409,6 +2364,20 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + mise__help__set) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; mise__help__settings) opts="get ls set unset" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -2619,6 +2588,20 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + mise__help__unset) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; mise__help__upgrade) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -3511,6 +3494,40 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + mise__set) + opts="-g -C -q -v -y -h --file --global --remove --debug --log-level --trace --cd --quiet --verbose --yes --help [ENV_VARS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --remove) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "error warn info debug trace" -- "${cur}")) + return 0 + ;; + --cd) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -C) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; mise__settings) opts="-C -q -v -y -h --debug --log-level --trace --cd --quiet --verbose --yes --help get ls set unset help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then @@ -4135,6 +4152,40 @@ _mise() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + mise__unset) + opts="-f -g -C -q -v -y -h --file --global --debug --log-level --trace --cd --quiet --verbose --yes --help [KEYS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --file) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -f) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "error warn info debug trace" -- "${cur}")) + return 0 + ;; + --cd) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -C) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; mise__upgrade) opts="-n -j -i -C -q -v -y -h --dry-run --jobs --interactive --raw --debug --log-level --trace --cd --quiet --verbose --yes --help [TOOL@VERSION]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/completions/mise.fish b/completions/mise.fish index b87bfd9ac..47a51cf43 100644 --- a/completions/mise.fish +++ b/completions/mise.fish @@ -5,7 +5,7 @@ complete -kxc mise -s C -l cd -a "(__fish_complete_directories)" -d 'Change dire complete -kxc mise -s q -l quiet -d 'Suppress non-error messages' complete -kxc mise -s v -l verbose -d 'Show extra output (use -vv for even more)' complete -kxc mise -s y -l yes -d 'Answer yes to all confirmation prompts' -set -l others activate alias bin-paths cache completion config current deactivate direnv doctor env env-vars exec implode install latest link ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which +set -l others activate alias bin-paths cache completion config current deactivate direnv doctor env exec implode install latest link ls ls-remote outdated plugins prune reshim run self-update set settings shell sync task trust uninstall unset upgrade use version watch where which complete -xc mise -n "not $fssf $others" -a activate -d 'Initializes mise in the current shell session' complete -xc mise -n "not $fssf $others" -a alias -d 'Manage aliases' complete -xc mise -n "not $fssf $others" -a bin-paths -d 'List all the active runtime bin paths' @@ -17,7 +17,6 @@ complete -xc mise -n "not $fssf $others" -a deactivate -d 'Disable mise for curr complete -xc mise -n "not $fssf $others" -a direnv -d 'Output direnv function to use mise inside direnv' complete -xc mise -n "not $fssf $others" -a doctor -d 'Check mise installation for possible problems.' complete -xc mise -n "not $fssf $others" -a env -d 'Exports env vars to activate mise a single time' -complete -xc mise -n "not $fssf $others" -a env-vars -d 'Manage environment variables' complete -xc mise -n "not $fssf $others" -a exec -d 'Execute a command with tool(s) set' complete -xc mise -n "not $fssf $others" -a implode -d 'Removes mise CLI and all related data' complete -xc mise -n "not $fssf $others" -a install -d 'Install a tool version' @@ -31,12 +30,14 @@ complete -xc mise -n "not $fssf $others" -a prune -d 'Delete unused versions of complete -xc mise -n "not $fssf $others" -a reshim -d 'rebuilds the shim farm' complete -xc mise -n "not $fssf $others" -a run -d '[experimental] Run a task' complete -xc mise -n "not $fssf $others" -a self-update -d 'Updates mise itself' +complete -xc mise -n "not $fssf $others" -a set -d 'Manage environment variables' complete -xc mise -n "not $fssf $others" -a settings -d 'Manage settings' complete -xc mise -n "not $fssf $others" -a shell -d 'Sets a tool version for the current shell session' complete -xc mise -n "not $fssf $others" -a sync -d 'Add tool versions from external tools to mise' complete -xc mise -n "not $fssf $others" -a task -d '[experimental] Manage tasks' complete -xc mise -n "not $fssf $others" -a trust -d 'Marks a config file as trusted' complete -xc mise -n "not $fssf $others" -a uninstall -d 'Removes runtime versions' +complete -xc mise -n "not $fssf $others" -a unset -d 'Remove environment variable(s) from the config file' complete -xc mise -n "not $fssf $others" -a upgrade -d 'Upgrades outdated tool versions' complete -xc mise -n "not $fssf $others" -a use -d 'Change the active version of a tool locally or globally.' complete -xc mise -n "not $fssf $others" -a version -d 'Show mise version' @@ -121,11 +122,6 @@ complete -kxc mise -n "$fssf env" -s J -l json -d 'Output in JSON format' complete -kxc mise -n "$fssf env" -s s -l shell -a "bash fish nu xonsh zsh" -d 'Shell type to generate environment variables for' complete -kxc mise -n "$fssf env" -a "(__mise_tool_versions)" -d 'Tool(s) to use' -# env-vars -complete -kxc mise -n "$fssf env-vars" -d 'Environment variable(s) to set' -complete -kxc mise -n "$fssf env-vars" -l file -a "(__fish_complete_path)" -d 'The TOML file to update' -complete -kxc mise -n "$fssf env-vars" -l remove -d 'Remove the environment variable from config file' - # exec complete -kxc mise -n "$fssf exec" -s c -l command -d 'Command string to execute' complete -kxc mise -n "$fssf exec" -d 'Command string to execute (same as --command)' @@ -238,6 +234,11 @@ complete -kxc mise -n "$fssf self-update" -l no-plugins -d 'Disable auto-updatin complete -kxc mise -n "$fssf self-update" -d 'Update to a specific version' complete -kxc mise -n "$fssf self-update" -s y -l yes -d 'Skip confirmation prompt' +# set +complete -kxc mise -n "$fssf set" -d 'Environment variable(s) to set' +complete -kxc mise -n "$fssf set" -l file -a "(__fish_complete_path)" -d 'The TOML file to update' +complete -kxc mise -n "$fssf set" -s g -l global -d 'Set the environment variable in the global config file' + # settings set -l others get ls set unset complete -xc mise -n "$fssf settings; and not $fssf $others" -a get -d 'Show a current setting' @@ -317,6 +318,11 @@ complete -kxc mise -n "$fssf uninstall" -s a -l all -d 'Delete all installed ver complete -kxc mise -n "$fssf uninstall" -s n -l dry-run -d 'Do not actually delete anything' complete -kxc mise -n "$fssf uninstall" -a "(__mise_installed_tool_versions)" -d 'Tool(s) to remove' +# unset +complete -kxc mise -n "$fssf unset" -s f -l file -a "(__fish_complete_path)" -d 'Specify a file to use instead of ".mise.toml"' +complete -kxc mise -n "$fssf unset" -s g -l global -d 'Use the global config file' +complete -kxc mise -n "$fssf unset" -d 'Environment variable(s) to remove' + # upgrade complete -kxc mise -n "$fssf upgrade" -s n -l dry-run -d 'Just print what would be done, don'\''t actually do it' complete -kxc mise -n "$fssf upgrade" -s i -l interactive -d 'Display multiselect menu to choose which tools to upgrade' diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 93d52b908..d6ee925d0 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -326,35 +326,6 @@ Examples: $ execx($(mise env -s xonsh)) ``` -## `mise env-vars [OPTIONS] [ENV_VARS]...` - -**Aliases:** `ev` - -```text -Manage environment variables - -By default this command modifies ".mise.toml" in the current directory. -You can specify the file name by either setting the MISE_DEFAULT_CONFIG_FILENAME environment variable, or by using the --file option. - -Usage: env-vars [OPTIONS] [ENV_VARS]... - -Arguments: - [ENV_VARS]... - Environment variable(s) to set - e.g.: NODE_ENV=production - -Options: - --file - The TOML file to update - - Defaults to MISE_DEFAULT_CONFIG_FILENAME environment variable, or ".mise.toml". - - --remove - Remove the environment variable from config file - - Can be used multiple times. -``` - ## `mise exec [OPTIONS] [TOOL@VERSION]... [-- ...]` **Aliases:** `x` @@ -1007,6 +978,40 @@ Options: Skip confirmation prompt ``` +## `mise set [OPTIONS] [ENV_VARS]...` + +```text +Manage environment variables + +By default this command modifies ".mise.toml" in the current directory. + +Usage: set [OPTIONS] [ENV_VARS]... + +Arguments: + [ENV_VARS]... + Environment variable(s) to set + e.g.: NODE_ENV=production + +Options: + --file + The TOML file to update + + Defaults to MISE_DEFAULT_CONFIG_FILENAME environment variable, or ".mise.toml". + + -g, --global + Set the environment variable in the global config file + +Examples: + $ mise set NODE_ENV=production + + $ mise set NODE_ENV + production + + $ mise set + key value source + NODE_ENV production ~/.config/mise/config.toml +``` + ## `mise settings get ` ```text @@ -1368,6 +1373,28 @@ Examples: $ mise uninstall --all node@18.0.0 # will uninstall all node versions ``` +## `mise unset [OPTIONS] [KEYS]...` + +```text +Remove environment variable(s) from the config file + +By default this command modifies ".mise.toml" in the current directory. + +Usage: unset [OPTIONS] [KEYS]... + +Arguments: + [KEYS]... + Environment variable(s) to remove + e.g.: NODE_ENV + +Options: + -f, --file + Specify a file to use instead of ".mise.toml" + + -g, --global + Use the global config file +``` + ## `mise upgrade [OPTIONS] [TOOL@VERSION]...` **Aliases:** `up` diff --git a/e2e/test_poetry b/e2e/test_poetry index 9db0d99ad..8b600ed00 100755 --- a/e2e/test_poetry +++ b/e2e/test_poetry @@ -3,6 +3,10 @@ set -euo pipefail # shellcheck source-path=SCRIPTDIR source "$(dirname "$0")/assert.sh" +if [ "${TEST_ALL:-}" != 1 ]; then + exit +fi + rm -rf "$MISE_DATA_DIR/cache/poetry" export POETRY_HOME=".poetry" diff --git a/man/man1/mise.1 b/man/man1/mise.1 index 7e63df1e8..802f9cb0e 100644 --- a/man/man1/mise.1 +++ b/man/man1/mise.1 @@ -68,9 +68,6 @@ Check mise installation for possible problems. mise\-env(1) Exports env vars to activate mise a single time .TP -mise\-env\-vars(1) -Manage environment variables -.TP mise\-exec(1) Execute a command with tool(s) set .TP @@ -110,6 +107,9 @@ mise\-run(1) mise\-self\-update(1) Updates mise itself .TP +mise\-set(1) +Manage environment variables +.TP mise\-settings(1) Manage settings .TP @@ -131,6 +131,9 @@ Removes runtime versions mise\-upgrade(1) Upgrades outdated tool versions .TP +mise\-unset(1) +Remove environment variable(s) from the config file +.TP mise\-use(1) Change the active version of a tool locally or globally. .TP diff --git a/src/cli/args/env_var.rs b/src/cli/args/env_var.rs index bc37cd4c0..1e78c0bc0 100644 --- a/src/cli/args/env_var.rs +++ b/src/cli/args/env_var.rs @@ -1,22 +1,25 @@ use std::ffi::OsStr; -use clap::{ - error::{ContextKind, ContextValue, ErrorKind}, - Arg, Command, Error, -}; +use clap::{Arg, Command, Error}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct EnvVarArg { pub key: String, - pub value: String, + pub value: Option, } impl EnvVarArg { - pub fn parse(input: &str) -> Option { - input.split_once('=').map(|(k, v)| Self { - key: k.to_string(), - value: v.to_string(), - }) + pub fn parse(input: &str) -> Self { + input + .split_once('=') + .map(|(k, v)| Self { + key: k.to_string(), + value: Some(v.to_string()), + }) + .unwrap_or_else(|| Self { + key: input.to_string(), + value: None, + }) } } @@ -28,26 +31,11 @@ impl clap::builder::TypedValueParser for EnvVarArgParser { fn parse_ref( &self, - cmd: &Command, - arg: Option<&Arg>, + _cmd: &Command, + _arg: Option<&Arg>, value: &OsStr, ) -> Result { - if let Some(parsed) = EnvVarArg::parse(&value.to_string_lossy()) { - return Ok(parsed); - } - - let mut err = clap::Error::new(ErrorKind::ValueValidation).with_cmd(cmd); - if let Some(arg) = arg { - err.insert( - ContextKind::InvalidArg, - ContextValue::String(arg.to_string()), - ); - } - err.insert( - ContextKind::InvalidValue, - ContextValue::String(value.to_string_lossy().into()), - ); - Err(err) + Ok(EnvVarArg::parse(&value.to_string_lossy())) } } @@ -55,29 +43,24 @@ impl clap::builder::TypedValueParser for EnvVarArgParser { mod tests { use super::EnvVarArg; - #[test] - fn invalid_value() { - let res = EnvVarArg::parse("NO_EQUAL_SIGN"); - assert!(res.is_none()); - } - #[test] fn valid_values() { let values = [ - ("FOO=", new_arg("FOO", "")), - ("FOO=bar", new_arg("FOO", "bar")), + ("FOO", new_arg("FOO", None)), + ("FOO=", new_arg("FOO", Some(""))), + ("FOO=bar", new_arg("FOO", Some("bar"))), ]; for (input, want) in values { let got = EnvVarArg::parse(input); - assert_eq!(got, Some(want)); + assert_eq!(got, want); } } - fn new_arg(key: &str, value: &str) -> EnvVarArg { + fn new_arg(key: &str, value: Option<&str>) -> EnvVarArg { EnvVarArg { key: key.to_string(), - value: value.to_string(), + value: value.map(|s| s.to_string()), } } } diff --git a/src/cli/mod.rs b/src/cli/mod.rs index ee2f96a7b..b4d2ec8c2 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -17,7 +17,6 @@ mod deactivate; mod direnv; mod doctor; mod env; -mod env_vars; pub mod exec; mod external; mod global; @@ -42,12 +41,14 @@ mod render_mangen; mod reshim; mod run; mod self_update; +mod set; mod settings; mod shell; mod sync; mod task; mod trust; mod uninstall; +mod unset; mod upgrade; mod r#use; pub mod version; @@ -71,7 +72,6 @@ pub enum Commands { Direnv(direnv::Direnv), Doctor(doctor::Doctor), Env(env::Env), - EnvVars(env_vars::EnvVars), Exec(exec::Exec), Global(global::Global), HookEnv(hook_env::HookEnv), @@ -89,6 +89,7 @@ pub enum Commands { Reshim(reshim::Reshim), Run(run::Run), SelfUpdate(self_update::SelfUpdate), + Set(set::Set), Settings(settings::Settings), Shell(shell::Shell), Sync(sync::Sync), @@ -96,6 +97,7 @@ pub enum Commands { Trust(trust::Trust), Uninstall(uninstall::Uninstall), Upgrade(upgrade::Upgrade), + Unset(unset::Unset), Use(r#use::Use), Version(version::Version), Watch(watch::Watch), @@ -127,7 +129,6 @@ impl Commands { Self::Direnv(cmd) => cmd.run(), Self::Doctor(cmd) => cmd.run(), Self::Env(cmd) => cmd.run(), - Self::EnvVars(cmd) => cmd.run(), Self::Exec(cmd) => cmd.run(), Self::Global(cmd) => cmd.run(), Self::HookEnv(cmd) => cmd.run(), @@ -145,12 +146,14 @@ impl Commands { Self::Reshim(cmd) => cmd.run(), Self::Run(cmd) => cmd.run(), Self::SelfUpdate(cmd) => cmd.run(), + Self::Set(cmd) => cmd.run(), Self::Settings(cmd) => cmd.run(), Self::Shell(cmd) => cmd.run(), Self::Sync(cmd) => cmd.run(), Self::Task(cmd) => cmd.run(), Self::Trust(cmd) => cmd.run(), Self::Uninstall(cmd) => cmd.run(), + Self::Unset(cmd) => cmd.run(), Self::Upgrade(cmd) => cmd.run(), Self::Use(cmd) => cmd.run(), Self::Version(cmd) => cmd.run(), diff --git a/src/cli/env_vars.rs b/src/cli/set.rs similarity index 54% rename from src/cli/env_vars.rs rename to src/cli/set.rs index caa05baff..dccd21a67 100644 --- a/src/cli/env_vars.rs +++ b/src/cli/set.rs @@ -1,31 +1,37 @@ +use std::path::{Path, PathBuf}; + use miette::{IntoDiagnostic, Result}; +use tabled::Tabled; use crate::config::config_file::mise_toml::MiseToml; use crate::config::config_file::ConfigFile; use crate::config::Config; use crate::env; -use crate::env::MISE_DEFAULT_CONFIG_FILENAME; use crate::file::display_path; +use crate::ui::table; use super::args::env_var::{EnvVarArg, EnvVarArgParser}; /// Manage environment variables /// /// By default this command modifies ".mise.toml" in the current directory. -/// You can specify the file name by either setting the MISE_DEFAULT_CONFIG_FILENAME environment variable, or by using the --file option. #[derive(Debug, clap::Args)] -#[clap(visible_alias = "ev", verbatim_doc_comment)] -pub struct EnvVars { +#[clap(aliases = ["ev", "env-vars"], verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)] +pub struct Set { /// The TOML file to update /// /// Defaults to MISE_DEFAULT_CONFIG_FILENAME environment variable, or ".mise.toml". #[clap(long, verbatim_doc_comment, required = false, value_hint = clap::ValueHint::FilePath)] - file: Option, + file: Option, + + /// Set the environment variable in the global config file + #[clap(short, long, verbatim_doc_comment, overrides_with = "file")] + global: bool, /// Remove the environment variable from config file /// /// Can be used multiple times. - #[clap(long, value_name = "ENV_VAR", verbatim_doc_comment, aliases = ["rm", "unset"])] + #[clap(long, value_name = "ENV_VAR", verbatim_doc_comment, aliases = ["rm", "unset"], hide = true)] remove: Option>, /// Environment variable(s) to set @@ -34,22 +40,31 @@ pub struct EnvVars { env_vars: Option>, } -impl EnvVars { +impl Set { pub fn run(self) -> Result<()> { let config = Config::try_get()?; if self.remove.is_none() && self.env_vars.is_none() { - for (key, value) in &config.env { - let source = config.env_sources.get(key).unwrap(); - miseprintln!("{key}={value} {}", display_path(source)); - } + let rows = config + .env + .iter() + .map(|(key, value)| Row { + key: key.clone(), + value: value.clone(), + source: display_path(&config.env_sources.get(key).unwrap().clone()), + }) + .collect::>(); + let mut table = tabled::Table::new(rows); + table::default_style(&mut table, false); + miseprintln!("{table}"); return Ok(()); } - let filename = self - .file - .unwrap_or_else(|| MISE_DEFAULT_CONFIG_FILENAME.to_string()); + let filename = self.file.unwrap_or_else(|| match self.global { + true => env::MISE_GLOBAL_CONFIG_FILE.clone(), + false => env::MISE_DEFAULT_CONFIG_FILENAME.clone().into(), + }); - let mut mise_toml = get_mise_toml(filename.as_str())?; + let mut mise_toml = get_mise_toml(&filename)?; if let Some(env_names) = &self.remove { for name in env_names { @@ -58,15 +73,26 @@ impl EnvVars { } if let Some(env_vars) = self.env_vars { + if env_vars.len() == 1 && env_vars[0].value.is_none() { + let key = &env_vars[0].key; + match config.env.get(key) { + Some(value) => miseprintln!("{value}"), + None => bail!("Environment variable {key} not found"), + } + return Ok(()); + } for ev in env_vars { - mise_toml.update_env(&ev.key, ev.value); + match ev.value { + Some(value) => mise_toml.update_env(&ev.key, value), + None => bail!("{} has no value", ev.key), + } } } mise_toml.save() } } -fn get_mise_toml(filename: &str) -> Result { +fn get_mise_toml(filename: &Path) -> Result { let path = env::current_dir().into_diagnostic()?.join(filename); let mise_toml = if path.exists() { MiseToml::from_file(&path)? @@ -77,11 +103,32 @@ fn get_mise_toml(filename: &str) -> Result { Ok(mise_toml) } +#[derive(Tabled)] +struct Row { + key: String, + value: String, + source: String, +} + +static AFTER_LONG_HELP: &str = color_print::cstr!( + r#"Examples: + $ mise set NODE_ENV=production + + $ mise set NODE_ENV + production + + $ mise set + key value source + NODE_ENV production ~/.config/mise/config.toml +"# +); + #[cfg(test)] mod tests { - use crate::{env, file}; use std::path::PathBuf; + use crate::{env, file}; + fn remove_config_file(filename: &str) -> PathBuf { let cf_path = env::current_dir().unwrap().join(filename); let _ = file::remove_file(&cf_path); @@ -98,14 +145,15 @@ mod tests { // Using the default file let filename = ".test.mise.toml"; let cf_path = remove_config_file(filename); - assert_cli!("env-vars", "FOO=bar"); + assert_cli_snapshot!("env-vars", "FOO=bar", @""); + assert_cli_snapshot!("env-vars", "FOO", @"bar"); assert_snapshot!(file::read_to_string(cf_path).unwrap()); remove_config_file(filename); // Using a custom file let filename = ".test-custom.mise.toml"; let cf_path = remove_config_file(filename); - assert_cli!("env-vars", "--file", filename, "FOO=bar"); + assert_cli_snapshot!("env-vars", "--file", filename, "FOO=bar", @""); assert_snapshot!(file::read_to_string(cf_path).unwrap()); remove_config_file(filename); } @@ -115,16 +163,16 @@ mod tests { // Using the default file let filename = ".test.mise.toml"; let cf_path = remove_config_file(filename); - assert_cli!("env-vars", "BAZ=quux"); - assert_cli!("env-vars", "--remove", "BAZ"); + assert_cli_snapshot!("env-vars", "BAZ=quux", @""); + assert_cli_snapshot!("env-vars", "--remove", "BAZ", @""); assert_snapshot!(file::read_to_string(cf_path).unwrap()); remove_config_file(filename); // Using a custom file let filename = ".test-custom.mise.toml"; let cf_path = remove_config_file(filename); - assert_cli!("env-vars", "--file", filename, "BAZ=quux"); - assert_cli!("env-vars", "--file", filename, "--remove", "BAZ"); + assert_cli_snapshot!("env-vars", "--file", filename, "BAZ=quux", @""); + assert_cli_snapshot!("env-vars", "--file", filename, "--remove", "BAZ", @""); assert_snapshot!(file::read_to_string(cf_path).unwrap()); remove_config_file(filename); } diff --git a/src/cli/snapshots/mise__cli__env_vars__tests__show_env_vars.snap b/src/cli/snapshots/mise__cli__env_vars__tests__show_env_vars.snap deleted file mode 100644 index a067a978b..000000000 --- a/src/cli/snapshots/mise__cli__env_vars__tests__show_env_vars.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: src/cli/env_vars.rs -expression: output ---- -TEST_ENV_VAR=test-123 ~/config/config.toml diff --git a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars-2.snap b/src/cli/snapshots/mise__cli__set__tests__env_vars-3.snap similarity index 74% rename from src/cli/snapshots/mise__cli__env_vars__tests__env_vars-2.snap rename to src/cli/snapshots/mise__cli__set__tests__env_vars-3.snap index e712390a1..28d4b0ee1 100644 --- a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars-2.snap +++ b/src/cli/snapshots/mise__cli__set__tests__env_vars-3.snap @@ -1,5 +1,5 @@ --- -source: src/cli/env_vars.rs +source: src/cli/set.rs expression: "file::read_to_string(cf_path).unwrap()" --- [env] diff --git a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars.snap b/src/cli/snapshots/mise__cli__set__tests__env_vars-5.snap similarity index 74% rename from src/cli/snapshots/mise__cli__env_vars__tests__env_vars.snap rename to src/cli/snapshots/mise__cli__set__tests__env_vars-5.snap index e712390a1..28d4b0ee1 100644 --- a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars.snap +++ b/src/cli/snapshots/mise__cli__set__tests__env_vars-5.snap @@ -1,5 +1,5 @@ --- -source: src/cli/env_vars.rs +source: src/cli/set.rs expression: "file::read_to_string(cf_path).unwrap()" --- [env] diff --git a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove.snap b/src/cli/snapshots/mise__cli__set__tests__env_vars_remove-3.snap similarity index 70% rename from src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove.snap rename to src/cli/snapshots/mise__cli__set__tests__env_vars_remove-3.snap index a76beff3f..769565740 100644 --- a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove.snap +++ b/src/cli/snapshots/mise__cli__set__tests__env_vars_remove-3.snap @@ -1,5 +1,5 @@ --- -source: src/cli/env_vars.rs +source: src/cli/set.rs expression: "file::read_to_string(cf_path).unwrap()" --- [env] diff --git a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove-2.snap b/src/cli/snapshots/mise__cli__set__tests__env_vars_remove-6.snap similarity index 70% rename from src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove-2.snap rename to src/cli/snapshots/mise__cli__set__tests__env_vars_remove-6.snap index a76beff3f..769565740 100644 --- a/src/cli/snapshots/mise__cli__env_vars__tests__env_vars_remove-2.snap +++ b/src/cli/snapshots/mise__cli__set__tests__env_vars_remove-6.snap @@ -1,5 +1,5 @@ --- -source: src/cli/env_vars.rs +source: src/cli/set.rs expression: "file::read_to_string(cf_path).unwrap()" --- [env] diff --git a/src/cli/snapshots/mise__cli__set__tests__show_env_vars.snap b/src/cli/snapshots/mise__cli__set__tests__show_env_vars.snap new file mode 100644 index 000000000..6939175fd --- /dev/null +++ b/src/cli/snapshots/mise__cli__set__tests__show_env_vars.snap @@ -0,0 +1,5 @@ +--- +source: src/cli/set.rs +expression: output +--- +TEST_ENV_VAR test-123 ~/config/config.toml diff --git a/src/cli/snapshots/mise__cli__unset__tests__unset_remove-4.snap b/src/cli/snapshots/mise__cli__unset__tests__unset_remove-4.snap new file mode 100644 index 000000000..e6e57a16b --- /dev/null +++ b/src/cli/snapshots/mise__cli__unset__tests__unset_remove-4.snap @@ -0,0 +1,6 @@ +--- +source: src/cli/unset.rs +expression: "file::read_to_string(cf_path).unwrap()" +--- +[env] + diff --git a/src/cli/unset.rs b/src/cli/unset.rs new file mode 100644 index 000000000..365c578fc --- /dev/null +++ b/src/cli/unset.rs @@ -0,0 +1,80 @@ +use std::path::{Path, PathBuf}; + +use miette::{IntoDiagnostic, Result}; + +use crate::config::config_file::mise_toml::MiseToml; +use crate::config::config_file::ConfigFile; +use crate::env; + +/// Remove environment variable(s) from the config file +/// +/// By default this command modifies ".mise.toml" in the current directory. +#[derive(Debug, clap::Args)] +#[clap(verbatim_doc_comment)] +pub struct Unset { + /// Environment variable(s) to remove + /// e.g.: NODE_ENV + #[clap(verbatim_doc_comment)] + keys: Vec, + + /// Specify a file to use instead of ".mise.toml" + #[clap(short, long, value_hint = clap::ValueHint::FilePath)] + file: Option, + + /// Use the global config file + #[clap(short, long, overrides_with = "file")] + global: bool, +} + +impl Unset { + pub fn run(self) -> Result<()> { + let filename = self.file.unwrap_or_else(|| match self.global { + true => env::MISE_GLOBAL_CONFIG_FILE.clone(), + false => env::MISE_DEFAULT_CONFIG_FILENAME.clone().into(), + }); + + let mut mise_toml = get_mise_toml(&filename)?; + + for name in self.keys.iter() { + mise_toml.remove_env(name); + } + + mise_toml.save() + } +} + +fn get_mise_toml(filename: &Path) -> Result { + let path = env::current_dir().into_diagnostic()?.join(filename); + let mise_toml = if path.exists() { + MiseToml::from_file(&path)? + } else { + MiseToml::init(&path) + }; + + Ok(mise_toml) +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use crate::{env, file}; + + fn remove_config_file(filename: &str) -> PathBuf { + let cf_path = env::current_dir().unwrap().join(filename); + let _ = file::remove_file(&cf_path); + cf_path + } + + #[test] + fn test_unset_remove() { + // Using the default file + let filename = ".test.mise.toml"; + let cf_path = remove_config_file(filename); + assert_cli_snapshot!("env-vars", "BAZ=quux", @""); + assert_cli_snapshot!("set", "BAZ", @"quux"); + assert_cli_snapshot!("unset", "BAZ", @""); + assert_snapshot!(file::read_to_string(cf_path).unwrap()); + remove_config_file(filename); + } +}