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

Read our documentation on viewing source code .

Loading