0.0
The project is in a healthy, maintained state
Wraps the commands Capistrano runs on a host with `mise exec`, so they resolve their tool versions from the mise config that shipped with the revision.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

~> 13.0
~> 3.0

Runtime

 Project Readme

capistrano-mise

Runs Capistrano's remote commands through mise, so they resolve their tool versions from the mise config that shipped with the revision being deployed.

# Gemfile
gem "capistrano-mise", github: "7a6163/capistrano-mise", require: false
# Capfile — after capistrano/deploy, which defines the tasks this hooks
require "capistrano/deploy"
require "capistrano/mise"

That is the whole setup. There is no version to configure: mise.toml or .tool-versions in your repository already says which versions to run, and that file is in the release directory by the time anything runs. A setting naming the version would be a second source of truth that can silently disagree with the repository, so there isn't one.

mise must already be installed on your hosts. Installing it is provisioning's job, the same way capistrano-rbenv does not install rbenv.

What it does

  • Prefixes the mapped commands with mise exec --, so bundle install becomes $HOME/.local/bin/mise exec -- bundle install. The prefix goes in front of any prefix another plugin added, so capistrano-bundler's bundle exec still applies.
  • Adds mise:check after deploy:check, which fails the deploy if mise is not executable where it is expected.
  • Adds mise:install after deploy:updating, which runs mise install in the new release so the versions that revision declares are present before anything tries to use them. This installs the tools mise manages, not mise itself — same as the CLI command it is named after.
  • Sets MISE_EXEC_AUTO_INSTALL=0. Mise installs missing tools on demand by default, which would bury a multi-minute Ruby build inside whichever command happened to run first; mise:install owns installation instead, as a step you can see.

Settings

Setting Default
:mise_path $HOME/.local/bin/mise Absolute path to the mise binary. A host provisioned from a package manager has it at /usr/bin/mise or /usr/local/bin/mise and must say so.
:mise_map_bins %w[rake gem bundle ruby rails] Which commands to run through mise.
:mise_roles :all Which roles mise:check and mise:install run on. See the note below.

:mise_roles does not limit the mise exec prefix. SSHKit keeps one command map for every host, so once mise:map_bins has run, bundle, rake and the rest go through mise wherever they run. Narrowing :mise_roles only narrows where mise is checked for and installed into. Every host that runs a mapped command needs mise, whatever its role. capistrano-rbenv's :rbenv_roles behaves the same way, for the same reason.

:mise_map_bins is Ruby-only on purpose. Adding node here when your repo does not declare node means mise exec -- node quietly falls back to whatever node is on the host's PATH — worse than not mapping it. Add a tool here once the repo declares it:

set :mise_map_bins, fetch(:mise_map_bins) + %w[node npm yarn]

What it does not do

Capistrano can only wrap the commands it runs itself. Anything else on the host that runs your application — a systemd unit's ExecStart, a crontab entry, a login shell — never passes through Capistrano and is unaffected by this plugin. A deploy can succeed completely while every one of those still runs the wrong Ruby.

Each needs its own arrangement:

# systemd: call mise explicitly, with an absolute path
ExecStart=/home/deploy/.local/bin/mise exec -- bundle exec puma -C /path/to/puma.rb
# login shells (which is what `bash -l -c` in a crontab gets): put mise's shims on PATH,
# in ~/.profile or /etc/profile.d/mise.sh
export PATH="$HOME/.local/share/mise/shims:$PATH"

Use shims rather than eval "$(mise activate bash)" there: activation recomputes the environment when a prompt is displayed, and a cron job never displays one.

Gotcha: .ruby-version alone is not enough

Mise does not read .ruby-version, .nvmrc and friends unless you opt in per tool, so a repository carrying only a .ruby-version resolves to no version at all. mise:install warns when it sees this. Either add a mise.toml / .tool-versions, or opt in on the host:

mise settings add idiomatic_version_file_enable_tools ruby

Migrating from capistrano-rbenv or capistrano-rvm

Swap the require, then move the version declaration out of your deploy config and into the repository — set :rvm_ruby_version, "3.3.12" becomes a line in mise.toml or .tool-versions. This is the only part that is not mechanical, and it is deliberate.

If your systemd unit was generated by capistrano3-puma, its ExecStart contains whatever prefix your old plugin contributed (/usr/local/rvm/bin/rvm 3.3.12 do …) baked in at the time it was written. Update it by hand, and delete any Environment="PATH=…" line pointing into the old version manager's directories — mise exec sets up the child's PATH itself, and those directories are about to stop existing.

Releasing

Bump lib/capistrano/mise/version.rb, commit, then tag:

git tag v0.1.0 && git push origin v0.1.0

That fires .github/workflows/release.yml, which publishes to RubyGems (over OIDC — there is no API key stored anywhere), to GitHub Packages, and as a GitHub Release with the .gem attached.

License

MIT.