Repository navigation
feat(mise): opt-in mise module, per-module tool declarations, nodejs provider=mise - #48
Merged
Merged
Conversation
garygentry
force-pushed
the
feat/mise-module
branch
from
September 26, 2026 20:51
fd9ef87 to
1dbd8f1
Compare
garygentry
changed the base branch from
feat/zsh-startup-correctness
to
main
September 27, 2026 16:16
- modules/mise: pinned, SHA-256-verified mise binary at ~/.local/bin/mise (no self-update; a mise from another channel is used as-is, never replaced); tools/settings rendered from modules.mise.* to ~/.config/mise/conf.d/dotfiles.toml (user config.toml untouched); optional overlay lockfile -> --locked install scoped to declared tools (no GitHub API); optional github_token_command for this run only; shims on PATH via managed blocks in ~/.zshenv, the login profile and ~/.bashrc; engine ships an empty tool list - nodejs: provider tarball (default, unchanged) | mise (asserts mise provides node, enforces min_version, keeps npm prefix ~/.local; verify checks PATH provenance) - zsh: shims first in the .zshenv PATH block and .zshrc (order-independent of block position); mise activate for interactive zsh (not -c; DOTFILES_MISE_ACTIVATE=0 opts out) - fix render_template: --module got the module DIR, so .Module was always empty - integration: fixture overlay + 13 assertions (64 passed) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
…h scripts - settingToStr: lists -> one item per line, maps -> sorted key=value lines (was Go's map[a:b] formatting, unusable from shell); unit-tested - lib/helpers.sh: mise_bin + mise_sync_tools NAME [tool@ver...]: declares a module's tools in conf.d/<module>.toml and installs exactly those (--locked when the mise module manages a lockfile); exact versions only - mise module: own tools via the helper; settings -> conf.d/dotfiles-settings.toml; removes fragments whose owning module is no longer installed (prune only undoes engine-deployed files) - nodejs provider=mise declares node@modules.nodejs.version itself and checks mise resolves exactly that version - integration: overlay fixture module declaring its own tools + fragment GC (69 passed) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
…n profiles, TOML - zsh puts mise shims first / runs mise activate ONLY when the mise module is installed (a marker it deploys via files:, removed on uninstall/prune), not whenever a shims dir exists — a user's own mise is left alone (opt-in invariant) - nodejs provider tarball removes conf.d/nodejs.toml: switching back from mise no longer leaves mise owning node (two owners); mise_sync_tools with nothing declared removes the fragment and needs no mise - login shells: shims block in ~/.profile always, plus ~/.bash_profile or ~/.bash_login when one exists; ~/.bash_profile is never created (it would hide ~/.profile) - ~/.bashrc block goes at the TOP (upsert_managed_block gains a 'top' position): distro .bashrc files return early for non-interactive shells - settings.toml.tmpl: YAML maps -> TOML inline tables, null -> omitted (was invalid TOML) - with a lockfile, tools lacking an entry for this platform are skipped with a warning (eza has no macOS build) instead of failing the whole set - docs: tool versions must be quoted (unquoted 1.10 is a YAML float -> 1.1) - integration: provider switch-back, .bashrc top placement, marker deployed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
…musl]) Lets a module pick a release asset, e.g. "github:atuinsh/atuin[matching=musl]" (the gnu build of atuin 18.22 needs glibc 2.39; Ubuntu 22.04 has 2.35). The lockfile keys tools without their options, so the per-platform lookup strips them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
"github:atuinsh/atuin[matching=musl]" arrived as tool 'github:atuinsh/atuin[matching', version 'musl]=18.22.0' and was rejected. Versions never contain '=', so split at the last one. Integration fixture now declares that spec (re-locked) and asserts the binary runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
garygentry
force-pushed
the
feat/mise-module
branch
from
September 27, 2026 16:17
bf163d5 to
bfc3dc9
Compare
…l Arch) cmp ships in diffutils, which Arch base doesn't install; verify.sh failed with 'cmp: command not found' and reported the lock as drifted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on #47. Review and merge #47 first. This PR's diff is the commits after #47's head.
An opt-in
misemodule: pinned, checksum-verified CLI tools and language runtimes in user space. Alsonodejs.provider: mise. With no content overlay, or without selecting the module, nothing changes: the engine ships no tool list and no versions.mise module
~/.local/bin/mise, sudo-free, pinned withmodules.mise.version, SHA-256-verified against the releaseSHASUMS256.txt(installing unverified is refused), atomically replaced, no self-update. A mise from another channel (Homebrew, a distro package) is used as-is and never replaced.tools:setting (tool to exact version) and callmise_sync_tools "$DOTFILES_MODULE_NAME"(a new helper). Each module's tools go to~/.config/mise/conf.d/<module>.toml, and exactly those are installed. A tool set therefore follows whichever profile selects its module. The mise module declares its ownmodules.mise.toolsthe same way. When a module is uninstalled or pruned, the mise module removes its file, because prune only undoes files the engine deployed itself.modules.mise.lockfilepoints at an overlaymise.lock(frommise lock --global --platform …). The engine then owns~/.config/mise/mise.lock, and every declared set installs with--locked, using checksummed URLs and no GitHub API calls. A version bumped without re-locking fails loudly. Installs are limited to the declared tools, so a user's ownmise use -gtools can't break them.~/.zshenv, the login profile and~/.bashrccoverssh host cmd,bash -lcand agent tool shells. Interactive zsh also runsmise activate(skipped forzsh -i -c; turn it off withactivate: false). The zsh PATH block puts shims first regardless of which block appears first in the file: modules run in dependency order, so priority alone can't fix block order.settingsrender toconf.d/dotfiles-settings.toml. Your own~/.config/mise/config.tomlis never touched.nodejs
provider: miseDeclares
node@<modules.nodejs.version>(exact) through the helper and checks that mise resolves exactly that version.min_versionis still enforced, and npm's global prefix stays~/.local, so global packages survive a Node switch.verify.shchecks that a login shell'snodeis mise's. The default istarball, which is unchanged.Engine fixes this needed
render_templatepassed the module directory as--module(it expects a name), so.Modulewas always empty in script-rendered templates. This bug dates from the first commit; nothing used it until now.key=valuelines, instead of Go'smap[a:b]formatting. Unit-tested. Nothing consumed the old format.Measured (Ubuntu 22.04 container)
zsh -i -c exitabout 45 msmise activate, about 65 ms with activation offmise hook-envadds about 20 ms per prompt on a loaded 2-vCPU VMTest plan
make test-integration-ubuntu: 69 passed, 0 failed. The fixture overlay declares tools from 3 modules (mise, nodejs, and an overlay module) and runs with no GitHub token. It checks:zsh -cresolving node, fzf and rg at the pinned versions, andsh -lcresolving node.zshenvblockNo lockfile URL found … (--locked mode).go vet ./...andgo test ./...pass, including a newTestSettingToStr_NonScalars.dotfiles validatepasses for the engine and the fixture overlay.shellcheckis clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01S29GMkRB43pGsAUBUXwLdT
Review round 2 (adversarial, 2026-09-26): all addressed
nodejs.providerfrom mise back to tarball left mise owning node. The tarball provider now removesconf.d/nodejs.toml, andmise_sync_toolswith nothing declared removes the fragment without needing mise.mise activate, whenever a mise shims dir existed. It now depends on a marker the mise module deploys throughfiles:, which uninstall and prune remove. A user's own mise is left alone.~/.bash_profile, which hid~/.profile. The block now goes in~/.profilealways, plus~/.bash_profileor~/.bash_loginonly when one exists.~/.bashrcblock was below distro early-returns. It now goes at the top (newtopposition inupsert_managed_block).settings.toml.tmplproduced invalid TOML for maps and nulls. Maps now render as inline tables, and nulls are omitted. Validated throughmise settings get.1.10becoming1.1) are documented: versions must be quoted.github:atuinsh/atuin[matching=musl]), andtool=versionis split at the last=. The container end-to-end test caught this.Integration: 76 passed, 0 failed. The fixture now includes an options spec (atuin musl, asserted to run), a switch back from provider mise,
.bashrctop placement and the marker.