cipd 7.8 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 36 37
    # use amd64 binaries even on arm64 for now, see crbug.com/1102967
    # Once we have arm binaries of everything, this will change to "arm64"
    # Run `echo "mac-arm64" > .cipd_client_platform` to force arm64 binaries
    # before everything is ready, for testing.
    ARCH=amd64
38 39
    ;;
  *)
40
    >&2 echo "CIPD not supported on ${UNAME}"
41 42 43
    exit 1
esac

44 45 46 47 48 49 50 51 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
if [ -z $ARCH ]; then
  UNAME=`uname -m | tr '[:upper:]' '[:lower:]'`
  case "${UNAME}" in
    x86_64|amd64)
      ARCH=amd64
      ;;
    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
77

78 79 80 81 82 83 84 85
# 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}"

86 87 88 89 90 91
# 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

92 93 94 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
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
}
147 148


149
# clean_bootstrap bootstraps the client from scratch using 'curl' or 'wget'.
150 151 152
#
# 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.
153
function clean_bootstrap() {
154 155 156 157
  local expected_hash=$(expected_sha256 "${PLATFORM}")
  if [ -z "${expected_hash}" ] ; then
    exit 1
  fi
158

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

167
  if hash curl 2> /dev/null ; then
168
    curl "${URL}" -s --show-error -f --retry 3 --retry-delay 5 -A "${USER_AGENT}" -L -o "${CIPD_CLIENT_TMP}"
169
  elif hash wget 2> /dev/null ; then
170
    wget "${URL}" -q -t 3 -w 5 --retry-connrefused -U "${USER_AGENT}" -O "${CIPD_CLIENT_TMP}"
171
  else
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    >&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
189
    rm "${CIPD_CLIENT_TMP}"
190 191
    exit 1
  fi
192

193 194 195 196 197 198 199 200 201 202
  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
203 204

  set +e
205 206
  chmod +x "${CIPD_CLIENT_TMP}"
  mv "${CIPD_CLIENT_TMP}" "${CLIENT}"
207
  set -e
208 209
}

210 211 212 213

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

218

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
# 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.
234
if [ ! -x "${CLIENT}" ]; then
235
  clean_bootstrap
236 237
fi

238
# If the client binary exists, ask it to self-update.
239 240 241 242 243 244
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 ""
245 246
  clean_bootstrap
  if ! self_update ; then  # need to run it again to setup .cipd_version file
247
    >&2 echo -n ""
248
    >&2 echo -n "Bootstrap from scratch for ${PLATFORM} failed! "
249 250 251 252 253
    >&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
254
  fi
255 256
fi

257 258 259
# 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/.
260
if ${CYGWIN}; then
261 262 263 264 265 266 267 268 269
  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
270
  echo "${CLIENT}" "${@}"
271 272
fi

273
exec "${CLIENT}" "${@}"