1*e7b1675dSTing-Kang Chang#!/bin/bash 2*e7b1675dSTing-Kang Chang# Copyright 2020 Google LLC 3*e7b1675dSTing-Kang Chang# 4*e7b1675dSTing-Kang Chang# Licensed under the Apache License, Version 2.0 (the "License"); 5*e7b1675dSTing-Kang Chang# you may not use this file except in compliance with the License. 6*e7b1675dSTing-Kang Chang# You may obtain a copy of the License at 7*e7b1675dSTing-Kang Chang# 8*e7b1675dSTing-Kang Chang# http://www.apache.org/licenses/LICENSE-2.0 9*e7b1675dSTing-Kang Chang# 10*e7b1675dSTing-Kang Chang# Unless required by applicable law or agreed to in writing, software 11*e7b1675dSTing-Kang Chang# distributed under the License is distributed on an "AS IS" BASIS, 12*e7b1675dSTing-Kang Chang# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*e7b1675dSTing-Kang Chang# See the License for the specific language governing permissions and 14*e7b1675dSTing-Kang Chang# limitations under the License. 15*e7b1675dSTing-Kang Chang################################################################################ 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Chang# This script creates the release artifacts of Tink Python which includes a 18*e7b1675dSTing-Kang Chang# source distribution and binary wheels for Linux and macOS. All Python tests 19*e7b1675dSTing-Kang Chang# are exectued for each binary wheel and the source distribution. 20*e7b1675dSTing-Kang Chang 21*e7b1675dSTing-Kang Changset -euox pipefail 22*e7b1675dSTing-Kang Chang 23*e7b1675dSTing-Kang Changdeclare -a PYTHON_VERSIONS= 24*e7b1675dSTing-Kang ChangPYTHON_VERSIONS+=("3.7") 25*e7b1675dSTing-Kang ChangPYTHON_VERSIONS+=("3.8") 26*e7b1675dSTing-Kang ChangPYTHON_VERSIONS+=("3.9") 27*e7b1675dSTing-Kang ChangPYTHON_VERSIONS+=("3.10") 28*e7b1675dSTing-Kang Changreadonly PYTHON_VERSIONS 29*e7b1675dSTing-Kang Chang 30*e7b1675dSTing-Kang Changreadonly PLATFORM="$(uname | tr '[:upper:]' '[:lower:]')" 31*e7b1675dSTing-Kang Chang 32*e7b1675dSTing-Kang Changexport TINK_PYTHON_ROOT_PATH="${PWD}" 33*e7b1675dSTing-Kang Changreadonly TINK_VERSION="$(grep ^TINK "${TINK_PYTHON_ROOT_PATH}/VERSION" \ 34*e7b1675dSTing-Kang Chang | awk '{gsub(/"/, "", $3); print $3}')" 35*e7b1675dSTing-Kang Chang 36*e7b1675dSTing-Kang Changreadonly IMAGE_NAME="quay.io/pypa/manylinux2014_x86_64" 37*e7b1675dSTing-Kang Changreadonly IMAGE_DIGEST="sha256:31d7d1cbbb8ea93ac64c3113bceaa0e9e13d65198229a25eee16dc70e8bf9cf7" 38*e7b1675dSTing-Kang Changreadonly IMAGE="${IMAGE_NAME}@${IMAGE_DIGEST}" 39*e7b1675dSTing-Kang Chang 40*e7b1675dSTing-Kang Chang####################################### 41*e7b1675dSTing-Kang Chang# Builds Tink Python built distribution (Wheel) [1]. 42*e7b1675dSTing-Kang Chang# 43*e7b1675dSTing-Kang Chang# This function must be called from within the Tink Python's root folder. 44*e7b1675dSTing-Kang Chang# 45*e7b1675dSTing-Kang Chang# [1] https://packaging.python.org/en/latest/glossary/#term-Built-Distribution 46*e7b1675dSTing-Kang Chang# Globals: 47*e7b1675dSTing-Kang Chang# None 48*e7b1675dSTing-Kang Chang# Arguments: 49*e7b1675dSTing-Kang Chang# None 50*e7b1675dSTing-Kang Chang####################################### 51*e7b1675dSTing-Kang Chang__create_and_test_wheels_for_linux() { 52*e7b1675dSTing-Kang Chang echo "### Building and testing Linux binary wheels ###" 53*e7b1675dSTing-Kang Chang # Use signatures for getting images from registry (see 54*e7b1675dSTing-Kang Chang # https://docs.docker.com/engine/security/trust/content_trust/). 55*e7b1675dSTing-Kang Chang export DOCKER_CONTENT_TRUST=1 56*e7b1675dSTing-Kang Chang 57*e7b1675dSTing-Kang Chang # We use setup.py to build wheels; setup.py makes changes to the WORKSPACE 58*e7b1675dSTing-Kang Chang # file so we save a copy for backup. 59*e7b1675dSTing-Kang Chang cp WORKSPACE WORKSPACE.bak 60*e7b1675dSTing-Kang Chang 61*e7b1675dSTing-Kang Chang local -r tink_base_dir="/tmp/tink" 62*e7b1675dSTing-Kang Chang local -r tink_py_relative_path="${PWD##*/}" 63*e7b1675dSTing-Kang Chang local -r workdir="${tink_base_dir}/${tink_py_relative_path}" 64*e7b1675dSTing-Kang Chang # Build binary wheels. 65*e7b1675dSTing-Kang Chang docker run \ 66*e7b1675dSTing-Kang Chang --volume "${TINK_PYTHON_ROOT_PATH}/..:${tink_base_dir}" \ 67*e7b1675dSTing-Kang Chang --workdir "${workdir}" \ 68*e7b1675dSTing-Kang Chang -e TINK_PYTHON_SETUPTOOLS_OVERRIDE_BASE_PATH="${tink_base_dir}" \ 69*e7b1675dSTing-Kang Chang "${IMAGE}" \ 70*e7b1675dSTing-Kang Chang "${workdir}/tools/distribution/build_linux_binary_wheels.sh" 71*e7b1675dSTing-Kang Chang 72*e7b1675dSTing-Kang Chang ## Test binary wheels. 73*e7b1675dSTing-Kang Chang docker run \ 74*e7b1675dSTing-Kang Chang --volume "${TINK_PYTHON_ROOT_PATH}/..:${tink_base_dir}" \ 75*e7b1675dSTing-Kang Chang --workdir "${workdir}" \ 76*e7b1675dSTing-Kang Chang "${IMAGE}" \ 77*e7b1675dSTing-Kang Chang "${workdir}/tools/distribution/test_linux_binary_wheels.sh" 78*e7b1675dSTing-Kang Chang 79*e7b1675dSTing-Kang Chang # Docker runs as root so we transfer ownership to the non-root user. 80*e7b1675dSTing-Kang Chang sudo chown -R "$(id -un):$(id -gn)" "${TINK_PYTHON_ROOT_PATH}" 81*e7b1675dSTing-Kang Chang # Restore the original WORKSPACE. 82*e7b1675dSTing-Kang Chang mv WORKSPACE.bak WORKSPACE 83*e7b1675dSTing-Kang Chang} 84*e7b1675dSTing-Kang Chang 85*e7b1675dSTing-Kang Chang####################################### 86*e7b1675dSTing-Kang Chang# Builds Tink Python source distribution [1]. 87*e7b1675dSTing-Kang Chang# 88*e7b1675dSTing-Kang Chang# This function must be called from within the Tink Python's root folder. 89*e7b1675dSTing-Kang Chang# 90*e7b1675dSTing-Kang Chang# [1] https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist 91*e7b1675dSTing-Kang Chang# Globals: 92*e7b1675dSTing-Kang Chang# PYTHON_VERSIONS 93*e7b1675dSTing-Kang Chang# Arguments: 94*e7b1675dSTing-Kang Chang# None 95*e7b1675dSTing-Kang Chang####################################### 96*e7b1675dSTing-Kang Chang__create_and_test_sdist_for_linux() { 97*e7b1675dSTing-Kang Chang echo "### Building and testing Linux source distribution ###" 98*e7b1675dSTing-Kang Chang local sorted=( $( echo "${PYTHON_VERSIONS[@]}" \ 99*e7b1675dSTing-Kang Chang | xargs -n1 | sort -V | xargs ) ) 100*e7b1675dSTing-Kang Chang local latest="${sorted[${#sorted[@]}-1]}" 101*e7b1675dSTing-Kang Chang enable_py_version "${latest}" 102*e7b1675dSTing-Kang Chang 103*e7b1675dSTing-Kang Chang # Patch the workspace to use http_archive rules which specify the release tag. 104*e7b1675dSTing-Kang Chang # 105*e7b1675dSTing-Kang Chang # This is done so that an already patched version of WORKSPACE is present in 106*e7b1675dSTing-Kang Chang # the sdist. Then, when building from the sdist, the default patching logic 107*e7b1675dSTing-Kang Chang # in performed by setup.py will be a no-op. 108*e7b1675dSTing-Kang Chang # 109*e7b1675dSTing-Kang Chang # TODO(b/281635529): Use a container for a more hermetic testing environment. 110*e7b1675dSTing-Kang Chang cp WORKSPACE WORKSPACE.bak 111*e7b1675dSTing-Kang Chang 112*e7b1675dSTing-Kang Chang # Build source distribution. 113*e7b1675dSTing-Kang Chang TINK_PYTHON_SETUPTOOLS_TAGGED_VERSION="${TINK_VERSION}" \ 114*e7b1675dSTing-Kang Chang python3 setup.py sdist --owner=root --group=root 115*e7b1675dSTing-Kang Chang local sdist_filename="tink-${TINK_VERSION}.tar.gz" 116*e7b1675dSTing-Kang Chang cp "dist/${sdist_filename}" release/ 117*e7b1675dSTing-Kang Chang 118*e7b1675dSTing-Kang Chang # Restore the original WORKSPACE. 119*e7b1675dSTing-Kang Chang mv WORKSPACE.bak WORKSPACE 120*e7b1675dSTing-Kang Chang 121*e7b1675dSTing-Kang Chang # Test install from source distribution. 122*e7b1675dSTing-Kang Chang python3 --version 123*e7b1675dSTing-Kang Chang python3 -m pip list 124*e7b1675dSTing-Kang Chang # Install Tink dependencies. 125*e7b1675dSTing-Kang Chang python3 -m pip install --require-hashes -r requirements.txt 126*e7b1675dSTing-Kang Chang python3 -m pip install --no-deps --no-index -v "release/${sdist_filename}" 127*e7b1675dSTing-Kang Chang python3 -m pip list 128*e7b1675dSTing-Kang Chang find tink/ -not -path "*cc/pybind*" -type f -name "*_test.py" -print0 \ 129*e7b1675dSTing-Kang Chang | xargs -0 -n1 python3 130*e7b1675dSTing-Kang Chang} 131*e7b1675dSTing-Kang Chang 132*e7b1675dSTing-Kang Chang####################################### 133*e7b1675dSTing-Kang Chang# Creates a Tink Python distribution for Linux. 134*e7b1675dSTing-Kang Chang# 135*e7b1675dSTing-Kang Chang# This function must be called from within the Tink Python's root folder. 136*e7b1675dSTing-Kang Chang# 137*e7b1675dSTing-Kang Chang# Globals: 138*e7b1675dSTing-Kang Chang# None 139*e7b1675dSTing-Kang Chang# Arguments: 140*e7b1675dSTing-Kang Chang# None 141*e7b1675dSTing-Kang Chang####################################### 142*e7b1675dSTing-Kang Changcreate_distribution_for_linux() { 143*e7b1675dSTing-Kang Chang __create_and_test_wheels_for_linux 144*e7b1675dSTing-Kang Chang __create_and_test_sdist_for_linux 145*e7b1675dSTing-Kang Chang} 146*e7b1675dSTing-Kang Chang 147*e7b1675dSTing-Kang Chang####################################### 148*e7b1675dSTing-Kang Chang# Creates a Tink Python distribution for MacOS. 149*e7b1675dSTing-Kang Chang# 150*e7b1675dSTing-Kang Chang# This function must be called from within the Tink Python's root folder. 151*e7b1675dSTing-Kang Chang# 152*e7b1675dSTing-Kang Chang# Globals: 153*e7b1675dSTing-Kang Chang# PYTHON_VERSIONS 154*e7b1675dSTing-Kang Chang# Arguments: 155*e7b1675dSTing-Kang Chang# None 156*e7b1675dSTing-Kang Chang####################################### 157*e7b1675dSTing-Kang Changcreate_distribution_for_macos() { 158*e7b1675dSTing-Kang Chang echo "### Building macOS binary wheels ###" 159*e7b1675dSTing-Kang Chang 160*e7b1675dSTing-Kang Chang for v in "${PYTHON_VERSIONS[@]}"; do 161*e7b1675dSTing-Kang Chang enable_py_version "${v}" 162*e7b1675dSTing-Kang Chang 163*e7b1675dSTing-Kang Chang # Build binary wheel. 164*e7b1675dSTing-Kang Chang python3 -m pip wheel -w release . 165*e7b1675dSTing-Kang Chang 166*e7b1675dSTing-Kang Chang # Test binary wheel. 167*e7b1675dSTing-Kang Chang # TODO(ckl): Implement test. 168*e7b1675dSTing-Kang Chang done 169*e7b1675dSTing-Kang Chang} 170*e7b1675dSTing-Kang Chang 171*e7b1675dSTing-Kang Changenable_py_version() { 172*e7b1675dSTing-Kang Chang # A partial version number (e.g. "3.9"). 173*e7b1675dSTing-Kang Chang local partial_version="$1" 174*e7b1675dSTing-Kang Chang 175*e7b1675dSTing-Kang Chang # The latest installed Python version that matches the partial version number 176*e7b1675dSTing-Kang Chang # (e.g. "3.9.5"). 177*e7b1675dSTing-Kang Chang local version="$(pyenv versions --bare | grep "${partial_version}" | tail -1)" 178*e7b1675dSTing-Kang Chang 179*e7b1675dSTing-Kang Chang # Set current Python version via environment variable. 180*e7b1675dSTing-Kang Chang pyenv shell "${version}" 181*e7b1675dSTing-Kang Chang 182*e7b1675dSTing-Kang Chang # Update environment. 183*e7b1675dSTing-Kang Chang python3 -m pip install --require-hashes -r \ 184*e7b1675dSTing-Kang Chang "${TINK_PYTHON_ROOT_PATH}/tools/distribution/requirements.txt" 185*e7b1675dSTing-Kang Chang} 186*e7b1675dSTing-Kang Chang 187*e7b1675dSTing-Kang Changmain() { 188*e7b1675dSTing-Kang Chang eval "$(pyenv init -)" 189*e7b1675dSTing-Kang Chang mkdir -p release 190*e7b1675dSTing-Kang Chang 191*e7b1675dSTing-Kang Chang if [[ "${PLATFORM}" == 'linux' ]]; then 192*e7b1675dSTing-Kang Chang create_distribution_for_linux 193*e7b1675dSTing-Kang Chang elif [[ "${PLATFORM}" == 'darwin' ]]; then 194*e7b1675dSTing-Kang Chang create_distribution_for_macos 195*e7b1675dSTing-Kang Chang else 196*e7b1675dSTing-Kang Chang echo "${PLATFORM} is not a supported platform." 197*e7b1675dSTing-Kang Chang exit 1 198*e7b1675dSTing-Kang Chang fi 199*e7b1675dSTing-Kang Chang} 200*e7b1675dSTing-Kang Chang 201*e7b1675dSTing-Kang Changmain "$@" 202