@beplus/be 0.8.0 → 0.10.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 (2) hide show
  1. package/bin/be +148 -211
  2. package/package.json +1 -1
package/bin/be CHANGED
@@ -64,7 +64,7 @@ function n_grep() {
64
64
  # Setup and state
65
65
  #
66
66
 
67
- VERSION="v0.8.0"
67
+ VERSION="v0.10.0"
68
68
 
69
69
  BE_PREFIX="${BE_PREFIX-/usr/local}"
70
70
  BE_PREFIX=${BE_PREFIX%/}
@@ -83,18 +83,6 @@ BE_DOWNLOAD_MIRROR=${BE_DOWNLOAD_MIRROR:-https://github.com/beplus/cli/releases/
83
83
  BE_DOWNLOAD_MIRROR=${BE_DOWNLOAD_MIRROR%/}
84
84
  readonly BE_DOWNLOAD_MIRROR
85
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
86
  BE_MAX_REMOTE_MATCHES=${BE_MAX_REMOTE_MATCHES:-20}
99
87
 
100
88
  g_mirror_url=${BE_MIRROR}
@@ -188,22 +176,6 @@ display_major_version() {
188
176
  echo "${version}"
189
177
  }
190
178
 
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
179
  #
208
180
  # Synopsis: update_arch_settings_for_version numeric-version
209
181
  # Globals modified:
@@ -306,6 +278,7 @@ Commands:
306
278
 
307
279
  be Display downloaded beplus CLI versions and install selection
308
280
  be latest Install the latest beplus CLI release (downloading if necessary)
281
+ be auto Install the beplus CLI version the nearest beplus.estate.json pins
309
282
  be <version> Install beplus CLI <version> (downloading if necessary)
310
283
  be install <version> Install beplus CLI <version> (downloading if necessary)
311
284
  be run <version> [args ...] Execute downloaded beplus CLI <version> with [args ...]
@@ -327,7 +300,6 @@ Options:
327
300
  -a, --arch Override system architecture
328
301
  --all ls-remote displays all matches instead of last 20
329
302
  --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
303
 
332
304
  Aliases:
333
305
 
@@ -345,7 +317,8 @@ Versions:
345
317
 
346
318
  0.17.0, 1, v1.2 Numeric versions
347
319
  latest, current Newest official release
348
- auto Read version from file: .bepluscloud, .beplus-version, or package.json
320
+ auto The exact version pinned as "cli": { "version": "x.y.z" } in the nearest
321
+ beplus.estate.json (in the current directory or above it); fails without one
349
322
 
350
323
  EOF
351
324
  }
@@ -383,28 +356,23 @@ display_be_version() {
383
356
 
384
357
  #
385
358
  # Synopsis: set_active_beplus
386
- # Checks cached downloads for a binary matching the active beplus CLI.
359
+ # Checks cached downloads for the one the installed beplus CLI was copied from: activate copies
360
+ # <cache>/beplus/<version>/beplus to $BE_PREFIX/bin/beplus, so the two files are identical.
387
361
  # Globals modified:
388
362
  # - g_active_beplus
389
363
  #
390
364
 
391
365
  function set_active_beplus() {
392
366
  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
367
+ local cached
368
+ for cached in "${CACHE_DIR}"/*/*/beplus; do
369
+ # cmp stops at the first difference; diff may read both binaries (~110 MB each) whole.
370
+ if cmp -s "${cached}" "${BE_PREFIX}/bin/beplus"; then
371
+ cached="${cached#"${CACHE_DIR}/"}"
372
+ g_active_beplus="${cached%/beplus}"
373
+ break
374
+ fi
375
+ done
408
376
  }
409
377
 
410
378
  #
@@ -638,18 +606,10 @@ install() {
638
606
  get_latest_resolved_version "$1" || return 2
639
607
  version="${g_target_beplus}"
640
608
  [[ -n "${version}" ]] || abort "no version found for '$1'"
641
- update_xz_settings_for_version "${version}"
642
609
  update_arch_settings_for_version "${version}"
643
610
 
644
611
  local dir="${CACHE_DIR}/${g_mirror_folder_name}/${version}"
645
612
 
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
613
  if test -d "$dir"; then
654
614
  if [[ ! -e "$dir/be.lock" ]] ; then
655
615
  if [[ "$DOWNLOAD" == "false" ]] ; then
@@ -664,6 +624,15 @@ install() {
664
624
  local url="$(tarball_url "$version")"
665
625
  is_ok "${url}" || abort "download preflight failed for '$version' (${url})"
666
626
 
627
+ # The release publishes SHA256SUMS beside its tarballs. Read this tarball's line before
628
+ # downloading it, and extract only a download that matches it. 2.x lists `<hash> <name>`, as
629
+ # sha256sum writes it; v1.0.5 lists `<name>: <hash>`.
630
+ local sums_url="${url%/*}/SHA256SUMS"
631
+ local sums
632
+ sums="$(do_get_index "${sums_url}")" || abort "failed to download SHA256SUMS for $version (${sums_url})"
633
+ local checksum="$(echo "${sums}" | awk -v name="${url##*/}" '$2 == name { print $1; exit } $1 == name ":" { print $2; exit }')"
634
+ [[ -n "${checksum}" ]] || abort "no checksum for ${url##*/} in ${sums_url}"
635
+
667
636
  log mkdir "$dir"
668
637
  mkdir -p "$dir" || abort "sudo required (or change ownership, or define BE_PREFIX)"
669
638
  touch "$dir/be.lock"
@@ -671,15 +640,21 @@ install() {
671
640
  cd "${dir}" || abort "Failed to cd to ${dir}"
672
641
 
673
642
  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
643
+ local archive="${dir}/${url##*/}"
644
+ if ! do_get "${url}" > "${archive}"; then
645
+ rm -f "${archive}"
677
646
  abort "failed to download archive for $version"
678
647
  fi
679
- if [[ "${pipe_results[1]}" -ne 0 ]]; then
680
- abort "failed to extract archive for $version"
681
- fi
682
648
  [ "$GET_SHOWS_PROGRESS" = "true" ] && erase_line
649
+ local download_checksum="$(display_sha256 "${archive}")"
650
+ if [[ "${download_checksum}" != "${checksum}" ]]; then
651
+ rm -f "${archive}"
652
+ abort "checksum mismatch for ${url}: ${sums_url} lists ${checksum}, the download is ${download_checksum}"
653
+ fi
654
+ tar -zx --no-same-owner -f "${archive}" # --strip-components=1
655
+ local tar_status=$?
656
+ rm -f "${archive}"
657
+ [[ "${tar_status}" -eq 0 ]] || abort "failed to extract archive for $version"
683
658
  rm -f "$dir/be.lock"
684
659
 
685
660
  disable_pax_mprotect bin/beplus
@@ -713,6 +688,23 @@ function do_get() {
713
688
  fi
714
689
  }
715
690
 
691
+ #
692
+ # Synopsis: display_sha256 file
693
+ # sha256sum where it exists, otherwise shasum, which macOS has. Both hash stdin rather than a
694
+ # named file: given a name with a backslash in it, they escape it and put a backslash before the
695
+ # hash.
696
+ #
697
+
698
+ function display_sha256() {
699
+ local line
700
+ if command -v sha256sum &> /dev/null; then
701
+ line="$(sha256sum < "$1")"
702
+ else
703
+ line="$(shasum -a 256 < "$1")"
704
+ fi
705
+ echo "${line%% *}"
706
+ }
707
+
716
708
  #
717
709
  # Synopsis: do_get_index [option...] url
718
710
  # Call curl or wget with combination of global and passed options,
@@ -836,30 +828,6 @@ function is_ok() {
836
828
  fi
837
829
  }
838
830
 
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
831
  #
864
832
  # Synopsis: display_tarball_platform
865
833
  #
@@ -926,145 +894,114 @@ function tarball_url() {
926
894
  # Artifact version (filename MUST NOT contain +suffix)
927
895
  local artifact_version="${version%%+*}"
928
896
 
929
- local ext="gz"
930
- [ "$BE_USE_XZ" = "true" ] && ext="xz"
931
-
932
- echo "${g_mirror_url}/v${url_version}/beplus-cli-v${artifact_version}-$(display_tarball_platform).tar.${ext}"
897
+ # The beplus CLI release publishes .tar.gz only.
898
+ echo "${g_mirror_url}/v${url_version}/beplus-cli-v${artifact_version}-$(display_tarball_platform).tar.gz"
933
899
  }
934
900
 
935
901
  #
936
- # Synopsis: get_file_node_version filename
937
- # Sets g_target_beplus
902
+ # Synopsis: display_estate_manifest_path
903
+ # Output the path of the nearest beplus.estate.json: in PWD, or else in the closest directory above
904
+ # it. Fails when there is none.
938
905
  #
939
906
 
940
- function get_file_node_version() {
941
- g_target_beplus=
942
- local filepath="$1"
943
- verbose_log "found" "${filepath}"
944
- # read returns a non-zero status but does still work if there is no line ending
945
- local version
946
- <"${filepath}" read -r version
947
- # trim possible trailing \d from a Windows created file
948
- version="${version%%[[:space:]]}"
949
- verbose_log "read" "${version}"
950
- g_target_beplus="${version}"
907
+ function display_estate_manifest_path() {
908
+ local dir="${PWD}"
909
+ while true; do
910
+ if [[ -f "${dir%/}/beplus.estate.json" ]]; then
911
+ echo "${dir%/}/beplus.estate.json"
912
+ return 0
913
+ fi
914
+ [[ -z "${dir}" || "${dir}" == "/" ]] && return 1
915
+ dir="${dir%/*}"
916
+ done
951
917
  }
952
918
 
953
919
  #
954
- # Synopsis: get_package_engine_version\
955
- # Sets g_target_beplus
956
- #
957
-
958
- function get_package_engine_version() {
959
- g_target_beplus=
960
- local filepath="$1"
961
- verbose_log "found" "${filepath}"
962
- command -v node &> /dev/null || abort "an active version of node is required to read 'engines' from package.json"
963
- local range
964
- range="$(node -e "package = require('${filepath}'); if (package && package.engines && package.engines.node) console.log(package.engines.node)")"
965
- verbose_log "read" "${range}"
966
- [[ -n "${range}" ]] || return 2
967
- if [[ "*" == "${range}" ]]; then
968
- verbose_log "target" "current"
969
- g_target_beplus="current"
970
- return
971
- fi
972
-
973
- local version
974
- if [[ "${range}" =~ ^([>~^=]|\>\=)?v?([0-9]+(\.[0-9]+){0,2})(.[xX*])?$ ]]; then
975
- local operator="${BASH_REMATCH[1]}"
976
- version="${BASH_REMATCH[2]}"
977
- case "${operator}" in
978
- '' | =) ;;
979
- \> | \>=) version="current" ;;
980
- \~) [[ "${version}" =~ ^([0-9]+\.[0-9]+)\.[0-9]+$ ]] && version="${BASH_REMATCH[1]}" ;;
981
- ^) [[ "${version}" =~ ^([0-9]+) ]] && version="${BASH_REMATCH[1]}" ;;
982
- esac
983
- verbose_log "target" "${version}"
920
+ # Synopsis: display_estate_cli_version node|jq manifest
921
+ # Output the manifest's "cli": { "version" } as JSON, on one line: a string when the pin is one,
922
+ # null when the manifest has none. Fails, with the reason on stderr, when the file is not exactly
923
+ # one JSON value.
924
+ #
925
+
926
+ function display_estate_cli_version() {
927
+ if [[ "$1" == "node" ]]; then
928
+ node -e '
929
+ const has = (value, key) =>
930
+ value !== null && typeof value === "object" && !Array.isArray(value)
931
+ && Object.prototype.hasOwnProperty.call(value, key);
932
+ let manifest;
933
+ try {
934
+ manifest = JSON.parse(require("fs").readFileSync(process.argv[1], "utf8"));
935
+ } catch (error) {
936
+ console.error(error.message);
937
+ process.exit(1);
938
+ }
939
+ const version = has(manifest, "cli") && has(manifest.cli, "version") ? manifest.cli.version : null;
940
+ console.log(JSON.stringify(version));
941
+ ' "$2"
984
942
  else
985
- command -v npx &> /dev/null || abort "an active version of npx is required to use complex 'engine' ranges from package.json"
986
- verbose_log "resolving" "${range}"
987
- local version_per_line="$(be lsr --all)"
988
- local versions_one_line=$(echo "${version_per_line}" | tr '\n' ' ')
989
- # Using semver@7 so works with older versions of node.
990
- # shellcheck disable=SC2086
991
- version=$(npm_config_yes=true npx --quiet semver@7 -r "${range}" ${versions_one_line} | tail -n 1)
943
+ # --slurp so that an empty file, or one holding several values, fails as JSON.parse does.
944
+ jq -r --slurp '
945
+ if length != 1 then error("expected one JSON value, found \(length)")
946
+ else .[0]
947
+ | if type == "object" and (.cli | type) == "object" and (.cli | has("version"))
948
+ then .cli.version
949
+ else null
950
+ end
951
+ | tojson
952
+ end
953
+ ' "$2"
992
954
  fi
993
- g_target_beplus="${version}"
994
955
  }
995
956
 
996
957
  #
997
- # Synopsis: get_nvmrc_version
958
+ # Synopsis: get_auto_version
959
+ # Resolve auto to the beplus CLI version the nearest beplus.estate.json pins as
960
+ # "cli": { "version": "x.y.z" }. The pin must be one exact version (a leading v and a pre-release
961
+ # are allowed, as everywhere in be); a missing, floating or unreadable pin aborts, naming the file,
962
+ # rather than falling back to another version. The nearest manifest decides, even when it pins
963
+ # nothing: one further up is never consulted.
998
964
  # Sets g_target_beplus
999
965
  #
1000
966
 
1001
- function get_nvmrc_version() {
967
+ function get_auto_version() {
1002
968
  g_target_beplus=
1003
- local filepath="$1"
1004
- verbose_log "found" "${filepath}"
1005
- local version
1006
- <"${filepath}" read -r version
1007
- verbose_log "read" "${version}"
1008
- # Translate from nvm aliases
1009
- case "${version}" in
1010
- lts/\*) version="lts" ;;
1011
- lts/*) version="${version:4}" ;;
1012
- node) version="current" ;;
1013
- *) ;;
1014
- esac
1015
- g_target_beplus="${version}"
1016
- }
969
+ local manifest
970
+ manifest="$(display_estate_manifest_path)" \
971
+ || abort "auto found no beplus.estate.json in ${PWD} or above it, so no beplus CLI version is pinned"
972
+ verbose_log "found" "${manifest}"
1017
973
 
1018
- #
1019
- # Synopsis: get_engine_version [error-message]
1020
- # Sets g_target_beplus
1021
- #
974
+ # node is on every deploy path; jq is what be's remote lookups use.
975
+ local reader
976
+ if command -v node &> /dev/null; then
977
+ reader="node"
978
+ elif command -v jq &> /dev/null; then
979
+ reader="jq"
980
+ else
981
+ abort "reading the beplus CLI pin from ${manifest} needs node or jq, and neither is on PATH"
982
+ fi
1022
983
 
1023
- function get_engine_version() {
1024
- g_target_beplus=
1025
- local error_message="${1-package.json not found}"
1026
- local parent
1027
- parent="${PWD}"
1028
- while [[ -n "${parent}" ]]; do
1029
- if [[ -e "${parent}/package.json" ]]; then
1030
- get_package_engine_version "${parent}/package.json"
1031
- else
1032
- parent=${parent%/*}
1033
- continue
1034
- fi
1035
- break
1036
- done
1037
- [[ -n "${parent}" ]] || abort "${error_message}"
1038
- [[ -n "${g_target_beplus}" ]] || abort "did not find supported version of node in 'engines' field of package.json"
1039
- }
984
+ local value
985
+ if ! value="$(display_estate_cli_version "${reader}" "${manifest}" 2> /dev/null)"; then
986
+ abort "${manifest} could not be read as JSON by ${reader}: $(display_estate_cli_version "${reader}" "${manifest}" 2>&1 > /dev/null)"
987
+ fi
1040
988
 
1041
- #
1042
- # Synopsis: get_auto_version
1043
- # Sets g_target_beplus
1044
- #
989
+ local version
990
+ case "${value}" in
991
+ null)
992
+ abort "${manifest} pins no beplus CLI version; add \"cli\": { \"version\": \"x.y.z\" }" ;;
993
+ \"*\")
994
+ version="${value#\"}"
995
+ version="${version%\"}"
996
+ ;;
997
+ *)
998
+ abort "\"cli\": { \"version\" } in ${manifest} is ${value}, not a version string like \"2.11.0\"" ;;
999
+ esac
1000
+ verbose_log "read" "${version}"
1045
1001
 
1046
- # @todo
1047
- function get_auto_version() {
1048
- g_target_beplus=
1049
- # Search for a version control file first
1050
- local parent
1051
- parent="${PWD}"
1052
- while [[ -n "${parent}" ]]; do
1053
- if [[ -e "${parent}/.n-node-version" ]]; then
1054
- get_file_node_version "${parent}/.n-node-version"
1055
- elif [[ -e "${parent}/.node-version" ]]; then
1056
- get_file_node_version "${parent}/.node-version"
1057
- elif [[ -e "${parent}/.nvmrc" ]]; then
1058
- get_nvmrc_version "${parent}/.nvmrc"
1059
- else
1060
- parent=${parent%/*}
1061
- continue
1062
- fi
1063
- break
1064
- done
1065
- # Fallback to package.json
1066
- [[ -n "${parent}" ]] || get_engine_version "no file found for auto version (.n-node-version, .node-version, .nvmrc, or package.json)"
1067
- [[ -n "${g_target_beplus}" ]] || abort "file found for auto did not contain target version of node"
1002
+ is_exact_numeric_version "${version}" \
1003
+ || abort "\"cli\": { \"version\" } in ${manifest} is \"${version}\", not one exact version like \"2.11.0\""
1004
+ g_target_beplus="${version#v}"
1068
1005
  }
1069
1006
 
1070
1007
  #
@@ -1075,6 +1012,12 @@ function get_auto_version() {
1075
1012
  function get_latest_resolved_version() {
1076
1013
  g_target_beplus=
1077
1014
  local version=${1}
1015
+ if [[ "${version}" == "auto" ]]; then
1016
+ # The pin is one exact version, so there is nothing to look up: auto installs exactly that
1017
+ # version or fails, and never reads the release index (where "newest" lives).
1018
+ get_auto_version
1019
+ return
1020
+ fi
1078
1021
  simple_version=${version}
1079
1022
  if is_exact_numeric_version "${simple_version}"; then
1080
1023
  # Just numbers, already resolved, no need to lookup first.
@@ -1217,7 +1160,8 @@ function delete_with_echo() {
1217
1160
 
1218
1161
  uninstall_installed() {
1219
1162
  while true; do
1220
- read -r -p "Do you wish to delete beplus CLI from ${BE_PREFIX}? " yn
1163
+ # End of input answers no.
1164
+ read -r -p "Do you wish to delete beplus CLI from ${BE_PREFIX}? " yn || yn="n"
1221
1165
  case $yn in
1222
1166
  [Yy]* ) break ;;
1223
1167
  [Nn]* ) exit ;;
@@ -1432,8 +1376,6 @@ while [[ $# -ne 0 ]]; do
1432
1376
  -q|--quiet) set_quiet ;;
1433
1377
  -d|--download) DOWNLOAD="true" ;;
1434
1378
  --insecure) set_insecure ;;
1435
- --use-xz) BE_USE_XZ="true" ;;
1436
- --no-use-xz) BE_USE_XZ="false" ;;
1437
1379
  --latest) display_remote_versions latest; exit ;;
1438
1380
  --stable) display_remote_versions latest; exit ;;
1439
1381
  -a|--arch) shift; set_arch "$1";; # set arch and continue
@@ -1452,11 +1394,6 @@ while [[ $# -ne 0 ]]; do
1452
1394
  shift
1453
1395
  done
1454
1396
 
1455
- if [[ -z "${BE_USE_XZ+defined}" ]]; then
1456
- BE_USE_XZ="true" # Default to using xz
1457
- can_use_xz || BE_USE_XZ="false"
1458
- fi
1459
-
1460
1397
  set -- "${unprocessed_args[@]}"
1461
1398
 
1462
1399
  if test $# -eq 0; then
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beplus/be",
3
3
  "description": "Interactively Manage beplus CLI Versions",
4
- "version": "0.8.0",
4
+ "version": "0.10.0",
5
5
  "author": "Igor Lamos <igor@be.plus>",
6
6
  "homepage": "https://github.com/beplus/be",
7
7
  "bugs": "https://github.com/beplus/be",