1
|
|
#!/bin/sh
|
2
|
|
# shellcheck disable=SC2034
|
3
|
|
|
4
|
2
|
test || __() { :; }
|
5
|
|
|
6
|
2
|
installer="https://git.io/shellspec"
|
7
|
2
|
repo="https://github.com/shellspec/shellspec.git"
|
8
|
2
|
archive="https://github.com/shellspec/shellspec/archive"
|
9
|
2
|
project="shellspec"
|
10
|
2
|
exec="shellspec"
|
11
|
|
|
12
|
2
|
set -eu && :<<'USAGE'
|
13
|
|
Usage: [sudo] ${0##*/} [VERSION] [OPTIONS...]
|
14
|
|
or : wget -O- $installer | [sudo] sh
|
15
|
|
or : wget -O- $installer | [sudo] sh -s -- [OPTIONS...]
|
16
|
|
or : wget -O- $installer | [sudo] sh -s VERSION [OPTIONS...]
|
17
|
|
or : curl -fsSL $installer | [sudo] sh
|
18
|
|
or : curl -fsSL $installer | [sudo] sh -s -- [OPTIONS...]
|
19
|
|
or : curl -fsSL $installer | [sudo] sh -s VERSION [OPTIONS...]
|
20
|
|
|
21
|
|
VERSION:
|
22
|
|
Specify install version and method
|
23
|
|
|
24
|
|
e.g
|
25
|
|
1.0.0 Install 1.0.0 from git
|
26
|
|
master Install master from git
|
27
|
|
1.0.0.tar.gz Install 1.0.0 from tar.gz archive
|
28
|
|
. Install from local directory
|
29
|
|
|
30
|
|
OPTIONS:
|
31
|
|
-p, --prefix PREFIX Specify prefix [default: \$HOME/.local]
|
32
|
|
-b, --bin BIN Specify bin directory [default: <PREFIX>/bin]
|
33
|
|
-d, --dir DIR Specify installation directory [default: <PREFIX>/lib/$project]
|
34
|
|
-s, --switch Switch version (requires installation via git)
|
35
|
|
-l, --list List available versions (tags)
|
36
|
|
--pre Include pre-release
|
37
|
|
--fetch FETCH Force command to use when installing from archive (curl or wget)
|
38
|
|
-y, --yes Automatic yes to prompts
|
39
|
|
-h, --help You're looking at it
|
40
|
|
USAGE
|
41
|
|
|
42
|
|
usage() {
|
43
|
2
|
while IFS= read -r line && [ ! "${line#*:}" = \<\<"'$1'" ]; do :; done
|
44
|
2
|
while IFS= read -r line && [ ! "$line" = "$1" ]; do set "$@" "$line"; done
|
45
|
2
|
shift && [ $# -eq 0 ] || printf '%s\n' cat\<\<"$line" "$@" "$line"
|
46
|
|
}
|
47
|
|
|
48
|
2
|
CDPATH=''
|
49
|
2
|
[ "${ZSH_VERSION:-}" ] && setopt shwordsplit
|
50
|
|
|
51
|
|
finish() { done=1; exit "${1:-0}"; }
|
52
|
|
error() { printf '\033[31m%s\033[0m\n' "$1"; }
|
53
|
|
abort() { [ "${1:-}" ] && error "$1" >&2; finish 1; }
|
54
|
|
finished() { [ "$done" ] || error "Failed to install"; }
|
55
|
|
|
56
|
|
exists() {
|
57
|
2
|
type "$1" >/dev/null 2>&1 && return 0
|
58
|
2
|
( IFS=:; for p in $PATH; do [ -x "${p%/}/$1" ] && return 0; done; return 1 )
|
59
|
|
}
|
60
|
|
|
61
|
|
prompt() {
|
62
|
2
|
set -- "$1" "$2" "${3:-/dev/tty}"
|
63
|
2
|
printf "%s " "$1"
|
64
|
2
|
if eval "[ \"\$$2\" ] && :"; then
|
65
|
2
|
eval "printf \"%s\n\" \"\$$2\""
|
66
|
|
else
|
67
|
2
|
IFS= read -r "$2" < "$3" || return 1
|
68
|
2
|
[ "$3" = "/dev/tty" ] || eval "printf \"%s\n\" \"\$$2\""
|
69
|
|
fi
|
70
|
|
}
|
71
|
|
|
72
|
|
is_yes() {
|
73
|
2
|
case $1 in ( [Yy] | [Yy][Ee][Ss] ) return 0; esac
|
74
|
2
|
return 1
|
75
|
|
}
|
76
|
|
|
77
|
|
confirm() {
|
78
|
2
|
prompt "$@" || return 1
|
79
|
2
|
eval "is_yes \"\$$2\" &&:"
|
80
|
|
}
|
81
|
|
|
82
|
|
fetch() {
|
83
|
2
|
tmpfile="${TMPDIR:-${TMP:-/tmp}}/${1##*/}.$$"
|
84
|
2
|
case $FETCH in
|
85
|
2
|
curl) curl --head -sSfL -o /dev/null "$1" && curl -SfL "$1" ;;
|
86
|
2
|
wget) wget --spider -q "$1" && wget -O- "$1" ;;
|
87
|
2
|
esac > "$tmpfile" &&:
|
88
|
2
|
error=$?
|
89
|
2
|
if [ "$error" -eq 0 ]; then
|
90
|
2
|
unarchive "$tmpfile" "$1" "$2" &&:
|
91
|
2
|
error=$?
|
92
|
2
|
[ "$error" -ne 0 ] && [ -d "$2" ] && rm -rf "$2"
|
93
|
|
fi
|
94
|
2
|
rm "$tmpfile"
|
95
|
2
|
return "$error"
|
96
|
|
}
|
97
|
|
|
98
|
|
unarchive() {
|
99
|
2
|
mkdir -p "${3%/*}"
|
100
|
2
|
gunzip -c "$1" | (cd "${3%/*}"; tar xf -)
|
101
|
2
|
set -- "$1" "${2##*/}" "$3"
|
102
|
2
|
mv "$(components_path "${3%/*}/$project-${2%.tar.gz}"*)" "$3"
|
103
|
|
}
|
104
|
|
|
105
|
|
components_path() {
|
106
|
2
|
( set +u
|
107
|
2
|
cd "${1%/*}"
|
108
|
2
|
for p in *; do
|
109
|
2
|
case $p in (${1##*/}*) echo "${1%/*}/$p"; break ; esac
|
110
|
|
done
|
111
|
|
)
|
112
|
|
}
|
113
|
|
|
114
|
|
git_remote_tags() {
|
115
|
2
|
git ls-remote --tags "$repo" | while read -r line; do
|
116
|
2
|
tag=${line##*/} && pre=${tag#${tag%%[-+]*}}
|
117
|
2
|
[ "${1:-}" = "--pre" ] || case $pre in (-*) continue; esac
|
118
|
2
|
echo "${tag%\^\{\}}"
|
119
|
2
|
done | uniq
|
120
|
|
}
|
121
|
|
|
122
|
|
get_versions() {
|
123
|
2
|
git_remote_tags "${PRE:+--pre}"
|
124
|
|
}
|
125
|
|
|
126
|
|
sort_by_first_key() {
|
127
|
|
# Retry if sort is Windows version
|
128
|
|
( export LC_ALL=C; sort -k 1 2>/dev/null || command -p sort -k 1 )
|
129
|
|
}
|
130
|
|
|
131
|
|
version_sort() {
|
132
|
2
|
while read -r version; do
|
133
|
2
|
ver=${version%%+*} && num=${ver%%-*} && pre=${ver#$num}
|
134
|
|
#shellcheck disable=SC2086
|
135
|
2
|
case $num in
|
136
|
2
|
*[!0-9.]*) set -- 0 0 0 0 ;;
|
137
|
2
|
*) IFS=. && set -- $num ;;
|
138
|
|
esac
|
139
|
2
|
printf '%08d%08d%08d%08d' "${1:-0}" "${2:-0}" "${3:-0}" "${4:-0}"
|
140
|
2
|
printf '%s %s\n' "${pre:-=}" "$version"
|
141
|
2
|
done | sort_by_first_key | while read -r kv; do echo "${kv#* }"; done
|
142
|
|
}
|
143
|
|
|
144
|
|
join() {
|
145
|
2
|
s=''
|
146
|
2
|
while read -r v; do
|
147
|
2
|
s="$s$v$1"
|
148
|
|
done
|
149
|
2
|
echo "${s%"$1"}"
|
150
|
|
}
|
151
|
|
|
152
|
|
last() {
|
153
|
2
|
version=''
|
154
|
2
|
while read -r v; do
|
155
|
2
|
version=$v
|
156
|
|
done
|
157
|
2
|
echo "$version"
|
158
|
|
}
|
159
|
|
|
160
|
|
list_versions() {
|
161
|
2
|
get_versions | version_sort | join ", "
|
162
|
|
}
|
163
|
|
|
164
|
|
latest_version() {
|
165
|
2
|
get_versions | version_sort | last
|
166
|
|
}
|
167
|
|
|
168
|
2
|
${__SOURCED__:+return}
|
169
|
|
|
170
|
2
|
trap finished EXIT
|
171
|
2
|
VERSION='' PREFIX=$HOME/.local BIN='' DIR='' SWITCH='' PRE='' YES='' FETCH=''
|
172
|
2
|
done='' mode=install
|
173
|
|
|
174
|
2
|
__ parse_option __
|
175
|
|
|
176
|
2
|
while [ $# -gt 0 ]; do
|
177
|
2
|
case $1 in
|
178
|
0
|
-p | --prefix ) [ "${2:-}" ] || abort "PREFIX not specified"
|
179
|
0
|
PREFIX=$2 && shift ;;
|
180
|
0
|
-b | --bin ) [ "${2:-}" ] || abort "BIN not specified"
|
181
|
0
|
BIN=$2 && shift ;;
|
182
|
0
|
-d | --dir ) [ "${2:-}" ] || abort "DIR not specified"
|
183
|
0
|
DIR=$2 && shift ;;
|
184
|
0
|
-s | --switch ) SWITCH=1 ;;
|
185
|
0
|
-y | --yes ) YES=y ;;
|
186
|
0
|
-l | --list ) mode=list ;;
|
187
|
0
|
--pre ) PRE=1 ;;
|
188
|
0
|
--fetch ) [ "${2:-}" ] || abort "FETCH not specified"
|
189
|
0
|
case $2 in ( curl | wget ) FETCH=$2 && shift ;;
|
190
|
0
|
*) abort "FETCH must be 'curl' or 'wget'."
|
191
|
0
|
esac ;;
|
192
|
2
|
-h | --help ) eval "$(usage "USAGE" < "$0")" && finish ;;
|
193
|
2
|
-* ) abort "Unknown option $1" ;;
|
194
|
0
|
* ) VERSION=$1 ;;
|
195
|
|
esac
|
196
|
0
|
shift
|
197
|
|
done
|
198
|
|
|
199
|
0
|
if [ "$mode" = "list" ]; then
|
200
|
0
|
list_versions
|
201
|
0
|
finish
|
202
|
|
fi
|
203
|
|
|
204
|
0
|
BIN=${BIN:-${PREFIX%/}/bin} DIR=${DIR:-${PREFIX%/}/lib/$project}
|
205
|
|
|
206
|
0
|
__ main __
|
207
|
|
|
208
|
0
|
case $VERSION in
|
209
|
|
.)
|
210
|
0
|
method=local DIR=$PWD
|
211
|
0
|
[ -x "$DIR/$exec" ] || abort "Not found '$exec' in installation directory: '$DIR'"
|
212
|
0
|
VERSION=$("$DIR/$exec" --version)
|
213
|
|
;;
|
214
|
|
*.tar.gz)
|
215
|
0
|
[ "$SWITCH" ] && abort "Can not switch version when install from archive"
|
216
|
0
|
[ -e "$DIR" ] && abort "Already exists installation directory: '$DIR'"
|
217
|
0
|
method=archive
|
218
|
0
|
[ ! "$FETCH" ] && exists curl && FETCH=curl
|
219
|
0
|
[ ! "$FETCH" ] && exists wget && FETCH=wget
|
220
|
0
|
[ "$FETCH" ] || abort "Requires 'curl' or 'wget' when install from archive"
|
221
|
0
|
exists tar || abort "Not found 'tar' when install from archive"
|
222
|
|
;;
|
223
|
|
*)
|
224
|
0
|
if [ "$SWITCH" ]; then
|
225
|
0
|
method=switch
|
226
|
0
|
[ -d "$DIR" ] || abort "Not found installation directory: '$DIR'"
|
227
|
0
|
[ -d "$DIR/.git" ] || abort "Can't switch it's not a git repository: '$DIR'"
|
228
|
|
else
|
229
|
0
|
method=git
|
230
|
0
|
[ -e "$DIR" ] && abort "Already exists installation directory: '$DIR'"
|
231
|
|
fi
|
232
|
|
# requires git >= 1.7.10.4
|
233
|
0
|
exists git || abort "Requires 'git' when install from git repository"
|
234
|
0
|
[ "$VERSION" ] || VERSION=$(latest_version)
|
235
|
|
esac
|
236
|
|
|
237
|
0
|
echo "Executable file : $BIN/$exec"
|
238
|
0
|
echo "Installation directory : $DIR"
|
239
|
0
|
echo "Version (tag or commit): $VERSION"
|
240
|
0
|
case $method in
|
241
|
0
|
git) echo "[git] $repo" ;;
|
242
|
0
|
archive) echo "[$FETCH] $archive/$VERSION" ;;
|
243
|
|
esac
|
244
|
0
|
echo
|
245
|
|
|
246
|
0
|
confirm "Do you want to continue? [y/N]" YES || abort "Canceled"
|
247
|
|
|
248
|
0
|
case $method in
|
249
|
|
git)
|
250
|
0
|
git init "$DIR" && cd "$DIR"
|
251
|
0
|
git remote add origin "$repo"
|
252
|
0
|
git fetch --depth=1 origin "$VERSION"
|
253
|
0
|
git checkout -b "$VERSION" FETCH_HEAD
|
254
|
|
;;
|
255
|
|
archive)
|
256
|
0
|
fetch "$archive/$VERSION" "$DIR"
|
257
|
|
;;
|
258
|
|
switch)
|
259
|
0
|
cd "$DIR"
|
260
|
0
|
if message=$(git checkout "$VERSION" 2>&1); then
|
261
|
0
|
echo "$message"
|
262
|
|
else
|
263
|
0
|
git fetch --depth=1 origin "$VERSION"
|
264
|
0
|
git checkout -b "$VERSION" FETCH_HEAD
|
265
|
|
fi
|
266
|
|
;;
|
267
|
0
|
local) # Do nothing
|
268
|
|
esac
|
269
|
|
|
270
|
0
|
mkdir -p "$BIN"
|
271
|
0
|
ln -sf "$DIR/$exec" "$BIN/$exec"
|
272
|
|
|
273
|
0
|
if [ ! -L "$BIN/$exec" ]; then
|
274
|
0
|
rm "$BIN/$exec"
|
275
|
0
|
printf '#!/bin/sh\nexec "%s" "$@"\n' "$DIR/$exec" > "$BIN/$exec"
|
276
|
0
|
chmod +x "$BIN/$exec"
|
277
|
|
fi
|
278
|
|
|
279
|
0
|
echo "Done"
|
280
|
0
|
finish
|