-
Notifications
You must be signed in to change notification settings - Fork 6
/
private.sh
175 lines (159 loc) · 5.57 KB
/
private.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
#
# ProfileGem Private Functions
# Used internally to prepare the profile, not meant to be called by the user
#
# Print a warning if ProfileGem hasn't been updated recently.
#
# "Recently" is left unspecified, but currently we use a random number of days
# [62,72) to smooth update-prompting over time. This should reduce spikes caused
# by multiple installations/updates on a particular day. Two installations last
# updated on the same day will not necessarily be prompted to update again at
# the same time, and ideally will wander apart over time.
pg::_check_out_of_date() {
# Seed RANDOM with a value that is typically stable in a given installation.
local cksum _
read -r cksum _ < <(cksum <<<"$USER:$HOSTNAME")
# shellcheck disable=SC2030
if ! [[ -e "$_PGEM_LAST_UPDATE_MARKER" ]]; then
touch "$_PGEM_LAST_UPDATE_MARKER"
elif [[ -z "$(RANDOM="$cksum"; find "$_PGEM_LAST_UPDATE_MARKER" -newermt "-$((RANDOM%10 + 62)) days")" ]]; then
pg::err "ProfileGem is more than two months out of date; run 'pgem_update' to update."
pg::err " Or run 'pgem_snooze_update' to snooze this message."
pgem_snooze_update() {
touch "$_PGEM_LAST_UPDATE_MARKER"
unset -f pgem_snooze_update
}
fi
}
# Creates the config file if it doesn't exist, and returns its name.
# The auto-generated gem ordering may not be ideal, but this at least gets things set up.
pg::_configFile() {
local conf_file='local.conf.sh'
if ! [[ -f "$conf_file" ]]; then
# if it doesn't exist, create it
local gems=(*.gem)
{
printf '# %s\n' \
'ProfileGem configuration' '' \
'"#GEM ..." directives configure which .gems will be loaded, and in what order.' \
'You may need to re-order these directives to allow dependent gems to interact.' '' \
'Many gems support customizations which can be configured here as well.' \
"See each gem's documentation or base.conf.sh for details."
echo
printf '#GEM %s\n' "${gems[@]%.gem}"
} > "$conf_file"
fi
echo "$conf_file"
}
# Run "$@" in each gem - should generally be a function
# Sets _PGEM_EACHGEM_EXIT_CODE (to a non-zero value) if the command fails in one or more gems, which will cause load.sh
# to return a non-zero exit code.
pg::_eachGem() {
local i gem gem_dir exit oldpwd="$PWD"
for i in "${!_GEMS[@]}"; do
gem="${_GEMS[$i]}"
gem_dir="${_PGEM_LOC}/${gem}"
if [[ -d "$gem_dir" ]]; then
cd "$gem_dir" || continue
"$@"
exit=$?
if (( exit != 0 )); then
_PGEM_EACHGEM_EXIT_CODE=$exit
pg::err "'$*' failed in ${gem}"
fi
else
pg::log "${gem} is not a directory."
# http://wiki.bash-hackers.org/syntax/arrays
unset -v "_GEMS[$i]"
_PGEM_EACHGEM_EXIT_CODE=10
fi
done
# something will probably blow up if we can't cd back, but there's not a lot we can do
# shellcheck disable=SC2164
cd "$oldpwd"
return "${_PGEM_EACHGEM_EXIT_CODE:-0}"
}
# Prints a line describing the status of a local repo vs. its remote source.
# No output means no (known) changes to pull.
pg::_incomingRepo() {
local dir
dir=$(basename "$PWD")
local incoming=0
if [[ -d ".hg" ]]; then
incoming=$(hg incoming -q | wc -l)
elif [[ -d ".git" ]]; then
incoming=$(git fetch >& /dev/null && git log '..@{u}' --format=tformat:%h 2> /dev/null | wc -l)
fi
if (( incoming > 0 )); then
echo "$dir is $incoming change(s) behind."
fi
} && bc::cache pg::_incomingRepo 5m 10s PWD
# Pulls in updates for the current directory, currently aware of Mercurial and Git
# Alternatively create an update.sh script in the current directory to specify
# custom update behavior
pg::_updateRepo() {
local dir
dir=$(basename "$PWD")
if [[ -f "noupdate" ]]; then
echo "Not updating $dir"
return
fi
echo "Updating $dir"
if [[ -f "update.sh" ]]; then
_PGEM_DEBUG="${_PGEM_DEBUG:-false}" ./update.sh
elif [[ -d ".hg" ]]; then
# separate steps, so that we update even if pull doesn't
# find anything (i.e. someone pushed to this repo)
# TODO this should alert more clearly if the user needs to merge heads
hg pull > /dev/null
hg update -c tip > /dev/null
elif [[ -e ".git" ]]; then
# TODO are there failure modes for this?
git pull --quiet --rebase > /dev/null
else
pg::err "Could not update $dir"
return 1
fi
}
# Sources a file if it exists, skips if not
pg::_srcIfExist() {
if [[ -f "${1:?}" ]]; then
# pg::relative_path is slow, so we need to guard this call
if "${_PGEM_DEBUG:-false}"; then pg::log "Including $(pg::relative_path "$1" "$_PGEM_LOC")"; fi;
source "$1"
fi
}
# Initialize environment
pg::_loadBase() { pg::_srcIfExist "base.conf.sh"; }
# Evaluates the config file - not called by pg::_eachGem
pg::_evalConfig() { pg::_srcIfExist "$(pg::_configFile)"; }
# Set environment variables
pg::_loadEnv() { pg::_srcIfExist "environment.sh"; }
# Load aliases
pg::_loadAlias() { pg::_srcIfExist "aliases.sh"; }
# Define functions
pg::_loadFuncs() { pg::_srcIfExist "functions.sh"; }
# Add scripts directory to PATH
pg::_loadScripts() {
if [[ -d "scripts" ]]; then
pg::add_path "scripts"
fi
}
# Run commands
pg::_loadCmds() { pg::_srcIfExist "commands.sh"; }
# Output first paragraph of info.txt, indented
pg::_printDocLead() {
basename "$PWD"
if [[ -f "info.txt" ]]; then
# http://stackoverflow.com/a/1603584/113632
# BSD sed (OSX) lacks the q and Q flags mentioned in some other answers
awk -v 'RS=\n\n' '1;{exit}' info.txt | sed 's/^/ /'
echo
fi
}
# Output info.txt and check for incoming changes
pg::_printDoc() {
if [[ -f "info.txt" ]]; then
cat "info.txt"
fi
}