Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
with:
persist-credentials: false

- name: Check REUSE compliance
- name: Run ShellCheck
shell: bash
run: |
find . -type f -name '*.sh' -execdir shellcheck '{}' +
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ after installation (such as setting up the Homebrew cache and running

If Homebrew has not already been set up on the user's system, a systemd-tmpfiles
configuration copies this installation to `/home/linuxbrew` and transfers
ownership of it to UID 1000. The package also sets up systemd services to
automatically update Homebrew, as well as shell completions for the bash and
fish shells.
ownership of it to the `linuxbrew` user. The package also sets up systemd
services to automatically update Homebrew, as well as shell completions for the
bash and fish shells.

To make it more convenient for authorized users to manage the Homebrew
installation, the `brew-proxy` package can be installed. This includes a
DBus-activated service that allows authorized users to execute `brew` commands
as the `linuxbrew` user via the `brew-proxy` command.

## Credit

Expand Down
18 changes: 15 additions & 3 deletions etc/profile.d/brew.sh
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#!/usr/bin/env bash
#!/bin/sh

# SPDX-FileCopyrightText: Copyright 2025 Universal Blue
# SPDX-FileCopyrightText: Copyright 2025 The BlueBuild Authors
# SPDX-FileCopyrightText: Copyright 2026 Daniel Hast
#
# SPDX-License-Identifier: Apache-2.0

if [[ -d /home/linuxbrew/.linuxbrew && $- == *i* && "$(/usr/bin/id -u)" != 0 ]]; then
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ] && [ "$(/usr/bin/id -u)" != 0 ]; then
case "$-" in
*i*)
# Begin output of `brew shellenv bash` (lightly edited for formatting)
export HOMEBREW_PREFIX='/home/linuxbrew/.linuxbrew'
export HOMEBREW_CELLAR='/home/linuxbrew/.linuxbrew/Cellar'
export HOMEBREW_REPOSITORY='/home/linuxbrew/.linuxbrew/Homebrew'
export PATH="/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin${PATH+:$PATH}";
[ -z "${MANPATH-}" ] || export MANPATH=":${MANPATH#:}"
export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:${INFOPATH:-}"
# End output of `brew shellenv bash`
;;
esac
fi
43 changes: 26 additions & 17 deletions homebrew-template.spec
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Requires: gcc
Requires: git-core >= 2.7.0
Requires: zstd
%{?systemd_requires}
Recommends: brew-proxy

# Filter out unwanted automatic dependencies. For documentation, see:
# https://docs.fedoraproject.org/en-US/packaging-guidelines/AutoProvidesAndRequiresFiltering/
Expand All @@ -52,6 +53,7 @@ patch -p0 < homebrew-install.patch
mkdir .linuxbrew
env -i HOME=/home/linuxbrew PATH=/usr/bin:/bin:/usr/sbin:/sbin NONINTERACTIVE=1 \
HOMEBREW_BREW_LOCAL_GIT_REMOTE="${PWD}/brew.git" /bin/bash ./homebrew-install.sh
chmod -R u=rwX,go=rX .linuxbrew

%install
# main brew installation
Expand All @@ -62,46 +64,53 @@ cp -a .linuxbrew %{buildroot}%{_datadir}/homebrew
install -Dp -m 644 -t %{buildroot}%{_sysconfdir}/homebrew etc/homebrew/brew.env

