github.com/noqcks/syft@v0.0.0-20230920222752-a9e2c4e288e5/install.sh (about) 1 #!/bin/sh 2 # note: we require errors to propagate (don't set -e) 3 set -u 4 5 PROJECT_NAME="syft" 6 OWNER=anchore 7 REPO="${PROJECT_NAME}" 8 GITHUB_DOWNLOAD_PREFIX=https://github.com/${OWNER}/${REPO}/releases/download 9 INSTALL_SH_BASE_URL=https://raw.githubusercontent.com/${OWNER}/${PROJECT_NAME} 10 PROGRAM_ARGS=$@ 11 12 # do not change the name of this parameter (this must always be backwards compatible) 13 DOWNLOAD_TAG_INSTALL_SCRIPT=${DOWNLOAD_TAG_INSTALL_SCRIPT:-true} 14 15 # 16 # usage [script-name] 17 # 18 usage() ( 19 this=$1 20 cat <<EOF 21 $this: download go binaries for anchore/syft 22 23 Usage: $this [-b] dir [-d] [tag] 24 -b the installation directory (dDefaults to ./bin) 25 -d turns on debug logging 26 -dd turns on trace logging 27 [tag] the specific release to use (if missing, then the latest will be used) 28 EOF 29 exit 2 30 ) 31 32 33 # ------------------------------------------------------------------------ 34 # https://github.com/client9/shlib - portable posix shell functions 35 # Public domain - http://unlicense.org 36 # https://github.com/client9/shlib/blob/master/LICENSE.md 37 # but credit (and pull requests) appreciated. 38 # ------------------------------------------------------------------------ 39 40 is_command() ( 41 command -v "$1" >/dev/null 42 ) 43 44 echo_stderr() ( 45 echo "$@" 1>&2 46 ) 47 48 _logp=2 49 log_set_priority() { 50 _logp="$1" 51 } 52 53 log_priority() ( 54 if test -z "$1"; then 55 echo "$_logp" 56 return 57 fi 58 [ "$1" -le "$_logp" ] 59 ) 60 61 init_colors() { 62 RED='' 63 BLUE='' 64 PURPLE='' 65 BOLD='' 66 RESET='' 67 # check if stdout is a terminal 68 if test -t 1 && is_command tput; then 69 # see if it supports colors 70 ncolors=$(tput colors) 71 if test -n "$ncolors" && test $ncolors -ge 8; then 72 RED='\033[0;31m' 73 BLUE='\033[0;34m' 74 PURPLE='\033[0;35m' 75 BOLD='\033[1m' 76 RESET='\033[0m' 77 fi 78 fi 79 } 80 81 init_colors 82 83 log_tag() ( 84 case $1 in 85 0) echo "${RED}${BOLD}[error]${RESET}" ;; 86 1) echo "${RED}[warn]${RESET}" ;; 87 2) echo "[info]${RESET}" ;; 88 3) echo "${BLUE}[debug]${RESET}" ;; 89 4) echo "${PURPLE}[trace]${RESET}" ;; 90 *) echo "[$1]" ;; 91 esac 92 ) 93 94 95 log_trace_priority=4 96 log_trace() ( 97 priority=$log_trace_priority 98 log_priority "$priority" || return 0 99 echo_stderr "$(log_tag $priority)" "${@}" "${RESET}" 100 ) 101 102 log_debug_priority=3 103 log_debug() ( 104 priority=$log_debug_priority 105 log_priority "$priority" || return 0 106 echo_stderr "$(log_tag $priority)" "${@}" "${RESET}" 107 ) 108 109 log_info_priority=2 110 log_info() ( 111 priority=$log_info_priority 112 log_priority "$priority" || return 0 113 echo_stderr "$(log_tag $priority)" "${@}" "${RESET}" 114 ) 115 116 log_warn_priority=1 117 log_warn() ( 118 priority=$log_warn_priority 119 log_priority "$priority" || return 0 120 echo_stderr "$(log_tag $priority)" "${@}" "${RESET}" 121 ) 122 123 log_err_priority=0 124 log_err() ( 125 priority=$log_err_priority 126 log_priority "$priority" || return 0 127 echo_stderr "$(log_tag $priority)" "${@}" "${RESET}" 128 ) 129 130 uname_os_check() ( 131 os=$1 132 case "$os" in 133 darwin) return 0 ;; 134 dragonfly) return 0 ;; 135 freebsd) return 0 ;; 136 linux) return 0 ;; 137 android) return 0 ;; 138 nacl) return 0 ;; 139 netbsd) return 0 ;; 140 openbsd) return 0 ;; 141 plan9) return 0 ;; 142 solaris) return 0 ;; 143 windows) return 0 ;; 144 esac 145 log_err "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" 146 return 1 147 ) 148 149 uname_arch_check() ( 150 arch=$1 151 case "$arch" in 152 386) return 0 ;; 153 amd64) return 0 ;; 154 arm64) return 0 ;; 155 armv5) return 0 ;; 156 armv6) return 0 ;; 157 armv7) return 0 ;; 158 ppc64) return 0 ;; 159 ppc64le) return 0 ;; 160 mips) return 0 ;; 161 mipsle) return 0 ;; 162 mips64) return 0 ;; 163 mips64le) return 0 ;; 164 s390x) return 0 ;; 165 amd64p32) return 0 ;; 166 esac 167 log_err "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" 168 return 1 169 ) 170 171 unpack() ( 172 archive=$1 173 174 log_trace "unpack(archive=${archive})" 175 176 case "${archive}" in 177 *.tar.gz | *.tgz) tar --no-same-owner -xzf "${archive}" ;; 178 *.tar) tar --no-same-owner -xf "${archive}" ;; 179 *.zip) unzip -q "${archive}" ;; 180 *.dmg) extract_from_dmg "${archive}" ;; 181 *) 182 log_err "unpack unknown archive format for ${archive}" 183 return 1 184 ;; 185 esac 186 ) 187 188 extract_from_dmg() ( 189 dmg_file=$1 190 191 mount_point="/Volumes/tmp-dmg" 192 hdiutil attach -quiet -nobrowse -mountpoint "${mount_point}" "${dmg_file}" 193 cp -fR "${mount_point}/." ./ 194 hdiutil detach -quiet -force "${mount_point}" 195 ) 196 197 http_download_curl() ( 198 local_file=$1 199 source_url=$2 200 header=$3 201 202 log_trace "http_download_curl(local_file=$local_file, source_url=$source_url, header=$header)" 203 204 if [ -z "$header" ]; then 205 code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") 206 else 207 code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") 208 fi 209 210 if [ "$code" != "200" ]; then 211 log_err "received HTTP status=$code for url='$source_url'" 212 return 1 213 fi 214 return 0 215 ) 216 217 http_download_wget() ( 218 local_file=$1 219 source_url=$2 220 header=$3 221 222 log_trace "http_download_wget(local_file=$local_file, source_url=$source_url, header=$header)" 223 224 if [ -z "$header" ]; then 225 wget -q -O "$local_file" "$source_url" 226 else 227 wget -q --header "$header" -O "$local_file" "$source_url" 228 fi 229 ) 230 231 http_download() ( 232 log_debug "http_download(url=$2)" 233 if is_command curl; then 234 http_download_curl "$@" 235 return 236 elif is_command wget; then 237 http_download_wget "$@" 238 return 239 fi 240 log_err "http_download unable to find wget or curl" 241 return 1 242 ) 243 244 http_copy() ( 245 tmp=$(mktemp) 246 http_download "${tmp}" "$1" "$2" || return 1 247 body=$(cat "$tmp") 248 rm -f "${tmp}" 249 echo "$body" 250 ) 251 252 hash_sha256() ( 253 TARGET=${1:-/dev/stdin} 254 if is_command gsha256sum; then 255 hash=$(gsha256sum "$TARGET") || return 1 256 echo "$hash" | cut -d ' ' -f 1 257 elif is_command sha256sum; then 258 hash=$(sha256sum "$TARGET") || return 1 259 echo "$hash" | cut -d ' ' -f 1 260 elif is_command shasum; then 261 hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 262 echo "$hash" | cut -d ' ' -f 1 263 elif is_command openssl; then 264 hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 265 echo "$hash" | cut -d ' ' -f a 266 else 267 log_err "hash_sha256 unable to find command to compute sha-256 hash" 268 return 1 269 fi 270 ) 271 272 hash_sha256_verify() ( 273 TARGET=$1 274 checksums=$2 275 if [ -z "$checksums" ]; then 276 log_err "hash_sha256_verify checksum file not specified in arg2" 277 return 1 278 fi 279 BASENAME=${TARGET##*/} 280 want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) 281 if [ -z "$want" ]; then 282 log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" 283 return 1 284 fi 285 got=$(hash_sha256 "$TARGET") 286 if [ "$want" != "$got" ]; then 287 log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" 288 return 1 289 fi 290 ) 291 292 # ------------------------------------------------------------------------ 293 # End of functions from https://github.com/client9/shlib 294 # ------------------------------------------------------------------------ 295 296 # asset_file_exists [path] 297 # 298 # returns 1 if the given file does not exist 299 # 300 asset_file_exists() ( 301 path="$1" 302 if [ ! -f "${path}" ]; then 303 return 1 304 fi 305 ) 306 307 308 # github_release_json [owner] [repo] [version] 309 # 310 # outputs release json string 311 # 312 github_release_json() ( 313 owner=$1 314 repo=$2 315 version=$3 316 test -z "$version" && version="latest" 317 giturl="https://github.com/${owner}/${repo}/releases/${version}" 318 json=$(http_copy "$giturl" "Accept:application/json") 319 320 log_trace "github_release_json(owner=${owner}, repo=${repo}, version=${version}) returned '${json}'" 321 322 test -z "$json" && return 1 323 echo "${json}" 324 ) 325 326 # extract_value [key-value-pair] 327 # 328 # outputs value from a colon delimited key-value pair 329 # 330 extract_value() ( 331 key_value="$1" 332 IFS=':' read -r _ value << EOF 333 ${key_value} 334 EOF 335 echo "$value" 336 ) 337 338 # extract_json_value [json] [key] 339 # 340 # outputs value of the key from the given json string 341 # 342 extract_json_value() ( 343 json="$1" 344 key="$2" 345 key_value=$(echo "${json}" | grep -o "\"$key\":[^,]*[,}]" | tr -d '",}') 346 347 extract_value "$key_value" 348 ) 349 350 # github_release_tag [release-json] 351 # 352 # outputs release tag string 353 # 354 github_release_tag() ( 355 json="$1" 356 tag=$(extract_json_value "${json}" "tag_name") 357 test -z "$tag" && return 1 358 echo "$tag" 359 ) 360 361 # download_github_release_checksums [release-url-prefix] [name] [version] [output-dir] 362 # 363 # outputs path to the downloaded checksums file 364 # 365 download_github_release_checksums() ( 366 download_url="$1" 367 name="$2" 368 version="$3" 369 output_dir="$4" 370 371 log_trace "download_github_release_checksums(url=${download_url}, name=${name}, version=${version}, output_dir=${output_dir})" 372 373 checksum_filename=${name}_${version}_checksums.txt 374 checksum_url=${download_url}/${checksum_filename} 375 output_path="${output_dir}/${checksum_filename}" 376 377 http_download "${output_path}" "${checksum_url}" "" 378 asset_file_exists "${output_path}" 379 380 log_trace "download_github_release_checksums() returned '${output_path}'" 381 382 echo "${output_path}" 383 ) 384 385 # search_for_asset [checksums-file-path] [name] [os] [arch] [format] 386 # 387 # outputs name of the asset to download 388 # 389 search_for_asset() ( 390 checksum_path="$1" 391 name="$2" 392 os="$3" 393 arch="$4" 394 format="$5" 395 396 log_trace "search_for_asset(checksum-path=${checksum_path}, name=${name}, os=${os}, arch=${arch}, format=${format})" 397 398 asset_glob="${name}_.*_${os}_${arch}.${format}" 399 output_path=$(grep -o "${asset_glob}" "${checksum_path}" || true) 400 401 log_trace "search_for_asset() returned '${output_path}'" 402 403 echo "${output_path}" 404 ) 405 406 # uname_os 407 # 408 # outputs an adjusted os value 409 # 410 uname_os() ( 411 os=$(uname -s | tr '[:upper:]' '[:lower:]') 412 case "$os" in 413 cygwin_nt*) os="windows" ;; 414 mingw*) os="windows" ;; 415 msys_nt*) os="windows" ;; 416 esac 417 418 uname_os_check "$os" 419 420 log_trace "uname_os() returned '${os}'" 421 422 echo "$os" 423 ) 424 425 # uname_arch 426 # 427 # outputs an adjusted architecture value 428 # 429 uname_arch() ( 430 arch=$(uname -m) 431 case $arch in 432 x86_64) arch="amd64" ;; 433 x86) arch="386" ;; 434 i686) arch="386" ;; 435 i386) arch="386" ;; 436 aarch64) arch="arm64" ;; 437 armv5*) arch="armv5" ;; 438 armv6*) arch="armv6" ;; 439 armv7*) arch="armv7" ;; 440 esac 441 442 uname_arch_check "${arch}" 443 444 log_trace "uname_arch() returned '${arch}'" 445 446 echo "${arch}" 447 ) 448 449 # get_release_tag [owner] [repo] [tag] 450 # 451 # outputs tag string 452 # 453 get_release_tag() ( 454 owner="$1" 455 repo="$2" 456 tag="$3" 457 458 log_trace "get_release_tag(owner=${owner}, repo=${repo}, tag=${tag})" 459 460 json=$(github_release_json "${owner}" "${repo}" "${tag}") 461 real_tag=$(github_release_tag "${json}") 462 if test -z "${real_tag}"; then 463 return 1 464 fi 465 466 log_trace "get_release_tag() returned '${real_tag}'" 467 468 echo "${real_tag}" 469 ) 470 471 # tag_to_version [tag] 472 # 473 # outputs version string 474 # 475 tag_to_version() ( 476 tag="$1" 477 value="${tag#v}" 478 479 log_trace "tag_to_version(tag=${tag}) returned '${value}'" 480 481 echo "$value" 482 ) 483 484 # get_binary_name [os] [arch] [default-name] 485 # 486 # outputs a the binary string name 487 # 488 get_binary_name() ( 489 os="$1" 490 arch="$2" 491 binary="$3" 492 original_binary="${binary}" 493 494 case "${os}" in 495 windows) binary="${binary}.exe" ;; 496 esac 497 498 log_trace "get_binary_name(os=${os}, arch=${arch}, binary=${original_binary}) returned '${binary}'" 499 500 echo "${binary}" 501 ) 502 503 504 # get_format_name [os] [arch] [default-format] 505 # 506 # outputs an adjusted file format 507 # 508 get_format_name() ( 509 os="$1" 510 arch="$2" 511 format="$3" 512 original_format="${format}" 513 514 case ${os} in 515 windows) format=zip ;; 516 esac 517 518 log_trace "get_format_name(os=${os}, arch=${arch}, format=${original_format}) returned '${format}'" 519 520 echo "${format}" 521 ) 522 523 # download_and_install_asset [release-url-prefix] [download-path] [install-path] [name] [os] [arch] [version] [format] [binary] 524 # 525 # attempts to download the archive and install it to the given path. 526 # 527 download_and_install_asset() ( 528 download_url="$1" 529 download_path="$2" 530 install_path=$3 531 name="$4" 532 os="$5" 533 arch="$6" 534 version="$7" 535 format="$8" 536 binary="$9" 537 538 asset_filepath=$(download_asset "${download_url}" "${download_path}" "${name}" "${os}" "${arch}" "${version}" "${format}") 539 540 # don't continue if we couldn't download an asset 541 if [ -z "${asset_filepath}" ]; then 542 log_err "could not find release asset for os='${os}' arch='${arch}' format='${format}' " 543 return 1 544 fi 545 546 install_asset "${asset_filepath}" "${install_path}" "${binary}" 547 ) 548 549 # download_asset [release-url-prefix] [download-path] [name] [os] [arch] [version] [format] [binary] 550 # 551 # outputs the path to the downloaded asset asset_filepath 552 # 553 download_asset() ( 554 download_url="$1" 555 destination="$2" 556 name="$3" 557 os="$4" 558 arch="$5" 559 version="$6" 560 format="$7" 561 562 log_trace "download_asset(url=${download_url}, destination=${destination}, name=${name}, os=${os}, arch=${arch}, version=${version}, format=${format})" 563 564 checksums_filepath=$(download_github_release_checksums "${download_url}" "${name}" "${version}" "${destination}") 565 566 log_trace "checksums content:\n$(cat ${checksums_filepath})" 567 568 asset_filename=$(search_for_asset "${checksums_filepath}" "${name}" "${os}" "${arch}" "${format}") 569 570 # don't continue if we couldn't find a matching asset from the checksums file 571 if [ -z "${asset_filename}" ]; then 572 return 1 573 fi 574 575 asset_url="${download_url}/${asset_filename}" 576 asset_filepath="${destination}/${asset_filename}" 577 http_download "${asset_filepath}" "${asset_url}" "" 578 579 hash_sha256_verify "${asset_filepath}" "${checksums_filepath}" 580 581 log_trace "download_asset_by_checksums_file() returned '${asset_filepath}'" 582 583 echo "${asset_filepath}" 584 ) 585 586 # install_asset [asset-path] [destination-path] [binary] 587 # 588 install_asset() ( 589 asset_filepath="$1" 590 destination="$2" 591 binary="$3" 592 593 log_trace "install_asset(asset=${asset_filepath}, destination=${destination}, binary=${binary})" 594 595 # don't continue if we don't have anything to install 596 if [ -z "${asset_filepath}" ]; then 597 return 598 fi 599 600 archive_dir=$(dirname "${asset_filepath}") 601 602 # unarchive the downloaded archive to the temp dir 603 (cd "${archive_dir}" && unpack "${asset_filepath}") 604 605 # create the destination dir 606 test ! -d "${destination}" && install -d "${destination}" 607 608 # install the binary to the destination dir 609 install "${archive_dir}/${binary}" "${destination}/" 610 ) 611 612 main() ( 613 # parse arguments 614 615 # note: never change default install directory (this must always be backwards compatible) 616 install_dir=${install_dir:-./bin} 617 618 # note: never change the program flags or arguments (this must always be backwards compatible) 619 while getopts "b:dh?x" arg; do 620 case "$arg" in 621 b) install_dir="$OPTARG" ;; 622 d) 623 if [ "$_logp" = "$log_info_priority" ]; then 624 # -d == debug 625 log_set_priority $log_debug_priority 626 else 627 # -dd (or -ddd...) == trace 628 log_set_priority $log_trace_priority 629 fi 630 ;; 631 h | \?) usage "$0" ;; 632 x) set -x ;; 633 esac 634 done 635 shift $((OPTIND - 1)) 636 set +u 637 tag=$1 638 639 if [ -z "${tag}" ]; then 640 log_debug "checking github for the current release tag" 641 tag="" 642 else 643 log_debug "checking github for release tag='${tag}'" 644 fi 645 set -u 646 647 tag=$(get_release_tag "${OWNER}" "${REPO}" "${tag}") 648 649 if [ "$?" != "0" ]; then 650 log_err "unable to find tag='${tag}'" 651 log_err "do not specify a version or select a valid version from https://github.com/${OWNER}/${REPO}/releases" 652 return 1 653 fi 654 655 # run the application 656 657 version=$(tag_to_version "${tag}") 658 os=$(uname_os) 659 arch=$(uname_arch) 660 format=$(get_format_name "${os}" "${arch}" "tar.gz") 661 binary=$(get_binary_name "${os}" "${arch}" "${PROJECT_NAME}") 662 download_url="${GITHUB_DOWNLOAD_PREFIX}/${tag}" 663 664 # we always use the install.sh script that is associated with the tagged release. Why? the latest install.sh is not 665 # guaranteed to be able to install every version of the application. We use the DOWNLOAD_TAG_INSTALL_SCRIPT env var 666 # to indicate if we should continue processing with the existing script or to download the script from the given tag. 667 if [ "${DOWNLOAD_TAG_INSTALL_SCRIPT}" = "true" ]; then 668 export DOWNLOAD_TAG_INSTALL_SCRIPT=false 669 log_info "fetching release script for tag='${tag}'" 670 http_copy "${INSTALL_SH_BASE_URL}/${tag}/install.sh" "" | sh -s -- ${PROGRAM_ARGS} 671 exit $? 672 fi 673 674 log_info "using release tag='${tag}' version='${version}' os='${os}' arch='${arch}'" 675 676 download_dir=$(mktemp -d) 677 trap 'rm -rf -- "$download_dir"' EXIT 678 679 log_debug "downloading files into ${download_dir}" 680 681 download_and_install_asset "${download_url}" "${download_dir}" "${install_dir}" "${PROJECT_NAME}" "${os}" "${arch}" "${version}" "${format}" "${binary}" 682 683 # don't continue if we couldn't install the asset 684 if [ "$?" != "0" ]; then 685 log_err "failed to install ${PROJECT_NAME}" 686 return 1 687 fi 688 689 log_info "installed ${install_dir}/${binary}" 690 ) 691 692 # entrypoint 693 694 set +u 695 if [ -z "${TEST_INSTALL_SH}" ]; then 696 set -u 697 exit $(main "$@") 698 fi 699 set -u