brew icon indicating copy to clipboard operation
brew copied to clipboard

Unable to migrate Formula-to-Cask within the same tap

Open philrz opened this issue 3 months ago • 3 comments

brew doctor output

$ brew doctor
Your system is ready to brew.

Verification

  • [x] I ran brew update twice and am still able to reproduce my issue.
  • [x] My "brew doctor output" above says Your system is ready to brew or a definitely unrelated Tier message.
  • [x] This issue's title and/or description do not reference a single formula e.g. brew install wget. If they do, open an issue at https://github.com/Homebrew/homebrew-core/issues/new/choose instead.

brew config output

$ brew config
HOMEBREW_VERSION: 5.0.5
ORIGIN: https://github.com/Homebrew/brew
HEAD: 70badad488056fb48cf687aa777f2880aac58fe9
Last commit: 2 days ago
Branch: stable
Core tap JSON: 10 Dec 00:59 UTC
Core cask tap JSON: 10 Dec 00:59 UTC
HOMEBREW_PREFIX: /opt/homebrew
HOMEBREW_CASK_OPTS: ["--no-quarantine"]
HOMEBREW_DOWNLOAD_CONCURRENCY: 6
HOMEBREW_FORBID_PACKAGES_FROM_PATHS: set
HOMEBREW_MAKE_JOBS: 3
HOMEBREW_NO_AUTO_UPDATE: set
HOMEBREW_NO_INSTALL_CLEANUP: set
Homebrew Ruby: 3.4.7 => /opt/homebrew/Library/Homebrew/vendor/portable-ruby/3.4.7/bin/ruby
CPU: 3-core 64-bit dunno
Clang: 17.0.0 build 1700
Git: 2.52.0 => /opt/homebrew/bin/git
Curl: 8.7.1 => /usr/bin/curl
macOS: 15.7.2-arm64
CLT: 16.4.0.0.1.1747106510
Xcode: 16.4 => /Applications/Xcode_16.4.app/Contents/Developer
Rosetta 2: false

What were you trying to do (and why)?

I'm trying to migrate users of an older Formula to a newer Cask in a one-shot operation. It's my understanding this functionality was enabled in https://github.com/Homebrew/brew/pull/20800 and specifically the tap_migrations.json config described here would have allowed this, but per my repro steps it did not seem to work.

What happened (include all command output)?

Starting from a state where the old Formula has been removed from the repo and the tap_migrations.json added to the repo, a brew upgrade returns no output. The binary associated with the old Formula remains installed.

What did you expect to happen?

I expected the binary associated with the old Formula to be removed and the binary associated with the Cask to be installed in its place.

Step-by-step reproduction instructions (by running brew commands)

The personal/scratch repo I'm using for repro is https://github.com/philrz/homebrew-tap.

As prep steps, this old super.rb Formula is installed on the host when no tap_migrations.json is yet present in the repo. This is the starting state I expect my production target users will be starting from. The super version string shown below is indicative of the binary from the old Formula being present.

$ brew install philrz/tap/super
==> Tapping philrz/tap
Cloning into '/opt/homebrew/Library/Taps/philrz/homebrew-tap'...
remote: Enumerating objects: 368, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 368 (delta 0), reused 1 (delta 0), pack-reused 364 (from 2)
Receiving objects: 100% (368/368), 42.55 KiB | 495.00 KiB/s, done.
Resolving deltas: 100% (168/168), done.
Tapped 1 cask and 1 formula (18 files, 67.2KB).
Warning: Treating philrz/tap/super as a formula. For the cask, use philrz/tap/super or specify the `--cask` flag. To silence this message, use the `--formula` flag.
==> Fetching downloads for: super
✔︎ Bottle Manifest [email protected] (1.24.11)                                    [Downloaded    7.5KB/  7.5KB]
✔︎ Formula super (4130d71)                                              [Verifying     6.4MB/  6.4MB]
✔︎ Bottle [email protected] (1.24.11)                                             [Downloaded   77.7MB/ 77.7MB]
==> Installing super from philrz/tap
==> Installing philrz/tap/super dependency: [email protected]
==> Pouring [email protected]_sequoia.bottle.tar.gz
🍺  /opt/homebrew/Cellar/[email protected]/1.24.11: 14,123 files, 256.0MB
==> GOPATH=$PWD/build go install github.com/brimdata/super/cmd/super@4130d71
🍺  /opt/homebrew/Cellar/super/4130d71: 7 files, 68.9MB, built in 1 minute 10 seconds

$ super -version
Version: v0.0.0-20250819180341-4130d7106717

