#!/usr/bin/env bash set -eu set -o pipefail # Sourced from: # - https://github.com/LnL7/nix-darwin/blob/8c29d0985d74b4a990238497c47a2542a5616b3c/bootstrap.sh # - https://gist.github.com/expipiplus1/e571ce88c608a1e83547c918591b149f/ac504c6c1b96e65505fbda437a28ce563408ecb0 # - https://github.com/NixOS/nixos-org-configurations/blob/a122f418797713d519aadf02e677fce0dc1cb446/delft/scripts/nix-mac-installer.sh # - https://github.com/matthewbauer/macNixOS/blob/f6045394f9153edea417be90c216788e754feaba/install-macNixOS.sh # - https://gist.github.com/LnL7/9717bd6cdcb30b086fd7f2093e5f8494/86b26f852ce563e973acd30f796a9a416248c34a # # however tracking which bits came from which would be impossible. readonly ESC='\033[0m' readonly BOLD='\033[1m' readonly BLUE='\033[34m' readonly BLUE_UL='\033[4;34m' readonly GREEN='\033[32m' readonly GREEN_UL='\033[4;32m' readonly RED='\033[31m' # installer allows overriding build user count to speed up installation # as creating each user takes non-trivial amount of time on macos readonly NIX_USER_COUNT=${NIX_USER_COUNT:-32} readonly NIX_BUILD_GROUP_ID="${NIX_BUILD_GROUP_ID:-30000}" readonly NIX_BUILD_GROUP_NAME="nixbld" # darwin installer needs to override these NIX_FIRST_BUILD_UID="${NIX_FIRST_BUILD_UID:-30001}" NIX_BUILD_USER_NAME_TEMPLATE="nixbld%d" # Please don't change this. We don't support it, because the # default shell profile that comes with Nix doesn't support it. readonly NIX_ROOT="/nix" readonly NIX_EXTRA_CONF=${NIX_EXTRA_CONF:-} readonly PROFILE_TARGETS=("/etc/bashrc" "/etc/profile.d/nix.sh" "/etc/zshrc" "/etc/bash.bashrc" "/etc/zsh/zshrc") readonly PROFILE_BACKUP_SUFFIX=".backup-before-nix" readonly PROFILE_NIX_FILE="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" # Fish has different syntax than zsh/bash, treat it separate readonly PROFILE_FISH_SUFFIX="conf.d/nix.fish" readonly PROFILE_FISH_PREFIXES=( # each of these are common values of $__fish_sysconf_dir, # under which Fish will look for a file named # $PROFILE_FISH_SUFFIX. "/etc/fish" # standard "/usr/local/etc/fish" # their installer .pkg for macOS "/opt/homebrew/etc/fish" # homebrew "/opt/local/etc/fish" # macports ) readonly PROFILE_NIX_FILE_FISH="$NIX_ROOT/var/nix/profiles/default/etc/profile.d/nix-daemon.fish" readonly NIX_INSTALLED_NIX="@nix@" readonly NIX_INSTALLED_CACERT="@cacert@" #readonly NIX_INSTALLED_NIX="/nix/store/j8dbv5w6jl34caywh2ygdy88knx1mdf7-nix-2.3.6" #readonly NIX_INSTALLED_CACERT="/nix/store/7dxhzymvy330i28ii676fl1pqwcahv2f-nss-cacert-3.49.2" readonly EXTRACTED_NIX_PATH="$(dirname "$0")" readonly ROOT_HOME=~root if [ -t 0 ] && [ -z "${NIX_INSTALLER_YES:-}" ]; then readonly IS_HEADLESS='no' else readonly IS_HEADLESS='yes' fi headless() { if [ "$IS_HEADLESS" = "yes" ]; then return 0 else return 1 fi } is_root() { if [ "$EUID" -eq 0 ]; then return 0 else return 1 fi } is_os_linux() { if [ "$(uname -s)" = "Linux" ]; then return 0 else return 1 fi } is_os_darwin() { if [ "$(uname -s)" = "Darwin" ]; then return 0 else return 1 fi } contact_us() { echo "You can open an issue at" echo "https://github.com/NixOS/nix/issues/new?labels=installer&template=installer.md" echo "" echo "Or get in touch with the community: https://nixos.org/community" } get_help() { echo "We'd love to help if you need it." echo "" contact_us } uninstall_directions() { subheader "Uninstalling nix:" local step=0 if poly_service_installed_check; then step=$((step + 1)) poly_service_uninstall_directions "$step" fi for profile_target in "${PROFILE_TARGETS[@]}"; do if [ -e "$profile_target" ] && [ -e "$profile_target$PROFILE_BACKUP_SUFFIX" ]; then step=$((step + 1)) cat < $1" } bold() { echo "$BOLD$*$ESC" } ok() { _textout "$GREEN" "$@" } warning() { warningheader "warning!" cat echo "" } failure() { header "oh no!" _textout "$RED" "$@" echo "" _textout "$RED" "$(get_help)" trap finish_cleanup EXIT exit 1 } ui_confirm() { _textout "$GREEN$GREEN_UL" "$1" if headless; then echo "No TTY, assuming you would say yes :)" return 0 fi local prompt="[y/n] " echo -n "$prompt" while read -r y; do if [ "$y" = "y" ]; then echo "" return 0 elif [ "$y" = "n" ]; then echo "" return 1 else _textout "$RED" "Sorry, I didn't understand. I can only understand answers of y or n" echo -n "$prompt" fi done echo "" return 1 } printf -v _UNCHANGED_GRP_FMT "%b" $'\033[2m%='"$ESC" # "dim" # bold+invert+red and bold+invert+green just for the +/- below # red/green foreground for rest of the line printf -v _OLD_LINE_FMT "%b" $'\033[1;7;31m-'"$ESC ${RED}%L${ESC}" printf -v _NEW_LINE_FMT "%b" $'\033[1;7;32m+'"$ESC ${GREEN}%L${ESC}" _diff() { # simple colorized diff comatible w/ pre `--color` versions diff --unchanged-group-format="$_UNCHANGED_GRP_FMT" --old-line-format="$_OLD_LINE_FMT" --new-line-format="$_NEW_LINE_FMT" --unchanged-line-format=" %L" "$@" } confirm_rm() { local path="$1" if ui_confirm "Can I remove $path?"; then _sudo "to remove $path" rm "$path" fi } confirm_edit() { local path="$1" local edit_path="$2" cat < 1 )); then header "Reminders" for line in "${_reminders[@]}"; do echo "$line" if ! headless && [ "${#line}" = 0 ]; then if read -r -p "Press enter/return to acknowledge."; then printf $'\033[A\33[2K\r' fi fi done fi } reminder() { printf -v label "${BLUE}[ %d ]${ESC}" "$_remind_num" _reminders+=("$label") if [[ "$*" = "" ]]; then while read -r line; do _reminders+=("$line") done else # this expands each arg to an array entry (and each entry will # ultimately be a separate line in the output) _reminders+=("$@") fi _reminders+=("") ((_remind_num++)) } __sudo() { local expl="$1" local cmd="$2" shift header "sudo execution" echo "I am executing:" echo "" printf " $ sudo %s\\n" "$cmd" echo "" echo "$expl" echo "" return 0 } _sudo() { local expl="$1" shift if ! headless || is_root; then __sudo "$expl" "$*" >&2 fi if is_root; then env "$@" else sudo "$@" fi } # Ensure that $TMPDIR exists if defined. if [[ -n "${TMPDIR:-}" ]] && [[ ! -d "${TMPDIR:-}" ]]; then mkdir -m 0700 -p "${TMPDIR:-}" fi readonly SCRATCH=$(mktemp -d) finish_cleanup() { rm -rf "$SCRATCH" } finish_fail() { finish_cleanup failure < /dev/null >&2; then warning < /dev/null; then # a backup file for the rc-file exist, but they are identical, # so we can safely ignore it and overwrite it with the same # content later continue fi failure </dev/null)" command -vp chown)" if [[ -z "$get_chr_own" ]]; then get_chr_own="$(command -v chown)" fi if [[ -z "$get_chr_own" ]]; then reminder < "$SCRATCH/.nix-channels" _sudo "to set up the default system channel (part 1)" \ install -m 0664 "$SCRATCH/.nix-channels" "$ROOT_HOME/.nix-channels" fi } check_selinux() { if command -v getenforce > /dev/null 2>&1; then if [ "$(getenforce)" = "Enforcing" ]; then failure < "$SCRATCH/nix.conf" $NIX_EXTRA_CONF build-users-group = $NIX_BUILD_GROUP_NAME EOF _sudo "to place the default nix daemon configuration (part 2)" \ install -m 0664 "$SCRATCH/nix.conf" /etc/nix/nix.conf } main() { check_selinux if is_os_darwin; then # shellcheck source=./install-darwin-multi-user.sh . "$EXTRACTED_NIX_PATH/install-darwin-multi-user.sh" elif is_os_linux; then # shellcheck source=./install-systemd-multi-user.sh . "$EXTRACTED_NIX_PATH/install-systemd-multi-user.sh" # most of this works on non-systemd distros also else failure "Sorry, I don't know what to do on $(uname)" fi welcome_to_nix if ! is_root; then chat_about_sudo fi cure_artifacts # TODO: there's a tension between cure and validate. I moved the # the sudo/root check out of validate to the head of this func. # Cure is *intended* to subsume the validate-and-abort approach, # so it may eventually obsolete it. validate_starting_assumptions setup_report if ! ui_confirm "Ready to continue?"; then ok "Alright, no changes have been made :)" get_help trap finish_cleanup EXIT exit 1 fi poly_prepare_to_install create_build_group create_build_users create_directories place_channel_configuration install_from_extracted_nix configure_shell_profile set +eu # shellcheck disable=SC1091 . /etc/profile set -eu setup_default_profile place_nix_configuration poly_configure_nix_daemon_service trap finish_success EXIT } # set an empty initial arg for bare invocations in case we need to # disambiguate someone directly invoking this later. if [ "${#@}" = 0 ]; then set "" fi # ACTION for override case "${1-}" in # uninstall) # shift # uninstall "$@";; # install == same as the no-arg condition for now (but, explicit) ""|install) main;; *) # holding space for future options (like uninstall + install?) failure "install-multi-user: invalid argument";; esac