Skip to content

Conversation

@ahmedadan
Copy link

@ahmedadan ahmedadan commented Nov 17, 2025

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew lgtm (style, typechecking and tests) with your changes locally?

Adds support for Flatpak applications and remotes in Brewfile. This change extends Brewfile to support managing Flatpak applications and remotes, making it easier to maintain a consistent set of installed applications across Linux systems.

Sample Brewfile

brew "git"
brew "neovim"

# Add Flatpak custom remotes
flatpak_remote "flathub-beta", "https://flathub.org/beta-repo/flathub-beta.flatpakrepo"

# Install Flatpak applications
flatpak "org.gnome.Calculator"
flatpak "org.mozilla.firefox"
flatpak "com.spotify.Client"
flatpak "org.godotengine.Godot", remote: "flathub-beta"

Bundle Commands

All Brewfile commands now support Flatpak operations:

  • brew bundle install - Installs Flatpak applications and adds remotes
  • brew bundle dump - Exports installed Flatpak applications and remotes to Brewfile
  • brew bundle check - Verifies Flatpak applications are installed
  • brew bundle cleanup - Removes Flatpak applications and remotes not in Brewfile

Implementation Details

  • macOS handling: Flatpak entries are automatically skipped on macOS with appropriate warnings
  • Bundle modules: Adds installer, dumper, and checker modules for both Flatpak applications and remotes
  • Remote support: Flatpak applications can specify which remote to install from (defaults to "flathub")

Closes #21029

@gromgit
Copy link
Contributor

gromgit commented Nov 18, 2025

flatpak_remote "flathub", "https://flathub.org/repo/flathub.flatpakrepo"

Is this the default Flatpak repo? If so, I think it should not have to be specified, especially since most of your flatpak examples have no explicit remote refs.

@ahmedadan
Copy link
Author

flatpak_remote "flathub", "https://flathub.org/repo/flathub.flatpakrepo"

Is this the default Flatpak repo? If so, I think it should not have to be specified, especially since most of your flatpak examples have no explicit remote refs.

Good call, that is the default. I'll remove it from the example.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Thanks for the PR!

Some more questions:

  • do you have example Brewfiles for this?
  • can you describe why you're building this? Is it just for your personal use, your company's use, your distro's use, etc.? Just trying to get an understanding of how many people are wanting to use this and why. Hopefully you appreciate that adding ~1300 lines of code if it's mainly for your personal usage isn't going to work for us.

I do wonder if there's room for brew bundle to have its own extension system so it's easier/possible to maintain things like this in a Homebrew tap instead of in Homebrew/brew. Thoughts?

def self.dump
remotes.map do |remote|
# Always include URL for flatpak remotes
"flatpak_remote \"#{remote[:name]}\", \"#{remote[:url]}\""
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I understand why we need both flatpak_remote and flatpack ..., remote:. Can you explain? Do you have some example Brewfiles that you are expecting to use or create?

Is a flatpak_remote useful without a flatpak (and vice versa)?

Copy link
Author

Choose a reason for hiding this comment

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

I'm not sure I understand why we need both flatpak_remote and flatpack ..., remote:. Can you explain? Do you have some example Brewfiles that you are expecting to use or create?

Is a flatpak_remote useful without a flatpak (and vice versa)?

I was thinking of this in the same pattern as tap and brew. Just as all Homebrew formulae come from a tap, all flatpaks come from a remote. If we take the position that Flathub is the default remote (much like homebrew/core is the default tap), then:

  • flatpak_remote is analogous to tap - it registers a remote repository
  • flatpak is analogous to brew - it installs a flatpak, specifying which remote with remote:
  • Most flatpaks would come from the default Flathub remote (just as most formulae come from homebrew/core)
  • Custom remotes can be added with flatpak_remote, and flatpaks from those remotes can be installed with flatpak "app.id", remote: "custom-remote"

A flatpak_remote without any flatpak entries would still be useful for setting up the remote (you might add flatpaks later). Similarly, flatpak entries without an explicit flatpak_remote would work fine as long as they were available in the default Flathub remote.

Here is the Brewfile I use on my development machine generated by brew bundle dump:

flatpak_remote "flathub", "https://dl.flathub.org/repo/"
tap "agammemnon/tap"
brew "shellcheck"
brew "actionlint"
brew "gcc"
brew "shfmt"
vscode "anykeyh.simplecov-vscode"
vscode "ban.spellright"
vscode "davidanson.vscode-markdownlint"
vscode "editorconfig.editorconfig"
vscode "foxundermoon.shell-format"
vscode "github.vscode-github-actions"
vscode "github.vscode-pull-request-github"
vscode "koichisasada.vscode-rdbg"
vscode "ms-azuretools.vscode-containers"
vscode "ms-azuretools.vscode-docker"
vscode "ms-python.debugpy"
vscode "ms-python.python"
vscode "ms-python.vscode-pylance"
vscode "ms-python.vscode-python-envs"
vscode "ms-vscode-remote.remote-containers"
vscode "ms-vscode-remote.remote-ssh"
vscode "ms-vscode-remote.remote-ssh-edit"
vscode "ms-vscode.makefile-tools"
vscode "ms-vscode.remote-explorer"
vscode "redhat.vscode-yaml"
vscode "shopify.ruby-lsp"
vscode "sorbet.sorbet-vscode-extension"
vscode "timonwong.shellcheck"
flatpak "com.discordapp.Discord"
flatpak "com.github.PintaProject.Pinta"
flatpak "com.github.rafostar.Clapper"
flatpak "com.github.tchx84.Flatseal"
flatpak "com.mattjakeman.ExtensionManager"
flatpak "com.obsproject.Studio"
flatpak "com.ranfdev.DistroShelf"
flatpak "com.slack.Slack"
flatpak "io.github.dvlv.boxbuddyrs"
flatpak "io.github.flattool.Ignition"
flatpak "io.github.flattool.Warehouse"
flatpak "io.github.gaheldev.Millisecond"
flatpak "io.github.getnf.embellish"
flatpak "io.github.kolunmi.Bazaar"
flatpak "io.gitlab.adhami3310.Impression"
flatpak "io.missioncenter.MissionCenter"
flatpak "io.podman_desktop.PodmanDesktop"
flatpak "me.iepure.devtoolbox"
flatpak "org.gnome.Calculator"
flatpak "org.gnome.Calendar"
flatpak "org.gnome.Characters"
flatpak "org.gnome.Connections"
flatpak "org.gnome.Contacts"
flatpak "org.gnome.DejaDup"
flatpak "org.gnome.FileRoller"
flatpak "org.gnome.Firmware"
flatpak "org.gnome.Logs"
flatpak "org.gnome.Loupe"
flatpak "org.gnome.Maps"
flatpak "org.gnome.NautilusPreviewer"
flatpak "org.gnome.Papers"
flatpak "org.gnome.SimpleScan"
flatpak "org.gnome.TextEditor"
flatpak "org.gnome.Weather"
flatpak "org.gnome.baobab"
flatpak "org.gnome.clocks"
flatpak "org.gnome.font-viewer"
flatpak "org.mozilla.Thunderbird"
flatpak "org.mozilla.firefox"
flatpak "org.openrgb.OpenRGB"
flatpak "page.tesk.Refine"
flatpak "sh.loft.devpod"
flatpak "us.zoom.Zoom"

Copy link
Member

Choose a reason for hiding this comment

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

Thanks! So you don't personally make use of any flatpack_remote beyond the default one (which I'd argue should be implicit like @gromgit did above)?

Naming/MethodParameterName:
AllowedNames:
- go
- flatpak
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure this is needed?

Copy link

@castrojo castrojo Nov 18, 2025

Choose a reason for hiding this comment

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

can you describe why you're building this?

Hi Mike, we ship homebrew by default in a family of Linux operating systems: Aurora, Bazzite, and Bluefin, total percentage is 1.99%, with about ~41k weekly device checkins

Our intent is to enable end users to be able to add their Flatpak gui apps to their Brewfiles so that there's just one file they can carry around with all their apps.

(Here's some back story, we're trying to round up volunteers around Linux brew users to help out, happy to discuss seperately! https://www.youtube.com/watch?v=viAuzuXir0U )

Copy link
Member

Choose a reason for hiding this comment

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

@castrojo Good context, thanks!
@ahmedadan What's the failure without this line?

Copy link
Author

Choose a reason for hiding this comment

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

I solved the issue and forgot to pull this out, it was an oversight on my end. I'll push a commit to resolve this shortly

@botantony
Copy link
Member

Does it really need flatpak_remote? I think having flatpak <package>, remote: where remote is a URL should be enough

@ahmedadan
Copy link
Author

I do wonder if there's room for brew bundle to have its own extension system so it's easier/possible to maintain things like this in a Homebrew tap instead of in Homebrew/brew. Thoughts?

I'd have to refer to your expertise here, I can see the value of an abstraction like this especially for smaller or more niche tools, I think with a system like Flatpaks and their universality on Linux distributions I think it's at the scale where this could be warranted.

If your hesitation is with maintaining this moving forward I'd definitely volunteer to do so if that would be any help. This also enables a level of curation for our family of distributions that would be invaluable.

@MikeMcQuaid
Copy link
Member

Does it really need flatpak_remote? I think having flatpak <package>, remote: where remote is a URL should be enough

Yeh, I agree here. I think it makes sense for taps because they are Homebrew primitives and some people would/do use them in Brewfiles without a formula or cask. What would you think about just specifying the format like suggested by @botantony?

If your hesitation is with maintaining this moving forward I'd definitely volunteer to do so if that would be any help. This also enables a level of curation for our family of distributions that would be invaluable.

Ok, good to know, I will CC you on relevant issues/PRs ❤️

@ahmedadan
Copy link
Author

Does it really need flatpak_remote? I think having flatpak <package>, remote: where remote is a URL should be enough

Yeh, I agree here. I think it makes sense for taps because they are Homebrew primitives and some people would/do use them in Brewfiles without a formula or cask. What would you think about just specifying the format like suggested by @botantony?

If your hesitation is with maintaining this moving forward I'd definitely volunteer to do so if that would be any help. This also enables a level of curation for our family of distributions that would be invaluable.

Ok, good to know, I will CC you on relevant issues/PRs ❤️

Does it really need flatpak_remote? I think having flatpak <package>, remote: where remote is a URL should be enough

That can definitely be done. What I was trying to avoid was the example below, which I find difficult to parse if each and every line needs to mention the remote by URL.

brew "git"
brew "neovim"

# Install Flatpak applications
flatpak "org.gnome.Calculator", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.mozilla.firefox", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "com.spotify.Client", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.godotengine.Godot", remote: "https://flathub.org/beta-repo/flathub-beta.flatpakrepo"
flatpak "io.github.dvlv.boxbuddyrs", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.github.flattool.Ignition", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.github.flattool.Warehouse", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.github.gaheldev.Millisecond", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.github.getnf.embellish", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.github.kolunmi.Bazaar", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.gitlab.adhami3310.Impression", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.missioncenter.MissionCenter", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "io.podman_desktop.PodmanDesktop", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "me.iepure.devtoolbox", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Calculator", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Calendar", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Characters", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Connections", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Contacts", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.DejaDup", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.FileRoller", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Firmware", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Logs", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Loupe", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Maps", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.NautilusPreviewer", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.Papers", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.SimpleScan", remote: "https://flathub.org/repo/flathub.flatpakrepo"
flatpak "org.gnome.TextEditor", remote: "https://flathub.org/repo/flathub.flatpakrepo"

Would either of you be open to FlatHub remaining the default and then only custom remotes using remote:?

We then could have something like:

flatpak "org.gnome.Calculator"
flatpak "org.mozilla.firefox"
flatpak "com.spotify.Client"
flatpak "org.godotengine.Godot", remote: "https://flathub.org/beta-repo/flathub-beta.flatpakrepo"
flatpak "io.github.dvlv.boxbuddyrs"
flatpak "io.github.flattool.Ignition"
flatpak "io.github.flattool.Warehouse"
flatpak "io.github.gaheldev.Millisecond"
flatpak "io.github.getnf.embellish"
flatpak "io.github.kolunmi.Bazaar"
flatpak "io.gitlab.adhami3310.Impression"
flatpak "io.missioncenter.MissionCenter"
flatpak "io.podman_desktop.PodmanDesktop"
flatpak "me.iepure.devtoolbox"
flatpak "org.gnome.Calculator"
flatpak "org.gnome.Calendar"
flatpak "org.gnome.Characters"
flatpak "org.gnome.Connections"
flatpak "org.gnome.Contacts"
flatpak "org.gnome.DejaDup"
flatpak "org.gnome.FileRoller"
flatpak "org.gnome.Firmware"
flatpak "org.gnome.Logs"
flatpak "org.gnome.Loupe"
flatpak "org.gnome.Maps"
flatpak "org.gnome.NautilusPreviewer"
flatpak "org.gnome.Papers"
flatpak "org.gnome.SimpleScan"
flatpak "org.gnome.TextEditor"

Thoughts? @MikeMcQuaid @botantony

@MikeMcQuaid
Copy link
Member

Would either of you be open to FlatHub remaining the default and then only custom remotes using remote:?

Yeh, this seems good/appropriate for now 👍🏻

@ahmedadan ahmedadan force-pushed the feat/add-flatpak-to-brewfile branch 2 times, most recently from ef17a37 to be20d8e Compare November 20, 2025 03:48
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

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

Thanks! Almost there.

@ahmedadan ahmedadan force-pushed the feat/add-flatpak-to-brewfile branch from be20d8e to 02412e8 Compare November 21, 2025 16:52
@ramcq
Copy link

ramcq commented Nov 22, 2025

Hey folks, Flatpak / Flathub person here - this is super exciting to see, and a very cool addition to brew! If you can excuse the driveby, I did have a couple of pieces of feedback. Of course, your project your choice, feel free to take or leave. 🙇

  1. It's really non-idiomatic to refer to remotes by URL in Flatpak land. You use a URL typically once to add a remote - think git - and then Flatpak has to pull and cache a bunch of metadata, verify GPG keyring, etc - and then you use your local name for it after that. If you don't want a separate verb to add a remote, could you perhaps consider a way to use a named remote with a fallback URL, and then use it after that? This would echo the semantic of flatpak remote-add --if-not-exists, e.g.
# defines the flathub-beta remote if it's not defined
flatpak "org.godotengine.Godot", remote: flathub-beta, repo: "https://flathub.org/beta-repo/flathub-beta.flatpakrepo"
# then use it for these ones
flatpak "io.github.dvlv.boxbuddyrs", remote: flathub-beta
flatpak "io.github.flattool.Ignition", remote: flathub-beta

There's one counter-example: if you use a .flatpakref file it basically encodes a remote + app tuple; in this case if you don't have that remote already, Flatpak adds the remote as app.name-origin which has a semantic of filtering the remote, and only using it for that app ID only. This would be maybe a less surprising way to support the "add just this one app from this URL" semantic, without the side-effect of installing or redefining a remote that's used for other apps.

# add this repo as a remote named org.nottrustworthy.CrazyApp-origin, which won't be used for other apps
flatpak "org.nottrustworthy.CrazyApp", repo: "https://repo.nottrustworthy.org/repo"
  1. If it's possible I'd also recommend trying to bunch up the installs from the same remote and issue them as one flatpak install invocation; each flatpak install transaction contains a number of one-time housekeeping & metadata update operations, which are very IO intensive. Issuing all of the flatpak installs in one process will speed things up by avoiding repeating these housekeeping tasks dozens of times.

Thanks for reading! Hopefully you find this helpful. -Rob

@MikeMcQuaid
Copy link
Member

Thanks for the input Rob!

  1. It's really non-idiomatic to refer to remotes by URL in Flatpak land. You use a URL typically once to add a remote - think git - and then Flatpak has to pull and cache a bunch of metadata, verify GPG keyring, etc - and then you use your local name for it after that.

In the case of brew bundle, though, a remote is not useful by itself. git is a good example; for 99% of use no-one ever has to care about non-origin remotes and the more this is forced into user's experience the more confused they are (citation: I authored a published Git book, I have taught 10+ in-person workshops).

We don't want something added here that will make the Brewfile hard to e.g. remove one line. As a result, using the same URL and deduping internally makes more sense to me here.

2. If it's possible I'd also recommend trying to bunch up the installs from the same remote and issue them as one flatpak install invocation

This doesn't really work for us as 1) it will not be possible to identify easily which invocation failed without output parsing and 2) this is different to how all other brew bundle tool invocations are.

The common theme in the above: it's less important to map to how Flatpak does things as how brew bundle does things.

@ramcq
Copy link

ramcq commented Nov 24, 2025

In the case of brew bundle, though, a remote is not useful by itself. git is a good example; for 99% of use no-one ever has to care about non-origin remotes and the more this is forced into user's experience the more confused they are (citation: I authored a published Git book, I have taught 10+ in-person workshops).

No objections here! 😆 My team ends up teaching Git to highschoolers, and when there is more than one repo, things go south very quickly.

We don't want something added here that will make the Brewfile hard to e.g. remove one line. As a result, using the same URL and deduping internally makes more sense to me here.

This makes sense. With that as the criteria, and expecting that 99.999% of installs will just want the apps off Flathub, maybe the least worst semantic is:

  • no URL ⇒ use Flathub
  • URL ⇒ define it as a single-app remote (safest)
  • URL + a name ⇒ define it as a named remote if this URL isn't already available (unambiguous)
  1. If it's possible I'd also recommend trying to bunch up the installs from the same remote and issue them as one flatpak install invocation

This doesn't really work for us as 1) it will not be possible to identify easily which invocation failed without output parsing and 2) this is different to how all other brew bundle tool invocations are.

Fair enough! Just an optimization in any case. At the API level, you definitely can do discrete install operations without the hooks/pruning, but I'm not immediately seeing that on the CLI. And I entirely appreciate that accessing the Flatpak API is not a practical approach here!

The common theme in the above: it's less important to map to how Flatpak does things as how brew bundle does things.

👌

@MikeMcQuaid
Copy link
Member

My team ends up teaching Git to highschoolers, and when there is more than one repo, things go south very quickly.

Ooof, good job, rather you than me!

  • no URL ⇒ use Flathub
  • URL ⇒ define it as a single-app remote (safest)
  • URL + a name ⇒ define it as a named remote if this URL isn't already available (unambiguous)

This makes sense to me. @ahmedadan thoughts?

@ahmedadan
Copy link
Author

Thanks for the thoughtful discussion @ramcq @MikeMcQuaid! I really like where this landed. The 3-tier approach makes a lot of sense, just to make sure I understand we're looking at the following:

# Tier 1: Flathub (default)
flatpak "org.gnome.Calculator"

# Tier 2: Single-app remote - creates "org.godotengine.Godot-origin"
flatpak "org.godotengine.Godot", remote: "https://dl.flathub.org/beta-repo/"

# Tier 3: Named shared remote - creates "flathub-beta" if not exists
flatpak "org.godotengine.Godot", remote: "flathub-beta", url: "https://dl.flathub.org/beta-repo/"
flatpak "io.github.dvlv.boxbuddyrs", remote: "flathub-beta"  # reuses existing remote

The dumper will detect the remote type and output the appropriate format:

  • Flathub packages → flatpak "app" (clean, no remote needed)
  • Single-app remotes (*-origin) → flatpak "app", remote: "https://..."
  • Named remotes → flatpak "app", remote: "name", url: "https://..." (first occurrence) then flatpak "app", remote: "name" (subsequent)

One question: for Tier 3, should we validate that the url: matches if the named remote already exists?

Options:

  1. Warn only - "Remote 'flathub-beta' exists with different URL, using existing"
  2. Error - "Remote 'flathub-beta' exists with different URL, please resolve"
  3. Silent - Trust the user, use whatever's there

I'm leaning toward (1) for safety with flexibility. Thoughts?

@MikeMcQuaid
Copy link
Member

@ahmedadan All sounds good to me!

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.

brew bundle: Add support for Flatpak packages and remotes

6 participants