-
Notifications
You must be signed in to change notification settings - Fork 1
/
install.sh
executable file
ยท251 lines (218 loc) ยท 7.75 KB
/
install.sh
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
#!/usr/bin/env bash
# Root is $DOTPATH if it exists, otherwise the directory of this script
root=$(realpath "${DOTPATH:-$(dirname "$(realpath "$0")")}")
# Source the bash_traceback.sh file
source "${root}/bash_traceback.sh"
###############################################################################
# Install apps and packages #
###############################################################################
echo -e "๐ฒ \033[1;34mInstalling apps and packages...\033[0m"
# Check if the first argument is -y or --yes
auto_yes=false
if [[ ${1-} == "-y" ]] || [[ ${1-} == "--yes" ]]; then
auto_yes=true
fi
# Ensure yq is installed to parse the apps.toml file
if ! command -v yq &>/dev/null; then
echo -e "โฌ๏ธ \033[1;34mInstalling yq to parse apps.toml...\033[0m"
brew install yq
fi
apps_toml="${root}/apps.toml"
# Initialize arrays to store installed apps
installed_casks=()
installed_formulas=()
installed_mas=()
installed_uv=()
# Populate the arrays with installed apps
populate_installed_apps() {
while IFS= read -r app; do
installed_casks+=("${app}")
done < <(brew list --cask)
while IFS= read -r app; do
installed_formulas+=("${app}")
done < <(brew list --formula)
while IFS= read -r app; do
installed_mas+=("${app}")
done < <(mas list | cut -d' ' -f1)
while IFS= read -r app; do
installed_uv+=("${app}")
done < <(uv tool list | cut -d' ' -f1)
}
# Function to check if an item is in an array
in_array() {
local needle="$1"
shift
local item
for item; do
[[ ${item} == "${needle}" ]] && return 0
done
return 1
}
# Install function that uses the correct command based on the installation method
install() {
local app="$1"
local app_name="${app##*/}" # Extract the part after the last slash
local method="$2"
local cmd=""
local is_installed=false
case "${method}" in
"cask")
cmd="brew install --cask"
in_array "${app_name}" "${installed_casks[@]}" && is_installed=true
;;
"formula")
cmd="brew install --formula"
in_array "${app_name}" "${installed_formulas[@]}" && is_installed=true
;;
"mas")
if mas list | grep -q "${app} "; then
is_installed=true
app_name=$(mas list | grep "${app} " | sed -E 's/.*[0-9]+[[:space:]]+(.*)[[:space:]]+\(.*/\1/' | sed -E 's/[[:space:]]*$//')
else
app_name=$(mas info "${app}" | head -n 1 | sed -E 's/(.*)[[:space:]]+[0-9\.]+ \[.*\]/\1/')
cmd="mas install"
fi
;;
"uv")
cmd="uv tool install"
in_array "${app}" "${installed_uv[@]}" && is_installed=true
;;
*)
echo -e "โ \033[1;31mUnknown installation method: ${method} for ${app_name}\033[0m"
return 1
;;
esac
if ! ${is_installed}; then
echo -e "โฌ๏ธ \033[1;34mInstalling ${app_name}...\033[0m"
if ! ${cmd} "${app}"; then
echo -e "โ \033[1;31mFailed to install ${app_name}. Please check manually.\033[0m"
return 1
fi
else
echo -e "โ
\033[1;32m${app_name} is already installed.\033[0m"
fi
}
brew_sync() {
local toml_apps
toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "cask" or .value == "formula") | .key)) | flatten | .[]' "${apps_toml}")
toml_apps=$(echo "${toml_apps}" | sed -E 's|.*/||') # get name from tapped apps (slashes in name)
local missing_formulae
missing_formulae=$(comm -23 <(brew leaves | sort) <(echo "${toml_apps}" | sort))
local missing_casks
missing_casks=$(comm -23 <(brew list --cask | sort) <(echo "${toml_apps}" | sort))
local missing_apps
missing_apps=$(echo -e "${missing_formulae}\n${missing_casks}" | sort -u)
if [[ -n ${missing_apps} ]]; then
echo -e "โ๏ธ \033[1;31mThe following Homebrew-installed formulae and casks are missing from apps.toml:\033[0m"
# shellcheck disable=SC2001
echo "${missing_formulae}" | sed 's/^/ /'
# shellcheck disable=SC2001
echo "${missing_casks}" | sed 's/^/ /'
if [[ ${auto_yes} == false ]]; then
read -rp $'โ \e[1;31mDo you want to uninstall these apps? (y/n)\e[0m ' choice
else
choice="y"
fi
if [[ ${choice} == "y" ]]; then
for app in ${missing_apps}; do
brew uninstall --zap "${app}"
echo -e "๐ฎ \033[1;35mUninstalled ${app}.\033[0m"
done
else
echo -e "๐ \033[1;35mNo apps were uninstalled.\033[0m"
fi
else
echo -e "โ
\033[1;32mAll Homebrew-installed formulae and casks are present in apps.toml.\033[0m"
fi
}
uv_sync() {
local toml_apps
toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "uv") | .key)) | flatten | .[]' "${apps_toml}")
local missing_uv_apps
missing_uv_apps=$(comm -23 <(uv tool list | awk '{print $1}' | grep -v '^-*$' | sort) <(echo "${toml_apps}" | sort))
if [[ -n ${missing_uv_apps} ]]; then
echo -e "โ๏ธ \033[1;31mThe following uv-installed apps are missing from apps.toml:\033[0m"
# shellcheck disable=SC2001
echo "${missing_uv_apps}" | sed 's/^/ /'
if [[ ${auto_yes} == false ]]; then
read -rp $'โ \e[1;31mDo you want to uninstall these apps? (y/n)\e[0m ' choice
else
choice="y"
fi
if [[ ${choice} == "y" ]]; then
for app in ${missing_uv_apps}; do
uv tool uninstall "${app}"
echo -e "๐ฎ \033[1;35mUninstalled ${app}.\033[0m"
done
else
echo -e "๐ \033[1;35mNo apps were uninstalled.\033[0m"
fi
else
echo -e "โ
\033[1;32mAll uv-installed apps are present in apps.toml.\033[0m"
fi
}
mas_sync() {
local toml_apps
toml_apps=$(yq eval 'to_entries | map(.value | to_entries | map(select(.value == "mas") | .key)) | flatten | .[]' "${apps_toml}")
local installed_mas_apps
installed_mas_apps=$(mas list | sed -E 's/^([0-9]+)[[:space:]]+(.*)[[:space:]]+\(.*/\1 \2/' | sort)
# `-A` requires bash 4+, can't use Apple-provided bash which is 3.2
declare -A missing_mas_apps=() # Ensure it's initialized as an empty associative array
while read -r id name; do
if ! echo "${toml_apps}" | grep -q "^${id}$"; then
missing_mas_apps["${id}"]="${name}" # Store ID as key and app name as value
fi
done <<<"${installed_mas_apps}"
if [[ ${#missing_mas_apps[@]} -gt 0 ]]; then
echo -e "โ๏ธ \033[1;31mThe following Mac App Store apps are missing from apps.toml:\033[0m"
for id in "${!missing_mas_apps[@]}"; do
echo -e " ${missing_mas_apps[${id}]} (${id})"
done
if [[ ${auto_yes} == false ]]; then
read -rp $'โ \e[1;31mDo you want to uninstall these apps? (y/n)\e[0m ' choice
else
choice="y"
fi
if [[ ${choice} == "y" ]]; then
for id in "${!missing_mas_apps[@]}"; do
name="${missing_mas_apps[${id}]}"
if ! mas uninstall "${id}"; then
echo -e "โ \033[1;31mFailed to uninstall ${name} (${id}). Please uninstall it manually.\033[0m"
else
echo -e "๐ฎ \033[1;35mUninstalled ${name} (${id}).\033[0m"
fi
done
else
echo -e "๐ \033[1;35mNo apps were uninstalled.\033[0m"
fi
else
echo -e "โ
\033[1;32mAll Mac App Store apps are present in apps.toml.\033[0m"
fi
}
# Populate the arrays with installed apps
populate_installed_apps
# Use yq to parse the TOML file and store the output in a variable
# shellcheck disable=2016
parsed_toml=$(yq e 'to_entries | .[] | .key as $category | .value | to_entries[] | [$category, .key, .value] | @tsv' "${apps_toml}")
# Install apps from each category in the apps.toml file
current_category=""
echo "${parsed_toml}" | while IFS=$'\t' read -r category app method; do
if [[ ${category} != "${current_category}" ]]; then
suffix=$([[ ${category} == *s ]] && echo "" || echo " apps")
echo -e "\n๐ฆ \033[1;35mInstalling ${category}${suffix}...\033[0m"
current_category="${category}"
fi
install "${app}" "${method}"
done
echo -e "\n๐ \033[1;35mSyncing installed apps to apps.toml...\033[0m"
brew_sync
uv_sync
mas_sync
# Update Homebrew and installed formulas, casks and uv apps
echo -e "\n๐ผ \033[1;35mUpdating existing apps and packages...\033[0m"
brew update
brew upgrade
uv tool upgrade --all
mas upgrade
# Remove outdated versions from the cellar
brew cleanup