cipd 7.77 KB
Newer Older
1
#!/usr/bin/env bash
2 3 4 5 6

# Copyright (c) 2016 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

7 8
set -e -o pipefail

9
MYPATH=$(dirname "${BASH_SOURCE[0]}")
10
CYGWIN=false
11

12 13 14
# Make sure this starts empty
ARCH=

15
UNAME=`uname -s | tr '[:upper:]' '[:lower:]'`
16
case "${UNAME}" in
17 18 19 20
  aix)
    OS="${UNAME}"
    ARCH="ppc64"  # apparently 'uname -m' returns something very different
    ;;
21
  linux)
22
    OS="${UNAME}"
23
    ;;
24
  cygwin*)
25
    OS=windows
26 27 28
    CYGWIN=true
    ;;
  msys*|mingw*)
29
    OS=windows
30 31
    ;;
  darwin)
32
    OS=mac
33 34
    ;;
  *)
35
    >&2 echo "CIPD not supported on ${UNAME}"
36 37 38
    exit 1
esac

39

40 41 42 43 44
if [ -z $ARCH ]; then
  UNAME=`uname -m | tr '[:upper:]' '[:lower:]'`
  case "${UNAME}" in
    x86_64|amd64)
      ARCH=amd64
45 46 47 48 49 50 51
      # Check if Mac ARM running under Rosetta
      if [ $OS == 'mac' ]; then
        TRANSLATED=`/usr/sbin/sysctl -n sysctl.proc_translated 2> /dev/null || echo 0`
        if [ $TRANSLATED == "1" ]; then
          ARCH="arm64"
        fi
      fi
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
      ;;
    s390x|ppc64|ppc64le)  # best-effort support
      ARCH="${UNAME}"
      ;;
    aarch64)
      ARCH=arm64
      ;;
    armv7l)
      ARCH=armv6l
      ;;
    arm*)
      ARCH="${UNAME}"
      ;;
    *86)
      ARCH=386
      ;;
    mips*)
      # detect mips64le vs mips64.
      ARCH="${UNAME}"
      if lscpu | grep -q "Little Endian"; then
        ARCH+=le
      fi
      ;;
    *)
      >&2 echo "UNKNOWN Machine architecture: ${UNAME}"
      exit 1
  esac
fi
80

81 82 83 84 85 86 87 88
# CIPD_BACKEND can be changed to ...-dev for manual testing.
CIPD_BACKEND="https://chrome-infra-packages.appspot.com"
VERSION_FILE="${MYPATH}/cipd_client_version"

CLIENT="${MYPATH}/.cipd_client"
VERSION=`cat "${VERSION_FILE}"`
PLATFORM="${OS}-${ARCH}"

89 90 91 92 93 94
# A value in .cipd_client_platform overrides the "guessed" platform.
PLATFORM_OVERRIDE_FILE="${MYPATH}/.cipd_client_platform"
if [ -f "${PLATFORM_OVERRIDE_FILE}" ]; then
  PLATFORM=`cat ${PLATFORM_OVERRIDE_FILE}`
fi

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
URL="${CIPD_BACKEND}/client?platform=${PLATFORM}&version=${VERSION}"
USER_AGENT="depot_tools/$(git -C ${MYPATH} rev-parse HEAD 2>/dev/null || echo "???")"


# calc_sha256 is "portable" variant of sha256sum. It uses sha256sum when
# available (most Linuxes and cygwin) and 'shasum -a 256' otherwise (for OSX).
#
# Args:
#   Path to a file.
# Stdout:
#   Lowercase SHA256 hex digest of the file.
function calc_sha256() {
  if hash sha256sum 2> /dev/null ; then
    sha256sum "$1" | cut -d' ' -f1
  elif hash shasum 2> /dev/null ; then
    shasum -a 256 "$1" | cut -d' ' -f1
  else
    >&2 echo -n ""
    >&2 echo -n "Don't know how to calculate SHA256 on your platform. "
    >&2 echo -n "Please use your package manager to install one before continuing:"
    >&2 echo
    >&2 echo "  sha256sum"
    >&2 echo -n "  shasum"
    >&2 echo ""
    return 1
  fi
}


# expected_sha256 reads the expected SHA256 hex digest from *.digests file.
#
# Args:
#   Name of the platform to get client's digest for.
# Stdout:
#   Lowercase SHA256 hex digest.
function expected_sha256() {
  local line
  while read -r line; do
    if [[ "${line}" =~ ^([0-9a-z\-]+)[[:blank:]]+sha256[[:blank:]]+([0-9a-f]+)$ ]] ; then
      local plat="${BASH_REMATCH[1]}"
      local hash="${BASH_REMATCH[2]}"
      if [ "${plat}" ==  "$1" ]; then
        echo "${hash}"
        return 0
      fi
    fi
  done < "${VERSION_FILE}.digests"

  >&2 echo -n ""
  >&2 echo -n "Platform $1 is not supported by the CIPD client bootstrap: "
  >&2 echo -n "there's no pinned SHA256 hash for it in the *.digests file."
  >&2 echo ""

  return 1
}
150 151