At this point I remove the old super.rb Formula from the repo and add the tap_migrations.json to the repo. This represents the change I'd make when I want to users to be migrated from Formula to Cask as part of normal Homebrew update+upgrade workflow. With the repo in that updated state, as a user I do:

$ brew update
==> Updating Homebrew...
Updated 1 tap (philrz/tap).
No changes to formulae or casks.

$ brew upgrade

$ super -version
Version: v0.0.0-20250819180341-4130d7106717

As you can see, the brew upgrade generated no output, indicating there was no action taken due to the tap having been updated with what I was led to understand is the appropriate config to migrate this user to the Cask. The fact the old super version string is still present is further validation that nothing changed regarding the installed binary.

At this point, as a user I can force my way onto the Cask by manually doing:

$ brew remove super
Warning: Treating super as a formula. For the cask, use philrz/tap/super or specify the `--cask` flag. To silence this message, use the `--formula` flag.
Uninstalling /opt/homebrew/Cellar/super/4130d71... (7 files, 68.9MB)
==> Autoremoving 1 unneeded formula:
[email protected]
Uninstalling /opt/homebrew/Cellar/[email protected]/1.24.11... (14,123 files, 256.0MB)

$ brew install --cask philrz/tap/super
==> Downloading https://super-prereleases.s3.us-east-2.amazonaws.com/8012b6d/super-8012b6d.darwin-ar
############################################################################################# 100.0%
==> Installing Cask super
Warning: --no-quarantine bypasses macOS’s Gatekeeper, reducing system security. Do not use this flag unless you understand the risks.
==> Linking Binary 'super' to '/opt/homebrew/bin/super'
🍺  super was successfully installed!

$ super -version
Version: 8012b6d

That final super version string is indicative of now having the binary associated with the Cask installed. But this is effectively what I'd been hoping the result would have been when I ran brew upgrade after the migration config had been put in place.

philrz avatar Dec 10 '25 01:12 philrz

Thanks for opening @philrz!

MikeMcQuaid avatar Dec 10 '25 09:12 MikeMcQuaid

From quick glance, could need updates at: https://github.com/Homebrew/brew/blob/dc7c173da24384799ad09257db2515712fc9ebe0/Library/Homebrew/cmd/update-report.rb#L583-L591

Based on code, it may be possible to do following with current brew

{ "super": "philrz/tap" }

Though not tested so could be rejected elsewhere in brew.

cho-m avatar Dec 11 '25 16:12 cho-m

@cho-m: Thanks for giving it a look! Your comment made it sound like it was worth a test, so I just ran through the same repro steps in with my scratch repo while using your proposed { "super": "philrz/tap" } in my tap_migrations.json, but the result was the same as before: No output from brew upgrade and in the end am still left with the binary that was installed via the old Formula.

philrz avatar Dec 11 '25 18:12 philrz

Tap migration code is triggered via brew update when git revision changes.

However, it looks like there are a few issues, e.g.

  • After https://github.com/Homebrew/brew/commit/ae43415fb4d, deleted formulae that are tap migrated are no longer listed so the tap migration code is skipped, i.e. in following :D is for deleted formulae and :DC is for deleted casks: https://github.com/Homebrew/brew/blob/3419bb55512b351cad64018a16ee8884174912de/Library/Homebrew/cmd/update-report.rb#L577-L578
  • In https://github.com/Homebrew/brew/commit/48f7d5680cf954a2b33815a0a6294af68fe545ff the cask_tokens will be full name in 3rd party taps while name is short name.
  • And { "<name>": "<name>" } is not supported in update-report.rb

Above is at least one location we perform the tap migration handling. Not sure if there is somewhere else

cho-m avatar Dec 12 '25 17:12 cho-m

Following may help with running migration during brew update.

  • #21224

A couple of notes:

  1. Migration is only run when brew update moves the git repo to a new commit. So, just locally modifying a repo won't work. You will need to commit the change into remote and then pull it down via brew update.
  2. We intentionally do not uninstall previous formula and only inform user to run the command to avoid distrusting any active usage. What does occur is new Cask is installed and takes precedence on PATH.

cho-m avatar Dec 12 '25 20:12 cho-m

Thanks for the update @cho-m! That all sounds encouraging. Regarding your second bullet, since you said the Cask would get installed and take precedence it seems that'll get to the final state I was hoping for even if the old Formula remains installed. I'll definitely give this all a test and update here once #21224 merges and there's a release that includes it.

philrz avatar Dec 12 '25 21:12 philrz