github jdx/mise v2026.10.3
v2026.10.3: Dotfile groups, templated Compose projects, and secret hygiene for shell state

4 hours ago

This release adds dotfile groups for Stow-style dotfiles repositories, templated [bootstrap.compose] values, and custom labels for task confirm prompts. Secret values no longer get copied into __MISE_DIFF/__MISE_SESSION or written to the env cache. It also fixes dotenv ${VAR} expansion, mise x tool@latest reporting other tools as missing, and Ruby source builds ignoring depends tools.

Added

  • Dotfile groups. You can now declare a directory tree as a group with [dotfile_groups.<name>], using one directory per app or machine role like GNU Stow. mise walks the group's root and deploys each file to the same path under target (default ~), so you don't have to list files one by one. A machine picks which groups to apply with [bootstrap] dotfile_groups. If that list isn't set, every group applies. #13945

    [dotfile_groups.home]
    root = "home"            # relative to dotfiles.root
    dot_prefix = true        # dot-config/ deploys as .config/
    exclude = ["README.md"]
    
    [dotfile_groups.home.entries]
    "~/.config/kitty" = { mode = "symlink" }                            # link this directory whole
    "~/.gitconfig" = { source = "git/config.tmpl", mode = "template" }
    
    [bootstrap]
    dotfile_groups = ["home", "zsh"]
    • Groups support mode (symlink-each by default, or copy or symlink), exclude, dot_prefix, manifest and relative. A [dotfile_groups.<name>.entries] table uses the same syntax as [dotfiles] and removes those paths from the walk, so you can link a directory whole, render a template, or mark a file absent.
    • If two selected groups write the same file, apply and status fail before anything is written, and the error names both groups.
    • mise records what each group deployed under $MISE_STATE_DIR/dotfiles/groups/. mise dot status shows files that no active entry deploys any more as orphaned. mise dot apply --prune removes them after asking you, and mise dot unapply --group <name> removes one group's files even after the group is gone from config. mise only removes links that still point at their source and copies that still match what it wrote, unless you pass --force.
    • mise dot add and mise dot edit put files under a group's target into that group's root, and both accept --group when more than one group could match.
    • mise oci build does not include group trees.
  • Templates in [bootstrap.compose]. Every string field in a Compose project, including project_dir, files, env_files, command and depends_on, is now rendered as a Tera template in the context of the config that declares it. Shared configs can use {{ config_root }}, {{ vars.* }} or {{ env.* }} instead of hardcoded absolute paths. exec() isn't allowed in these templates. #13938

    [bootstrap.compose.mempalace]
    project_dir = "{{ config_root }}/mempalace"
    files = ["compose.yaml"]
    env_files = [".env"]
  • Custom labels for task confirm prompts. The object form of confirm now accepts yes and no labels, which support the same templates as message. default is now optional and still defaults to yes. Piped answers still take y/n, and --yes still skips the prompt. #13944

    [tasks.deploy]
    confirm = { message = "Deploy to production?", yes = "Deploy", no = "Cancel", default = "no" }
    run = "deploy.sh"

Fixed

  • Dotenv ${VAR} expansion checks the file's own values first. Before, ${VAR} in a dotenv file read the process environment first. With mise activate exporting another .env, a reference could expand against the shell instead of an earlier line in the same file. Now the file's own earlier assignments come first, then values already loaded (with expand = true) or the process environment. The ${VAR:-default}, ${VAR:+alt} and ${VAR:?message} forms now work too. When the env_file setting hits a syntax error, mise keeps the assignments it read before the error and shows one warning for the file. #13946
  • mise x tool@latest no longer reports other tools as missing. Passing any @latest argument used to resolve every configured tool against its newest release and ignore the lockfile, so tools that were installed and locked showed up as missing. Now only the tools named on the command line resolve to latest. #13943
  • Ruby source builds can see depends tools. ruby-build now gets the declared dependencies on PATH. For example, JRuby builds use a mise-managed java instead of the system JDK. PATH stays the same when no dependencies are declared. #13942 by @seuros

Security

  • No extra plaintext copies of secrets. #13950
    • Shell state: __MISE_DIFF and __MISE_SESSION are passed to every child process. They now store a blake3: digest of each value mise sets instead of the value itself. The previous (old) values are still stored in plain text because mise needs them to restore the environment. These are plain hashes, not keyed ones, so a low-entropy value could be brute-forced from its digest.
    • Env cache: mise no longer writes environments that contain secrets to the env cache. This covers age values (also when used through [vars]), sops-encrypted _.file entries, any directive with redact = true, and env modules that return cacheable = false. Before, a non-tool module's cacheable = false was ignored.
    • Env module redact: a redact setting on an env module, such as _.my-plugin = { redact = false }, now overrides the plugin's own preference. Before, it was ignored. A non-boolean value is now a config error.
    • mise x -- fish: env values no longer go in fish's command-line arguments, where ps could read them. --deny-env now applies there too.
    • Upgrading: shells started with an older mise still restore their environment correctly. mise rewrites their state in the new format at the next prompt.

Registry

  • Added jactionlint (github:jdx/jactionlint), a maintained fork of actionlint with upstream fixes and new checks. actionlint is now deprecated, with a message pointing to jactionlint. Existing actionlint installs keep working. #13960

Documentation

  • The installation guide shows how to pin the packslip bootstrapper while letting mise install the latest release. The Docker cookbook has a new multi-stage Debian example with a digest-pinned packslip image. #13935

Full Changelog: vfox-v2026.10.2...v2026.10.3

💚 Sponsor mise

mise is built and maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.

If mise saves you or your team time, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep mise fast, free, and independent.

Don't miss a new mise release

NewReleases is sending notifications on new releases.