152
# clean_bootstrap bootstraps the client from scratch using 'curl' or 'wget'.
153 154 155
#
# It checks that the SHA256 of the downloaded file is known. Exits the script
# if the client can't be downloaded or its hash doesn't match the expected one.
156
function clean_bootstrap() {
157 158 159 160
  local expected_hash=$(expected_sha256 "${PLATFORM}")
  if [ -z "${expected_hash}" ] ; then
    exit 1
  fi
161

162 163
  # Download the client into a temporary file, check its hash, then move it into
  # the final location.
164 165
  #
  # This wonky tempdir method works on Linux and Mac.
166
  local CIPD_CLIENT_TMP=$(\
167 168
    mktemp -p "${MYPATH}" 2>/dev/null || \
    mktemp "${MYPATH}/.cipd_client.XXXXXXX")
169

170
  if hash curl 2> /dev/null ; then
171
    curl "${URL}" -s --show-error -f --retry 3 --retry-delay 5 -A "${USER_AGENT}" -L -o "${CIPD_CLIENT_TMP}"
172
  elif hash wget 2> /dev/null ; then
173
    wget "${URL}" -q -t 3 -w 5 --retry-connrefused -U "${USER_AGENT}" -O "${CIPD_CLIENT_TMP}"
174
  else
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
    >&2 echo -n ""
    >&2 echo -n "Your platform is missing a supported fetch command. "
    >&2 echo "Please use your package manager to install one before continuing:"
    >&2 echo
    >&2 echo "  curl"
    >&2 echo "  wget"
    >&2 echo
    >&2 echo "Alternately, manually download:"
    >&2 echo "  ${URL}"
    >&2 echo -n "To ${CLIENT}, and then re-run this command."
    >&2 echo ""
    rm "${CIPD_CLIENT_TMP}"
    exit 1
  fi

  local actual_hash=$(calc_sha256 "${CIPD_CLIENT_TMP}")
  if [ -z "${actual_hash}" ] ; then
192
    rm "${CIPD_CLIENT_TMP}"
193 194
    exit 1
  fi
195

196 197 198 199 200 201 202 203 204 205
  if [ "${actual_hash}" != "${expected_hash}" ]; then
    >&2 echo -n ""
    >&2 echo "SHA256 digest of the downloaded CIPD client is incorrect:"
    >&2 echo "  Expecting ${expected_hash}"
    >&2 echo "  Got       ${actual_hash}"
    >&2 echo -n "Refusing to run it. Check that *.digests file is up-to-date."
    >&2 echo ""
    rm "${CIPD_CLIENT_TMP}"
    exit 1
  fi
206 207

  set +e
208 209
  chmod +x "${CIPD_CLIENT_TMP}"
  mv "${CIPD_CLIENT_TMP}" "${CLIENT}"
210
  set -e
211 212
}

213 214 215 216

# self_update launches CIPD client's built-in selfupdate mechanism.
#
# It is more efficient that redownloading the binary all the time.
217
function self_update() {
218
  "${CLIENT}" selfupdate -version-file "${VERSION_FILE}" -service-url "${CIPD_BACKEND}"
219 220
}

221

222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
# Nuke the existing client if its platform doesn't match what we want now. We
# crudely search for a CIPD client package name in the .cipd_version JSON file.
# It has only "instance_id" as the other field (looking like a base64 string),
# so mismatches are very unlikely.
INSTALLED_VERSION_FILE="${MYPATH}/.versions/.cipd_client.cipd_version"
if [ -f "${INSTALLED_VERSION_FILE}" ]; then
  JSON_BODY=`cat "${INSTALLED_VERSION_FILE}"`
  if [[ "$JSON_BODY" != *"infra/tools/cipd/${PLATFORM}"* ]]; then
    >&2 echo "Detected CIPD client platform change to ${PLATFORM}."
    >&2 echo "Deleting the existing client to trigger the bootstrap..."
    rm -f "${CLIENT}" "${INSTALLED_VERSION_FILE}"
  fi
fi

# If the client binary doesn't exist, do the bootstrap from scratch.
237
if [ ! -x "${CLIENT}" ]; then
238
  clean_bootstrap
239 240
fi

241
# If the client binary exists, ask it to self-update.
242 243 244 245 246 247
export CIPD_HTTP_USER_AGENT_PREFIX="${USER_AGENT}"
if ! self_update 2> /dev/null ; then
  >&2 echo -n ""
  >&2 echo -n "CIPD selfupdate failed. "
  >&2 echo -n "Trying to bootstrap the CIPD client from scratch..."
  >&2 echo ""
248 249
  clean_bootstrap
  if ! self_update ; then  # need to run it again to setup .cipd_version file
250
    >&2 echo -n ""
251
    >&2 echo -n "Bootstrap from scratch for ${PLATFORM} failed! "
252 253 254 255 256
    >&2 echo "Run the following commands to diagnose if this is repeating:"
    >&2 echo "  export CIPD_HTTP_USER_AGENT_PREFIX=${USER_AGENT}/manual"
    >&2 echo -n "  ${CLIENT} selfupdate -version-file ${VERSION_FILE}"
    >&2 echo ""
    exit 1
257
  fi
258 259
fi

260 261 262
# CygWin requires changing absolute paths to Windows form. Relative paths
# are typically okay as Windows generally accepts both forward and back
# slashes. This could possibly be constrained to only /tmp/ and /cygdrive/.
263
if ${CYGWIN}; then
264 265 266 267 268 269 270 271 272
  args=("$@")
  for i in `seq 2 $#`; do
    arg="${@:$i:1}"
    if [ "${arg:0:1}" == "/" ]; then
      last=$((i-1))
      next=$((i+1))
      set -- "${@:1:$last}" `cygpath -w "$arg"` "${@:$next}"
    fi
  done
273
  echo "${CLIENT}" "${@}"
274 275
fi

276
exec "${CLIENT}" "${@}"