#!/bin/bash -e # 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. set -e -o pipefail MYPATH=$(dirname "${BASH_SOURCE[0]}") CYGWIN=false UNAME=`uname -s | tr '[:upper:]' '[:lower:]'` case "${UNAME}" in linux) OS=linux ;; cygwin*) OS=windows CYGWIN=true ;; msys*|mingw*) OS=windows ;; darwin) OS=mac ;; *) >&2 echo "CIPD not supported on ${UNAME}" exit 1 esac UNAME=`uname -m | tr '[:upper:]' '[:lower:]'` case "${UNAME}" in x86_64|amd64) ARCH=amd64 ;; s390x) # best-effort support for IBM s390x: crbug.com/764087 ARCH=s390x ;; ppc64) # best-effort support for 64-bit PowerPC: crbug.com/773857 ARCH=ppc64 ;; ppc64le) # best-effort support for 64-bit PowerPC/LE: crbug.com/773857 ARCH=ppc64le ;; 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 # 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}" 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 "[31;1m" >&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 "[0m" 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 "[31;1m" >&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 "[0m" return 1 } # clean_bootstrap bootstraps the client from scratch using 'curl' or 'wget'. # # 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. function clean_bootstrap() { local expected_hash=$(expected_sha256 "${PLATFORM}") if [ -z "${expected_hash}" ] ; then exit 1 fi # Download the client into a temporary file, check its hash, then move it into # the final location. # # This wonky tempdir method works on Linux and Mac. local CIPD_CLIENT_TMP=$(\ mktemp -p "${MYPATH}" 2>/dev/null || \ mktemp "${MYPATH}/.cipd_client.XXXXXXX") if hash curl 2> /dev/null ; then curl "${URL}" -s --show-error -f -A "${USER_AGENT}" -L -o "${CIPD_CLIENT_TMP}" elif hash wget 2> /dev/null ; then wget "${URL}" -q -U "${USER_AGENT}" -O "${CIPD_CLIENT_TMP}" else >&2 echo -n "[31;1m" >&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 "[0m" rm "${CIPD_CLIENT_TMP}" exit 1 fi local actual_hash=$(calc_sha256 "${CIPD_CLIENT_TMP}") if [ -z "${actual_hash}" ] ; then rm "${CIPD_CLIENT_TMP}" exit 1 fi if [ "${actual_hash}" != "${expected_hash}" ]; then >&2 echo -n "[31;1m" >&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 "[0m" rm "${CIPD_CLIENT_TMP}" exit 1 fi set +e chmod +x "${CIPD_CLIENT_TMP}" mv "${CIPD_CLIENT_TMP}" "${CLIENT}" set -e } # self_update launches CIPD client's built-in selfupdate mechanism. # # It is more efficient that redownloading the binary all the time. function self_update() { "${CLIENT}" selfupdate -version-file "${VERSION_FILE}" -service-url "${CIPD_BACKEND}" } if [ ! -x "${CLIENT}" ]; then clean_bootstrap fi export CIPD_HTTP_USER_AGENT_PREFIX="${USER_AGENT}" if ! self_update 2> /dev/null ; then >&2 echo -n "[31;1m" >&2 echo -n "CIPD selfupdate failed. " >&2 echo -n "Trying to bootstrap the CIPD client from scratch..." >&2 echo "[0m" clean_bootstrap if ! self_update ; then # need to run it again to setup .cipd_version file >&2 echo -n "[31;1m" >&2 echo -n "Bootstrap from scratch failed, something is seriously broken. " >&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 "[0m" exit 1 fi fi # 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/. if ${CYGWIN}; then 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 echo "${CLIENT}" "${@}" fi exec "${CLIENT}" "${@}"