Skip to content

Commit

Permalink
Enable non-admin and non-sudo bootstrap.sh usage
Browse files Browse the repository at this point in the history
The `bootstrap.sh` script in this repo previously required both admin
and sudo permissions. A plausible use case could exist in which an
admin runs `bootstrap.sh` to configure the system initially, then a
non-admin runs `bootstrap.sh` to configure their own account. In this
use case, the non-admin user should not need admin or sudo privileges,
because all the pertinent setup (FileVault disk encryption, XCode
developer tools, Homebrew, etc) is already complete.

This commit will update `bootstrap.sh` so that it can run successfully
without admin or sudo.

This means `bootstrap.sh` needs to detect both admin and sudo and adjust
accordingly. The command `groups | grep -qE "\b(admin)\b"` will be used
to detect admin. The method for detecting sudo will vary based on how
the script is running. When running in non-interactive mode
(`[ "$STRAP_INTERACTIVE" -eq 0 ]`), `sudo -n -l mkdir &>/dev/null` will
be used as a simple check for sudo. When running interactively, the
script will allow activation of sudo by TouchID or password entry. The
variables `STRAP_ADMIN` and `STRAP_SUDO` will track the outcome of these
admin and sudo checks. Any commands requiring admin will be guarded
under `[ "$STRAP_ADMIN" -gt 0 ]`, and any commands requiring sudo will
be guarded under `[ "$STRAP_SUDO" -gt 0 ]`.

Many of the print/logging commands required sudo unnecessarily. This
commit will overhaul the logging functions, adding separate non-sudo
versions, and only using the sudo commands when sudo is needed.

Homebrew installation will be updated to require sudo but not admin.
Initial setup of Homebrew itself does not require an admin user account,
but does require sudo (https://docs.brew.sh/Installation). After
Homebrew setup, use of sudo with `brew` commands is discouraged
(https://docs.brew.sh/FAQ#why-does-homebrew-say-sudo-is-bad).
Permissions issues with Homebrew directories can sometimes occur. After
Homebrew setup, commands such as `brew bundle install --global` should
be run from the same user account used for setup. Attempts to run `brew`
commands from another user account may result in errors, because
directories that need to be updated are owned by the setup account.
If access to the setup account is not routinely available, an
alternative approach could be to change ownership of Homebrew
directories to a group that includes the user account used for Homebrew
setup as well as other users that need to run Homebrew commands. The
effectiveness of this approach is not guaranteed and may depend on
implementation details of how Homebrew detects directory permissions.

GitHub Actions workflows will be updated to test `bootstrap.sh` on
non-admin and non-sudo accounts.
  • Loading branch information
br3ndonland committed Nov 14, 2024
1 parent 7ff6245 commit 5fe4c3d
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 117 deletions.
68 changes: 62 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
needs: [check]
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-14, ubuntu-latest]
timeout-minutes: 100
Expand All @@ -69,21 +70,76 @@ jobs:
run: |
# mitigate https://github.com/actions/runner-images/issues/6283
echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >>"$GITHUB_PATH"
- name: Clean up Homebrew
- name: Enable passwordless sudo commands on macOS runners
if: runner.os == 'macOS'
run: |
type brew &>/dev/null && brew test-bot --only-cleanup-before ||
echo "Homebrew not found."
# mitigate https://github.com/actions/runner-images/issues/10484
sudo sed -i '' 's/%admin ALL = (ALL) ALL/%admin ALL = (ALL) NOPASSWD: ALL/g' /etc/sudoers
sudo -v
- name: Clean up macOS
if: runner.os == 'macOS'
run: |
sudo rm -rf /usr/local/Caskroom /usr/local/Homebrew /usr/local/bin/brew \
/usr/local/.??* /Applications/Xcode.app /Library/Developer/CommandLineTools
- name: Run bootstrap.sh
- name: Clean up Homebrew
run: |
type brew &>/dev/null && brew test-bot --only-cleanup-before ||
echo "Homebrew not found."
- name: Create a non-admin user account
run: |
username=standard-user
if ${{ runner.os == 'Linux' }}; then
sudo adduser --disabled-password --gecos "" "$username"
home_prefix=/home
elif ${{ runner.os == 'macOS' }}; then
sudo sysadminctl -addUser "$username"
sudo passwd -u "$username"
sudo dseditgroup -o edit -d "$username" -t user admin
home_prefix=/Users
sudo dscl . -create "$home_prefix/$username" NFSHomeDirectory "$home_prefix/$username"
fi
echo "NON_ADMIN_USER=$username" >>"$GITHUB_ENV"
echo "NON_ADMIN_USER_HOME=$home_prefix/$username" >>"$GITHUB_ENV"
- name: Set bootstrap script URL
run: |
STRAP_SCRIPT_URL="https://raw.githubusercontent.com/$STRAP_GITHUB_USER/dotfiles/$STRAP_DOTFILES_BRANCH/bootstrap.sh"
echo "STRAP_SCRIPT_URL=$STRAP_SCRIPT_URL"
echo "STRAP_SCRIPT_URL=$STRAP_SCRIPT_URL" >>"$GITHUB_ENV"
- name: >
Run bootstrap.sh with a non-admin non-sudo user without Homebrew installed
(Homebrew installation requires sudo)
id: bootstrap-non-admin-non-sudo
run: |
bootstrap_script_url="https://raw.githubusercontent.com/$STRAP_GITHUB_USER/dotfiles/$STRAP_DOTFILES_BRANCH/bootstrap.sh"
/usr/bin/env bash -c "$(curl -fsSL $bootstrap_script_url)"
HOME="$NON_ADMIN_USER_HOME"
sudo \
--preserve-env=HOME,STRAP_CI,STRAP_DEBUG,STRAP_DOTFILES_BRANCH,STRAP_GIT_EMAIL,STRAP_GIT_NAME,STRAP_GITHUB_USER,STRAP_SCRIPT_URL \
-u "$NON_ADMIN_USER" bash -c '/usr/bin/env bash -c "$(curl -fsSL ${{ env.STRAP_SCRIPT_URL }})"'
working-directory: ${{ env.NON_ADMIN_USER_HOME }}
- name: Update non-admin user account with sudo permissions
run: |
SUDOERS_FILE="/etc/sudoers.d/$NON_ADMIN_USER"
echo "$NON_ADMIN_USER ALL=(ALL) NOPASSWD:ALL" | sudo tee "$SUDOERS_FILE"
sudo chmod 0440 "$SUDOERS_FILE"
- name: Run bootstrap.sh with a non-admin sudo user without Homebrew installed
run: |
HOME="$NON_ADMIN_USER_HOME"
sudo \
--preserve-env=HOME,STRAP_CI,STRAP_DEBUG,STRAP_DOTFILES_BRANCH,STRAP_GIT_EMAIL,STRAP_GIT_NAME,STRAP_GITHUB_USER,STRAP_SCRIPT_URL \
-u "$NON_ADMIN_USER" bash -c '/usr/bin/env bash -c "$(curl -fsSL ${{ env.STRAP_SCRIPT_URL }})"'
working-directory: ${{ env.NON_ADMIN_USER_HOME }}
- name: Rerun bootstrap.sh with a non-admin sudo user after Homebrew has been installed
run: |
HOME="$NON_ADMIN_USER_HOME"
sudo \
--preserve-env=HOME,STRAP_CI,STRAP_DEBUG,STRAP_DOTFILES_BRANCH,STRAP_GIT_EMAIL,STRAP_GIT_NAME,STRAP_GITHUB_USER,STRAP_SCRIPT_URL \
-u "$NON_ADMIN_USER" bash -c '/usr/bin/env bash "${{ env.NON_ADMIN_USER_HOME }}/.dotfiles/bootstrap.sh"'
working-directory: ${{ env.NON_ADMIN_USER_HOME }}
- name: Run bootstrap.sh
run: /usr/bin/env bash -c "$(curl -fsSL $STRAP_SCRIPT_URL)"
- name: Rerun bootstrap.sh to test idempotence
run: bash "$HOME/.dotfiles/bootstrap.sh"
- name: Check Homebrew formulae
run: brew list | grep -qE "\b(bash|grep|sed)\b"
- name: Check Homebrew configuration
run: brew config
- name: Check for potential problems with brew doctor
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,16 @@ The following environment variables can be used to configure _bootstrap.sh_, and
- `STRAP_DOTFILES_URL`: URL from which the dotfiles repo will be cloned. Defaults to `https://github.com/$STRAP_GITHUB_USER/dotfiles`, but any [Git-compatible URL](https://www.git-scm.com/docs/git-clone#_git_urls) can be used, so long as it is accessible at the time the script runs.
- `STRAP_DOTFILES_BRANCH`: Git branch to check out after cloning dotfiles repo. Defaults to `main`.

_bootstrap.sh_ will set up macOS and Homebrew, run scripts in the _scripts/_ directory, and install Homebrew packages and casks from the _[Brewfile](Brewfile)_.
There are some additional variables for advanced usage. Consult the _[bootstrap.sh](bootstrap.sh)_ script to see all supported variables.

A Brewfile is a list of [Homebrew](https://brew.sh/) packages and casks (applications) that can be installed in a batch by [Homebrew Bundle](https://github.com/Homebrew/homebrew-bundle). The Brewfile can even be used to install Mac App Store apps with the `mas` CLI. Note that you must sign in to the App Store ahead of time for `mas` to work.
_bootstrap.sh_ will set up macOS and Homebrew, run scripts in the _scripts/_ directory, and install Homebrew packages and casks from the _[Brewfile](Brewfile)_. A Brewfile is a list of [Homebrew](https://brew.sh/) packages and casks (applications) that can be installed in a batch by [Homebrew Bundle](https://github.com/Homebrew/homebrew-bundle). The Brewfile can even be used to install Mac App Store apps with the `mas` CLI. Note that you must sign in to the App Store ahead of time for `mas` to work.

The following list is a brief summary of permissions related to _bootstrap.sh_.

- Initial setup of Homebrew itself does not require an admin user account, but does require `sudo`. See the [Homebrew installation docs](https://docs.brew.sh/Installation), [Homebrew/install#312](https://github.com/Homebrew/install/issues/312), and [Homebrew/install#315](https://github.com/Homebrew/install/pull/315/files).
- [After Homebrew setup, use of `sudo` with `brew` commands is discouraged](https://docs.brew.sh/FAQ#why-does-homebrew-say-sudo-is-bad).
- After Homebrew setup, commands such as `brew bundle install --global` should be run from the same user account used for setup. Attempts to run `brew` commands from another user account will result in errors, because directories that need to be updated are owned by the setup account. If access to the setup account is not routinely available, an alternative approach could be to change ownership of Homebrew directories to a group that includes the user account used for Homebrew setup as well as other users that need to run Homebrew commands.
- _bootstrap.sh_ can run with limited functionality on non-admin and non-`sudo` user accounts. A plausible use case could exist in which an admin runs `bootstrap.sh` to configure the system initially, then a non-admin runs `bootstrap.sh` to configure their own account. In this use case, the non-admin user should not need admin or `sudo` privileges, because all the pertinent setup (FileVault disk encryption, XCode developer tools, Homebrew, etc) is already complete.

Users with more complex needs for multi-environment dotfiles management might consider a tool like [`chezmoi`](https://www.chezmoi.io/).

Expand Down
Loading

0 comments on commit 5fe4c3d

Please sign in to comment.