-
-
Notifications
You must be signed in to change notification settings - Fork 202
Expand file tree
/
Copy pathbootstrap
More file actions
executable file
·572 lines (442 loc) · 15.4 KB
/
Copy pathbootstrap
File metadata and controls
executable file
·572 lines (442 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
#!/usr/bin/env bash
# Bootstrap and kick off installing DotFriedRice.
#
# Examples:
# ./bootstrap # Clone from the default remote location
# ./bootstrap --local # Copy from your $PWD instead of cloning (useful for testing)
#
# You can also customize the branch and more (check the env vars below).
set -o errexit
set -o pipefail
set -o nounset
export C_RED=$'\e[1;31;1m'
export C_GREEN=$'\e[1;32;1m'
export C_YELLOW=$'\e[1;33;1m'
# shellcheck disable=2034
export C_BLUE=$'\e[1;34;1m'
export C_MAGENTA=$'\e[1;35;1m'
export C_CYAN=$'\e[1;36;1m'
export C_WHITE=$'\e[1;37;1m'
export C_RESET=$'\e[0m'
export PACKAGES_CRITICAL=("git" "nano")
export LOCAL="0"
export CPU_ARCH=
export OS_TYPE=
export OS_KERNEL=
export OS_DISTRO=
export OS_DISTRO_ORIGINAL=
export OS_DISTRO_LIKE=
export OS_VERSION=
export OS_IN_WSL="0"
export OS_IN_CONTAINER="${OS_IN_CONTAINER:-0}"
export GUI_LINUX="0"
export GUI_ELIGIBLE="0"
export GUI_ENABLED="0"
export DOTFRIEDRICE_PATH="${DOTFRIEDRICE_PATH:-"${HOME}/dotfriedrice"}"
export DOTFRIEDRICE_BRANCH="${DOTFRIEDRICE_BRANCH:-master}"
export DOTFRIEDRICE_CLONE_URL="${DOTFRIEDRICE_CLONE_URL:-https://github.com/nickjj/dotfriedrice}"
export DOTFRIEDRICE_UPSTREAM_URL="${DOTFRIEDRICE_UPSTREAM_URL:-"${DOTFRIEDRICE_CLONE_URL}"}"
for arg in "${@}"; do
case "${arg}" in
--local)
LOCAL=1
shift
;;
esac
done
# -----------------------------------------------------------------------------
# Helper functions
# -----------------------------------------------------------------------------
_skipping() {
local message="${1}"
local pre_message="${2:-}"
printf "%b%bSkipping: %b%b\n" "${pre_message}" "${C_WHITE}" "${message}" "${C_RESET}"
}
_info() {
local message="${1}"
local pre_message="${2-\n\n}"
printf "%b%b:: %b%b\n\n" "${pre_message}" "${C_CYAN}" "${message}" "${C_RESET}"
}
_warning() {
local message="${1}"
local pre_message="${2:-}"
printf "%b%bWarning: %b%b\n" "${pre_message}" "${C_YELLOW}" "${message}" "${C_RESET}"
}
_error() {
local message="${1}"
local exit_strategy="${2-1}"
printf "%bError: %b%b\n" "${C_RED}" "${message}" "${C_RESET}" >&2
# If the file is sourced, return instead of exit.
if [[ "${BASH_SOURCE[0]}" != "${0}" && "${exit_strategy}" == "1" ]]; then
exit_strategy="2"
fi
case "${exit_strategy}" in
1)
exit 1
;;
2)
return 1
;;
"")
return 0
;;
esac
}
_run_as_root() {
if command -v sudo >/dev/null 2>&1; then
sudo bash -c "${*}"
else
su -c "${*}"
fi
}
_package_install() {
local packages=("${@}")
case "${OS_DISTRO}" in
arch)
_run_as_root pacman -Syu --noconfirm --needed "${packages[*]}"
;;
debian)
_run_as_root apt-get update && _run_as_root apt-get install --yes --no-install-recommends "${packages[*]}"
;;
*)
brew install "${packages[@]}"
;;
esac
}
_dotfriedrice_env() {
local flag="${1:-}"
local gui_enabled=
local commit="-"
if [ "${flag}" == "--gui-enabled" ]; then
gui_enabled=$'\n'"GUI_ENABLED=\"${GUI_ENABLED}\""
fi
if [[ -d "${DOTFRIEDRICE_PATH}/.git" && -f "${DOTFRIEDRICE_PATH}/dotfriedrice" ]]; then
commit="$(git -C "${DOTFRIEDRICE_PATH}" log -1 --format="%as :: %h :: %s" 2>/dev/null || echo "-")"
fi
cat <<EOF
CPU_ARCH="${CPU_ARCH}"
OS_KERNEL="${OS_KERNEL}"
OS_DISTRO="${OS_DISTRO_ORIGINAL} (${OS_VERSION})"
OS_DISTRO_LIKE="${OS_DISTRO_LIKE}"
OS_IN_WSL="${OS_IN_WSL}"
OS_IN_CONTAINER="${OS_IN_CONTAINER}"
GUI_ELIGIBLE="${GUI_ELIGIBLE}"${gui_enabled}
DOTFRIEDRICE_FIRST_RUN="${DOTFRIEDRICE_FIRST_RUN:-1}"
DOTFRIEDRICE_CLONE_URL="${DOTFRIEDRICE_CLONE_URL}"
DOTFRIEDRICE_BRANCH="${DOTFRIEDRICE_BRANCH}"
DOTFRIEDRICE_COMMIT="${commit}"
EOF
}
# -----------------------------------------------------------------------------
# Main functions
# -----------------------------------------------------------------------------
detect_env() {
case "${OSTYPE}" in
linux*)
OS_TYPE="linux"
if [ -r /etc/os-release ]; then
# shellcheck disable=SC1091
read -r OS_DISTRO OS_DISTRO_LIKE OS_VERSION <<<"$(. /etc/os-release && echo "${ID}" "${ID_LIKE:-"${ID}"}" "${BUILD_ID:-"${VERSION_ID:-"${VERSION_CODENAME:-}"}"}")"
else
_error "'/etc/os-release' file is missing which is not supported, aborting!"
fi
OS_DISTRO_ORIGINAL="${OS_DISTRO}"
# Technically ID_LIKE can have a space separated list of distros but
# we really only care that we have a Debian based system where apt is
# available to use.
if [[ "${OS_DISTRO_LIKE}" =~ (ubuntu|debian) ]]; then
OS_DISTRO="debian"
fi
# Only fully support these Linux distros.
if [[ -z "${OS_DISTRO_LIKE}" || ! "arch ubuntu debian raspbian" =~ ${OS_DISTRO_LIKE} ]]; then
cat <<EOF >&2
Your distro identified as: '${OS_DISTRO_ORIGINAL}' (based on: '${OS_DISTRO_LIKE}')
Supported distros are Arch and Debian based distros. If your distro identifies
as one of those the expectation is it should be supported.
If you feel this is a bug please open an issue at: ${DOTFRIEDRICE_CLONE_URL}
Please include the output of running: cat /etc/os-release
EOF
_error "Your distro isn't supported, aborting!"
fi
# Are we in WSL?
if grep --quiet --ignore-case "microsoft" /proc/version; then
OS_IN_WSL="1"
fi
# We can't fully depend on these which is why this env var exists, but it
# at least protects someone running this script who didn't pass in the
# environment variable in the common case (Docker or Podman).
[[ -f "/.dockerenv" || -f "/run/.containerenv" ]] && OS_IN_CONTAINER="1"
# Can we install and configure a window manager and GUI tools?
if [[ "${OS_DISTRO}" = "arch" && "${OS_IN_WSL}" != "1" && "${OS_IN_CONTAINER}" != "1" ]]; then
GUI_ELIGIBLE="1"
fi
if [[ "${GUI_LINUX}" == "1" && "${GUI_ELIGIBLE}" == "1" ]]; then
GUI_ENABLED="1"
fi
;;
darwin*)
OS_TYPE="darwin"
OS_DISTRO="${OS_TYPE}"
OS_DISTRO_ORIGINAL="${OS_DISTRO}"
OS_DISTRO_LIKE="${OS_DISTRO}"
OS_VERSION="$(uname -r)"
;;
*)
_error "'${OSTYPE}' isn't supported, aborting!"
;;
esac
OS_KERNEL="$(uname -r)"
CPU_ARCH="$(uname -m)"
}
detect_env_results() {
_info "DETECT ENVIRONMENT"
local flag="${1:-}"
detect_env
_dotfriedrice_env "${flag}"
# This will come up when running the dotfriedrice script for the first time.
if [[ "${DOTFRIEDRICE_FIRST_RUN:-}" == "1" && "${GUI_ENABLED}" == "1" && "${OS_DISTRO_ORIGINAL}" != "${OS_DISTRO}" ]]; then
cat <<EOF
---
${C_YELLOW}
DotFriedRice is designed for vanilla Arch and you have an Arch based distro.
There could be minor issues with packages or system config files. It might also
be perfect, it really depends on the distro!${C_RESET}
If you run into an issue please report it at: ${DOTFRIEDRICE_CLONE_URL}
${C_WHITE}
This message only pops up once to give you a heads up. By the way, if you
answer no to the prompt you can re-run this script with: ./dotfriedrice
${C_RESET}
EOF
while true; do
printf "Ready to continue with the installation? (y/n) "
read -r yn
case "${yn}" in
[Yy]*)
break
;;
[Nn]*)
exit
;;
*) _error "'${yn}' is not a valid answer, please input 'y' or 'n'" "" ;;
esac
done
fi
}
warn_root() {
_info "ROOT USER WARNING"
# This could be empty in a containerized environment.
local user="${USER:-$(whoami)}"
local sudo_user="${SUDO_USER:-}"
if [ "${OS_IN_CONTAINER}" == "1" ]; then
_skipping "Script is being run in a container"
return
fi
# Root will have an effective uid of 0.
if [ "${EUID}" != "0" ]; then
_skipping "Script is being run as '${user}'"
return
fi
cat <<EOF
Root user detected!
$(id)
USER=${user}
SUDO_USER=${sudo_user}
EOF
local msg="as root"
[ -n "${sudo_user}" ] && msg="with sudo as '${sudo_user}'"
while true; do
printf "\nThis script is meant to run as a non-root user,\nare you sure you want to run this %s? (y/n) " "${msg}"
read -r yn
case "${yn}" in
[Yy]*)
break
;;
[Nn]*)
exit
;;
*) _error "'${yn}' is not a valid answer, please input 'y' or 'n'" "" ;;
esac
done
}
ensure_sudo_works() {
_info "ENSURE SUDO WORKS"
if ! command -v sudo >/dev/null 2>&1; then
_package_install "sudo"
fi
if sudo --validate >/dev/null 2>&1; then
echo "Confirmed, it's working"
else
local sudoers_dir="/etc/sudoers.d"
local real_user=
real_user="${SUDO_USER:-${USER:-$(whoami)}}"
local user_file="${sudoers_dir}/00_${real_user}"
local user_entry="${real_user} ALL=(ALL:ALL) ALL"
_run_as_root "mkdir -p ${sudoers_dir} && echo '${user_entry}' > ${user_file} && chmod 0440 ${user_file}"
echo "'${user_file}' was created with '${user_entry}'"
sudo --validate
fi
}
install_homebrew() {
_info "INSTALL HOMEBREW"
local brew_prefix="/opt/homebrew"
[ "${CPU_ARCH}" != "arm64" ] && brew_prefix="/usr/local"
local brew_bin_path="${brew_prefix}/bin/brew"
if [ -f "${brew_bin_path}" ]; then
_skipping "Already installed"
return
fi
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
eval "$(${brew_bin_path} shellenv)"
}
install_critical_packages() {
_info "INSTALL CRITICAL PACKAGES"
if [ "${OS_TYPE}" == "darwin" ]; then
PACKAGES_CRITICAL+=("bash")
fi
_package_install "${PACKAGES_CRITICAL[@]}"
}
_local_clone() {
[ "${LOCAL}" != "1" ] && return 1
local source_path
source_path="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
if [ "$(realpath "${source_path}")" == "$(realpath "${DOTFRIEDRICE_PATH}")" ]; then
# This is already protected against in the clone_dotfriedrice function but
# this is an extra layer of defense in case that function changes.
_skipping "'${source_path}' is your real DotFriedRice path"
else
cp -R "${source_path}/." "${DOTFRIEDRICE_PATH}"
# Ensure the containerized environment starts fresh.
if [ "${OS_IN_CONTAINER}" == "1" ]; then
rm -f \
"${DOTFRIEDRICE_PATH}/.config/ghostty/config.local" \
"${DOTFRIEDRICE_PATH}/.config/git/config.local" \
"${DOTFRIEDRICE_PATH}/.config/mako/config.local" \
"${DOTFRIEDRICE_PATH}/.config/niri/config.kdl.local" \
"${DOTFRIEDRICE_PATH}/.config/zsh/.aliases.local" \
"${DOTFRIEDRICE_PATH}/.config/zsh/.zprofile.local" \
"${DOTFRIEDRICE_PATH}/.config/zsh/.zsh_history" \
"${DOTFRIEDRICE_PATH}/.config/zsh/.zshrc.local"
local git_config="${DOTFRIEDRICE_PATH}/.git/config"
if grep --quiet "git@github.com:" "${git_config}"; then
perl -pi -e "s|git@github.com:|https://github.com/|g" "${git_config}"
fi
cp -n "${DOTFRIEDRICE_PATH}/dotfriedrice-config.example" "${DOTFRIEDRICE_PATH}/dotfriedrice-config"
fi
fi
echo "Locally copied from '${source_path}' to '${DOTFRIEDRICE_PATH}'"
}
clone_dotfriedrice() {
_info "CLONE DOTFRIEDRICE"
cat <<EOF
Your DotFriedRice path is where it will live on disk. You can change this later,
nothing will be installed yet, it only clones this repo:
DOTFRIEDRICE_CLONE_URL="${DOTFRIEDRICE_CLONE_URL}"
DOTFRIEDRICE_BRANCH="${DOTFRIEDRICE_BRANCH}"
EOF
if [ "${LOCAL}" == "1" ]; then
printf "%bSince you've used --local, it will get copied from your \$PWD instead of the clone URL.%b\n\n" "${C_WHITE}" "${C_RESET}"
fi
while true; do
printf "Where would you like to clone DotFriedRice to [%s]? " "${DOTFRIEDRICE_PATH}"
read -r dotfriedrice_path
[ -z "${dotfriedrice_path}" ] && dotfriedrice_path="${DOTFRIEDRICE_PATH}"
# Expand ~ and normalize path to remove trailing slash.
DOTFRIEDRICE_PATH="${dotfriedrice_path/#~/"${HOME}"}"
DOTFRIEDRICE_PATH="${DOTFRIEDRICE_PATH%/}"
if [ -e "${DOTFRIEDRICE_PATH:-}" ]; then
_error "'${DOTFRIEDRICE_PATH}' already exists, it must be a new directory\n" ""
else
break
fi
done
echo
mkdir -p "${DOTFRIEDRICE_PATH}"
_local_clone && return
git clone --branch "${DOTFRIEDRICE_BRANCH}" "${DOTFRIEDRICE_CLONE_URL}" "${DOTFRIEDRICE_PATH}"
git -C "${DOTFRIEDRICE_PATH}" remote | grep --quiet --word-regexp upstream ||
git -C "${DOTFRIEDRICE_PATH}" remote add upstream "${DOTFRIEDRICE_UPSTREAM_URL}.git"
cp -n "${DOTFRIEDRICE_PATH}/dotfriedrice-config.example" "${DOTFRIEDRICE_PATH}/dotfriedrice-config"
}
_update_config() {
local gui=""
[ "${1:-}" == "--gui" ] && gui='s|export GUI_LINUX=.*|export GUI_LINUX="1"|g;'
perl -pi -e '
s|export DOTFRIEDRICE_CLONE_URL=".*"|export DOTFRIEDRICE_CLONE_URL="\${DOTFRIEDRICE_CLONE_URL:-'"${DOTFRIEDRICE_CLONE_URL}"'}"|g;
s|export DOTFRIEDRICE_UPSTREAM_URL=".*"|export DOTFRIEDRICE_UPSTREAM_URL="\${DOTFRIEDRICE_UPSTREAM_URL:-'"${DOTFRIEDRICE_UPSTREAM_URL}"'}"|g;
s|export DOTFRIEDRICE_BRANCH=".*"|export DOTFRIEDRICE_BRANCH="\${DOTFRIEDRICE_BRANCH:-'"${DOTFRIEDRICE_BRANCH}"'}"|g;
'"${gui}"'
' "${DOTFRIEDRICE_PATH}/dotfriedrice-config"
}
next_steps() {
_info "NEXT STEPS"
local not_eligible=" ${C_RED}[OS NOT ELIGIBLE]${C_RESET}"
[ "${GUI_ELIGIBLE}" == "1" ] && not_eligible=
cat <<EOF
${C_GREEN}Everything went as planned, your system is ready to install DotFriedRice.
${C_YELLOW}What would you like to do next?
1) Install DotFriedRice (command line)
2) Install DotFriedRice (command line + niri based GUI)${not_eligible}
${C_YELLOW}3) Review which packages get installed and edit your config (optional)${C_RESET}
${C_MAGENTA}You can also quit with Ctrl+c and browse the repo at your discretion. When
you're ready to install DotFriedRice, you can run:
cd "${DOTFRIEDRICE_PATH}" && ./dotfriedrice${C_RESET}
In any case, you're all set to install DotFriedRice however you see fit.
With option 3, you can press Alt + . (or Escape + .) to flip between the default
package list (read-only) and your config file (safe to edit). If editing your
config hit Ctrl+o to save it and Ctrl+x to exit (these are shown on the bottom).
${C_WHITE}Extra information:
Choosing option 1 runs the above dotfriedrice script. Option 2 does the same except
it edits your config to enable GUI mode. Option 3 opens nano to let you view and
edit files, then it shows this prompt again.${C_RESET}
EOF
while true; do
printf "\nWhich option number would you like to pick? "
read -r option
case "${option}" in
1)
_update_config
break
;;
2)
if [ "${GUI_ELIGIBLE}" != "1" ]; then
cat <<EOF
${C_RED}Sorry your OS isn't eligible, this option requires all of these to be true:${C_RESET}
- Running an Arch based Linux distro [Yours: ${OS_DISTRO}]
- Not running within WSL (Windows Subsystem for Linux) [Yours: ${OS_IN_WSL}]
- Not running in a container [Yours: ${OS_IN_CONTAINER}]
EOF
continue
fi
_update_config --gui
break
;;
3)
nano \
"${DOTFRIEDRICE_PATH}/_install/default/packages/${OS_DISTRO}" \
"${DOTFRIEDRICE_PATH}/dotfriedrice-config"
next_steps
;;
*) _error "'${option}' isn't a valid choice, pick 1, 2 or 3" "" ;;
esac
done
# Remove all traces of the bootstrap script once the dotfriedrice script is called.
cd "${DOTFRIEDRICE_PATH}" && DOTFRIEDRICE_PATH="" exec "./dotfriedrice"
}
main() {
detect_env_results "${@}"
warn_root
if [ "${OS_TYPE}" == "darwin" ]; then
install_homebrew
else
# It will work on macOS out of the box.
ensure_sudo_works
fi
install_critical_packages
clone_dotfriedrice
next_steps
}
# Is the script being run the same as this file? We know we're calling it
# directly. This protects against running everything when this file is sourced.
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "${@:-}"
fi