#!/bin/sh CI_VERBOSE=1 ci_err_msg() { echo "[ci] error: $1" >&2; } ci_err() { [ $1 -ne 0 ] && ci_err_msg "$2" && exit 1; } ci_is_osx() { [ X"$(uname -s)" == X"Darwin" ]; } ci_get_pypy_ver() { local _v="$1" [ -z "$_v" ] && _v=$(python -V 2>&1) case "$_v" in pypy-*|pypy2-*|pypy3-*|pypy3.*) echo "$_v"; return 0 ;; pypy|pypy2|pypy3) echo "$_v-unknown"; return 0 ;; esac echo "$_v" | tail -1 | grep -qi pypy if [ $? -eq 0 ]; then local _py_ver=$(echo "$_v" | head -1 | cut -d ' ' -sf 2) local _pypy_ver=$(echo "$_v" | tail -1 | cut -d ' ' -sf 2) [ -z "${_py_ver} " ] && _py_ver=2 [ -z "${_pypy_ver}" ] && _pypy_ver="unknown" case "${_py_ver}" in 2*) echo "pypy-${_pypy_ver}" ;; 3.3*) echo "pypy3.3-${_pypy_ver}" ;; 3.5*) echo "pypy3.5-${_pypy_ver}" ;; *) echo "pypy3-${_pypy_ver}" ;; esac return 0 else return 1 fi } 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" ] && set -- "$(python -V 2>&1)" _v=$(ci_get_pypy_ver "$1") [ -z "$_v" ] && _v=$(echo "$_v" | head -1 | cut -d ' ' -sf 2) ;; esac echo "${_v}" return 0 } ci_get_py_env() { [ -z "$1" ] && set -- "$(python -V 2>&1)" case "$(ci_get_pypy_ver "$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 "py${_v}" esac return 0 } ci_pyenv_setup() { [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] install pyenv" 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_err $? "failed to init pyenv" [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] pyenv init: $(pyenv -v 2>&1)" return 0 } 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 [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] pyenv install: ${_py_env}/${_py_ver}" [ -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] pyenv reuse ${_py_ver}" ln -s "${_py_ver_cached_dir}" "${_py_ver_dir}" fi fi fi if [ ! -d "${_py_ver_dir}" ]; then 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 return 0 } 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] pyenv using python: $(python -V 2>&1)" return 0 } ci_pip_setup() { local _py_ver=$(ci_get_py_ver "$1") local _py_env=$(ci_get_py_env "${_py_ver}") [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] install pip/venv for ${_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_err $? "failed to upgrade pip/venv" || return 0 } ci_venv_setup() { local _py_ver=$(ci_get_py_ver "$1") local _py_env=$(ci_get_py_env "${_py_ver}") [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] create venv for ${_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_err $? "failed to create venv" || return 0 } 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_err $? "could not actiavte virtualenv" [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] venv using python: $(python -V 2>&1)" return 0 } ci_get_filedir() { local _sdir=$(cd -- "$(dirname "$0")" && pwd) local _pdir=$(pwd) if [ -z "${_pdir##${_sdir}*}" ]; then _sdir="${_pdir}" fi local _first=1 while [ X"${_sdir}" != X"/" ]; do if [ ${_first} -eq 1 ]; then _first=0 local _f=$(find "${_sdir}" -name "$1" | head -1) if [ -n "${_f}" ]; then echo $(dirname -- "${_f}") return 0 fi else _f=$(find "${_sdir}" -mindepth 1 -maxdepth 1 -name "$1" | head -1) fi [ -n "${_f}" ] && echo "${_sdir}" && return 0 _sdir=$(cd -- "${_sdir}/.." && pwd) done return 1 } ci_sq_ensure_java() { type java >/dev/null 2>&1 if [ $? -ne 0 ]; then ci_err_msg "java not found" return 1 fi local _java_ver=$(java -version 2>&1 | head -1 | sed -e 's/[^0-9\._]//g') if [ -z "${_java_ver##1.8*}" ]; then return 0 fi ci_err_msg "unsupported java version: ${_java_ver}" return 1 } ci_sq_ensure_scanner() { local _cli_version="3.0.0.702" local _cli_basedir="$HOME/.bin" local _cli_postfix="" case "$(uname -s)" in Linux) [ X"$(uname -m)" = X"x86_64" ] && _cli_postfix="-linux" [ X"$(uname -m)" = X"amd64" ] && _cli_postfix="-linux" ;; Darwin) _cli_postfix="-macosx" ;; esac if [ X"${_cli_postfix}" = X"" ]; then ci_sq_ensure_java || return 1 fi if [ X"${SONAR_SCANNER_PATH}" != X"" ]; then if [ -e "${SONAR_SCANNER_PATH}" ]; then return 0 fi fi local _cli_fname="sonar-scanner-cli-${_cli_version}${_cli_postfix}" [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] ensure scanner ${_cli_fname}" local _cli_dname="sonar-scanner-${_cli_version}${_cli_postfix}" local _cli_archive="${_cli_basedir}/${_cli_fname}.zip" local _cli_dir="${_cli_basedir}/${_cli_dname}" local _cli_url="https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/${_cli_fname}.zip" if [ ! -e "${_cli_archive}" ]; then mkdir -p -- "${_cli_basedir}" > /dev/null 2>&1 if [ $? -ne 0 ]; then ci_err_msg "could not create ${_cli_basedir}" return 1 fi [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] downloading ${_cli_fname}" curl -kL -o "${_cli_archive}" "${_cli_url}" [ $? -ne 0 ] && ci_err_msg "download failed" && return 1 [ ! -e "${_cli_archive}" ] && ci_err_msg "download verify" && return 1 fi if [ ! -d "${_cli_dir}" ]; then [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] extracting ${_cli_fname}" unzip -od "${_cli_basedir}" "${_cli_archive}" [ $? -ne 0 ] && ci_err_msg "extract failed" && return 1 [ ! -d "${_cli_dir}" ] && ci_err_msg "extract verify" && return 1 fi if [ ! -e "${_cli_dir}/bin/sonar-scanner" ]; then ci_err_msg "sonar-scanner binary not found." return 1 fi SONAR_SCANNER_PATH="${_cli_dir}/bin/sonar-scanner" return 0 } ci_sq_run() { if [ X"${SONAR_SCANNER_PATH}" = X"" ]; then ci_err_msg "environment variable SONAR_SCANNER_PATH not set" return 1 fi if [ X"${SONAR_HOST_URL}" = X"" ]; then ci_err_msg "environment variable SONAR_HOST_URL not set" return 1 fi if [ X"${SONAR_AUTH_TOKEN}" = X"" ]; then ci_err_msg "environment variable SONAR_AUTH_TOKEN not set" return 1 fi local _pdir=$(ci_get_filedir "ssh-audit.py") if [ -z "${_pdir}" ]; then ci_err_msg "failed to find project directory" return 1 fi local _odir=$(pwd) cd -- "${_pdir}" local _branch=$(git name-rev --name-only HEAD | cut -d '~' -f 1) case "${_branch}" in master) ;; develop) ;; *) ci_err_msg "unknown branch: ${_branch}"; return 1 ;; esac local _junit=$(cd -- "${_pdir}" && ls -1 reports/junit.*.xml | sort -r | head -1) if [ X"${_junit}" = X"" ]; then ci_err_msg "no junit.xml found" return 1 fi local _project_ver=$(grep VERSION ssh-audit.py | head -1 | cut -d "'" -f 2) if [ -z "${_project_ver}" ]; then ci_err_msg "failed to get project version" return 1 fi if [ -z "${_project_ver##*dev}" ]; then local _git_commit=$(git rev-parse --short=8 HEAD) _project_ver="${_project_ver}.${_git_commit}" fi [ ${CI_VERBOSE} -gt 0 ] && echo "[ci] run sonar-scanner for ${_project_ver}" "${SONAR_SCANNER_PATH}" -X \ -Dsonar.projectKey=arthepsy-github:ssh-audit \ -Dsonar.sources=ssh-audit.py \ -Dsonar.tests=test \ -Dsonar.test.inclusions=test/*.py \ -Dsonar.host.url="${SONAR_HOST_URL}" \ -Dsonar.projectName=ssh-audit \ -Dsonar.projectVersion="${_project_ver}" \ -Dsonar.branch="${_branch}" \ -Dsonar.python.coverage.overallReportPath=reports/coverage.xml \ -Dsonar.python.xunit.reportPath="${_junit}" \ -Dsonar.organization=arthepsy-github \ -Dsonar.login="${SONAR_AUTH_TOKEN}" cd -- "${_odir}" return 0 } 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}" || return 1 done return 0 } 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}" || return 1 ci_pyenv_use "${_py_ver}" || return 1 ;; esac ci_pip_setup "${_py_ver}" || return 1 ci_venv_setup "${_py_ver}" || return 1 return 0 } 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" || return 1 if [ "${CI_PYENV_SETUP}" -eq 1 ]; then pyenv shell --unset [ ${CI_VERBOSE} -gt 0 ] && pyenv versions fi return 0 } ci_step_install_wrapped() { local _py_ver="$1" ci_venv_use "${_py_ver}" pip install -U tox coveralls codecov ci_err $? "failed to install dependencies" || return 0 } 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}" || return 1 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_err $? "tox failed" || return 0 } ci_step_success_wrapped() { local _py_ver="$1" local _py_ori="$2" if [ X"${SQ}" = X"1" ]; then ci_sq_ensure_scanner && ci_sq_run fi ci_venv_use "${_py_ver}" || return 1 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"; }