<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Environment on Canh Dinh</title><link>https://blog.canhdinh.com/tags/environment/</link><description>Recent content in Environment on Canh Dinh</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 26 Jun 2026 20:45:36 +0700</lastBuildDate><atom:link href="https://blog.canhdinh.com/tags/environment/index.xml" rel="self" type="application/rss+xml"/><item><title>Getting Started With mise: Dev Tools, Environments, and Tasks</title><link>https://blog.canhdinh.com/posts/getting-started-with-mise/</link><pubDate>Fri, 26 Jun 2026 20:45:36 +0700</pubDate><guid>https://blog.canhdinh.com/posts/getting-started-with-mise/</guid><description>&lt;p&gt;If you have ever juggled &lt;code&gt;nvm&lt;/code&gt;, &lt;code&gt;pyenv&lt;/code&gt;, &lt;code&gt;rbenv&lt;/code&gt;, a pile of &lt;code&gt;.env&lt;/code&gt; files, and a &lt;code&gt;Makefile&lt;/code&gt; in the same project, &lt;a href="https://mise.jdx.dev/"&gt;&lt;code&gt;mise&lt;/code&gt;&lt;/a&gt; (pronounced &amp;ldquo;meez&amp;rdquo;, short for &lt;em&gt;mise-en-place&lt;/em&gt;) is worth a look. It replaces all of them with a single tool and a single &lt;code&gt;mise.toml&lt;/code&gt; config file.&lt;/p&gt;
&lt;p&gt;In this post I&amp;rsquo;ll introduce the three major concepts that make &lt;code&gt;mise&lt;/code&gt; useful day to day:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://mise.jdx.dev/dev-tools/"&gt;Dev tools&lt;/a&gt; — install and switch between runtimes like Node, Python, and Go.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mise.jdx.dev/environments/"&gt;Environments&lt;/a&gt; — load the right environment variables per directory.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mise.jdx.dev/tasks/"&gt;Tasks&lt;/a&gt; — a built-in task runner for builds, tests, linting, and scripts.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then I&amp;rsquo;ll walk through a small but fully runnable example that ties all three together.&lt;/p&gt;</description><content:encoded><![CDATA[<p>If you have ever juggled <code>nvm</code>, <code>pyenv</code>, <code>rbenv</code>, a pile of <code>.env</code> files, and a <code>Makefile</code> in the same project, <a href="https://mise.jdx.dev/"><code>mise</code></a> (pronounced &ldquo;meez&rdquo;, short for <em>mise-en-place</em>) is worth a look. It replaces all of them with a single tool and a single <code>mise.toml</code> config file.</p>
<p>In this post I&rsquo;ll introduce the three major concepts that make <code>mise</code> useful day to day:</p>
<ol>
<li><a href="https://mise.jdx.dev/dev-tools/">Dev tools</a> — install and switch between runtimes like Node, Python, and Go.</li>
<li><a href="https://mise.jdx.dev/environments/">Environments</a> — load the right environment variables per directory.</li>
<li><a href="https://mise.jdx.dev/tasks/">Tasks</a> — a built-in task runner for builds, tests, linting, and scripts.</li>
</ol>
<p>Then I&rsquo;ll walk through a small but fully runnable example that ties all three together.</p>
<h2 id="what-is-mise">What is mise?</h2>
<p><code>mise</code> is a polyglot tool manager, environment manager, and task runner rolled into one binary. It is a spiritual successor to <a href="https://mise.jdx.dev/dev-tools/comparison-to-asdf.html">asdf</a> (and is compatible with asdf&rsquo;s <code>.tool-versions</code> files), but written in Rust and considerably faster.</p>
<p>The core idea: you declare what a project needs in a <code>mise.toml</code> file, and <code>mise</code> makes sure those tools, versions, and environment variables are active whenever you&rsquo;re inside that directory.</p>
<h2 id="install-mise">Install mise</h2>
<p>On Linux or macOS:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl https://mise.run <span class="p">|</span> sh
</span></span></code></pre></td></tr></table>
</div>
</div><p>By default <code>mise</code> installs to <code>~/.local/bin</code>. Verify it:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">~/.local/bin/mise --version
</span></span><span class="line"><span class="cl"><span class="c1"># mise 2026.x.x</span>
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>See the <a href="https://mise.jdx.dev/installing-mise.html">installation guide</a> for other methods: Homebrew, <code>apt</code>, <code>dnf</code>, Snap, Nix, Windows, and more.</p>
</blockquote>
<h3 id="activate-mise-recommended">Activate mise (recommended)</h3>
<p><code>mise exec</code> works for one-off commands, but for interactive shells you&rsquo;ll want to <em>activate</em> <code>mise</code> so tools and env vars load automatically as you <code>cd</code> around.</p>
<p>Add the appropriate line to your shell&rsquo;s rc file:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># bash</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;eval &#34;$(~/.local/bin/mise activate bash)&#34;&#39;</span> &gt;&gt; ~/.bashrc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># zsh</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;eval &#34;$(~/.local/bin/mise activate zsh)&#34;&#39;</span> &gt;&gt; ~/.zshrc
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># fish</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s1">&#39;~/.local/bin/mise activate fish | source&#39;</span> &gt;&gt; ~/.config/fish/config.fish
</span></span></code></pre></td></tr></table>
</div>
</div><p>Restart your shell, then run a health check:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise doctor
</span></span></code></pre></td></tr></table>
</div>
</div><blockquote>
<p>For CI/CD, IDEs, and non-interactive scripts, <code>mise</code> also supports <a href="https://mise.jdx.dev/dev-tools/shims.html">shims</a> instead of shell activation.</p>
</blockquote>
<h2 id="concept-1-dev-tools">Concept 1: Dev tools</h2>
<p><code>mise</code> manages multiple versions of programming language runtimes and CLI tools on the same machine, then switches between them automatically based on the directory you&rsquo;re in.</p>
<p>Tools are declared in the <code>[tools]</code> section of <code>mise.toml</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">node</span> <span class="p">=</span> <span class="s1">&#39;24&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nx">python</span> <span class="p">=</span> <span class="s1">&#39;3&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nx">go</span> <span class="p">=</span> <span class="s1">&#39;latest&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>You rarely write this by hand. The <code>mise use</code> command adds tools for you and installs them:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># add to the current project&#39;s mise.toml</span>
</span></span><span class="line"><span class="cl">mise use node@24
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># install globally (~/.config/mise/config.toml)</span>
</span></span><span class="line"><span class="cl">mise use --global node@24
</span></span></code></pre></td></tr></table>
</div>
</div><p>To run a tool once without installing it permanently, use <code>mise exec</code> (aliased to <code>mise x</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise <span class="nb">exec</span> node@26 -- node -v
</span></span><span class="line"><span class="cl"><span class="c1"># downloads node 26 if needed, then prints v26.x.x</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="how-version-switching-works">How version switching works</h3>
<p>Once <code>mise</code> is activated, it walks up the directory tree looking for config files (<code>mise.toml</code>, <code>.tool-versions</code>, <code>.node-version</code>, etc.), resolves the requested versions, and prepends the correct binaries to your <code>PATH</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># inside a project pinned to node 20</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$PATH</span>
</span></span><span class="line"><span class="cl"><span class="c1"># /home/user/.local/share/mise/installs/node/20.x.x/bin:/usr/local/bin:...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">node -v
</span></span><span class="line"><span class="cl"><span class="c1"># v20.x.x</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Move to another project pinned to a different version and <code>node</code> changes automatically. Tools come from multiple <a href="https://mise.jdx.dev/dev-tools/backend_architecture.html">backends</a> — core, <code>aqua</code>, <code>npm</code>, <code>pipx</code>, <code>cargo</code>, <code>github</code>, and asdf plugins — so the same <code>mise use</code> workflow installs almost anything. Browse the <a href="https://mise.jdx.dev/registry.html">registry</a> to see what&rsquo;s available.</p>
<h2 id="concept-2-environments">Concept 2: Environments</h2>
<p><code>mise</code> can load environment variables per project from the <code>[env]</code> section of <code>mise.toml</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">NODE_ENV</span> <span class="p">=</span> <span class="s1">&#39;production&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nx">APP_PORT</span> <span class="p">=</span> <span class="s1">&#39;3000&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>When <code>mise</code> is activated, these are set automatically when you <code>cd</code> into the project and unset when you leave. You can also manage them from the CLI:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise <span class="nb">set</span> <span class="nv">NODE_ENV</span><span class="o">=</span>development
</span></span><span class="line"><span class="cl">mise <span class="nb">set</span>
</span></span><span class="line"><span class="cl"><span class="c1"># key       value        source</span>
</span></span><span class="line"><span class="cl"><span class="c1"># NODE_ENV  development  mise.toml</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mise <span class="nb">unset</span> NODE_ENV
</span></span></code></pre></td></tr></table>
</div>
</div><p>A few handy features:</p>
<ul>
<li>
<p><strong>Unset a variable</strong> inherited from a parent config by setting it to <code>false</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">NODE_ENV</span> <span class="p">=</span> <span class="kc">false</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Provide a fallback</strong> that only applies if the variable isn&rsquo;t already set, using <code>default</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">NODE_ENV</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">default</span> <span class="p">=</span> <span class="s2">&#34;development&#34;</span> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Load a dotenv file</strong> with the <code>_.file</code> directive:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">_</span><span class="p">.</span><span class="nx">file</span> <span class="p">=</span> <span class="s1">&#39;.env&#39;</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
<li>
<p><strong>Redact secrets</strong> so they don&rsquo;t leak into task output:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">SECRET</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">value</span> <span class="p">=</span> <span class="s2">&#34;my_secret&#34;</span><span class="p">,</span> <span class="nx">redact</span> <span class="p">=</span> <span class="kc">true</span> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div></li>
</ul>
<p>Because env vars resolve alongside tools, you get a single source of truth for &ldquo;what does this project need to run&rdquo; — versions <em>and</em> configuration together.</p>
<h2 id="concept-3-tasks">Concept 3: Tasks</h2>
<p><code>mise</code> has a built-in task runner, so you can replace a <code>Makefile</code> or a pile of npm scripts. Tasks run with the full <code>mise</code> context loaded — your tools and env vars are already on <code>PATH</code>.</p>
<p>Define tasks in the <code>[tasks]</code> section:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">build</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Build the project&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go build ./...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">test</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Run the test suite&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go test ./...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">depends</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;build&#34;</span><span class="p">]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Run them with <code>mise run</code> (aliased to <code>mise r</code>):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise run build
</span></span><span class="line"><span class="cl">mise run <span class="nb">test</span>     <span class="c1"># runs build first because of `depends`</span>
</span></span><span class="line"><span class="cl">mise tasks        <span class="c1"># list all available tasks</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="task-dependencies">Task dependencies</h3>
<p>The <code>depends</code> array is what turns a list of tasks into a build graph. When you run a task, <code>mise</code> first runs everything in its <code>depends</code> list, and because dependencies execute <strong>in parallel by default</strong>, independent steps don&rsquo;t block each other:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;golangci-lint run ./...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">test</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go test ./...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">ci</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Run lint and test, then build&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">depends</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;lint&#34;</span><span class="p">,</span> <span class="s2">&#34;test&#34;</span><span class="p">]</span>   <span class="c"># these two run concurrently</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go build ./...&#34;</span>        <span class="c"># runs only after both succeed</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Running <code>mise run ci</code> executes <code>lint</code> and <code>test</code> at the same time, waits for both to finish, and only then runs <code>ci</code>&rsquo;s own <code>run</code> command. There are related keys too — <code>depends_post</code> for cleanup steps that run afterwards, and <code>wait_for</code> for soft ordering without forcing a task to run. See <a href="https://mise.jdx.dev/tasks/task-configuration.html">task dependencies</a> for the full list.</p>
<p>Some other highlights:</p>
<ul>
<li>
<p><strong>File watching</strong> with <code>mise watch</code> to rerun a task when sources change.</p>
</li>
<li>
<p><strong>File tasks</strong> — instead of inlining shell into TOML strings, you can drop an executable script into a <code>mise-tasks/</code> directory and get proper syntax highlighting and linting:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/usr/bin/env bash
</span></span></span><span class="line"><span class="cl"><span class="c1">#MISE description=&#34;Build the CLI&#34;</span>
</span></span><span class="line"><span class="cl">go build ./...
</span></span></code></pre></td></tr></table>
</div>
</div></li>
</ul>
<p>Tasks also receive useful variables like <code>MISE_PROJECT_ROOT</code> and <code>MISE_TASK_NAME</code>, so scripts can be location-independent.</p>
<h2 id="a-runnable-example">A runnable example</h2>
<p>Let&rsquo;s combine all three concepts into one small Go web project you can actually run. We&rsquo;ll also add <a href="https://golangci-lint.run/"><code>golangci-lint</code></a> as an additional tool to show how <code>mise</code> manages more than just language runtimes.</p>
<h3 id="1-create-the-project">1. Create the project</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mkdir mise-demo <span class="o">&amp;&amp;</span> <span class="nb">cd</span> mise-demo
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="2-add-the-tools">2. Add the tools</h3>
<p>Pin the exact Go version for this project. This creates <code>mise.toml</code> and installs Go if needed:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise use go@1.26.4
</span></span></code></pre></td></tr></table>
</div>
</div><p>Then add <code>golangci-lint</code> at a specific version. It lives in <code>mise</code>&rsquo;s <a href="https://mise.jdx.dev/registry.html">registry</a>, so the same <code>mise use</code> workflow installs it:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise use golangci-lint@2.12.2
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="3-configure-environment-and-tasks">3. Configure environment and tasks</h3>
<p>Open the generated <code>mise.toml</code> and make it look like this:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">settings</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lockfile</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">go</span> <span class="p">=</span> <span class="s2">&#34;1.26.4&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">golangci-lint</span> <span class="p">=</span> <span class="s2">&#34;2.12.2&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">env</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">APP_NAME</span> <span class="p">=</span> <span class="s2">&#34;mise-demo&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">APP_PORT</span> <span class="p">=</span> <span class="s2">&#34;8080&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">build</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Compile the web server&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go build -o bin/server .&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">lint</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Run golangci-lint&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;golangci-lint run ./...&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">serve</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Run the web server&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go run .&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tasks</span><span class="p">.</span><span class="nx">ci</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">description</span> <span class="p">=</span> <span class="s2">&#34;Lint and build (lint runs first)&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">depends</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;lint&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">run</span> <span class="p">=</span> <span class="s2">&#34;go build -o bin/server .&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <code>[tasks.ci]</code> task uses <code>depends</code> to guarantee linting happens before the build — a small example of the <a href="#task-dependencies">task dependencies</a> covered above.</p>
<h3 id="4-lock-the-tool-versions-with-miselock">4. Lock the tool versions with <code>mise.lock</code></h3>
<p>We set <code>lockfile = true</code> above, but lockfiles are <strong>not</strong> generated automatically. Run <code>mise lock</code> (or just <code>mise install</code>) to create one:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise lock
</span></span></code></pre></td></tr></table>
</div>
</div><p>This writes a <code>mise.lock</code> file next to <code>mise.toml</code>, pinning the exact resolved versions, checksums, and per-platform download URLs (truncated here for brevity):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># mise.lock</span>
</span></span><span class="line"><span class="cl"><span class="c"># @generated - this file is auto-generated by `mise lock`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">tools</span><span class="p">.</span><span class="nx">go</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;1.26.4&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">backend</span> <span class="p">=</span> <span class="s2">&#34;core:go&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">.</span><span class="nx">go</span><span class="p">.</span><span class="s2">&#34;platforms.linux-x64&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">checksum</span> <span class="p">=</span> <span class="s2">&#34;sha256:1153d3d50e0ac764b447adfe05c2bcf08e889d42a02e0fe0259bd47f6733ad7f&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">url</span> <span class="p">=</span> <span class="s2">&#34;https://dl.google.com/go/go1.26.4.linux-amd64.tar.gz&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">tools</span><span class="p">.</span><span class="nx">golangci-lint</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="nx">version</span> <span class="p">=</span> <span class="s2">&#34;2.12.2&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">backend</span> <span class="p">=</span> <span class="s2">&#34;aqua:golangci/golangci-lint&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">.</span><span class="nx">golangci-lint</span><span class="p">.</span><span class="s2">&#34;platforms.linux-x64&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">checksum</span> <span class="p">=</span> <span class="s2">&#34;sha256:8df580d2670fed8fa984aac0507099af8df275e665215f5c7a2ae3943893a553&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">url</span> <span class="p">=</span> <span class="s2">&#34;https://github.com/golangci/golangci-lint/releases/download/v2.12.2/golangci-lint-2.12.2-linux-amd64.tar.gz&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">provenance</span> <span class="p">=</span> <span class="s2">&#34;github-attestations&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Commit <code>mise.lock</code> alongside <code>mise.toml</code>. Now even loose specs (like <code>go = &quot;1&quot;</code>) resolve to the locked version on every machine, giving you reproducible installs. In CI you can go a step further with the <a href="https://mise.jdx.dev/configuration/settings.html#locked"><code>locked</code></a> setting (or <code>mise install --locked</code>) to fail fast if the lockfile is missing or incomplete.</p>
<h3 id="5-add-the-application-code">5. Add the application code</h3>
<p>Initialize the Go module:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">go mod init mise-demo
</span></span></code></pre></td></tr></table>
</div>
</div><p>Create <code>main.go</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;log&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;net/http&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;os&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="s">&#34;runtime&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">name</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">envOr</span><span class="p">(</span><span class="s">&#34;APP_NAME&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;unknown&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">port</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">envOr</span><span class="p">(</span><span class="s">&#34;APP_PORT&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;8080&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">http</span><span class="p">.</span><span class="nf">HandleFunc</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span><span class="w"> </span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span><span class="w"> </span><span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span><span class="w"> </span><span class="nx">r</span><span class="w"> </span><span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">if</span><span class="w"> </span><span class="nx">_</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintf</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello from %q, served by Go %s\n&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">runtime</span><span class="p">.</span><span class="nf">Version</span><span class="p">());</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">			</span><span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;write response: %v&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">})</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">addr</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="s">&#34;:&#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="nx">port</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">&#34;%s listening on http://localhost%s&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">name</span><span class="p">,</span><span class="w"> </span><span class="nx">addr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">http</span><span class="p">.</span><span class="nf">ListenAndServe</span><span class="p">(</span><span class="nx">addr</span><span class="p">,</span><span class="w"> </span><span class="kc">nil</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nf">envOr</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span><span class="w"> </span><span class="nx">fallback</span><span class="w"> </span><span class="kt">string</span><span class="p">)</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">if</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nf">Getenv</span><span class="p">(</span><span class="nx">key</span><span class="p">);</span><span class="w"> </span><span class="nx">v</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s">&#34;&#34;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="k">return</span><span class="w"> </span><span class="nx">v</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="nx">fallback</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="p">}</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Note that the handler checks the error returned by <code>fmt.Fprintf</code>. This isn&rsquo;t just good practice — <code>golangci-lint</code>&rsquo;s default <code>errcheck</code> linter would otherwise flag the unchecked return value and the <code>ci</code> task would fail at the lint step.</p>
<h3 id="6-run-it">6. Run it</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># confirm the tools are active</span>
</span></span><span class="line"><span class="cl">mise <span class="nb">exec</span> -- go version
</span></span><span class="line"><span class="cl"><span class="c1"># go version go1.26.4 ...</span>
</span></span><span class="line"><span class="cl">mise <span class="nb">exec</span> -- golangci-lint version
</span></span><span class="line"><span class="cl"><span class="c1"># golangci-lint has version 2.12.2 ...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># list the tasks mise discovered</span>
</span></span><span class="line"><span class="cl">mise tasks
</span></span><span class="line"><span class="cl"><span class="c1"># build  Compile the web server</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ci     Lint and build (lint runs first)</span>
</span></span><span class="line"><span class="cl"><span class="c1"># lint   Run golangci-lint</span>
</span></span><span class="line"><span class="cl"><span class="c1"># serve  Run the web server</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># lint then build in one shot (lint runs first via `depends`)</span>
</span></span><span class="line"><span class="cl">mise run ci
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># start the server (uses APP_PORT from [env])</span>
</span></span><span class="line"><span class="cl">mise run serve
</span></span><span class="line"><span class="cl"><span class="c1"># mise-demo listening on http://localhost:8080</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>In another terminal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl localhost:8080
</span></span><span class="line"><span class="cl"><span class="c1"># Hello from &#34;mise-demo&#34;, served by Go go1.26.4</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>That&rsquo;s the whole loop: <code>mise.toml</code> declares the <strong>tools</strong> (Go 1.26.4 plus <code>golangci-lint</code> 2.12.2), the <strong>environment</strong> (<code>APP_NAME</code>, <code>APP_PORT</code>), and the <strong>tasks</strong> (<code>build</code>, <code>lint</code>, <code>serve</code>, <code>ci</code>); <code>mise.lock</code> pins exactly what gets installed. Anyone who clones the repo and runs <code>mise install</code> followed by <code>mise run ci</code> gets the exact same setup — no README full of &ldquo;first install the right Go version, then <code>go install golangci-lint</code>, then&hellip;&rdquo; steps.</p>
<h3 id="7-clean-up-optional">7. Clean up (optional)</h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> .. <span class="o">&amp;&amp;</span> rm -rf mise-demo
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="bonus-global-quality-of-life-cli-tools">Bonus: global quality-of-life CLI tools</h2>
<p>So far we&rsquo;ve used <code>mise</code> for per-project toolchains, but it&rsquo;s just as handy for the CLI tools you want available <em>everywhere</em>. The <code>-g</code> (<code>--global</code>) flag installs a tool and records it in your global config at <code>~/.config/mise/config.toml</code> instead of a project <code>mise.toml</code>.</p>
<p>Here&rsquo;s a starter kit of quality-of-life tools, each pulled from <code>mise</code>&rsquo;s <a href="https://mise.jdx.dev/registry.html">registry</a> across different backends (core, aqua, npm, GitHub releases):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise use -g lazygit@latest    <span class="c1"># terminal UI for git</span>
</span></span><span class="line"><span class="cl">mise use -g fzf@latest        <span class="c1"># fuzzy finder</span>
</span></span><span class="line"><span class="cl">mise use -g ripgrep@latest    <span class="c1"># fast grep (rg)</span>
</span></span><span class="line"><span class="cl">mise use -g fd@latest         <span class="c1"># fast, friendly find</span>
</span></span><span class="line"><span class="cl">mise use -g gh@latest         <span class="c1"># GitHub CLI</span>
</span></span><span class="line"><span class="cl">mise use -g copilot@latest    <span class="c1"># GitHub Copilot CLI</span>
</span></span><span class="line"><span class="cl">mise use -g neovim            <span class="c1"># hyperextensible Vim-based editor</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Each command updates the same global config:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># ~/.config/mise/config.toml</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="nx">tools</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="nx">lazygit</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">fzf</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">ripgrep</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">fd</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">gh</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">copilot</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">neovim</span> <span class="p">=</span> <span class="s2">&#34;latest&#34;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Because this file lives in your dotfiles location, you can commit it to your dotfiles repo and reproduce your entire CLI toolbox on a new machine with a single command:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mise install
</span></span></code></pre></td></tr></table>
</div>
</div><p>A couple of tips:</p>
<ul>
<li>Run <code>mise ls</code> to see everything <code>mise</code> manages (global and local) and which versions are active.</li>
<li>Pin a tool to a real version instead of <code>latest</code> (e.g. <code>mise use -g fzf@0.56.3</code>) when you want reproducibility over always-newest.</li>
<li>Upgrade everything later with <code>mise upgrade</code>.</li>
</ul>
<h2 id="why-this-matters">Why this matters</h2>
<p>The payoff is reproducibility with almost no ceremony. A single committed <code>mise.toml</code> answers three questions at once:</p>
<ul>
<li>Which tool versions does this project use?</li>
<li>Which environment variables does it expect?</li>
<li>How do I build, test, and run it?</li>
</ul>
<p>New contributors run <code>mise install</code> and they&rsquo;re ready — and because <code>mise.lock</code> pins exact versions, they get the same toolchain you do. CI runs the same <code>mise run</code> commands as your laptop. And you stop maintaining three separate tools to do one job.</p>
<h2 id="useful-links">Useful links</h2>
<ul>
<li><a href="https://mise.jdx.dev/getting-started.html">mise — Getting Started</a></li>
<li><a href="https://mise.jdx.dev/dev-tools/">Dev Tools</a></li>
<li><a href="https://mise.jdx.dev/environments/">Environments</a></li>
<li><a href="https://mise.jdx.dev/tasks/">Tasks</a></li>
<li><a href="https://mise.jdx.dev/configuration.html">Configuration reference</a></li>
<li><a href="https://mise.jdx.dev/configuration/settings.html#lockfile">Lockfiles (<code>lockfile</code> setting)</a></li>
<li><a href="https://mise.jdx.dev/registry.html">Tool registry</a></li>
<li><a href="https://mise.jdx.dev/dev-tools/comparison-to-asdf.html">Comparison to asdf</a></li>
</ul>
]]></content:encoded></item></channel></rss>