-
Notifications
You must be signed in to change notification settings - Fork 13
/
base.sh
executable file
·399 lines (368 loc) · 12.6 KB
/
base.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
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
#!/usr/bin/env bash
#
# base.sh
#
# This is a wrapper or a common entry point for Base. It helps in two main ways:
#
# 1. Install base or change Base settings (install, embrace, set-team, set-shared-teams)
# 2. Do things with the installed version of Base (status, run, shell)
#
# In shell mode, we would create a Bash shell:
# a) using Base's bash_profile in case Base is installed
# b) using the in-line common bash_profile in case Base is not installed
#
error_exit() { printf 'ERROR: %s\n' "$@" >&2; exit 1; }
exit_if_error() { local ec=$1; shift; (($ec)) && error_exit "$@"; }
cd_base() { cd -- "$BASE_HOME" || exit_if_error 1 "Can't cd to BASE_HOME at '$BASE_HOME'"; }
usage_error() {
printf '%s\n' "$@" >&2
show_common_help >&2
exit 2
}
show_common_help() {
cat << EOF
Usage: base [-b DIR] [-t TEAM] [-x] [install|embrace|update|run|status|shell|set-team|set-shared-teams|version|help] ...
-b DIR - use DIR as BASE_HOME directory
-t TEAM - use TEAM as BASE_TEAM
-s TEAM - use TEAM as BASE_SHARED_TEAMS [use space delimited strings for multiple teams]
-f - ignore the existing installation and force install [relevant only for 'base install' command]
-v - show the CLI version
-x - turn on bash debug mode
install - install Base
embrace - override .bash_profile and .bashrc so that Base gets enabled upon login
update - update Base by running 'git pull' in BASE_HOME directory
run - run the rest of the command line after initializing Base
shell - if Base is installed, create an interactive Bash shell with Base initialized
if Base is not installed, create an interactive Bash shell with default settings
status - check if Base is installed or not
set-team TEAM - set BASE_TEAM in $HOME/.baserc
set-shared-teams TEAM - set shared BASE_SHARED_TEAMS in $HOME/.baserc [use space delimited strings for multiple teams]
version - show the CLI version
help - show this help message
man - print one line summary of all Base scripts, use '-t team' to filter by team
Invoking without any arguments would result in an interactive Bash shell with default settings.
EOF
}
base_init() {
local base_init=$BASE_HOME/base_init.sh
[[ -f $base_init ]] && source "$base_init"
}
get_base_home() {
if [[ ! $HOME ]]; then
error_exit "Environment variable 'HOME' is not set!"
fi
if [[ ! -d $HOME ]]; then
error_exit "\$HOME '$HOME' is not a directory!"
fi
# if BASE_HOME is not already set, source .baserc to see it is defined there
if [[ ! $BASE_HOME ]]; then
local baserc=$HOME/.baserc
[[ -f $baserc ]] && source "$baserc"
fi
# if BASE_HOME is still not set, go with the default value
BASE_HOME=${BASE_HOME:-$HOME/base}
}
create_base_home() {
# if set, BASE_HOME must hold a directory name
if [[ -e $BASE_HOME && ! -d $BASE_HOME ]]; then
error_exit "$BASE_HOME exists but it is not a directory!"
else
mkdir -- "$BASE_HOME"
exit_if_error $? "Can't create directory '$BASE_HOME'"
fi
}
verify_base() {
# now make sure BASE_HOME directory is actually a git repo
local git=$BASE_HOME/.git
if [[ ! -d $git ]]; then
glb_error_message="Directory '$BASE_HOME' isn't a git repo; check if Base is installed"
return 1
else
local oldpwd=$PWD
cd -- "$git" || { glb_error_message="Can't cd to '$git' directory"; return 1; }
if ! git rev-parse --git-dir &>/dev/null; then
glb_error_message="Directory '$git' isn't a git repo; check if Base is installed"
return 1
fi
local file missing=()
for file in base_init.sh lib/bash_profile lib/bashrc; do
if [[ ! -f $BASE_HOME/$file ]]; then
missing+=($file)
fi
done
cd -- "$oldpwd"
if (( ${#missing[@]} > 0)); then
glb_error_message="Files missing in Base repo: ${missing[@]}"
return 1
fi
fi
return 0
}
patch_baserc() {
local var value base_text_array=() grep_expr
local marker="# BASE_MARKER, do not delete"
local baserc=$HOME/.baserc baserc_temp=$HOME/.baserc.temp
for var; do
value=${!var}
if [[ $value ]]; then
base_text_array+=("export $var=\"$value\" $marker")
fi
if [[ $grep_expr ]]; then
grep_expr="$grep_expr|$var=.*$marker"
else
grep_expr="$var=.*$marker"
fi
done
if [[ ! -f $baserc ]]; then
touch -- "$baserc"
exit_if_error $? "Couldn't create '$baserc'"
fi
rm -f "$baserc_temp"
if [[ $grep_expr ]]; then
grep -Ev -- "$grep_expr" "$baserc" > "$baserc_temp"
else
touch -- "$baserc_temp"
fi
[[ -f $baserc_temp ]] || exit_if_error 1 "Couldn't create '$baserc_temp'"
printf '%s\n' "${base_text_array[@]}" >> "$baserc_temp"
exit_if_error $? "Couldn't append to '$baserc_temp'"
mv -f -- "$baserc_temp" "$baserc"
exit_if_error $? "Couldn't overwrite '$baserc'"
return 0
}
do_install() {
local repo="ssh://[email protected]:codeforester/base.git"
if [[ -d $BASE_HOME ]]; then
if ((force_install)); then
local base_home_backup=$BASE_HOME.$current_time
if mv -- "$BASE_HOME" "$base_home_backup"; then
printf '%s\n' "Moved current base home directory '$BASE_HOME' to '$base_home_backup'"
else
exit_if_error 1 "Couldn't move current base home directory '$BASE_HOME' to '$base_home_backup'"
fi
else
printf '%s\n' "Base is already installed at '$BASE_HOME'"
exit 0
fi
fi
git clone "$repo" "$BASE_HOME"
exit_if_error $? "Couldn't install Base"
printf '%s\n' "Installed Base at '$BASE_HOME'"
#
# patch .baserc
# This is how we remember custom BASE_HOME path and BASE_TEAM values.
# The user is free to put custom code into the .baserc file.
# A marker is appended to the lines managed by base CLI.
#
BASE_TEAM=$base_team
BASE_SHARED_TEAMS=$base_shared_teams
patch_baserc BASE_HOME BASE_TEAM BASE_SHARED_TEAMS
exit 0
}
do_embrace() {
if ! verify_base; then
error_exit "$glb_error_message"
fi
local base_bash_profile=$BASE_HOME/lib/bash_profile
local base_bashrc=$BASE_HOME/lib/bashrc
local bash_profile=$HOME/.bash_profile
local bashrc=$HOME/.bashrc
if [[ -L $bash_profile ]]; then
local bash_profile_link=$(readlink "$bash_profile")
fi
if [[ -L $bashrc ]]; then
local bashrc_link=$(readlink "$bashrc")
fi
if [[ $bash_profile_link = $base_bash_profile ]]; then
printf '%s\n' "$bash_profile is already symlinked to $base_bash_profile"
else
if [[ -f $bash_profile ]]; then
local bash_profile_backup=$HOME/.bash_profile.$current_time
printf '%s\n' "Backing up $bash_profile to $bash_profile_backup and overriding it with $base_bash_profile"
if ! cp -- "$bash_profile" "$bash_profile_backup"; then
exit_if_error $? "ERROR: can't create a backup of $bash_profile"
fi
fi
if ln -sf -- "$base_bash_profile" "$bash_profile"; then
printf '%s\n' "Symlinked '$bash_profile' to '$base_bash_profile'"
fi
fi
if [[ $bashrc_link = $base_bashrc ]]; then
printf '%s\n' "$bashrc is already symlinked to $base_bashrc"
else
if [[ -f $bashrc ]]; then
local bashrc_backup=$HOME/.bashrc.$current_time
printf '%s\n' "Backing up $bashrc to $bashrc_backup and overriding it with $base_bashrc"
if ! cp -- "$bashrc" "$bashrc_backup"; then
exit_if_error $? "ERROR: can't create a backup of $bashrc"
fi
fi
if ln -sf -- "$base_bashrc" "$bashrc"; then
printf '%s\n' "Symlinked '$bash_profile' to '$base_bash_profile'"
fi
fi
}
do_update() {
if [[ -d $BASE_HOME ]]; then
cd -- "$BASE_HOME" || error_exit "Can't cd to BASE_HOME at '$BASE_HOME'"
git pull
else
printf '%s\n' "ERROR: Base is not installed at BASE_HOME '$BASE_HOME'"
exit 1
fi
}
do_run() {
if ! verify_base; then
error_exit "$glb_error_message"
fi
base_init
"$@"
}
do_status() {
if [[ ! -d $BASE_HOME ]]; then
printf '%s\n' "Base is not installed at '$BASE_HOME'"
exit 1
fi
if ! verify_base; then
error_exit "$glb_error_message"
else
printf '%s\n' "Base is installed at $BASE_HOME"
fi
exit 0
}
do_shell() {
local base_bash_profile=$BASE_HOME/lib/bash_profile
if [[ -d $BASE_HOME && -f $base_bash_profile ]]; then
export BASE_SHELL=1
exec bash --rcfile "$base_bash_profile"
else
do_common_shell
fi
}
do_common_shell() {
local common_bash_profile=${0%/*}/bash_profile
if [[ -f $common_bash_profile ]]; then
exec bash --rcfile "$common_bash_profile"
else
error_exit "Common bash profile '$common_bash_profile' not found"
fi
}
do_version() {
printf '%s\n' "base version $BASE_VERSION"
}
do_man() {
local dir bin desc dirs team
local -A teams
if ! verify_base; then
error_exit "$glb_error_message"
fi
if ! command -v base-wrapper >/dev/null; then
error_exit "base-wrapper needs to be in your \$PATH to be able to run this command."
fi
dirs=(bin company/bin)
[[ $base_team ]] || base_team=$BASE_TEAM
if [[ $base_team ]]; then
dirs+=(team/$base_team/bin)
teams[$base_team]=1
fi
# note: BASE_SHARED_TEAMS could be a space delimited string or an array
for team in $BASE_SHARED_TEAMS "${BASE_SHARED_TEAMS[@]}"; do
[[ ${teams[$team]} ]] && continue
dirs+=(team/$team/bin)
teams[$team]=1
done
for dir in "${dirs[@]}"; do
dir=$BASE_HOME/$dir
[[ -d $dir ]] || continue
cd -- "$dir" || continue
printf '%s\n' "${dir#$BASE_HOME/}:"
for bin in *; do
[[ -f $bin ]] || continue
if head -1 "$bin" | grep -Eq '!/usr/bin/env[[:space:]]+base-wrapper[[:space:]]*'; then
desc=$(./"$bin" --describe)
printf '\t\t%s\n' "$bin: $desc"
fi
done
done
}
do_set_team() {
if (($# > 1)); then
usage_error "Got too many arguments"
else
if [[ $1 ]]; then
BASE_TEAM=$1
else
BASE_TEAM=$base_team
fi
if [[ ! $BASE_TEAM ]]; then
usage_error "Usage: base set-team TEAM"
fi
fi
patch_baserc BASE_TEAM
}
do_set_shared_teams() {
if (($# > 0)); then
BASE_SHARED_TEAMS=$*
elif [[ $base_shared_teams ]]; then
BASE_SHARED_TEAMS=$base_shared_teams
else
usage_error "Usage: base set-shared-teams TEAM ..."
fi
patch_baserc BASE_SHARED_TEAMS
}
assert_bash_version() {
local curr_version=$1 min_needed_version=$2
if ((curr_version < min_needed_version)); then
error_exit "Need Bash version >= $min_needed_version; your version is $curr_version"
fi
}
main() {
local bash_version="${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" timefmt="%Y-%m-%d:%H:%M:%S"
if [[ $1 =~ -h|--help|-help|help ]]; then
show_common_help
exit 0
fi
if [[ $1 =~ --version|-version|-v ]]; then
do_version
exit 0
fi
force_install=0
if ((bash_version >= 42)); then
printf -v current_time "%($timefmt)T" -1
else
current_time=$(date +"$timefmt")
fi
while getopts "fhb:s:t:vx" opt; do
case $opt in
b) export BASE_HOME=$OPTARG;;
t) export base_team=$OPTARG;;
s) export base_shared_teams=$OPTARG;;
f) force_install=1;;
v) do_version
exit 0;;
x) set -x;;
*) show_common_help >&2
exit 2;;
esac
done
shift $((OPTIND-1))
command=$1
shift 2>/dev/null
get_base_home
case $command in
embrace) do_embrace;;
help) show_common_help;;
install) assert_bash_version "$bash_version" 42; do_install;;
run) do_run "$@";;
set-shared-teams) do_set_shared_teams "$@";;
set-team) do_set_team "$@";;
shell) do_shell;;
status) do_status;;
update) do_update;;
version) do_version;;
man) do_man;;
"") do_common_shell;;
*) usage_error "Unrecognized command: $command";;
esac
}
main "$@"