# systemd units for automatic brew updates
install -Dp -m 644 -t %{buildroot}%{_userunitdir} usr/lib/systemd/user/*
install -Dp -m 644 -t %{buildroot}%{_unitdir} usr/lib/systemd/system/*

# brew shell environment and completions
install -Dp -m 644 -t %{buildroot}%{_sysconfdir}/profile.d etc/profile.d/brew*.sh
install -Dp -m 644 -t %{buildroot}%{_datadir}/fish/vendor_conf.d usr/share/fish/vendor_conf.d/brew-fish-completions.fish

# systemd-sysusers
install -Dp -m 644 -t %{buildroot}%{_sysusersdir} usr/lib/sysusers.d/homebrew.conf

# systemd-tmpfiles
install -Dp -m 644 -t %{buildroot}%{_tmpfilesdir} usr/lib/tmpfiles.d/homebrew.conf

%post
%systemd_user_post brew-update.service
%systemd_user_post brew-update.timer
%systemd_user_post brew-upgrade.service
%systemd_user_post brew-upgrade.timer
%systemd_post brew-update.service
%systemd_post brew-update.timer
%systemd_post brew-upgrade.service
%systemd_post brew-upgrade.timer

%preun
%systemd_user_preun brew-update.service
%systemd_user_preun brew-update.timer
%systemd_user_preun brew-upgrade.service
%systemd_user_preun brew-upgrade.timer
%systemd_preun brew-update.service
%systemd_preun brew-update.timer
%systemd_preun brew-upgrade.service
%systemd_preun brew-upgrade.timer

%postun
%systemd_user_postun_with_reload brew-update.service
%systemd_user_postun_with_restart brew-update.timer
%systemd_user_postun_with_reload brew-upgrade.service
%systemd_user_postun_with_restart brew-upgrade.timer
%systemd_postun_with_reload brew-update.service
%systemd_postun_with_restart brew-update.timer
%systemd_postun_with_reload brew-upgrade.service
%systemd_postun_with_restart brew-upgrade.timer

%files
%doc README.md
%{_datadir}/homebrew
%{_userunitdir}/brew-update.service
%{_userunitdir}/brew-update.timer
%{_userunitdir}/brew-upgrade.service
%{_userunitdir}/brew-upgrade.timer
%{_unitdir}/brew-update.service
%{_unitdir}/brew-update.timer
%{_unitdir}/brew-upgrade.service
%{_unitdir}/brew-upgrade.timer
%{_datadir}/fish/vendor_conf.d/brew-fish-completions.fish
%{_sysusersdir}/homebrew.conf
%{_tmpfilesdir}/homebrew.conf
%config(noreplace) %{_sysconfdir}/homebrew
%config(noreplace) %{_sysconfdir}/profile.d/brew.sh
%config(noreplace) %{_sysconfdir}/profile.d/brew-bash-completions.sh

%changelog
* Mon Apr 06 2026 Daniel Hast <[email protected]>
- Switch to linuxbrew-owned installation
* Fri Feb 27 2026 Daniel Hast <[email protected]>
- Use tmpfiles.d in place of brew-setup.service
* Mon Feb 23 2026 Daniel Hast <[email protected]>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@

[Unit]
Description=Auto-update Brew binary
After=default.target
ConditionUser=1000
ConditionFileIsExecutable=/home/linuxbrew/.linuxbrew/bin/brew
After=multi-user.target
ConditionFileIsExecutable=|/home/linuxbrew/.linuxbrew/bin/brew
# If the path is a symbolic link, the above check has to follow the link, which
# fails on some systems due to SELinux not allowing the system service manager
# to read file contents in home directories. So we add a second triggering
# condition making the unit start if the path is a symbolic link.
ConditionPathIsSymbolicLink=|/home/linuxbrew/.linuxbrew/bin/brew

[Service]
User=linuxbrew
Type=oneshot
Environment=HOMEBREW_CELLAR=/home/linuxbrew/.linuxbrew/Cellar
Environment=HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
[Unit]
Description=Timer for updating Brew binary
Wants=timers.target
ConditionUser=1000

[Timer]
OnStartupSec=10min
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@

[Unit]
Description=Auto-upgrade Brew packages
After=default.target
ConditionUser=1000
ConditionFileIsExecutable=/home/linuxbrew/.linuxbrew/bin/brew
After=multi-user.target
ConditionFileIsExecutable=|/home/linuxbrew/.linuxbrew/bin/brew
# If the path is a symbolic link, the above check has to follow the link, which
# fails on some systems due to SELinux not allowing the system service manager
# to read file contents in home directories. So we add a second triggering
# condition making the unit start if the path is a symbolic link.
ConditionPathIsSymbolicLink=|/home/linuxbrew/.linuxbrew/bin/brew

[Service]
User=linuxbrew
Type=oneshot
Environment=HOMEBREW_CELLAR=/home/linuxbrew/.linuxbrew/Cellar
Environment=HOMEBREW_PREFIX=/home/linuxbrew/.linuxbrew
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
[Unit]
Description=Timer for upgrading Brew packages
Wants=timers.target
ConditionUser=1000

[Timer]
OnStartupSec=30min
Expand Down
5 changes: 5 additions & 0 deletions usr/lib/sysusers.d/homebrew.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: Copyright 2026 Daniel Hast
#
# SPDX-License-Identifier: Apache-2.0 OR MIT

u! linuxbrew - "Homebrew administrator,,,,umask=0022" /home/linuxbrew
6 changes: 3 additions & 3 deletions usr/lib/tmpfiles.d/homebrew.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
#
# SPDX-License-Identifier: Apache-2.0 OR MIT

d /home/linuxbrew 0755 1000 1000 - -
C /home/linuxbrew/.linuxbrew - 1000 1000 - /usr/share/homebrew/.linuxbrew
d /var/tmp/homebrew 0700 1000 1000 3d -
d /home/linuxbrew 0755 linuxbrew linuxbrew -
C /home/linuxbrew/.linuxbrew - linuxbrew linuxbrew - /usr/share/homebrew/.linuxbrew
d /var/tmp/homebrew 0700 linuxbrew linuxbrew 3d
27 changes: 20 additions & 7 deletions usr/share/fish/vendor_conf.d/brew-fish-completions.fish
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,31 @@

# SPDX-FileCopyrightText: Copyright 2025 Universal Blue
# SPDX-FileCopyrightText: Copyright 2025 The BlueBuild Authors
# SPDX-FishCopyrightText: Copyright 2026 Daniel Hast
#
# SPDX-License-Identifier: Apache-2.0

#shellcheck disable=all
if status --is-interactive; and test $(/usr/bin/id -u) -ne 0
if [ -d /home/linuxbrew/.linuxbrew ]
/home/linuxbrew/.linuxbrew/bin/brew shellenv fish | source
if test -d (brew --prefix)/share/fish/completions
set -p fish_complete_path (brew --prefix)/share/fish/completions
if status --is-interactive && not fish_is_root_user
if test -d '/home/linuxbrew/.linuxbrew'
# Begin output of `brew shellenv fish` (lightly edited for formatting)
set --global --export HOMEBREW_PREFIX '/home/linuxbrew/.linuxbrew'
set --global --export HOMEBREW_CELLAR '/home/linuxbrew/.linuxbrew/Cellar'
set --global --export HOMEBREW_REPOSITORY '/home/linuxbrew/.linuxbrew/Homebrew'
fish_add_path --global --move --path '/home/linuxbrew/.linuxbrew/bin' '/home/linuxbrew/.linuxbrew/sbin'
if test -n "$MANPATH[1]"
set --global --export MANPATH '' $MANPATH
end
if test -d (brew --prefix)/share/fish/vendor_completions.d
set -p fish_complete_path (brew --prefix)/share/fish/vendor_completions.d
if not contains '/home/linuxbrew/.linuxbrew/share/info' $INFOPATH
set --global --export INFOPATH '/home/linuxbrew/.linuxbrew/share/info' $INFOPATH
end
# End output of `brew shellenv fish`

if test -d '/home/linuxbrew/.linuxbrew/share/fish/completions'
set -p fish_complete_path '/home/linuxbrew/.linuxbrew/share/fish/completions'
end
if test -d '/home/linuxbrew/.linuxbrew/share/fish/vendor_completions.d'
set -p fish_complete_path '/home/linuxbrew/.linuxbrew/share/fish/vendor_completions.d'
end
end
end