#!/usr/bin/env bash # 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 # Run custom-built cipd client. This is useful when cipd is not available upstream # (i.e. unsupported platform). if [ ! -z "${CUSTOM_CIPD_CLIENT}" ]; then exec "${CUSTOM_CIPD_CLIENT}" "${@}" fi # Export for other depot_tools scripts to re-use. export DEPOT_TOOLS_DIR="${DEPOT_TOOLS_DIR:-$(dirname "${BASH_SOURCE[0]}")}" export DEPOT_TOOLS_UNAME_S="${DEPOT_TOOLS_UNAME_S:-$(uname -s | tr '[:upper:]' '[:lower:]')}" CYGWIN=false # Make sure this starts empty ARCH= UNAME="${DEPOT_TOOLS_UNAME_S}" case "${UNAME}" in aix) OS="${UNAME}" ARCH="ppc64" # apparently 'uname -m' returns something very different ;; linux) OS="${UNAME}" ;; cygwin*) OS=windows CYGWIN=true ;; msys*|mingw*) OS=windows ;; darwin) OS=mac # Allow mac users to override easily override arch detection if [ ! -z "${ARCH_MAC_OVERRIDE}" ]; then ARCH="${ARCH_MAC_OVERRIDE}" fi ;; *) >&2 echo "CIPD not supported on ${UNAME}" exit 1 esac 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 ;; riscv64) ARCH=riscv64 ;; *) >&2 echo "UNKNOWN Machine architecture: ${UNAME}" exit 1 esac fi # CIPD_BACKEND can be changed to ...-dev for manual testing. CIPD_BACKEND="https://chrome-infra-packages.appspot.com" VERSION_FILE="${DEPOT_TOOLS_DIR}/cipd_client_version" CIPD_ROOT="${DEPOT_TOOLS_DIR}" # value in .cipd_client_root file overrides the default root. CIPD_ROOT_OVERRIDE_FILE="${DEPOT_TOOLS_DIR}/.cipd_client_root" if [ -f "${CIPD_ROOT_OVERRIDE_FILE}" ]; then CIPD_ROOT=$(<"${CIPD_ROOT_OVERRIDE_FILE}") mkdir -p "${CIPD_ROOT}" fi CLIENT="${CIPD_ROOT}/.cipd_client" PLATFORM="${OS}-${ARCH}" # A value in .cipd_client_platform overrides the "guessed" platform. PLATFORM_OVERRIDE_FILE="${DEPOT_TOOLS_DIR}/.cipd_client_platform" if [ -f "${PLATFORM_OVERRIDE_FILE}" ]; then PLATFORM=$(<"${PLATFORM_OVERRIDE_FILE}") fi USER_AGENT="depot_tools/$(git -C ${DEPOT_TOOLS_DIR} 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 } # 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 local VERSION=$(<"${VERSION_FILE}") local URL="${CIPD_BACKEND}/client?platform=${PLATFORM}&version=${VERSION}" # 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 "${CIPD_ROOT}" 2>/dev/null || \ mktemp "${CIPD_ROOT}/.cipd_client.XXXXXXX") if hash curl 2> /dev/null ; then curl "${URL}" -s --show-error -f --retry 3 --retry-delay 5 -A "${USER_AGENT}" -L -o "${CIPD_CLIENT_TMP}" elif hash wget 2> /dev/null ; then wget "${URL}" -q -t 3 -w 5 --retry-connrefused -U "${USER_AGENT}" -O "${CIPD_CLIENT_TMP}" else >&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 rm "${CIPD_CLIENT_TMP}" exit 1 fi 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 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}" } # 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="${CIPD_ROOT}/.versions/.cipd_client.cipd_version" if [ -f "${INSTALLED_VERSION_FILE}" ]; then JSON_BODY=$(<"${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. if [ ! -x "${CLIENT}" ]; then clean_bootstrap fi # If the client binary exists, ask it to self-update. 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 "" clean_bootstrap if ! self_update ; then # need to run it again to setup .cipd_version file >&2 echo -n "" >&2 echo -n "Bootstrap from scratch for ${PLATFORM} failed! " >&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 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}" "${@}"