github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/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