From e91bbb5e307c304d4e7b6f5f35a7c1e71615fe9d Mon Sep 17 00:00:00 2001 From: Andris Raugulis Date: Tue, 28 Mar 2017 05:53:47 +0300 Subject: [PATCH] Better testing environment. --- .travis.yml | 296 ++++++----------------------------------- test/tools/ci-linux.sh | 225 +++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+), 254 deletions(-) create mode 100755 test/tools/ci-linux.sh diff --git a/.travis.yml b/.travis.yml index 813934a..30191d0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,283 +1,71 @@ language: python +sudo: false matrix: include: + # (default) + - os: linux + python: 2.6 + - os: linux + python: 2.7 + - os: linux + python: 3.3 + - os: linux + python: 3.4 + - os: linux + python: 3.5 + - os: linux + python: 3.6 + - os: linux + python: 3.7-dev # Ubuntu 12.04 - os: linux dist: precise - python: 2.6 - env: TOXENV=py26 - - os: linux - dist: precise - python: 2.7 - env: TOXENV=py27 - - os: linux - dist: precise - python: 3.3 - env: TOXENV=py33 - - os: linux - dist: precise - python: 3.4 - env: TOXENV=py34 - - os: linux - dist: precise - python: 3.5 - env: TOXENV=py35 - - os: linux - dist: precise - python: 3.6 - env: TOXENV=py36 - - os: linux - dist: precise - python: 3.7-dev - env: TOXENV=py37 - - os: linux - dist: precise - python: nightly - env: TOXENV=py37 - - os: linux - dist: precise - python: pypy - env: TOXENV=pypy PYORIGIN=pyenv - - os: linux - dist: precise - python: pypy3 - env: TOXENV=pypy3 PYORIGIN=pyenv + language: generic + env: PY_VER=py26,py27,py33,py34,py35,py36 PY_ORIGIN=pyenv # Ubuntu 14.04 - os: linux dist: trusty - python: 2.6 - env: TOXENV=py26 - - os: linux - dist: trusty - python: 2.7 - env: TOXENV=py27 - - os: linux - dist: trusty - python: 3.3 - env: TOXENV=py33 - - os: linux - dist: trusty - python: 3.4 - env: TOXENV=py34 - - os: linux - dist: trusty - python: 3.5 - env: TOXENV=py35 - - os: linux - dist: trusty - python: 3.6 - env: TOXENV=py36 - - os: linux - dist: trusty - python: 3.7-dev - env: TOXENV=py37 - - os: linux - dist: trusty - python: nightly - env: TOXENV=py37 - - os: linux - dist: trusty - python: 2.7 # NOTE: workaround for trusty - env: TOXENV=pypy PYORIGIN=pyenv - - os: linux - dist: trusty - python: 3.5 # NOTE: workaround for trusty - env: TOXENV=pypy3 PYORIGIN=pyenv - # Mac OS X 10.10 Yosemite - - os: osx - osx_image: xcode6.4 language: generic - env: TOXENV=py26 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py27 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py33 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py34 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py35 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py36 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=py37 - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=pypy - - os: osx - osx_image: xcode6.4 - language: generic - env: TOXENV=pypy3 - # Mac OS X 10.11 El Capitan - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py26 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py27 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py33 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py34 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py35 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py36 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=py37 - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=pypy - - os: osx - osx_image: xcode7.3 - language: generic - env: TOXENV=pypy3 + env: PY_VER=py26,py27,py33,py34,py35,py36 PY_ORIGIN=pyenv # macOS 10.12 Sierra - os: osx osx_image: xcode8.2 language: generic - env: TOXENV=py26 + env: PY_VER=py26,py27,py33,py34,py35,py36 + # Mac OS X 10.11 El Capitan - os: osx - osx_image: xcode8.2 + osx_image: xcode7.3 language: generic - env: TOXENV=py27 + env: PY_VER=py26,py27,py33,py34,py35,py36 + # Mac OS X 10.10 Yosemite - os: osx - osx_image: xcode8.2 + osx_image: xcode6.4 language: generic - env: TOXENV=py33 - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=py34 - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=py35 - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=py36 - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=py37 - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=pypy - - os: osx - osx_image: xcode8.2 - language: generic - env: TOXENV=pypy3 + env: PY_VER=py26,py27,py33,py34,py35,py36 allow_failures: - - env: TOXENV=3.7-dev - - env: TOXENV=nightly -sudo: false + - python: 3.7-dev + - env: PY_VER=py37 + - env: PY_VER=py37/pyenv + - env: PY_VER=py37 PY_ORIGIN=pyenv + fast_finish: true + +cache: + - pip + - directories: + - $HOME/.pyenv.cache before_install: - - | - if [ X"$(uname -s)" == X"Darwin" ]; then - sw_vers - brew update || brew update - brew install autoconf pkg-config openssl readline xz - brew upgrade autoconf pkg-config openssl readline xz - PYORIGIN=pyenv - fi - - | - case "${PYORIGIN}" in - pyenv) - rm -rf ~/.pyenv - git clone --depth 1 https://github.com/yyuu/pyenv.git ~/.pyenv - PATH="$HOME/.pyenv/bin:$PATH" - eval "$(pyenv init -)" - pyenv -v - if [ X"$(uname -s)" == X"Darwin" ]; then - [ -z "${PYPY2VERSION}" ] && PYPY2VERSION=pypy2-5.7.0 - # NOTE: latest binary for osx is pypy3.3-5.5-alpha - [ -z "${PYPY3VERSION}" ] && PYPY3VERSION=pypy3.3-5.5-alpha - else - [ -z "${PYPY2VERSION}" ] && PYPY2VERSION=pypy-portable-5.7.0 - [ -z "${PYPY3VERSION}" ] && PYPY3VERSION=pypy3-portable-5.7.0 - fi - case "${TOXENV}" in - py26) pyenv install 2.6.9 || exit 1; pyenv global 2.6.9 ;; - py33) pyenv install 3.3.6 || exit 1; pyenv global 3.3.6 ;; - py34) pyenv install 3.4.6 || exit 1; pyenv global 3.4.6 ;; - py35) pyenv install 3.5.3 || exit 1; pyenv global 3.5.3 ;; - py36) pyenv install 3.6.1 || exit 1; pyenv global 3.6.1 ;; - py37) pyenv install 3.7-dev || exit 1; pyenv global 3.7-dev ;; - pypy) pyenv install ${PYPY2VERSION} || exit 1; pyenv global ${PYPY2VERSION} ;; - pypy3) pyenv install ${PYPY3VERSION} || exit 1; pyenv global ${PYPY3VERSION} ;; - esac - pyenv rehash - ;; - esac - - | - PIPOPT=$(python -c 'import sys; print("" if hasattr(sys, "real_prefix") else "--user")') - if [ -z "${TOXENV##py2*}" ]; then - curl -O https://bootstrap.pypa.io/get-pip.py - python get-pip.py ${PIPOPT} - fi - if [ X"${TOXENV}" == X"py26" ]; then - python -c 'import pip; pip.main();' install ${PIPOPT} -U pip virtualenv - python -c 'import virtualenv; virtualenv.main();' ~/.venv - else - python -m pip install ${PIPOPT} -U pip virtualenv - python -m virtualenv ~/.venv - fi - source ~/.venv/bin/activate + - source test/tools/ci-linux.sh + - ci_step_before_install install: - - pip install --upgrade tox coveralls codecov + - ci_step_install script: - - | - if [ -z "${TOXENV##*py3*}" ]; then - if [ -z "${TOXENV##*pypy3*}" ]; then - # NOTE: workaround for travis environment - _pydir=$(dirname $(which python)) - ln -s -- "${_pydir}/python" "${_pydir}/pypy3" - # NOTE: do not lint, as it hangs when flake8 is run - # NOTE: do not type, as it can't install dependencies - TOXENV=${TOXENV}-test - else - TOXENV=${TOXENV}-test,${TOXENV}-type,${TOXENV}-lint - fi - else - # NOTE: do not type, as it isn't supported on py2x - TOXENV=${TOXENV}-test,${TOXENV}-lint - fi - - tox -e $TOXENV,cov + - ci_step_script after_success: - - coveralls - - codecov + - ci_step_success after_failure: - - cat .tox/log/* - - cat .tox/*/log/* + - ci_step_failure diff --git a/test/tools/ci-linux.sh b/test/tools/ci-linux.sh new file mode 100755 index 0000000..c95f389 --- /dev/null +++ b/test/tools/ci-linux.sh @@ -0,0 +1,225 @@ +#!/bin/sh + +CI_VERBOSE=1 + +ci_err() { [ $1 -ne 0 ] && echo "err: $2" >&2 && exit 1; } +ci_is_osx() { [ X"$(uname -s)" == X"Darwin" ]; } + +ci_get_py_ver() { + local _v + case "$1" in + py26) _v=2.6.9 ;; + py27) _v=2.7.13 ;; + py33) _v=3.3.6 ;; + py34) _v=3.4.6 ;; + py35) _v=3.5.3 ;; + py36) _v=3.6.1 ;; + py37) _v=3.7-dev ;; + pypy) ci_is_osx && _v=pypy2-5.7.0 || _v=pypy-portable-5.7.0 ;; + pypy3) ci_is_osx && _v=pypy3.3-5.5-alpha || _v=pypy3-portable-5.7.0 ;; + *) [ -z "$1" ] && _v=$(python -V 2>&1 | cut -d ' ' -f 2) || _v="$1" ;; + esac + echo "${_v}" +} + +ci_get_py_env() { + if [ -z "$1" ]; then + set -- "$(python -V 2>&1)" + fi + case "$1" in + pypy|pypy2|pypy-*|pypy2-*) echo "pypy" ;; + pypy3|pypy3*) echo "pypy3" ;; + *) + local _v=$(echo "$1" | head -1 | sed -e 's/[^0-9]//g' | cut -c1-2;) + echo "$1" | tail -1 | grep -qi pypy + if [ $? -eq 0 ]; then + case "${_ver}" in + 2*) echo "pypy" ;; + *) echo "pypy3" ;; + esac + else + echo "py${_v}" + fi + esac +} + +ci_pyenv_setup() { + rm -rf ~/.pyenv + git clone --depth 1 https://github.com/yyuu/pyenv.git ~/.pyenv + PYENV_ROOT=$HOME/.pyenv + PATH="$HOME/.pyenv/bin:$PATH" + eval "$(pyenv init -)" + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] $(pyenv -v)" +} + +ci_pyenv_install() { + CI_PYENV_CACHE=~/.pyenv.cache + type pyenv > /dev/null 2>&1 + ci_err $? "pyenv not found" + local _py_ver=$(ci_get_py_ver "$1") + local _py_env=$(ci_get_py_env "${_py_ver}") + local _nocache + case "${_py_env}" in + py37) _nocache=1 ;; + esac + [ -z "${PYENV_ROOT}" ] && PYENV_ROOT="$HOME/.pyenv" + local _py_ver_dir="${PYENV_ROOT}/versions/${_py_ver}" + local _py_ver_cached_dir="${CI_PYENV_CACHE}/${_py_ver}" + if [ -z "${_nocache}" ]; then + if [ ! -d "${_py_ver_dir}" ]; then + if [ -d "${_py_ver_cached_dir}" ]; then + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] using cached pyenv ${_py_ver}" + ln -s "${_py_ver_cached_dir}" "${_py_ver_dir}" + fi + fi + fi + if [ ! -d "${_py_ver_dir}" ]; then + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] pyenv install ${_py_ver}" + pyenv install -s "${_py_ver}" + ci_err $? "pyenv failed to install ${_py_ver}" + if [ -z "${_nocache}" ]; then + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] pyenv cache ${_py_ver}" + rm -rf -- "${_py_ver_cached_dir}" + mkdir -p -- "${CI_PYENV_CACHE}" + mv "${_py_ver_dir}" "${_py_ver_cached_dir}" + ln -s "${_py_ver_cached_dir}" "${_py_ver_dir}" + fi + fi + pyenv rehash +} + +ci_pyenv_use() { + type pyenv > /dev/null 2>&1 + ci_err $? "pyenv not found" + local _py_ver=$(ci_get_py_ver "$1") + pyenv shell "${_py_ver}" + ci_err $? "pyenv could not use ${_py_ver}" + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] using python version: $(python -V 2>&1)" +} + +ci_pip_setup() { + local _py_ver=$(ci_get_py_ver "$1") + local _py_env=$(ci_get_py_env "${_py_ver}") + PIPOPT=$(python -c 'import sys; print("" if hasattr(sys, "real_prefix") else "--user")') + if [ -z "${_py_env##py2*}" ]; then + curl -O https://bootstrap.pypa.io/get-pip.py + python get-pip.py ${PIPOPT} + ci_err $? "failed to install pip" + fi + if [ X"${_py_env}" == X"py26" ]; then + python -c 'import pip; pip.main();' install ${PIPOPT} -U pip virtualenv + else + python -m pip install ${PIPOPT} -U pip virtualenv + fi +} + +ci_venv_setup() { + local _py_ver=$(ci_get_py_ver "$1") + local _py_env=$(ci_get_py_env "${_py_ver}") + local VENV_DIR=~/.venv/${_py_ver} + mkdir -p -- ~/.venv + rm -rf -- "${VENV_DIR}" + if [ X"${_py_env}" == X"py26" ]; then + python -c 'import virtualenv; virtualenv.main();' "${VENV_DIR}" + else + python -m virtualenv "${VENV_DIR}" + fi +} + +ci_venv_use() { + local _py_ver=$(ci_get_py_ver "$1") + local _py_env=$(ci_get_py_env "${_py_ver}") + local VENV_DIR=~/.venv/${_py_ver} + . "${VENV_DIR}/bin/activate" + [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] virtualenv python path: $(which python)" +} + +ci_run_wrapped() { + local _versions=$(echo "${PY_VER}" | sed -e 's/,/ /g') + [ -z "${_versions}" ] && eval "$1" + for _i in ${_versions}; do + local _v=$(echo "$_i" | cut -d '/' -f 1) + local _o=$(echo "$_i" | cut -d '/' -sf 2) + [ -z "${_o}" ] && _o="${PY_ORIGIN}" + eval "$1" "${_v}" "${_o}" + done +} + +ci_step_before_install_wrapped() { + local _py_ver="$1" + local _py_ori="$2" + case "${_py_ori}" in + pyenv) + if [ "${CI_PYENV_SETUP}" -eq 0 ]; then + ci_pyenv_setup + CI_PYENV_SETUP=1 + fi + ci_pyenv_install "${_py_ver}" + ci_pyenv_use "${_py_ver}" + ;; + esac + ci_pip_setup "${_py_ver}" + ci_venv_setup "${_py_ver}" +} + +ci_step_before_install() { + if ci_is_osx; then + [ ${CI_VERBOSE} -gt 0 ] && sw_vers + brew update || brew update + brew install autoconf pkg-config openssl readline xz + brew upgrade autoconf pkg-config openssl readline xz + PY_ORIGIN=pyenv + fi + CI_PYENV_SETUP=0 + ci_run_wrapped "ci_step_before_install_wrapped" + if [ "${CI_PYENV_SETUP}" -eq 1 ]; then + pyenv shell --unset + [ ${CI_VERBOSE} -gt 0 ] && pyenv versions + fi +} + +ci_step_install_wrapped() { + local _py_ver="$1" + ci_venv_use "${_py_ver}" + pip install -U tox coveralls codecov +} + +ci_step_script_wrapped() { + local _py_ver="$1" + local _py_ori="$2" + local _py_env=$(ci_get_py_env "${_py_ver}") + ci_venv_use "${_py_ver}" + if [ -z "${_py_env##*py3*}" ]; then + if [ -z "${_py_env##*pypy3*}" ]; then + # NOTE: workaround for travis environment + _pydir=$(dirname $(which python)) + ln -s -- "${_pydir}/python" "${_pydir}/pypy3" + # NOTE: do not lint, as it hangs when flake8 is run + # NOTE: do not type, as it can't install dependencies + TOXENV=${_py_env}-test + else + TOXENV=${_py_env}-test,${_py_env}-type,${_py_env}-lint + fi + else + # NOTE: do not type, as it isn't supported on py2x + TOXENV=${_py_env}-test,${_py_env}-lint + fi + tox -e $TOXENV,cov +} + +ci_step_success_wrapped() { + local _py_ver="$1" + local _py_ori="$2" + ci_venv_use "${_py_ver}" + coveralls + codecov +} + +ci_step_failure() { + cat .tox/log/* + cat .tox/*/log/* +} + +ci_step_install() { ci_run_wrapped "ci_step_install_wrapped"; } +ci_step_script() { ci_run_wrapped "ci_step_script_wrapped"; } +ci_step_success() { ci_run_wrapped "ci_step_success_wrapped"; }