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