Skip to content

fix(brew): support cask completion stanzas, system_command, and elevated artifact links - #11273

Merged
jdx merged 2 commits into
mainfrom
claude/mise-failure-investigation-b5a972
Jul 24, 2026
Merged

jdx merged 2 commits into
mainfrom
claude/mise-failure-investigation-b5a972

Conversation

@jdx

@jdx jdx commented Jul 24, 2026 •

Copy link
Copy Markdown
Owner

Fixes the two remaining failures installing the docker-desktop cask from #11168 (after #11174 and #11219).

1. Cask shim rejected completion stanzas

The lifecycle shim evaluates the entire cask DSL to reach preflight/postflight blocks, but had no stubs for bash_completion, zsh_completion, fish_completion, or manpage, so any cask using them died in method_missing:

Error: cask uses `bash_completion`, which mise's cask shim does not support

These artifacts are linked natively by the Rust side, so the shim now parses them as no-ops (same as app/binary/pkg).

2. Cask shim had no system_command

docker-desktop's postflight (and uninstall_postflight) call system_command, e.g.:

postflight do
  kubectl_target = Pathname("/usr/local/bin/kubectl")
  next if kubectl_target.exist?
  system_command "/bin/ln", args: ["-sfn", appdir/"Docker.app/Contents/Resources/bin/kubectl", kubectl_target],
                 sudo: !kubectl_target.dirname.writable?
end

