@beplus/be 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1 -0
  3. package/bin/be +1433 -0
  4. package/package.json +34 -0
package/bin/be ADDED
@@ -0,0 +1,1433 @@
1
+ #!/usr/bin/env bash
2
+ # shellcheck disable=SC2155
3
+ # Disabled "Declare and assign separately to avoid masking return values": https://github.com/koalaman/shellcheck/wiki/SC2155
4
+
5
+ # @todos
6
+ # brew install jq
7
+
8
+ #
9
+ # log <type> <msg>
10
+ #
11
+
12
+ log() {
13
+ printf " ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}\n" "$1" "$2"
14
+ }
15
+
16
+ #
17
+ # verbose_log <type> <msg>
18
+ # Can suppress with --quiet.
19
+ # Like log but to stderr rather than stdout, so can also be used from "display" routines.
20
+ #
21
+
22
+ verbose_log() {
23
+ if [[ "${SHOW_VERBOSE_LOG}" == "true" ]]; then
24
+ >&2 printf " ${SGR_CYAN}%10s${SGR_RESET} : ${SGR_FAINT}%s${SGR_RESET}\n" "$1" "$2"
25
+ fi
26
+ }
27
+
28
+ #
29
+ # Exit with the given <msg ...>
30
+ #
31
+
32
+ abort() {
33
+ >&2 printf "\n ${SGR_RED}Error: %s${SGR_RESET}\n\n" "$*" && exit 1
34
+ }
35
+
36
+ #
37
+ # Synopsis: trace message ...
38
+ # Debugging output to stderr, not used in production code.
39
+ #
40
+
41
+ function trace() {
42
+ >&2 printf "trace: %s\n" "$*"
43
+ }
44
+
45
+ #
46
+ # Synopsis: echo_red message ...
47
+ # Highlight message in colour (on stdout).
48
+ #
49
+
50
+ function echo_red() {
51
+ printf "${SGR_RED}%s${SGR_RESET}\n" "$*"
52
+ }
53
+
54
+ #
55
+ # Synopsis: n_grep <args...>
56
+ # grep wrapper to ensure consistent grep options and circumvent aliases.
57
+ #
58
+
59
+ function n_grep() {
60
+ GREP_OPTIONS='' command grep "$@"
61
+ }
62
+
63
+ #
64
+ # Setup and state
65
+ #
66
+
67
+ VERSION="v0.1.0"
68
+
69
+ BE_PREFIX="${BE_PREFIX-/usr/local}"
70
+ BE_PREFIX=${BE_PREFIX%/}
71
+ readonly BE_PREFIX
72
+
73
+ BE_CACHE_PREFIX="${BE_CACHE_PREFIX-${BE_PREFIX}}"
74
+ BE_CACHE_PREFIX=${BE_CACHE_PREFIX%/}
75
+ CACHE_DIR="${BE_CACHE_PREFIX}/be/versions"
76
+ readonly BE_CACHE_PREFIX CACHE_DIR
77
+
78
+ BE_MIRROR=${BE_MIRROR:-https://github.com/beplus/cli/releases/download}
79
+ BE_MIRROR=${BE_MIRROR%/}
80
+ readonly BE_MIRROR
81
+
82
+ BE_DOWNLOAD_MIRROR=${BE_DOWNLOAD_MIRROR:-https://github.com/beplus/cli/releases/download}
83
+ BE_DOWNLOAD_MIRROR=${BE_DOWNLOAD_MIRROR%/}
84
+ readonly BE_DOWNLOAD_MIRROR
85
+
86
+ # Using xz instead of gzip is enabled by default, if xz compatibility checks pass.
87
+ # User may set BE_USE_XZ to 0 to disable, or set to anything else to enable.
88
+ # May also be overridden by command line flags.
89
+
90
+ # Normalise external values to true/false
91
+ if [[ "${BE_USE_XZ}" = "0" ]]; then
92
+ BE_USE_XZ="false"
93
+ elif [[ -n "${BE_USE_XZ+defined}" ]]; then
94
+ BE_USE_XZ="true"
95
+ fi
96
+ # Not setting to readonly. Overriden by CLI flags, and update_xz_settings_for_version.
97
+
98
+ BE_MAX_REMOTE_MATCHES=${BE_MAX_REMOTE_MATCHES:-20}
99
+
100
+ g_mirror_url=${BE_MIRROR}
101
+ g_mirror_folder_name="beplus"
102
+
103
+ # Options for curl and wget.
104
+ # Defining commands in variables is fraught (https://mywiki.wooledge.org/BashFAQ/050)
105
+ # but we can follow the simple case and store arguments in an array.
106
+
107
+ GET_SHOWS_PROGRESS="false"
108
+ # --location to follow redirects
109
+ # --fail to avoid happily downloading error page from web server for 404 et al
110
+ # --show-error to show why failed (on stderr)
111
+ CURL_OPTIONS=( "--location" "--fail" "--show-error" )
112
+ if [[ -t 1 ]]; then
113
+ CURL_OPTIONS+=( "--progress-bar" )
114
+ command -v curl &> /dev/null && GET_SHOWS_PROGRESS="true"
115
+ else
116
+ CURL_OPTIONS+=( "--silent" )
117
+ fi
118
+ WGET_OPTIONS=( "-q" "-O-" )
119
+
120
+ # Set by set_active_beplus
121
+ g_active_beplus=
122
+
123
+ # Set by various lookups to allow mixed logging and return value from function
124
+ g_target_beplus=
125
+
126
+ DOWNLOAD=false # set to opt-out of activate (install), and opt-in to download (run, exec)
127
+ ARCH=
128
+ SHOW_VERBOSE_LOG="true"
129
+
130
+ # ANSI escape codes
131
+ # https://en.wikipedia.org/wiki/ANSI_escape_code
132
+ # https://no-color.org
133
+ # https://bixense.com/clicolors
134
+
135
+ USE_COLOR="true"
136
+ if [[ -n "${CLICOLOR_FORCE+defined}" && "${CLICOLOR_FORCE}" != "0" ]]; then
137
+ USE_COLOR="true"
138
+ elif [[ -n "${NO_COLOR+defined}" || "${CLICOLOR}" = "0" || ! -t 1 ]]; then
139
+ USE_COLOR="false"
140
+ fi
141
+ readonly USE_COLOR
142
+
143
+ # Select Graphic Rendition codes
144
+ if [[ "${USE_COLOR}" = "true" ]]; then
145
+ # KISS and use codes rather than tput, avoid dealing with missing tput or TERM.
146
+ readonly SGR_RESET="\033[0m"
147
+ readonly SGR_FAINT="\033[2m"
148
+ readonly SGR_RED="\033[31m"
149
+ readonly SGR_CYAN="\033[36m"
150
+ else
151
+ readonly SGR_RESET=
152
+ readonly SGR_FAINT=
153
+ readonly SGR_RED=
154
+ readonly SGR_CYAN=
155
+ fi
156
+
157
+ #
158
+ # set_arch <arch> to override $(uname -a)
159
+ #
160
+
161
+ set_arch() {
162
+ if test -n "$1"; then
163
+ ARCH="$1"
164
+ else
165
+ abort "missing -a|--arch value"
166
+ fi
167
+ }
168
+
169
+ #
170
+ # Synopsis: set_insecure
171
+ # Globals modified:
172
+ # - CURL_OPTIONS
173
+ # - WGET_OPTIONS
174
+ #
175
+
176
+ function set_insecure() {
177
+ CURL_OPTIONS+=( "--insecure" )
178
+ WGET_OPTIONS+=( "--no-check-certificate" )
179
+ }
180
+
181
+ #
182
+ # Synposis: display_major_version numeric-version
183
+ #
184
+ display_major_version() {
185
+ local version=$1
186
+ version="${version#v}"
187
+ version="${version%%.*}"
188
+ echo "${version}"
189
+ }
190
+
191
+ #
192
+ # Synopsis: update_xz_settings_for_version numeric-version
193
+ # Globals modified:
194
+ # - BE_USE_XZ
195
+ #
196
+
197
+ function update_xz_settings_for_version() {
198
+ # tarballs in xz format were available in later version of iojs, but KISS and only use xz from v4.
199
+ if [[ "${BE_USE_XZ}" = "true" ]]; then
200
+ local major_version="$(display_major_version "$1")"
201
+ if [[ "${major_version}" -lt 4 ]]; then
202
+ BE_USE_XZ="false"
203
+ fi
204
+ fi
205
+ }
206
+
207
+ #
208
+ # Synopsis: update_arch_settings_for_version numeric-version
209
+ # Globals modified:
210
+ # - ARCH
211
+ #
212
+
213
+ function update_arch_settings_for_version() {
214
+ local tarball_platform="$(display_tarball_platform)"
215
+ if [[ -z "${ARCH}" && "${tarball_platform}" = "darwin-arm64" ]]; then
216
+ # First native builds were for v16, but can use x64 in rosetta for older versions.
217
+ local major_version="$(display_major_version "$1")"
218
+ if [[ "${major_version}" -lt 16 ]]; then
219
+ ARCH=x64
220
+ fi
221
+ fi
222
+ }
223
+
224
+ #
225
+ # Synopsis: is_numeric_version version
226
+ #
227
+
228
+ function is_numeric_version() {
229
+ # e.g. 6, v7.1, 8.11.3
230
+ [[ "$1" =~ ^[v]{0,1}[0-9]+(\.[0-9]+){0,2}$ ]]
231
+ }
232
+
233
+ #
234
+ # Synopsis: is_exact_numeric_version version
235
+ #
236
+
237
+ function is_exact_numeric_version() {
238
+ # e.g. 6, v7.1, 8.11.3
239
+ [[ "$1" =~ ^[v]{0,1}[0-9]+\.[0-9]+\.[0-9]+$ ]]
240
+ }
241
+
242
+ #
243
+ # Synopsis: is_beplus_support_version version
244
+ #
245
+
246
+ function is_beplus_support_version() {
247
+ [[ "$1" =~ ^(active|current)$ ]]
248
+ }
249
+
250
+ #
251
+ # Synopsis: display_latest_beplus_support_alias version
252
+ # Map aliases onto existing be aliases, current and lts
253
+ #
254
+
255
+ # @todo
256
+ function display_latest_beplus_support_alias() {
257
+ case "$1" in
258
+ "active") printf "current" ;;
259
+ "current") printf "current" ;;
260
+ *) printf "unexpected-version"
261
+ esac
262
+ }
263
+
264
+ #
265
+ # Functions used when showing versions installed
266
+ #
267
+
268
+ enter_fullscreen() {
269
+ # Set cursor to be invisible
270
+ tput civis 2> /dev/null
271
+ # Save screen contents
272
+ tput smcup 2> /dev/null
273
+ stty -echo
274
+ }
275
+
276
+ leave_fullscreen() {
277
+ # Set cursor to normal
278
+ tput cnorm 2> /dev/null
279
+ # Restore screen contents
280
+ tput rmcup 2> /dev/null
281
+ stty echo
282
+ }
283
+
284
+ handle_sigint() {
285
+ leave_fullscreen
286
+ S="$?"
287
+ kill 0
288
+ exit $S
289
+ }
290
+
291
+ handle_sigtstp() {
292
+ leave_fullscreen
293
+ kill -s SIGSTOP $$
294
+ }
295
+
296
+ #
297
+ # Output usage information.
298
+ #
299
+
300
+ display_help() {
301
+ cat <<-EOF
302
+
303
+ Usage: be [options] [COMMAND] [args]
304
+
305
+ Commands:
306
+
307
+ be Display downloaded beplus CLI versions and install selection
308
+ be latest Install the latest beplus CLI release (downloading if necessary)
309
+ be <version> Install beplus CLI <version> (downloading if necessary)
310
+ be install <version> Install beplus CLI <version> (downloading if necessary)
311
+ be run <version> [args ...] Execute downloaded beplus CLI <version> with [args ...]
312
+ be which <version> Output path for downloaded beplus CLI <version>
313
+ be exec <vers> <cmd> [args...] Execute command with modified PATH, with downloaded beplus CLI <version> first
314
+ be rm <version ...> Remove the given downloaded version(s)
315
+ be prune Remove all downloaded versions except the installed version
316
+ be --latest Output the latest beplus CLI version available
317
+ be ls Output downloaded versions
318
+ be ls-remote [version] Output matching versions available for download
319
+ be uninstall Remove the installed beplus CLI
320
+
321
+ Options:
322
+
323
+ -V, --version Output version of n
324
+ -h, --help Display help information
325
+ -q, --quiet Disable curl output. Disable log messages processing "auto" label.
326
+ -d, --download Download if necessary, and don't make active
327
+ -a, --arch Override system architecture
328
+ --all ls-remote displays all matches instead of last 20
329
+ --insecure Turn off certificate checking for https requests (may be needed from behind a proxy server)
330
+ --use-xz/--no-use-xz Override automatic detection of xz support and enable/disable use of xz compressed beplus CLI downloads.
331
+
332
+ Aliases:
333
+
334
+ install: i
335
+ latest: current
336
+ ls: list
337
+ lsr: ls-remote
338
+ rm: -
339
+ run: use, as
340
+ which: bin
341
+
342
+ Versions:
343
+
344
+ Numeric version numbers can be complete or incomplete, with an optional leading 'v'.
345
+
346
+ 0.17.0, 1, v1.2 Numeric versions
347
+ latest, current Newest official release
348
+ auto Read version from file: .bepluscloud, .beplus-version, or package.json
349
+
350
+ EOF
351
+ }
352
+
353
+ err_no_installed_print_help() {
354
+ display_help
355
+ abort "no downloaded versions yet, see above help for commands"
356
+ }
357
+
358
+ #
359
+ # Synopsis: next_version_installed selected_version
360
+ # Output version after selected (which may be blank under some circumstances).
361
+ #
362
+
363
+ function next_version_installed() {
364
+ display_cache_versions | n_grep "$1" -A 1 | tail -n 1
365
+ }
366
+
367
+ #
368
+ # Synopsis: prev_version_installed selected_version
369
+ # Output version before selected (which may be blank under some circumstances).
370
+ #
371
+
372
+ function prev_version_installed() {
373
+ display_cache_versions | n_grep "$1" -B 1 | head -n 1
374
+ }
375
+
376
+ #
377
+ # Output be version.
378
+ #
379
+
380
+ display_be_version() {
381
+ echo "$VERSION" && exit 0
382
+ }
383
+
384
+ #
385
+ # Synopsis: set_active_beplus
386
+ # Checks cached downloads for a binary matching the active beplus CLI.
387
+ # Globals modified:
388
+ # - g_active_beplus
389
+ #
390
+
391
+ function set_active_beplus() {
392
+ g_active_beplus=
393
+ local beplus_path="$(command -v beplus)"
394
+ if [[ -x "${beplus_path}" ]]; then
395
+ local installed_version=$(beplus --version)
396
+ installed_version=${installed_version#v}
397
+ for dir in "${CACHE_DIR}"/*/ ; do
398
+ local folder_name="${dir%/}"
399
+ folder_name="${folder_name##*/}"
400
+ if diff &> /dev/null \
401
+ "${CACHE_DIR}/${folder_name}/${installed_version}/bin/beplus" \
402
+ "${beplus_path}" ; then
403
+ g_active_beplus="${folder_name}/${installed_version}"
404
+ break
405
+ fi
406
+ done
407
+ fi
408
+ }
409
+
410
+ #
411
+ # Display sorted versions directories paths.
412
+ #
413
+
414
+ display_versions_paths() {
415
+ find "$CACHE_DIR" -maxdepth 2 -type d \
416
+ | sed 's|'"$CACHE_DIR"'/||g' \
417
+ | n_grep -E "/[0-9]+\.[0-9]+\.[0-9]+" \
418
+ | sed 's|/|.|' \
419
+ | sort -k 1,1 -k 2,2n -k 3,3n -k 4,4n -t . \
420
+ | sed 's|\.|/|'
421
+ }
422
+
423
+ #
424
+ # Display installed versions with <selected>
425
+ #
426
+
427
+ display_versions_with_selected() {
428
+ local selected="$1"
429
+ echo
430
+ for version in $(display_versions_paths); do
431
+ if test "$version" = "$selected"; then
432
+ printf " ${SGR_CYAN}ο${SGR_RESET} %s\n" "$version"
433
+ else
434
+ printf " ${SGR_FAINT}%s${SGR_RESET}\n" "$version"
435
+ fi
436
+ done
437
+ echo
438
+ printf "Use up/down arrow keys to select a version, return key to install, d to delete, q to quit"
439
+ }
440
+
441
+ #
442
+ # Synopsis: display_cache_versions
443
+ #
444
+
445
+ function display_cache_versions() {
446
+ for folder_and_version in $(display_versions_paths); do
447
+ echo "${folder_and_version}"
448
+ done
449
+ }
450
+
451
+ #
452
+ # Display current beplus --version and others installed.
453
+ #
454
+
455
+ menu_select_cache_versions() {
456
+ enter_fullscreen
457
+ set_active_beplus
458
+ local selected="${g_active_beplus}"
459
+
460
+ clear
461
+ display_versions_with_selected "${selected}"
462
+
463
+ trap handle_sigint INT
464
+ trap handle_sigtstp SIGTSTP
465
+
466
+ ESCAPE_SEQ=$'\033'
467
+ UP=$'A'
468
+ DOWN=$'B'
469
+ CTRL_P=$'\020'
470
+ CTRL_N=$'\016'
471
+
472
+ while true; do
473
+ read -rsn 1 key
474
+ case "$key" in
475
+ "$ESCAPE_SEQ")
476
+ # Handle ESC sequences followed by other characters, i.e. arrow keys
477
+ read -rsn 1 -t 1 tmp
478
+ # See "[" if terminal in normal mode, and "0" in application mode
479
+ if [[ "$tmp" == "[" || "$tmp" == "O" ]]; then
480
+ read -rsn 1 -t 1 arrow
481
+ case "$arrow" in
482
+ "$UP")
483
+ clear
484
+ selected="$(prev_version_installed "${selected}")"
485
+ display_versions_with_selected "${selected}"
486
+ ;;
487
+ "$DOWN")
488
+ clear
489
+ selected="$(next_version_installed "${selected}")"
490
+ display_versions_with_selected "${selected}"
491
+ ;;
492
+ esac
493
+ fi
494
+ ;;
495
+ "d")
496
+ if [[ -n "${selected}" ]]; then
497
+ clear
498
+ # Note: prev/next is constrained to min/max
499
+ local after_delete_selection="$(next_version_installed "${selected}")"
500
+ if [[ "${after_delete_selection}" == "${selected}" ]]; then
501
+ after_delete_selection="$(prev_version_installed "${selected}")"
502
+ fi
503
+ remove_versions "${selected}"
504
+
505
+ if [[ "${after_delete_selection}" == "${selected}" ]]; then
506
+ clear
507
+ leave_fullscreen
508
+ echo "All downloaded versions have been deleted from cache."
509
+ exit
510
+ fi
511
+
512
+ selected="${after_delete_selection}"
513
+ display_versions_with_selected "${selected}"
514
+ fi
515
+ ;;
516
+ # Vim or Emacs 'up' key
517
+ "k"|"$CTRL_P")
518
+ clear
519
+ selected="$(prev_version_installed "${selected}")"
520
+ display_versions_with_selected "${selected}"
521
+ ;;
522
+ # Vim or Emacs 'down' key
523
+ "j"|"$CTRL_N")
524
+ clear
525
+ selected="$(next_version_installed "${selected}")"
526
+ display_versions_with_selected "${selected}"
527
+ ;;
528
+ "q")
529
+ clear
530
+ leave_fullscreen
531
+ exit
532
+ ;;
533
+ "")
534
+ # enter key returns empty string
535
+ leave_fullscreen
536
+ [[ -n "${selected}" ]] && activate "${selected}"
537
+ exit
538
+ ;;
539
+ esac
540
+ done
541
+ }
542
+
543
+ #
544
+ # Move up a line and erase.
545
+ #
546
+
547
+ erase_line() {
548
+ printf "\033[1A\033[2K"
549
+ }
550
+
551
+ #
552
+ # Disable PaX mprotect for <binary>
553
+ #
554
+
555
+ disable_pax_mprotect() {
556
+ test -z "$1" && abort "binary required"
557
+ local binary="$1"
558
+
559
+ # try to disable mprotect via XATTR_PAX header
560
+ local PAXCTL="$(PATH="/sbin:/usr/sbin:$PATH" command -v paxctl-ng 2>&1)"
561
+ local PAXCTL_ERROR=1
562
+ if [ -x "$PAXCTL" ]; then
563
+ $PAXCTL -l && $PAXCTL -m "$binary" >/dev/null 2>&1
564
+ PAXCTL_ERROR="$?"
565
+ fi
566
+
567
+ # try to disable mprotect via PT_PAX header
568
+ if [ "$PAXCTL_ERROR" != 0 ]; then
569
+ PAXCTL="$(PATH="/sbin:/usr/sbin:$PATH" command -v paxctl 2>&1)"
570
+ if [ -x "$PAXCTL" ]; then
571
+ $PAXCTL -Cm "$binary" >/dev/null 2>&1
572
+ fi
573
+ fi
574
+ }
575
+
576
+ #
577
+ # clean_copy_folder <source> <target>
578
+ #
579
+
580
+ clean_copy_folder() {
581
+ local source="$1"
582
+ local target="$2"
583
+ if [[ -d "${source}" ]]; then
584
+ rm -rf "${target}"
585
+ cp -fR "${source}" "${target}"
586
+ fi
587
+ }
588
+
589
+ #
590
+ # Activate <version>
591
+ #
592
+
593
+ activate() {
594
+ local version="$1"
595
+ local dir="$CACHE_DIR/$version"
596
+ local original_beplus="$(command -v beplus)"
597
+ local installed_beplus="${BE_PREFIX}/bin/beplus"
598
+ log "copying" "$version"
599
+
600
+
601
+ # We would just copy from cache to BE_PREFIX, but:
602
+ # - various linux versions use symlinks for folders in /usr/local and also error when copy folder onto symlink
603
+
604
+ # bin
605
+ mkdir -p "$BE_PREFIX/bin"
606
+ # Remove old beplus to avoid potential problems with firewall getting confused on Darwin by overwrite.
607
+ rm -f "$BE_PREFIX/bin/beplus"
608
+ # Copy bin item by hand, in case user has installed global npm modules into cache.
609
+ cp -f "$dir/beplus" "$BE_PREFIX/bin"
610
+
611
+ disable_pax_mprotect "${installed_beplus}"
612
+
613
+ local active_beplus="$(command -v beplus)"
614
+ if [[ -e "${active_beplus}" && -e "${installed_beplus}" && "${active_beplus}" != "${installed_beplus}" ]]; then
615
+ # Installed and active are different which might be a PATH problem. List both to give user some clues.
616
+ log "installed" "$("${installed_beplus}" --version) to ${installed_beplus}"
617
+ log "active" "$("${active_beplus}" --version) at ${active_beplus}"
618
+ else
619
+ log "installed" "$("${installed_beplus}" --version)"
620
+
621
+ # Extra tips for changed location.
622
+ if [[ -e "${active_beplus}" && -e "${original_beplus}" && "${active_beplus}" != "${original_beplus}" ]]; then
623
+ printf '\nNote: the beplus command changed location and the old location may be remembered in your current shell.\n'
624
+ log old "${original_beplus}"
625
+ log new "${active_beplus}"
626
+ printf 'If "beplus --version" shows the old version then start a new shell, or reset the location hash with:\nhash -r (for bash, zsh, ash, dash, and ksh)\nrehash (for csh and tcsh)\n'
627
+ fi
628
+ fi
629
+ }
630
+
631
+ #
632
+ # Install <version>
633
+ #
634
+
635
+ install() {
636
+ [[ -z "$1" ]] && abort "version required"
637
+ local version
638
+ get_latest_resolved_version "$1" || return 2
639
+ version="${g_target_beplus}"
640
+ [[ -n "${version}" ]] || abort "no version found for '$1'"
641
+ update_xz_settings_for_version "${version}"
642
+ update_arch_settings_for_version "${version}"
643
+
644
+ local dir="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
645
+
646
+ # Note: decompression flags ignored with default Darwin tar which autodetects.
647
+ if test "$BE_USE_XZ" = "true"; then
648
+ local tarflag="-Jx"
649
+ else
650
+ local tarflag="-zx"
651
+ fi
652
+
653
+ if test -d "$dir"; then
654
+ if [[ ! -e "$dir/be.lock" ]] ; then
655
+ if [[ "$DOWNLOAD" == "false" ]] ; then
656
+ activate "${g_mirror_folder_name}/${version}"
657
+ fi
658
+ exit
659
+ fi
660
+ fi
661
+
662
+ log installing "beplus CLI v$version"
663
+
664
+ local url="$(tarball_url "$version")"
665
+ is_ok "${url}" || abort "download preflight failed for '$version' (${url})"
666
+
667
+ log mkdir "$dir"
668
+ mkdir -p "$dir" || abort "sudo required (or change ownership, or define BE_PREFIX)"
669
+ touch "$dir/be.lock"
670
+
671
+ cd "${dir}" || abort "Failed to cd to ${dir}"
672
+
673
+ log fetch "$url"
674
+ do_get "${url}" | tar "$tarflag" --no-same-owner -f - # --strip-components=1
675
+ pipe_results=( "${PIPESTATUS[@]}" )
676
+ if [[ "${pipe_results[0]}" -ne 0 ]]; then
677
+ abort "failed to download archive for $version"
678
+ fi
679
+ if [[ "${pipe_results[1]}" -ne 0 ]]; then
680
+ abort "failed to extract archive for $version"
681
+ fi
682
+ [ "$GET_SHOWS_PROGRESS" = "true" ] && erase_line
683
+ rm -f "$dir/be.lock"
684
+
685
+ disable_pax_mprotect bin/beplus
686
+
687
+ if [[ "$DOWNLOAD" == "false" ]]; then
688
+ activate "${g_mirror_folder_name}/$version"
689
+ fi
690
+ }
691
+
692
+ #
693
+ # Be more silent.
694
+ #
695
+
696
+ set_quiet() {
697
+ SHOW_VERBOSE_LOG="false"
698
+ command -v curl > /dev/null && CURL_OPTIONS+=( "--silent" ) && GET_SHOWS_PROGRESS="false"
699
+ }
700
+
701
+ #
702
+ # Synopsis: do_get [option...] url
703
+ # Call curl or wget with combination of global and passed options.
704
+ #
705
+
706
+ function do_get() {
707
+ if command -v curl &> /dev/null; then
708
+ curl "${CURL_OPTIONS[@]}" "$@"
709
+ elif command -v wget &> /dev/null; then
710
+ wget "${WGET_OPTIONS[@]}" "$@"
711
+ else
712
+ abort "curl or wget command required"
713
+ fi
714
+ }
715
+
716
+ #
717
+ # Synopsis: do_get_index [option...] url
718
+ # Call curl or wget with combination of global and passed options,
719
+ # with options tweaked to be more suitable for getting index.
720
+ #
721
+
722
+ function do_get_index() {
723
+ if command -v curl &> /dev/null; then
724
+ # --silent to suppress progress et al
725
+ curl --silent --compressed "${CURL_OPTIONS[@]}" "$@"
726
+ elif command -v wget &> /dev/null; then
727
+ wget "${WGET_OPTIONS[@]}" "$@"
728
+ else
729
+ abort "curl or wget command required"
730
+ fi
731
+ }
732
+
733
+ #
734
+ # Synopsis: remove_versions version ...
735
+ #
736
+
737
+ function remove_versions() {
738
+ [[ -z "$1" ]] && abort "version(s) required"
739
+ while [[ $# -ne 0 ]]; do
740
+ local version
741
+ get_latest_resolved_version "$1" || break
742
+ version="${g_target_beplus}"
743
+ if [[ -n "${version}" ]]; then
744
+ local dir="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
745
+ if [[ -s "${dir}" ]]; then
746
+ rm -rf "${dir}"
747
+ else
748
+ echo "$1 (${version}) not in downloads cache"
749
+ fi
750
+ else
751
+ echo "No version found for '$1'"
752
+ fi
753
+ shift
754
+ done
755
+ }
756
+
757
+ #
758
+ # Synopsis: prune_cache
759
+ #
760
+
761
+ function prune_cache() {
762
+ set_active_beplus
763
+
764
+ for folder_and_version in $(display_versions_paths); do
765
+ if [[ "${folder_and_version}" != "${g_active_beplus}" ]]; then
766
+ echo "${folder_and_version}"
767
+ rm -rf "${CACHE_DIR:?}/${folder_and_version}"
768
+ fi
769
+ done
770
+ }
771
+
772
+ #
773
+ # Synopsis: find_cached_version version
774
+ # Finds cache directory for resolved version.
775
+ # Globals modified:
776
+ # - g_cached_version
777
+
778
+ function find_cached_version() {
779
+ [[ -z "$1" ]] && abort "version required"
780
+ local version
781
+ get_latest_resolved_version "$1" || exit 1
782
+ version="${g_target_beplus}"
783
+ [[ -n "${version}" ]] || abort "no version found for '$1'"
784
+
785
+ g_cached_version="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
786
+ if [[ ! -d "${g_cached_version}" && "${DOWNLOAD}" == "true" ]]; then
787
+ (install "${version}")
788
+ fi
789
+ [[ -d "${g_cached_version}" ]] || abort "'$1' (${version}) not in downloads cache"
790
+ }
791
+
792
+
793
+ #
794
+ # Synopsis: display_bin_path_for_version version
795
+ #
796
+
797
+ function display_bin_path_for_version() {
798
+ find_cached_version "$1"
799
+ echo "${g_cached_version}/beplus"
800
+ }
801
+
802
+ #
803
+ # Synopsis: run_with_version version [args...]
804
+ # Run the given <version> of node with [args ..]
805
+ #
806
+
807
+ function run_with_version() {
808
+ find_cached_version "$1"
809
+ shift # remove version from parameters
810
+ exec "${g_cached_version}/beplus" "$@"
811
+ }
812
+
813
+ #
814
+ # Synopsis: exec_with_version <version> command [args...]
815
+ # Modify the path to include <version> and execute command.
816
+ #
817
+
818
+ function exec_with_version() {
819
+ find_cached_version "$1"
820
+ shift # remove version from parameters
821
+ PATH="${g_cached_version}:$PATH" exec "$@"
822
+ }
823
+
824
+ #
825
+ # Synopsis: is_ok url
826
+ # Check the HEAD response of <url>.
827
+ #
828
+
829
+ function is_ok() {
830
+ # Note: both curl and wget can follow redirects, as present on some mirrors (e.g. https://npm.taobao.org/mirrors/node).
831
+ # The output is complicated with redirects, so keep it simple and use command status rather than parse output.
832
+ if command -v curl &> /dev/null; then
833
+ do_get --silent --head "$1" > /dev/null || return 1
834
+ else
835
+ do_get --spider "$1" > /dev/null || return 1
836
+ fi
837
+ }
838
+
839
+ #
840
+ # Synopsis: can_use_xz
841
+ # Test system to see if xz decompression is supported by tar.
842
+ #
843
+
844
+ function can_use_xz() {
845
+ # Be conservative and only enable if xz is likely to work. Unfortunately we can't directly query tar itself.
846
+ # For research, see https://github.com/shadowspawn/nvh/issues/8
847
+ local uname_s="$(uname -s)"
848
+ if [[ "${uname_s}" = "Linux" ]] && command -v xz &> /dev/null ; then
849
+ # tar on linux is likely to support xz if it is available as a command
850
+ return 0
851
+ elif [[ "${uname_s}" = "Darwin" ]]; then
852
+ local macos_version="$(sw_vers -productVersion)"
853
+ local macos_major_version="$(echo "${macos_version}" | cut -d '.' -f 1)"
854
+ local macos_minor_version="$(echo "${macos_version}" | cut -d '.' -f 2)"
855
+ if [[ "${macos_major_version}" -gt 10 || "${macos_minor_version}" -gt 8 ]]; then
856
+ # tar on recent Darwin has xz support built-in
857
+ return 0
858
+ fi
859
+ fi
860
+ return 2 # not supported
861
+ }
862
+
863
+ #
864
+ # Synopsis: display_tarball_platform
865
+ #
866
+
867
+ function display_tarball_platform() {
868
+ # https://en.wikipedia.org/wiki/Uname
869
+
870
+ local os="unexpected_os"
871
+ local uname_a="$(uname -a)"
872
+ case "${uname_a}" in
873
+ Linux*) os="linux" ;;
874
+ Darwin*) os="macos" ;;
875
+ SunOS*) >&2 echo_red "SunOS is not supported by be" ;;
876
+ AIX*) >&2 echo_red "Aix is not supported by be" ;;
877
+ CYGWIN*) >&2 echo_red "Cygwin is not supported by be" ;;
878
+ MINGW*) >&2 echo_red "Git BASH (MSYS) is not supported by be" ;;
879
+ esac
880
+
881
+ local arch="unexpected_arch"
882
+ local uname_m="$(uname -m)"
883
+ case "${uname_m}" in
884
+ x86_64) arch=x64 ;;
885
+ i386 | i686) arch="x86" ;; # @todo
886
+ aarch64) arch=arm64 ;;
887
+ armv8l) arch=arm64 ;; # armv8l probably supports arm64, and there is no specific armv8l build so give it a go
888
+ *)
889
+ # e.g. armv6l, armv7l, arm64
890
+ arch="${uname_m}"
891
+ ;;
892
+ esac
893
+ # Override from command line, or version specific adjustment.
894
+ [ -n "$ARCH" ] && arch="$ARCH"
895
+
896
+ echo "${os}-${arch}"
897
+ }
898
+
899
+ #
900
+ # Synopsis: display_compatible_file_field
901
+ # display <file> for current platform, as per <file> field in index.tab, which is different than actual download
902
+ #
903
+
904
+ function display_compatible_file_field {
905
+ local compatible_file_field="$(display_tarball_platform)"
906
+ if [[ -z "${ARCH}" && "${compatible_file_field}" = "darwin-arm64" ]]; then
907
+ # Look for arm64 for native but also x64 for older versions which can run in rosetta.
908
+ # (Downside is will get an install error if install version above 16 with x64 and not arm64.)
909
+ compatible_file_field="osx-arm64-tar|osx-x64-tar"
910
+ elif [[ "${compatible_file_field}" =~ darwin-(.*) ]]; then
911
+ compatible_file_field="osx-${BASH_REMATCH[1]}-tar"
912
+ fi
913
+ echo "${compatible_file_field}"
914
+ }
915
+
916
+ #
917
+ # Synopsis: tarball_url version
918
+ #
919
+
920
+ function tarball_url() {
921
+ local version="$1"
922
+ local ext=gz
923
+ [ "$BE_USE_XZ" = "true" ] && ext="xz"
924
+ echo "${g_mirror_url}/v${version}/beplus-cli-v${version}-$(display_tarball_platform).tar.${ext}"
925
+ }
926
+
927
+ #
928
+ # Synopsis: get_file_node_version filename
929
+ # Sets g_target_beplus
930
+ #
931
+
932
+ function get_file_node_version() {
933
+ g_target_beplus=
934
+ local filepath="$1"
935
+ verbose_log "found" "${filepath}"
936
+ # read returns a non-zero status but does still work if there is no line ending
937
+ local version
938
+ <"${filepath}" read -r version
939
+ # trim possible trailing \d from a Windows created file
940
+ version="${version%%[[:space:]]}"
941
+ verbose_log "read" "${version}"
942
+ g_target_beplus="${version}"
943
+ }
944
+
945
+ #
946
+ # Synopsis: get_package_engine_version\
947
+ # Sets g_target_beplus
948
+ #
949
+
950
+ function get_package_engine_version() {
951
+ g_target_beplus=
952
+ local filepath="$1"
953
+ verbose_log "found" "${filepath}"
954
+ command -v node &> /dev/null || abort "an active version of node is required to read 'engines' from package.json"
955
+ local range
956
+ range="$(node -e "package = require('${filepath}'); if (package && package.engines && package.engines.node) console.log(package.engines.node)")"
957
+ verbose_log "read" "${range}"
958
+ [[ -n "${range}" ]] || return 2
959
+ if [[ "*" == "${range}" ]]; then
960
+ verbose_log "target" "current"
961
+ g_target_beplus="current"
962
+ return
963
+ fi
964
+
965
+ local version
966
+ if [[ "${range}" =~ ^([>~^=]|\>\=)?v?([0-9]+(\.[0-9]+){0,2})(.[xX*])?$ ]]; then
967
+ local operator="${BASH_REMATCH[1]}"
968
+ version="${BASH_REMATCH[2]}"
969
+ case "${operator}" in
970
+ '' | =) ;;
971
+ \> | \>=) version="current" ;;
972
+ \~) [[ "${version}" =~ ^([0-9]+\.[0-9]+)\.[0-9]+$ ]] && version="${BASH_REMATCH[1]}" ;;
973
+ ^) [[ "${version}" =~ ^([0-9]+) ]] && version="${BASH_REMATCH[1]}" ;;
974
+ esac
975
+ verbose_log "target" "${version}"
976
+ else
977
+ command -v npx &> /dev/null || abort "an active version of npx is required to use complex 'engine' ranges from package.json"
978
+ verbose_log "resolving" "${range}"
979
+ local version_per_line="$(be lsr --all)"
980
+ local versions_one_line=$(echo "${version_per_line}" | tr '\n' ' ')
981
+ # Using semver@7 so works with older versions of node.
982
+ # shellcheck disable=SC2086
983
+ version=$(npm_config_yes=true npx --quiet semver@7 -r "${range}" ${versions_one_line} | tail -n 1)
984
+ fi
985
+ g_target_beplus="${version}"
986
+ }
987
+
988
+ #
989
+ # Synopsis: get_nvmrc_version
990
+ # Sets g_target_beplus
991
+ #
992
+
993
+ function get_nvmrc_version() {
994
+ g_target_beplus=
995
+ local filepath="$1"
996
+ verbose_log "found" "${filepath}"
997
+ local version
998
+ <"${filepath}" read -r version
999
+ verbose_log "read" "${version}"
1000
+ # Translate from nvm aliases
1001
+ case "${version}" in
1002
+ lts/\*) version="lts" ;;
1003
+ lts/*) version="${version:4}" ;;
1004
+ node) version="current" ;;
1005
+ *) ;;
1006
+ esac
1007
+ g_target_beplus="${version}"
1008
+ }
1009
+
1010
+ #
1011
+ # Synopsis: get_engine_version [error-message]
1012
+ # Sets g_target_beplus
1013
+ #
1014
+
1015
+ function get_engine_version() {
1016
+ g_target_beplus=
1017
+ local error_message="${1-package.json not found}"
1018
+ local parent
1019
+ parent="${PWD}"
1020
+ while [[ -n "${parent}" ]]; do
1021
+ if [[ -e "${parent}/package.json" ]]; then
1022
+ get_package_engine_version "${parent}/package.json"
1023
+ else
1024
+ parent=${parent%/*}
1025
+ continue
1026
+ fi
1027
+ break
1028
+ done
1029
+ [[ -n "${parent}" ]] || abort "${error_message}"
1030
+ [[ -n "${g_target_beplus}" ]] || abort "did not find supported version of node in 'engines' field of package.json"
1031
+ }
1032
+
1033
+ #
1034
+ # Synopsis: get_auto_version
1035
+ # Sets g_target_beplus
1036
+ #
1037
+
1038
+ # @todo
1039
+ function get_auto_version() {
1040
+ g_target_beplus=
1041
+ # Search for a version control file first
1042
+ local parent
1043
+ parent="${PWD}"
1044
+ while [[ -n "${parent}" ]]; do
1045
+ if [[ -e "${parent}/.n-node-version" ]]; then
1046
+ get_file_node_version "${parent}/.n-node-version"
1047
+ elif [[ -e "${parent}/.node-version" ]]; then
1048
+ get_file_node_version "${parent}/.node-version"
1049
+ elif [[ -e "${parent}/.nvmrc" ]]; then
1050
+ get_nvmrc_version "${parent}/.nvmrc"
1051
+ else
1052
+ parent=${parent%/*}
1053
+ continue
1054
+ fi
1055
+ break
1056
+ done
1057
+ # Fallback to package.json
1058
+ [[ -n "${parent}" ]] || get_engine_version "no file found for auto version (.n-node-version, .node-version, .nvmrc, or package.json)"
1059
+ [[ -n "${g_target_beplus}" ]] || abort "file found for auto did not contain target version of node"
1060
+ }
1061
+
1062
+ #
1063
+ # Synopsis: get_latest_resolved_version version
1064
+ # Sets g_target_beplus
1065
+ #
1066
+
1067
+ function get_latest_resolved_version() {
1068
+ g_target_beplus=
1069
+ local version=${1}
1070
+ simple_version=${version}
1071
+ if is_exact_numeric_version "${simple_version}"; then
1072
+ # Just numbers, already resolved, no need to lookup first.
1073
+ simple_version="${simple_version#v}"
1074
+ g_target_beplus="${simple_version}"
1075
+ else
1076
+ # Complicated recognising exact version, KISS and lookup.
1077
+ g_target_beplus=$(display_remote_versions "$version")
1078
+ fi
1079
+ }
1080
+
1081
+ #
1082
+ # Synopsis: display_remote_index
1083
+ # index.tab reference: https://github.com/nodejs/nodejs-dist-indexer
1084
+ # Index fields are: version date files npm v8 uv zlib openssl modules lts security
1085
+ # KISS and just return fields we currently care about: version files lts
1086
+ #
1087
+
1088
+ display_remote_index() {
1089
+ local index_url="${g_mirror_url}/index.tab"
1090
+ # tail to remove header line
1091
+ do_get_index "${index_url}" | tail -n +2 | cut -f 1,3,10
1092
+ if [[ "${PIPESTATUS[0]}" -ne 0 ]]; then
1093
+ # Reminder: abort will only exit subshell, but consistent error display
1094
+ abort "failed to download version index (${index_url})"
1095
+ fi
1096
+ }
1097
+
1098
+ #
1099
+ # Synopsis: display_remote_versions version
1100
+ #
1101
+
1102
+ function display_remote_versions() {
1103
+ local version="$1"
1104
+ local match='.'
1105
+ local match_count="${BE_MAX_REMOTE_MATCHES}"
1106
+
1107
+ # Transform some labels before processing further.
1108
+ if is_beplus_support_version "${version}"; then
1109
+ version="$(display_latest_beplus_support_alias "${version}")"
1110
+ elif [[ "${version}" = "auto" ]]; then
1111
+ # suppress stdout logging so lsr layout same as usual for scripting
1112
+ get_auto_version || return 2
1113
+ version="${g_target_beplus}"
1114
+ fi
1115
+
1116
+ if [[ -z "${version}" ]]; then
1117
+ match='.'
1118
+ elif [[ "${version}" = "stable" ]]; then
1119
+ match_count=1
1120
+ match='.'
1121
+ elif [[ "${version}" = "latest" || "${version}" = "current" ]]; then
1122
+ match_count=1
1123
+ match='.'
1124
+ elif is_numeric_version "${version}"; then
1125
+ version="v${version#v}"
1126
+ # Avoid restriction message if exact version
1127
+ is_exact_numeric_version "${version}" && match_count=1
1128
+ # Quote any dots in version so they are literal for expression
1129
+ match="${version//\./\.}"
1130
+ # Avoid 1.2 matching 1.23
1131
+ match="^${match}[^0-9]"
1132
+ else
1133
+ abort "invalid version '$1'"
1134
+ fi
1135
+
1136
+ local index_url="https://api.github.com/repos/beplus/cli/releases"
1137
+
1138
+ for row in $(do_get_index "${index_url}" | jq -r '.[] | @base64'); do
1139
+ _jq() {
1140
+ echo ${row} | base64 --decode | jq -r ${1}
1141
+ }
1142
+ echo $(_jq '.name')
1143
+ done | awk "NR<=${match_count}" \
1144
+ | cut -f 1 \
1145
+ | n_grep -E -o '[^v].*'
1146
+
1147
+ return "${PIPESTATUS[0]}"
1148
+ }
1149
+
1150
+ #
1151
+ # Synopsis: delete_with_echo target
1152
+ #
1153
+
1154
+ function delete_with_echo() {
1155
+ if [[ -e "$1" ]]; then
1156
+ echo "$1"
1157
+ rm -rf "$1"
1158
+ fi
1159
+ }
1160
+
1161
+ #
1162
+ # Synopsis: uninstall_installed
1163
+ # Uninstall the installed beplus CLI (leaving alone the cache)
1164
+ #
1165
+
1166
+ uninstall_installed() {
1167
+ while true; do
1168
+ read -r -p "Do you wish to delete beplus CLI from ${BE_PREFIX}? " yn
1169
+ case $yn in
1170
+ [Yy]* ) break ;;
1171
+ [Nn]* ) exit ;;
1172
+ * ) echo "Please answer yes or no.";;
1173
+ esac
1174
+ done
1175
+
1176
+ echo ""
1177
+ echo "Uninstalling beplus CLI"
1178
+ delete_with_echo "${BE_PREFIX}/bin/beplus"
1179
+ }
1180
+
1181
+ #
1182
+ # Synopsis: show_permission_suggestions
1183
+ #
1184
+
1185
+ function show_permission_suggestions() {
1186
+ echo "Suggestions:"
1187
+ echo "- run be with sudo, or"
1188
+ echo "- define BE_PREFIX to a writeable location, or"
1189
+ }
1190
+
1191
+ #
1192
+ # Synopsis: show_diagnostics
1193
+ # Show environment and check for common problems.
1194
+ #
1195
+
1196
+ function show_diagnostics() {
1197
+ echo "This information is to help you diagnose issues, and useful when reporting an issue."
1198
+ echo "Note: some output may contain passwords. Redact before sharing."
1199
+
1200
+ printf "\n\nCOMMAND LOCATIONS AND VERSIONS\n"
1201
+
1202
+ printf "\nbash\n"
1203
+ command -v bash && bash --version
1204
+
1205
+ printf "\nn\n"
1206
+ command -v be && be --version
1207
+
1208
+ printf "\nnode\n"
1209
+ if command -v node &> /dev/null; then
1210
+ command -v node && node --version
1211
+ node -e 'if (process.versions.v8) console.log("JavaScript engine: v8");'
1212
+
1213
+ printf "\nnpm\n"
1214
+ command -v npm && npm --version
1215
+ fi
1216
+
1217
+ printf "\ntar\n"
1218
+ if command -v tar &> /dev/null; then
1219
+ command -v tar && tar --version
1220
+ else
1221
+ echo_red "tar not found. Needed for extracting downloads."
1222
+ fi
1223
+
1224
+ printf "\ncurl or wget\n"
1225
+ if command -v curl &> /dev/null; then
1226
+ command -v curl && curl --version
1227
+ elif command -v wget &> /dev/null; then
1228
+ command -v wget && wget --version
1229
+ else
1230
+ echo_red "Neither curl nor wget found. Need one of them for downloads."
1231
+ fi
1232
+
1233
+ printf "\nuname\n"
1234
+ uname -a
1235
+
1236
+ printf "\n\nSETTINGS\n"
1237
+
1238
+ printf "\nn\n"
1239
+ echo "mirror: ${BE_MIRROR}"
1240
+ echo "downloads mirror: ${BE_DOWNLOAD_MIRROR}"
1241
+ echo "install destination: ${BE_PREFIX}"
1242
+ [[ -n "${BE_PREFIX}" ]] && echo "PATH: ${PATH}"
1243
+ echo "ls-remote max matches: ${BE_MAX_REMOTE_MATCHES}"
1244
+
1245
+ printf "\nProxy\n"
1246
+ # disable "var is referenced but not assigned": https://github.com/koalaman/shellcheck/wiki/SC2154
1247
+ # shellcheck disable=SC2154
1248
+ [[ -n "${http_proxy}" ]] && echo "http_proxy: ${http_proxy}"
1249
+ # shellcheck disable=SC2154
1250
+ [[ -n "${https_proxy}" ]] && echo "https_proxy: ${https_proxy}"
1251
+ if command -v curl &> /dev/null; then
1252
+ # curl supports lower case and upper case!
1253
+ # shellcheck disable=SC2154
1254
+ [[ -n "${all_proxy}" ]] && echo "all_proxy: ${all_proxy}"
1255
+ [[ -n "${ALL_PROXY}" ]] && echo "ALL_PROXY: ${ALL_PROXY}"
1256
+ [[ -n "${HTTP_PROXY}" ]] && echo "HTTP_PROXY: ${HTTP_PROXY}"
1257
+ [[ -n "${HTTPS_PROXY}" ]] && echo "HTTPS_PROXY: ${HTTPS_PROXY}"
1258
+ if [[ -e "${CURL_HOME}/.curlrc" ]]; then
1259
+ echo "have \$CURL_HOME/.curlrc"
1260
+ elif [[ -e "${HOME}/.curlrc" ]]; then
1261
+ echo "have \$HOME/.curlrc"
1262
+ fi
1263
+ elif command -v wget &> /dev/null; then
1264
+ if [[ -e "${WGETRC}" ]]; then
1265
+ echo "have \$WGETRC"
1266
+ elif [[ -e "${HOME}/.wgetrc" ]]; then
1267
+ echo "have \$HOME/.wgetrc"
1268
+ fi
1269
+ fi
1270
+
1271
+ printf "\n\nCHECKS\n"
1272
+
1273
+ printf "\nChecking be install destination is in PATH...\n"
1274
+ local install_bin="${BE_PREFIX}/bin"
1275
+ local path_wth_guards=":${PATH}:"
1276
+ if [[ "${path_wth_guards}" =~ :${install_bin}/?: ]]; then
1277
+ printf "good\n"
1278
+ else
1279
+ echo_red "'${install_bin}' is not in PATH"
1280
+ fi
1281
+ if command -v beplus &> /dev/null; then
1282
+ printf "\nChecking be install destination priority in PATH...\n"
1283
+ local node_dir="$(dirname "$(command -v beplus)")"
1284
+
1285
+ local index=0
1286
+ local path_entry
1287
+ local path_entries
1288
+ local install_bin_index=0
1289
+ local beplus_index=999
1290
+ IFS=':' read -ra path_entries <<< "${PATH}"
1291
+ for path_entry in "${path_entries[@]}"; do
1292
+ (( index++ ))
1293
+ [[ "${path_entry}" =~ ^${node_dir}/?$ ]] && beplus_index="${index}"
1294
+ [[ "${path_entry}" =~ ^${install_bin}/?$ ]] && install_bin_index="${index}"
1295
+ done
1296
+ if [[ "${beplus_index}" -lt "${install_bin_index}" ]]; then
1297
+ echo_red "There is a version of node installed which will be found in PATH before the be installed version."
1298
+ else
1299
+ printf "good\n"
1300
+ fi
1301
+ fi
1302
+
1303
+ printf "\nChecking permissions for cache folder...\n"
1304
+ # Most likely problem is ownership rather than than permissions as such.
1305
+ local cache_root="${BE_PREFIX}/n"
1306
+ if [[ -e "${BE_PREFIX}" && ! -w "${BE_PREFIX}" && ! -e "${cache_root}" ]]; then
1307
+ echo_red "You do not have write permission to create: ${cache_root}"
1308
+ show_permission_suggestions
1309
+ echo "- make a folder you own:"
1310
+ echo " sudo mkdir -p \"${cache_root}\""
1311
+ echo " sudo chown $(whoami) \"${cache_root}\""
1312
+ elif [[ -e "${cache_root}" && ! -w "${cache_root}" ]]; then
1313
+ echo_red "You do not have write permission to: ${cache_root}"
1314
+ show_permission_suggestions
1315
+ echo "- change folder ownership to yourself:"
1316
+ echo " sudo chown -R $(whoami) \"${cache_root}\""
1317
+ elif [[ ! -e "${cache_root}" ]]; then
1318
+ echo "Cache folder does not exist: ${cache_root}"
1319
+ echo "This is normal if you have not done an install yet, as cache is only created when needed."
1320
+ elif [[ -e "${CACHE_DIR}" && ! -w "${CACHE_DIR}" ]]; then
1321
+ echo_red "You do not have write permission to: ${CACHE_DIR}"
1322
+ show_permission_suggestions
1323
+ echo "- change folder ownership to yourself:"
1324
+ echo " sudo chown -R $(whoami) \"${CACHE_DIR}\""
1325
+ else
1326
+ echo "good"
1327
+ fi
1328
+
1329
+ if [[ -e "${BE_PREFIX}" ]]; then
1330
+ # Most likely problem is ownership rather than than permissions as such.
1331
+ printf "\nChecking permissions for install folders...\n"
1332
+ local install_writeable="true"
1333
+ for subdir in bin lib include share; do
1334
+ if [[ -e "${BE_PREFIX}/${subdir}" && ! -w "${BE_PREFIX}/${subdir}" ]]; then
1335
+ install_writeable="false"
1336
+ echo_red "You do not have write permission to: ${BE_PREFIX}/${subdir}"
1337
+ break
1338
+ fi
1339
+ done
1340
+ if [[ "${install_writeable}" = "true" ]]; then
1341
+ echo "good"
1342
+ else
1343
+ show_permission_suggestions
1344
+ echo "- change folder ownerships to yourself:"
1345
+ echo " (cd \"${BE_PREFIX}\" && sudo chown -R $(whoami) bin lib include share)"
1346
+ fi
1347
+ fi
1348
+
1349
+ printf "\nChecking mirror is reachable...\n"
1350
+ if is_ok "${BE_MIRROR}/"; then
1351
+ printf "good\n"
1352
+ else
1353
+ echo_red "mirror not reachable"
1354
+ printf "Showing failing command and output\n"
1355
+ if command -v curl &> /dev/null; then
1356
+ ( set -x; do_get --head "${BE_MIRROR}/" )
1357
+ else
1358
+ ( set -x; do_get --spider "${BE_MIRROR}/" )
1359
+ printf "\n"
1360
+ fi
1361
+ fi
1362
+ }
1363
+
1364
+ #
1365
+ # Handle arguments.
1366
+ #
1367
+
1368
+ # First pass. Process the options so they can come before or after commands,
1369
+ # particularly for `be lsr --all` and `be install --arch x686`
1370
+ # which feel pretty natural.
1371
+
1372
+ unprocessed_args=()
1373
+ positional_arg="false"
1374
+
1375
+ while [[ $# -ne 0 ]]; do
1376
+ case "$1" in
1377
+ --all) BE_MAX_REMOTE_MATCHES=32000 ;;
1378
+ -V|--version) display_be_version ;;
1379
+ -h|--help|help) display_help; exit ;;
1380
+ -q|--quiet) set_quiet ;;
1381
+ -d|--download) DOWNLOAD="true" ;;
1382
+ --insecure) set_insecure ;;
1383
+ --use-xz) BE_USE_XZ="true" ;;
1384
+ --no-use-xz) BE_USE_XZ="false" ;;
1385
+ --latest) display_remote_versions latest; exit ;;
1386
+ --stable) display_remote_versions latest; exit ;;
1387
+ -a|--arch) shift; set_arch "$1";; # set arch and continue
1388
+ exec|run|as|use)
1389
+ unprocessed_args+=( "$1" )
1390
+ positional_arg="true"
1391
+ ;;
1392
+ *)
1393
+ if [[ "${positional_arg}" == "true" ]]; then
1394
+ unprocessed_args+=( "$@" )
1395
+ break
1396
+ fi
1397
+ unprocessed_args+=( "$1" )
1398
+ ;;
1399
+ esac
1400
+ shift
1401
+ done
1402
+
1403
+ if [[ -z "${BE_USE_XZ+defined}" ]]; then
1404
+ BE_USE_XZ="true" # Default to using xz
1405
+ can_use_xz || BE_USE_XZ="false"
1406
+ fi
1407
+
1408
+ set -- "${unprocessed_args[@]}"
1409
+
1410
+ if test $# -eq 0; then
1411
+ test -z "$(display_versions_paths)" && err_no_installed_print_help
1412
+ menu_select_cache_versions
1413
+ else
1414
+ while test $# -ne 0; do
1415
+ case "$1" in
1416
+ bin|which) display_bin_path_for_version "$2"; exit ;;
1417
+ run|as|use) shift; run_with_version "$@"; exit ;;
1418
+ exec) shift; exec_with_version "$@"; exit ;;
1419
+ doctor) show_diagnostics; exit ;;
1420
+ rm|-) shift; remove_versions "$@"; exit ;;
1421
+ prune) prune_cache; exit ;;
1422
+ latest) install latest; exit ;;
1423
+ stable) install stable; exit ;;
1424
+ ls|list) display_versions_paths; exit ;;
1425
+ lsr|ls-remote|list-remote) shift; display_remote_versions "$1"; exit ;;
1426
+ uninstall) uninstall_installed; exit ;;
1427
+ i|install) shift; install "$1"; exit ;;
1428
+ N_TEST_DISPLAY_LATEST_RESOLVED_VERSION) shift; get_latest_resolved_version "$1" > /dev/null || exit 2; echo "${g_target_beplus}"; exit ;;
1429
+ *) install "$1"; exit ;;
1430
+ esac
1431
+ shift
1432
+ done
1433
+ fi