-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmac
executable file
·654 lines (514 loc) · 23.3 KB
/
mac
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
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
#!/bin/zsh
# Global Static Variables
# Directory where all coding projects will be cloned and stored.
code_directory="$HOME/code"
# Directory where a local copy of the Laptop repository will be cloned and stored.
laptop_repo_directory="$code_directory/laptop"
# Directory where the Laptop source code is stored.
laptop_src_directory="$laptop_repo_directory/src"
# Directory where local Laptop configurations are stored.
laptop_local_directory="$HOME/.config/laptop"
# Directory where a user's custom configurations and scripts that are specific to the current
# computer. Items in this folder will not be modified by Laptop, but it will run and source
# files from this directory.
laptop_custom_directory="$HOME/.laptop"
# Directory where the user's custom install script is stored.
laptop_custom_install_script="$laptop_custom_directory/custom_install.sh"
# Base URL for the remote Laptop repository.
remote_laptop_base_url="https://raw.githubusercontent.com/ssmereka/laptop/refs/heads/main"
# Remote URL of directory where the Laptop source code is stored.
remote_laptop_src_directory="$remote_laptop_base_url/src"
# Prompt to inform the user that there are unsaved changes in the Laptop git repository.
unsaved_changes_prompt='🚨 Attention: Cannot pull the latest changes from the remote Laptop
repository, because there are local changes or you are not
on the "main" branch.
Y: Proceed without the latest changes from the remote Laptop repository.
OR
N: Stash the local changes, then pull the latest from the remote Laptop repository.
Reapply the local changes with command: "cd '$laptop_repo_directory' && git stash pop"
'
# Global Secrets
# The name of the 1Password "item" where the Git configurations exist, including email, name, and
# username.
item_name_git="GitHub"
# Name of the 1Password "field" in the item "item_name_git" that contains the email address that
# should be used by the Git client.
field_name_git_email="email"
# Name of the 1Password "field" in the item "item_name_git" that contains the name that
# should be used by the Git client.
field_name_git_name="name"
# Name of the 1Password "field" in the item "item_name_git" that contains the git username that
# should be used by the Git client.
field_name_git_username="username"
# The name of the 1Password "item" where the Git SSH Key configurations exist. This key should be
# added to your remote Git repository, eg. GitHub.
item_name_ssh_key="GitHub SSH Key"
# Name of the 1Password "field" in the item "item_name_ssh_key" that contains the public ssh key
# used by your remote Git repository, eg. GitHub.
field_name_ssh_key_public="public key"
# Variables to store the secrets obtained from 1Password and injected into configurations.
git_email=""
git_name=""
git_ssh_public_key=""
git_username=""
# Lifecycle Methods
# Display an error message
error() {
print -- "\n🔴 [Error]: "$*"\n"
}
# Handle error messages by printing them and exiting with a failure status.
exit_with_error() {
error "$*"
exit 1
}
# Display a message to the user.
log() {
print -- $*
}
# Display a distinct header message to the user.
log_header() {
print -- "\n→ "$*"\n"
}
log_title() {
print -- "\n"$*"\n"
}
# Dynamic Global Variables
# Get the processor architecture from the operating system. Where "x86_64"
# is an Intel processor and "arm64" is an Apple Silicon processor.
processor_architecture="$(uname -m)"
# Set variables based on the processor architecture.
# Exit the script if the processor architecture is not supported.
if [ "$processor_architecture" = "arm64" ]; then
HOMEBREW_PREFIX="/opt/homebrew"
elif [ "$processor_architecture" = "x86_64" ]; then
HOMEBREW_PREFIX="/usr/local"
else
exit_with_error "Unsupported processor architecture: $processor_architecture"
fi
# Methods
# Removes all secrets from memory.
clear_secrets() {
log_header "🔐 Clearing Secrets from Memory"
unset git_email
unset git_name
unset git_ssh_public_key
unset git_username
}
# Clone the Laptop repository locally, if it doesn't exist locally
clone_laptop_repository() {
if [[ ! -d "$laptop_repo_directory" ]]; then
mkdir -p $laptop_repo_directory
git clone [email protected]:ssmereka/laptop.git "$laptop_repo_directory"
if [[ ! -f "$laptop_src_directory/mac" ]]; then
exit_with_error "Failed to clone Laptop to \"$laptop_repo_directory\".\n\n\tCommand: " \
"git clone [email protected]:ssmereka/laptop.git \"$laptop_repo_directory\""
fi
fi
}
# Configure ASDF to manage Node.js and Ruby runtime versions.
# Parameters:
# laptop_src_location: A local directory path or a remote URL for the Laptop source code
# directory. Defaults to the "laptop_src_directory" variable.
configure_asdf() {
laptop_src_location=${1:-"$laptop_src_directory"}
log_header "🔧 Configuring ASDF"
# Create a local ASDF configuration file, if one doesn't exist
# https://asdf-vm.com/manage/configuration.html#asdf-config-file
copy_file "$laptop_src_location/.asdfrc" "$laptop_local_directory/.asdfrc"
link_file "$laptop_local_directory/.asdfrc" "$HOME/.asdfrc"
# Create a local .default-gems file, if one doesn't exist.
# After Ruby is installed, the gems specified in this file will be installed.
# https://github.com/asdf-vm/asdf-ruby?tab=readme-ov-file#default-gems
copy_file "$laptop_src_location/.default-gems" "$laptop_local_directory/.default-gems"
link_file "$laptop_local_directory/.default-gems" "$HOME/.default-gems"
# Source the ASDF version manager
. $(brew --prefix asdf)/libexec/asdf.sh
# Install the lastest version of Node.js using ASDF
# https://github.com/asdf-vm/asdf-nodejs
log_header "🔧 Installing Node.js with ASDF"
asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git
asdf install nodejs latest
asdf global nodejs latest
log "\nUsing Node.js version $(node --version)"
# Install the lastest version of Python using ASDF
# https://github.com/asdf-community/asdf-python
log_header "🔧 Installing Python with ASDF"
asdf plugin-add python
asdf install python latest
asdf global python latest
log "\nUsing Python version $(python --version)"
# Install the latest version of Ruby using ASDF
# https://github.com/asdf-vm/asdf-ruby
log_header "🔧 Installing Ruby with ASDF"
asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git
asdf install ruby latest
asdf global ruby latest
log "\nUsing Ruby version $(ruby --version)"
# Install the latest version of Go using ASDF
# https://github.com/asdf-community/asdf-golang?tab=readme-ov-file#asdf-golang
log_header "🔧 Installing Go"
asdf plugin add golang https://github.com/asdf-community/asdf-golang.git
# Manually set this env varible to the non default value of "false". This will force go to use
# the exact versoins in .tool-versions and/or .go-version files. See
# https://github.com/asdf-community/asdf-golang?tab=readme-ov-file#version-selection
export ASDF_GOLANG_MOD_VERSION_ENABLED=false && asdf install golang latest
export ASDF_GOLANG_MOD_VERSION_ENABLED=false && asdf global golang latest
go_version=$(go version)
log "\nUsing Go version $go_version"
}
# Configure the 1Password GitHub CLI plugin so that it uses the Personal Access Token from
# 1Password for authentication.
configure_gh() {
log_header "Configuring GitHub CLI"
if [[ ! -f "$HOME/.config/op/plugins/gh.json" ]]; then
log "Signing into 1Password to retrieve the GitHub Personal Access Token"
op signin
log "\n🚨 Initializing the 1Password GitHub Plugin. Follow these steps:\n\n" \
" 1. When asked to locate your token, navigate to the name of the 1Password item where your Personal Access Token was stored.\n" \
" 2. When asked to configure when the chosen credentials will be used, select \"Use as global default on my system.\"\n\n" \
"More info can be found here:\n" \
"https://developer.1password.com/docs/cli/shell-plugins/github/#step-2-configure-your-default-credentials\n\n" \
"> Press any key to continue..."
read -n 1
op plugin init gh
fi
log "Verifying 1Password GitHub plugin"
op plugin inspect gh
}
# Create a Git configuration file with the user's GitHub information from 1Password.
# Parameters:
# laptop_src_location: A local directory path or a remote URL for the Laptop source code
# directory. Defaults to the "laptop_src_directory" variable.
configure_git() {
laptop_src_location=${1:-"$laptop_src_directory"}
log_header "🔧 Configuring Git"
if [[ -z "$git_email" || -z "$git_name" || -z "$git_username" || -z "$git_ssh_public_key" ]]; then
get_secrets
fi
# Create a local Git allowed signers file from the Laptop template.
local_git_allowed_signers="$laptop_local_directory/.git-allowed-signers"
copy_file "$laptop_src_location/.git-allowed-signers" "$local_git_allowed_signers"
chmod 600 "$local_git_allowed_signers"
# Create a local Git configuration file from the Laptop template.
local_gitconfig="$laptop_local_directory/.gitconfig"
copy_file "$laptop_src_location/.gitconfig" "$local_gitconfig"
# Replace the allowed signers template variables using the information from 1Password.
sed -i '' "s|GIT_NAME|${git_name}|" "$local_git_allowed_signers"
sed -i '' "s|GIT_SSH_PUBLIC_KEY|${git_ssh_public_key}|" "$local_git_allowed_signers"
# Replace the gitconfig template variables using the information from 1Password.
sed -i '' "s|GIT_EMAIL|${git_email}|" "$local_gitconfig"
sed -i '' "s|GIT_NAME|${git_name}|" "$local_gitconfig"
sed -i '' "s|GIT_SSH_PUBLIC_KEY|${git_ssh_public_key}|" "$local_gitconfig"
sed -i '' "s|GIT_USERNAME|${git_username}|" "$local_gitconfig"
# Replace the gitconfig template allowed signers path variable with the location of the newly
# configured allowed signers file.
sed -i '' "s|PATH_TO_GIT_ALLOWED_SIGNERS|${local_git_allowed_signers}|" "$local_gitconfig"
clear_secrets
# Symbolically link the .gitconfig to the proper location.
link_file "$local_gitconfig" "$HOME/.gitconfig"
}
# Configure SSH to use the SSH keys in 1Password.
# Parameters:
# laptop_src_location: A local directory path or a remote URL for the Laptop source code
# directory. Defaults to the "laptop_src_directory" variable.
configure_ssh() {
laptop_src_location=${1:-"$laptop_src_directory"}
log_header "🔧 Configuring SSH"
# Create the default SSH directory (if it doesn't already exist)
# and configure SSH to lookup SSH keys in 1Password
mkdir -p "$HOME/.ssh/"
chmod 700 "$HOME/.ssh"
# Create a local .ssh_config configuration file from the Laptop template.
local_ssh_config_path="$laptop_local_directory/.ssh_config"
copy_file "$laptop_src_location/.ssh_config" "$local_ssh_config_path"
# Grant the owner read/write permissions to the local SSH configuration file.
# This is the required permission level for SSH to use the file.
chmod 600 "$local_ssh_config_path"
# Symbolicially link the local ssh config to the default location.
link_file "$local_ssh_config_path" "$HOME/.ssh/config"
# Create a local 1Password agent configuration file from the Laptop template.
local_onepassword_agent_path="$laptop_local_directory/agent.toml"
copy_file "$laptop_src_location/agent.toml" "$local_onepassword_agent_path"
# Symbolicially link the local 1Password agent configuration to the default location.
mkdir -p "$HOME/.config/1Password/ssh/"
link_file "$local_onepassword_agent_path" "$HOME/.config/1Password/ssh/agent.toml"
# Follow 1Password's recommendation to create a shortcut to the 1Password SSH Agent
# Integration, ie "~/.1password/agent.sock".
mkdir -p "$HOME/.1password"
link_file "$HOME/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock" "$HOME/.1password/agent.sock"
}
# Configure ZSH to use the Laptop configuration files.
# Parameters:
# laptop_src_location: A local directory path or a remote URL for the Laptop source code
# directory. Defaults to the "laptop_src_directory" variable.
configure_zsh() {
laptop_src_location=${1:-"$laptop_src_directory"}
log_header "🔧 Configuring ZSH"
# Create a local ZSH configuration file.
copy_file "$laptop_src_location/.zshrc" "$laptop_local_directory/.zshrc"
link_file "$laptop_local_directory/.zshrc" "$ZDOTDIR/.zshrc"
# Create custom local ZSH configuration files.
copy_file "$laptop_src_location/.zshrc_aliases" "$laptop_local_directory/.zshrc_aliases"
copy_file "$laptop_src_location/.zshrc_methods" "$laptop_local_directory/.zshrc_methods"
# Create a Atuin configuration file.
copy_file "$laptop_src_location/atuin_config.toml" "$laptop_local_directory/atuin_config.toml"
link_file "$laptop_local_directory/atuin_config.toml" "$HOME/.config/atuin/config.toml"
}
# Copy a file from the source path to the destination path if the file
# does not already exist. When "overwrite" is true, the file will be replaced.
# Parameters:
# source: The path to the source file to copy or URL of the file to download.
# destination: The destination path where the file will be copied or downloaded.
# overwrite: A boolean indicating whether to replace the destination file; defaults to false.
copy_file() {
source=$1
destination=$2
overwrite=${3:-false}
destination_directory="$(dirname "${destination}")"
mkdir -p "$destination_directory"
if [[ $overwrite == true || ! -f "$destination" ]]; then
if [[ "$source" == http* ]]; then
log "Downloading file from $source to $destination"
curl -o "$destination" "$source"
else
log "Copying file from $source to $destination"
cp "$source" "$destination"
fi
else
log "No changes to file at $destination."
fi
}
clear_laptop_configs() {
log "Removing 1Password GitHub CLI configurations."
op plugin clear gh
log "Removing local Laptop configurations. Any changes made after the Laptop install will be" \
"removed."
rm -rf "$laptop_local_directory"
}
# Get secrets from 1Password and store them in global variables. This is a seperate method to
# so secrets can be gathered at the start of the script to prevent user interactions in the
# middle of a lengthy script.
get_secrets() {
log_header "🔐 Gathering Secrets"
# Make sure 1Password has an account configured and app integration enabled.
if [[ $(op account list --format=json 2>/dev/null) == "[]" ]]; then
exit_with_error "There are no accounts configured for use with 1Password CLI. You must"\
"enable app integration and then rerun this script.\n\n\t "\
"Guide: https://developer.1password.com/docs/cli/app-integration/"
fi
log "Unlocking the 1Password CLI."
$(op signin)
log "Retrieving fields \"$field_name_git_email\", \"$field_name_git_name\", and " \
"\"$field_name_git_username\" from 1Password item \"$item_name_git\"."
git_email=$(op item get "$item_name_git" --fields "$field_name_git_email")
git_name=$(op item get "$item_name_git" --fields "$field_name_git_name")
git_username=$(op item get "$item_name_git" --fields "$field_name_git_username")
if [[ -z "$git_email" || -z "$git_name" || -z "$git_username" ]]; then
exit_with_error "Failed to lookup secret fields \"email\", \"name\", and \"username\"" \
"from item \"GitHub\" in 1Password. Make sure they exist."
fi
log "Retrieving field \"$field_name_ssh_key_public\" from 1Password item \"$item_name_ssh_key\"."
git_ssh_public_key=$(op item get "$item_name_ssh_key" --fields "$field_name_ssh_key_public")
if [[ -z "$git_ssh_public_key" ]]; then
exit_with_error "Failed to lookup secret fields \"public keyl\" from item" \
"\"GitHub\" in 1Password. Make sure it exists."
fi
}
# Install Xcode Command Line Tools, like git
install_apple_developer_tools() {
if ! xcode-select -p &> /dev/null; then
log_header "🍏 Installing Apple's Developer Tools"
xcode-select --install
log_header "🍏 Accepting the Apple Xcode License"
sudo xcodebuild -license accept
fi
}
# Install applications using Homebrew
install_apps() {
log_header "🔧 Installing Homebrew Applications"
brew bundle --file=- <<EOF
brew 'gh' # https://cli.github.com
brew 'asdf' # Requires coreutils, curl, and gawk, https://asdf-vm.com
brew 'coreutils' # https://formulae.brew.sh/formula/coreutils
brew 'curl' # https://curl.se
brew 'gawk' # https://www.gnu.org/software/gawk/
brew 'gnupg' # https://www.gnupg.org
brew 'atuin' # https://atuin.sh
EOF
log_header "🔧 Installing Applications"
if ! brew list --cask visual-studio-code &>/dev/null; then
log_header "🔧 Installing Visual Studio Code"
brew install --cask visual-studio-code
else
log "Visual Studio Code is already installed"
fi
if ! brew list --cask 1password-cli &>/dev/null; then
log_header "🔧 Installing 1Password CLI"
brew install --cask 1password-cli
else
log "1Password CLI is already installed."
fi
}
# Run the custom installation script if it exists on the local machine.
install_custom() {
log_header "🪄 Running Customizations"
if [[ -f "$laptop_custom_install_script" ]]; then
log "Running local customization script at $laptop_custom_install_script"
chmod +x "$laptop_custom_install_script"
"$laptop_custom_install_script"
else
log "No customizations found at $laptop_custom_install_script".
fi
}
# Install and configure the user's PATH to include Homebrew. Or update
# Homebrew if it's already installed.
install_or_update_homebrew() {
if [[ $(command -v brew) == "" ]]; then
log_header "🍺 Installing Hombrew"
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to the user's PATH, if it's not already there.
if [[ ":$PATH:" == *":$HOMEBREW_PREFIX/bin:"* ]]; then
export PATH=$HOMEBREW_PREFIX/bin:$PATH
fi
else
log_header "🍺 Updating Homebrew"
brew update --force # https://github.com/Homebrew/brew/issues/1151
fi
}
# Returns zero when the Laptop script is considered installed locally, otherwise returns one.
is_laptop_installed() {
if [ -d "$laptop_repo_directory" ]; then
return 0 # Labtop is already installed.
else
return 1 # Laptop is not yet installed.
fi
}
# Create a soft symbolic link from the source file to the destination file, overwriting the link
# if it already exists.
# Parameters:
# source: The path to the source file to copy or URL of the file to download.
# destination: The destination path where the file will be copied or downloaded.
link_file() {
source="$1"
destination="$2"
log "Creating a new symbolic link from $source to $destination"
ln -sf "$source" "$destination"
}
# Update the local Laptop repository, pulling down all new changes. If there are local changes the
# user will be prompted to stash those changes before pulling. If the user declines the update will
# be cancelled, but the script will continue.
update_laptop_repository() {
pushd $laptop_repo_directory > /dev/null
log_header "🔁 Updating Laptop Repository"
# If there are changes in the local repository, ask the user if
# they want to stash and pull the latest changes.
if [[ `git status --porcelain` || $(git rev-parse --abbrev-ref HEAD) != "main" ]]; then
log "$unsaved_changes_prompt"
read "response?Continue without pulling the latest changes? [Y/n]? "
if [[ -z $response || $response =~ ^[Yy] ]]; then
log "\nSkipped pulling the latest changes from the remote Laptop repository."
return
else
log "\nStashing local changes.\n"
git stash
fi
fi
log "\nChecking out the \"main\" branch and pulling changes.\n"
git fetch
git checkout main
git pull origin main
popd > /dev/null
}
# Command Methods
install() {
force_flag="$1"
log_title "💻 Configuring your Laptop"
# By default, use the remote Laptop repository source files when configuring the computer.
local laptop_src_location=$remote_laptop_src_directory
if is_laptop_installed; then
# Prompt the user for access to 1Password before long running tasks.
get_secrets
# Use the local files for configuration
laptop_src_location=$laptop_src_directory
# Update the script's source code before continuing
update_laptop_repository
fi
if [[ $force_flag == true ]]; then
# Delete all local configurations so that they will be recreated with the default values.
# Effectively removing any local changes made after the script was initially run.
clear_laptop_configs
fi
install_apple_developer_tools
install_or_update_homebrew
install_apps
configure_ssh "$laptop_src_location"
configure_git "$laptop_src_location"
configure_zsh "$laptop_src_location"
configure_asdf "$laptop_src_location"
configure_gh
install_custom
clone_laptop_repository
clear_secrets
log_title "🎉 Done! Open a new shell to complete the installation."
}
# Remove everything this script added during installation.
uninstall() {
# TODO: This method is incomplete at the moment.
log_header "🔨 Uninstalling Applications"
if brew list asdf &>/dev/null; then
brew uninstall asdf
fi
if brew list atuin &>/dev/null; then
brew uninstall atuin # https://atuin.sh
fi
# TODO: Ask if you want to uninstall these
# brew uninstall gh
# brew uninstall coreutils
# brew uninstall curl
# brew uninstall gawk
# brew uninstall gnupg # https://www.gnupg.org
log_header "🗂️ Removing Application Data"
# Remove Laptop data
rm -rf "$laptop_local_directory"
# Remove Atuin Application Data
rm -rf "$HOME/.config/atuin"
log "\n🎉 Done. Restart your shell to complete the uninstall."
}
# Interprit commandline arguments and run the appropriate methods.
main() {
install_flag=false
uninstall_flag=false
force_flag=false
additional_args=()
while [[ $# -gt 0 ]]; do
case "$1" in
-f|--force)
force_flag=true
;;
install)
install_flag=true
;;
uninstall)
uninstall_flag=true
;;
*)
additional_args+=("$1")
;;
esac
shift
done
# Run the appropriate command(s)
if [[ $uninstall_flag == true ]]; then
uninstall
elif [[ $install_flag == true ]]; then
install $force_flag
# By default, run the install command.
else
install $force_flag
fi
}
# Hey... Thank you for reading this script before running some strangers code.
# Run main only if the script is executed directly; not when sourced.
if [[ "${(%):-%N}" == "$0" ]]; then
main "$@"
fi