The shim now implements system_command (mirroring Homebrew's SystemCommand.run!) with args:, sudo:, env:, must_succeed:, print_stdout:/print_stderr:/verbose:, input:, and chdir:; unknown options still fail loudly. Elevation follows mise's sudo policy, passed down from Rust via MISE_BREW_CASK_SUDO (interactive/noninteractive/deny), so it respects system_packages.sudo and never hangs on a password prompt without a TTY.

3. Artifact links into root-owned dirs failed with EACCES

docker-desktop hardcodes absolute binary targets under /usr/local (/usr/local/bin/docker, /usr/local/cli-plugins/docker-compose, …). On Apple Silicon /usr/local/bin is typically owned by root:wheel, so backing up the existing target and creating the new symlink failed:

mise ERROR failed rename: /usr/local/bin/docker -> /usr/local/bin/.mise-link-backup-…
mise ERROR Permission denied (os error 13)

Homebrew elevates its cask ln/mkdir/rm/mv calls when the target directory is not writable; mise now does the same: artifact-target mutations (link transaction backups/rollback/commit, binary/completion symlinks, obsolete-link removal) retry through the controlled sudo::run path on permission errors.

Testing

  • New shim unit tests: docker-desktop-modeled cask (completions + postflight + system_command symlink + result object), sudo-denied error path, failed-command error path. Pass under macOS system ruby 2.6 and Homebrew portable-ruby 4.0.6 (the interpreter mise actually selects).
  • All 1894 unit tests pass; lint clean.
  • End-to-end on an arm64 mac (macOS 26.5.2): mise bootstrap packages apply -y brew-cask:docker-desktop with the real cask now downloads, extracts, installs Docker.app, and runs the real postflight through portable-ruby 4.0.6 successfully. Binary linking into root-owned /usr/local/bin correctly escalates; in a non-TTY session it fails cleanly with the copy-pasteable command (sudo mv -f -- /usr/local/bin/docker …) instead of a raw EACCES. On an interactive terminal (the reporter's setup, where the fix(brew): remove protected cask app backups #11219 sudo chown prompt already worked via Touch ID) the same path prompts and proceeds.

🤖 Generated with Claude Code


Note

Medium Risk
Changes run sudo-backed file mutations and lifecycle hooks on system paths; behavior is gated by system_packages.sudo and TTY policy, but mis-elevation or symlink targets could still affect the host install layout.

Overview
Improves brew-cask installs for casks like docker-desktop by extending the Ruby lifecycle shim and retrying root-owned artifact paths with mise’s existing sudo policy.

The cask shim now treats bash_completion, zsh_completion, fish_completion, and manpage as parse-only no-ops (Rust still links completions), and adds system_command with argv execution, optional sudo:, and elevation driven by MISE_BREW_CASK_SUDO (interactive / noninteractive / deny) from new sudo::subprocess_mode().

On the Rust side, binary/completion symlinks and link-transaction mkdir / ln / mv / rm retry through sudo::run on permission errors, matching Homebrew when targets live under directories such as /usr/local/bin. Unit tests cover completion + system_command postflight, denied sudo, and failed commands.

Reviewed by Cursor Bugbot for commit bffa4ba. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Enhanced cask lifecycle handling with completion stanzas and a new system command execution capability that captures stdout/stderr and supports optional elevated runs.
    • Added smarter sudo elevation behavior (interactive vs non-interactive vs denied) for helper subprocesses.
  • Bug Fixes

    • Fixed permission-denied issues during cask artifact operations (create/move/remove/link) by retrying with elevated execution when appropriate.
    • Improved error reporting when sudo is denied or required commands fail, including actionable guidance.
  • Tests

    • Expanded coverage for cask completion + system command interactions and sudo-denied scenarios.

…ted artifact links

Fixes the remaining failures installing the docker-desktop cask reported in
#11168:

- The cask lifecycle shim rejected any cask using bash_completion,
  zsh_completion, fish_completion, or manpage stanzas because they hit
  method_missing while evaluating the DSL to reach preflight/postflight
  blocks. These artifacts are linked natively by Rust, so the shim now
  parses them as no-ops.
- Lifecycle hooks calling system_command (e.g. docker-desktop's postflight
  kubectl symlink) failed for the same reason. The shim now implements
  system_command with args:/sudo:/env:/must_succeed:/print_stdout:/input:/
  chdir: support. Elevation follows mise's sudo policy, passed down from
  Rust via MISE_BREW_CASK_SUDO (interactive/noninteractive/deny), so it
  never prompts without a TTY and respects system_packages.sudo.
- Linking binary/completion artifacts into root-owned directories (e.g.
  /usr/local/bin on Apple Silicon, where docker-desktop hardcodes absolute
  targets) failed with EACCES. Artifact target mutations now retry through
  the controlled sudo path when they hit a permission error, matching
  Homebrew's behavior of elevating its cask ln/mkdir/rm/mv calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Cask lifecycle hooks now execute system commands with captured results and configurable sudo behavior. Rust cask artifact linking and transaction cleanup retry permission-denied filesystem operations through sudo-aware helpers, with completion and command execution tests expanded.

Changes

Cask elevation flow

Layer / File(s) Summary
Cask shim command DSL
src/system/packages/brew/cask_shim.rb
Adds completion stanza stubs, system_command, SystemCommandResult, output handling, and sudo command construction.
Subprocess sudo policy
src/system/sudo.rs
Adds subprocess_mode() for interactive, noninteractive, or denied elevation policies.
Elevated artifact mutations
src/system/packages/brew/cask.rs
Propagates sudo mode to hooks and retries permission-denied linking, renaming, directory creation, backup, and removal operations; tests cover command and hook behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CaskHook
  participant CaskContext
  participant Open3
  participant ArtifactLinkTransaction
  CaskHook->>CaskContext: load hook with MISE_BREW_CASK_SUDO
  CaskContext->>Open3: execute system_command
  Open3-->>CaskContext: return command output and status
  CaskHook->>ArtifactLinkTransaction: link and manage cask artifacts
  ArtifactLinkTransaction-->>CaskHook: retry denied filesystem mutations with sudo
Loading

Possibly related PRs

  • jdx/mise#11197: Modifies overlapping cask artifact move and removal paths.
  • jdx/mise#11198: Extends the related cask completion parsing and linking pipeline.
  • jdx/mise#11219: Modifies overlapping permission-denied cask cleanup paths.

Poem

I hop through hooks where completions bloom,
While commands safely cross Ruby’s room.
When guarded paths refuse the light,
Sudo helps links land just right.
The cask garden grows tonight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main brew cask changes: completion stanzas, system_command support, and elevated artifact link handling.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Extends Homebrew cask installation support.

  • Accepts completion and manpage stanzas in the Ruby lifecycle shim.
  • Implements lifecycle system_command execution under mise’s sudo policy.
  • Retries permission-denied artifact-link mutations through controlled elevation.

Confidence Score: 5/5

The PR appears safe to merge with no eligible blocking failures identified in this follow-up review.

No blocking failure remains.

Important Files Changed

Filename Overview
src/system/packages/brew/cask.rs Adds sudo-aware artifact mutation helpers, passes elevation policy to lifecycle hooks, and expands shim tests.
src/system/packages/brew/cask_shim.rb Adds completion stanza no-ops and argv-based system_command execution with result handling and controlled sudo support.
src/system/sudo.rs Exposes the configured interactive, noninteractive, or denied elevation mode to helper subprocesses.

Reviews (2): Last reviewed commit: "fix(brew): force argv execution in cask ..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/system/packages/brew/cask_shim.rb`:
- Around line 326-333: Update the Open3.capture3 invocation in the
system_command execution flow to force argv-based execution when args is empty
and sudo is not applied, passing the executable as separate argv elements rather
than a single command string. Preserve the existing sudo_argv transformation and
option handling for elevated or multi-argument commands.

In `@src/system/sudo.rs`:
- Around line 53-59: Update the elevation-mode logic associated with the
documented helper-subprocess policy to base “interactive” on available stdin or
a controlling password TTY, rather than console::user_attended_stderr() alone.
When prompting is not demonstrably safe, return “noninteractive” so callers use
sudo -n; preserve “deny” when elevation is disabled or sudo is unavailable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Central YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 8010ea77-1e7a-47c8-b763-5f3329ce7fdb

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce0198 and 07adfa0.

📒 Files selected for processing (3)
  • src/system/packages/brew/cask.rs
  • src/system/packages/brew/cask_shim.rb
  • src/system/sudo.rs

Comment thread src/system/packages/brew/cask_shim.rb Resolved
Comment thread src/system/sudo.rs Resolved
A single-element argv passed to Open3.capture3 is treated as a shell
command line, breaking executables whose path contains spaces and
inviting shell interpretation. Pass [argv[0], argv[0]] to force argv
execution regardless of argument count.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jdx
jdx merged commit 2685909 into main Jul 24, 2026
34 checks passed
@jdx
jdx deleted the claude/mise-failure-investigation-b5a972 branch July 24, 2026 18:18
@github-actions

Copy link
Copy Markdown
Contributor

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.13 x -- echo 20.4 ± 1.3 18.0 28.6 1.00
mise x -- echo 22.1 ± 2.2 19.6 53.1 1.08 ± 0.13

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.13 env 18.4 ± 1.0 16.3 27.6 1.00
mise env 20.1 ± 1.4 17.7 30.3 1.09 ± 0.10

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.13 hook-env 19.6 ± 1.4 17.4 28.6 1.00
mise hook-env 20.7 ± 1.2 18.4 29.3 1.06 ± 0.10

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.7.13 ls 16.4 ± 0.8 14.7 22.1 1.00
mise ls 17.8 ± 0.9 16.0 25.6 1.08 ± 0.08

xtasks/test/perf

Command mise-2026.7.13 mise Variance
install (cached) 162ms 172ms -5%
ls (cached) 59ms 61ms -3%
bin-paths (cached) 60ms 62ms -3%
task-ls (cached) 127ms 130ms -2%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant