Iām interested in adding the ability for Homebrew users to enable:
brew update happens intermittently in the background)These would all be utilizing launchctl plists, possibly through brew services. I have launchctl plist files that I use on my machine that implement 1, 2, and 4 at https://github.com/zbeekman/launchctl-tasks. Should we decide to add these capabilities, I would like feedback on some implementation details:
sh.brew.<name> is a good label, but I donāt know if this is appropriately canonical and if weāve used a different label naming scheme for services in the pasthomebrew-core,Homebrew/brew that users would then opt in to enable, orbrew aliases I have that will update and clean it up when I run e.g, brew upgrade <pkg>brew services start brew-unattended-upgrades? Do they run a special new command, like brew unattended-upgrades-on?More responsive and seamless user experience. More information available to users and sys admins.
Prefetching outdated bottles will make brew upgrade faster. Background/scheduled brew update will prevent brew update from running when the user invokes an arbitrary command, or at least reduce the frequency of this happening. This in turn will make the command run significantly faster, especially if there is a slow internet connection, etc. Unattended upgrades will allow users who want the latest and greatest version of a package to always have it, without manual effort. Logging and caching outdated packages (and updating this when they are upgraded or uninstalled) will allow the user to know which installed packages are out of date without running brew outdated which can be slow. (This could be done for mas app-store software and casks as well.)
1) Using formulae instead of changes to core
2) Using a tap instead of changes to core
3) A ledger of outdated software could be maintained and updated whenever the brew update command is run, and, entries to this ledger could be pruned when brew upgrade or brew uninstall are run. This would not necessarily require using a scheduled task with launchctl.
I would love feedback from @Homebrew/brew and @Homebrew/core maintainers on what they think of this proposal, and what the implementation should look like.
I'm intrigued by this idea. Is there some sort of capability on macOS to detect metered connections and suspend background updates? Also, would it be possible to just attach certain actions to complete "after" other commands run? I'm thinking of something like, a forked and backgrounded brew fetch that runs after brew update as a starting point.
cc @Homebrew/tsc for their thoughts
- The service label. Currently I think
sh.brew.<name>is a good label, but I donāt know if this is appropriately canonical and if weāve used a different label naming scheme for services in the past
homebrew.mxcl. is what we use for all formulae. homebrew.mxcl.background. could be used for these. Would be nice to share a prefix.
- b) as functionality within
Homebrew/brewthat users would then opt in to enable, or- c) as a separate Tap which can define additional commands
Either of these seem good. I'd consider a tap to start with (perhaps with analytics data enabled) and then could be moved to Homebrew/brew if widely used.
3. Where the scheduled tasks should write logs & data about what is upgraded, outdated and fetched, and if running brew commands can and should update & cleanup this data.
Somewhere under $(brew --prefix)/var/homebrew/.
4. Do they run
brew services start brew-unattended-upgrades? Do they run a special new command, likebrew unattended-upgrades-on?
One of these sounds good to me.
Background/scheduled
brew updatewill preventbrew updatefrom running when the user invokes an arbitrary command, or at least reduce the frequency of this happening.
I'm unconvinced by this. We'll currently run auto-update by default every 60 seconds if a user hasn't run it before. Presumably we're not planning on running this every 60 seconds (and I'd rather keep the default super low as it avoids issues from people who don't know better).
(This could be done for
masapp-store software and casks as well.)
I would strongly advise against scope creep for this. I'd either decide this is going to be for e.g. everything brew bundle supports from the outset or just for formulae/brew update as it'll violate expectations to start with one and move to another.
All that said I think this is a cool idea and something that clearly some people will want to use at some level.
@jonchang:
Is there some sort of capability on macOS to detect metered connections and suspend background updates?
Not that I know of, but I'm no expert. That seems like more of an ios concern than macOS, no? Also, that would be specific to your ISP. Maybe you could detect if you're tethering to an iPhone? But my plan is to have it run by default when most users are asleep, say between 2:30 and 3:30 AM.
Also, would it be possible to just attach certain actions to complete "after" other commands run?
Interesting idea. This proposal is focused more on the use of services though, so that the user's connection and CPU power spends less time doing homebrew stuff while they're awake.
@MikeMcQuaid:
I'd consider a tap to start with (perhaps with analytics data enabled) and then could be moved to Homebrew/brew if widely used.
Do you have a recommendation for a tap to look at as an example for someone who's relatively new to contributing to homebrew/brew (and not super proficient with Ruby too)? Right now I'm probably going to work from brew/homebrew-bundle or brew/homebrew-alias or similar. How does one enable tap analytics?
I'd either decide this is going to be for e.g. everything
brew bundlesupports from the outset or just for formulae/brew updateas it'll violate expectations to start with one and move to another.
I'll stick to dealing with formulae that brew update and brew upgrade would pull in by default. (i.e., core and user taps)
We'll currently run auto-update by default every 60 seconds if a user hasn't run it before. Presumably we're not planning on running this every 60 seconds (and I'd rather keep the default super low as it avoids issues from people who don't know better).
I may have miss-spoken. I'm not sure if it's due to git (or my particular git settings) homebrew, or something else, but I find, with just two background updates a day, when I run brew update by hand, it typically returns Already up-to-date. It's possible that other brew commands have triggered a background update. I'm not super familiar with the internals, of brew, as I mentioned above. But things seem more responsive. (Maybe it's just confirmation bias.)
Is there some sort of capability on macOS to detect metered connections
macOS comes with great tools to manage network settings (like networksetup) so it seems feasible.
On the other hand, maybe it isnāt. Thereās an app called TripMode to limit app access to the internet when on metered connections, but it doesnāt seem like they auto-detect said connections. Might be worth sending them an email and asking.
It's possible that other brew commands have triggered a background update. I'm not super familiar with the internals
Basically, just about every command runs update unless you tell it not to. Even if you just brew tap to see your current taps, it may run.
Basically, just about every command runs
updateunless you tell it not to. Even if you justbrew tapto see your current taps, it may run.
Is there logic within update to see how recently github was queried and return if a git fetch/pull has happened recently? (I'll go code spelunking to answer this....)
Do you have a recommendation for a tap to look at as an example for someone who's relatively new to contributing to homebrew/brew (and not super proficient with Ruby too)?
Sure. Homebrew/bundle, Homebrew/test-bot and Homebrew/services are the best supported external command taps e.g. with style enforcement, CI, etc.
Right now I'm probably going to work from brew/homebrew-bundle or brew/homebrew-alias or similar. How does one enable tap analytics?
Call a Utils::Analytics. method. I'd note in your README you're doing this just so people aren't surprised.
I may have miss-spoken. I'm not sure if it's due to git (or my particular git settings) homebrew, or something else, but I find, with just two background updates a day, when I run
brew updateby hand, it typically returnsAlready up-to-date. It's possible that other brew commands have triggered a background update. I'm not super familiar with the internals, of brew, as I mentioned above.
No, that makes sense, I don't think you've misspoken. There's basically three cases:
I thought you were suggesting 2 and 3 would be sped up whereas 2 will remain the same speed and 3 will be faster š
Basically, just about every command runs
updateunless you tell it not to. Even if you justbrew tapto see your current taps, it may run.
These are the relevant commands:
https://github.com/Homebrew/brew/blob/master/Library/Homebrew/brew.sh#L417-L418
In the "no argument brew tap case": we could handle that more intelligently and not run brew tap.
Is there logic within
updateto see how recently github was queried and return if a git fetch/pull has happened recently? (I'll go code spelunking to answer this....)
Yes: https://github.com/Homebrew/brew/blob/master/Library/Homebrew/cmd/update.sh#L450-L454
Note also this API that was added by GitHub (specifically by me š) to allow a HTTP call to see if a git fetch is necessary: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
We use this in brew update and it produces a non-trivial speedup.
Note also this API that was added by GitHub (specifically by me š) to allow a HTTP call to see if a
git fetchis necessary: developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference
š²š¤Æ
VERY nice
OK, well I'm off to finish working my way through the CodeCademy Ruby lessons, so I can do this in a non-dumb way. (Otherwise it would be almost all bash scripts, although very well-written bash scripts...)
Just a small comment: please try to implement this so that it works on Linux. Or at least decouple the part of the code that does Mac-specific stuff from the rest. It it easier to work with that code later if this is planned right from the beginning :)
In the "no argument
brew tapcase": we could handle that more intelligently and not runbrew tap.
Was going to work on a PR to not run update on tap with no arguments but then I though āwhy run on tap at allā? Why not remove that case entirely? Whatās the use case for tap that ever benefits of an update beforehand?
Just a small comment: please try to implement this so that it works on Linux. Or at least decouple the part of the code that does Mac-specific stuff from the rest.
It's going to end up generating and running launchctl plists. Each "command" should be a standalone Ruby or shell script (it can be shell @zbeekman) but actually running them will be system-specific.
Was going to work on a PR to not run
updateontapwith no arguments but then I though āwhy run ontap_at all_ā? Why not remove that case entirely? Whatās the use case fortapthat ever benefits of anupdatebeforehand?
We don't allow taps to be tapped that have invalid syntax (i.e. brew readall $TAP fails). Updating before a brew tap means that people do not file bugs saying they are unable to tap a tap with invalid syntax because they are on an older Homebrew.
In the "no argument
brew tapcase": we could handle that more intelligently and not runbrew tap.
Just a small comment: please try to implement this so that it works on Linux. Or at least decouple the part of the code that does Mac-specific stuff from the rest.
It's going to end up generating and running
launchctlplists. Each "command" should be a standalone Ruby or shell script (it can be shell @zbeekman) but actually running them will be system-specific.
Yes, in my mind there are two parts to this:
brew update happens in the backgroundbrew update happens in the background and brew outdated packages are brew fetchedbrew upgrade happens in the background.brew update as well as brew upgrade, brew uninstall and others.I plan to start with just the easy part (1) which is mac specific since it will use plist files with launchctl. A similar approach could probably be hashed out for Linuxbrew using either launchd or crontab but IIRC, there is not 100% adoption of launchd among distros. (This might be wrong or outdated.)
For 1, aka the easy part, it will be important that:
brew are encoded into the plists since they may be run as agents or daemons etc. (i.e. the user may not even be logged in)
- Decent logging happens in a sane way, you should be able to look at older logs, but without gumming up the user's drive space
Advice: rely on https://formulae.brew.sh/formula/logrotate.
(This could be done for
masapp-store software and casks as well.)
I'd either decide this is going to be for e.g. everything
brew bundlesupports from the outset or just for formulae/brew updateas it'll violate expectations to start with one and move to another.
I'll stick to dealing with formulae that
brew updateandbrew upgradewould pull in by default. (i.e., core and user taps)
@MikeMcQuaid I would definately vote for the former⦠I think it would be inevitable that this will be wanted for everthing, rather than only brew formulae⦠and having it only available for those surely complicates matters for users wanting to have casks and stuff installed withmas kept up to date too?
homebrew.mxcl.is what we use for all formulae.
Is there any reason for this other than @mxcl being the creator of Homebrew? Seems like brew.sh. would be more appropriateā¦
I have
launchctlplist files that I use on my machine that implement 1, 2, and 4 at https://github.com/zbeekman/launchctl-tasks.
@zbeekman We seem to share mostly the same goal here, which is to have everything stay up to date automatically in the background, utilising launchctl, while being able to see what has changed with logs (also my motivation behind https://github.com/Homebrew/brew/issues/5744). Although I would extend this to casks and stuff installed with mas as well, as mentioned above.
it would be almost all bash scripts, although very well-written bash scriptsā¦
actually running them will be system-specific.
I personally use zsh scripts for anything mac specific⦠they are so much cleaner and nicer to write than bash!
Is there any reason for this other than @mxcl being the creator of Homebrew? Seems like
brew.sh.would be more appropriateā¦
Typically, you reverse the FQDN, so I think sh.brew.blah would be more accurate.
I personally use zsh scripts for anything mac specific⦠they are so much cleaner and nicer to write than bash!
I've never ventured over to the dark side. However, the goal is to have fairly minimal contents in the plists, so, most likely, I would embed commands directly in the plist without any intermediate scripts. I've been working on my ruby, so I think I'll just use string interpolation and a template of some sort to accomplish this. In fact, I'd like to use your hash to plist functionality open in the other PR, but I have to figure out a sane implementation.
After thinking about this some more, I think it would most naturally fit into brew-services; when this is tapped it would provide some default services from Homebrew. i.e., brew services start brew-unattended-upgrades. But I'm still prototyping.
- a) Formula that just configure and install the plist files, that could be added to
homebrew-core
Yeah I've also been wrestling with the best way of implementing this overall⦠so far my thinking has been along the same lines as this⦠service only formulas that don't actually download anything (maybe url stage_only or something). I also actually made a simple command line utitlity to easily write these files (thinking I could depends_on "max-os/launch", and use it in post_install at one pointā¦
The use case of being able to have launchctl automatically keep everything up to date is only one of many though for me⦠I also want to have homebrew install a bunch of other scripts and plists that do other things, using my own taps⦠sometimes running an actual script file, but in other cases can just be a couple of commands written directly into the plist file, as you mention.
But my update script for example also handles stuff unrelated to Homebrew, like global npm packages, and RubyGems⦠so is a seperate script. I think the more we can seperate concerns here and keep the functionality more general, the more useful it will be for users.
Although I would extend this to
casks and stuff installed withmasas well, as mentioned above.
If that ends up being the case then my suggestion be it all be wrappers around brew bundle and most of the logic moved there. The codebase there is pretty easy to follow and well tested.
Is there any reason for this other than @mxcl being the creator of Homebrew? Seems like
brew.sh.would be more appropriate.
That's the legacy reason, yes, but consistency is valuable for its own right.
But my update script for example also handles stuff unrelated to Homebrew, like global
npmpackages, and RubyGems⦠so is a seperate script. I think the more we can seperate concerns here and keep the functionality more general, the more useful it will be for users.
Global npm and rubygems installation could also be added into brew bundle, perhaps.
Well, global npm and rubygems installation would be benefital for brew bundle, but there would be a higher chance that npm would fail, and we would have to write more files.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
please go away stalebot, I'm still working on this...
@zbeekman You can set an "in progress" label if you're working on it. I'd suggest this can perhaps be closed out if it's something you're working on yourself rather than writing up for others to implement, though.
You can set an "in progress" label if you're working on it.
Another way is to delete the botās message. Doing so auto-removes the label, the issue stays cleaner, and the countdown resets.
@vitorgalvao Nice trick, didnāt know that!
@MikeMcQuaid and @vitorgalvao thanks for the advice! I'd be happy to delete the message or remove the label, but I'm not currently a maintainer on Homebrew/brew, so I can't do that.
FWIW, I've been practicing my ruby, and am no longer terrified of pushing a catastrophic commit, so I'd happily take on that responsibility should you choose to entrust me with it... but I'm happy either way.
I'd suggest this can perhaps be closed out if it's something you're working on yourself rather than writing up for others to implement, though.
Fair. I think we hit most of the discussion points. I've put this on the back burner as I've been watching the plist work from @danielbayley progress; I figure it makes sense to let that stabilize a bit and then use it for this. We can close it if you like, but it's a nice reminder to me.
We can close it if you like, but it's a nice reminder to me.
Yeh, I think so, thanks. The issue tracker is best placed for tracking bugs or, in the case of features, those we're actively soliciting the community work on.
Most helpful comment
Sure. Homebrew/bundle, Homebrew/test-bot and Homebrew/services are the best supported external command taps e.g. with style enforcement, CI, etc.
Call a
Utils::Analytics.method. I'd note in your README you're doing this just so people aren't surprised.No, that makes sense, I don't think you've misspoken. There's basically three cases:
I thought you were suggesting 2 and 3 would be sped up whereas 2 will remain the same speed and 3 will be faster š
These are the relevant commands:
https://github.com/Homebrew/brew/blob/master/Library/Homebrew/brew.sh#L417-L418
In the "no argument
brew tapcase": we could handle that more intelligently and not runbrew tap.Yes: https://github.com/Homebrew/brew/blob/master/Library/Homebrew/cmd/update.sh#L450-L454
Note also this API that was added by GitHub (specifically by me š) to allow a HTTP call to see if a
git fetchis necessary: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-referenceWe use this in
brew updateand it produces a non-trivial speedup.