#!/bin/sh # Syntax: # - ci-test-extensions from-commits "" # Automatically detect the extensions to be tested by inspecting the git commits # - ci-test-extensions from-list "" # Test the specified extensions # - ci-test-extensions all # Test all the supported extensions # # Global environment variables used: # - IPETEST_DOCKER_DISTRO (required) the handle of the docker distibution to be used (eg 'buster', 'alpine3.14') # - IPETEST_ONLY_PHPVERSIONS (optional) a space-separeted list of PHP versions: if set, we'll test only those PHP versions (eg '8.0 8.1') # Let's set a sane environment set -o errexit set -o nounset # Check if we should use a specific PHP version for a specific distribution # # Arguments: # $1: the distribution # $2: the PHP version shouldProcessPhpVersionForDistro() { case "$2@$1" in 8.1@alpine3.12) return 1 ;; *) return 0 ;; esac } # Extract the extensions to be tested from commit messages # # Update: EXTENSIONS_TO_BE_TESTED # Set: STOP_EXTENSIONS_FOUND extractExtensionsFromCommits() { STOP_EXTENSIONS_FOUND=0 IFS=' ' for extractExtensionsFromCommits_hash in $(git -C "$CI_BUILD_DIR" log --pretty='format:%H' "$CI_COMMIT_RANGE"); do extractExtensionsFromCommits_firstLine=1 extractExtensionsFromCommits_message="$(git -C "$CI_BUILD_DIR" log --pretty='format:%B' -n 1 "$extractExtensionsFromCommits_hash")" IFS=' ' for extractExtensionsFromCommits_messageLine in $extractExtensionsFromCommits_message; do if test $extractExtensionsFromCommits_firstLine -eq 1; then extractExtensionsFromCommits_firstLine=0 else extractExtensionsFromCommits_testList= case "$extractExtensionsFromCommits_messageLine" in Test:*) extractExtensionsFromCommits_testList=${extractExtensionsFromCommits_messageLine#Test:} ;; TEST:*) extractExtensionsFromCommits_testList=${extractExtensionsFromCommits_messageLine#TEST:} ;; test:*) extractExtensionsFromCommits_testList=${extractExtensionsFromCommits_messageLine#test:} ;; esac if test -n "$extractExtensionsFromCommits_testList"; then IFS=' ,;' for extractExtensionsFromCommits_extension in $extractExtensionsFromCommits_testList; do if test $extractExtensionsFromCommits_extension = '-STOP-'; then STOP_EXTENSIONS_FOUND=1 break 2 fi if ! stringInList "$extractExtensionsFromCommits_extension" "$EXTENSIONS_TO_BE_TESTED"; then EXTENSIONS_TO_BE_TESTED="$EXTENSIONS_TO_BE_TESTED $extractExtensionsFromCommits_extension" fi done fi fi done done EXTENSIONS_TO_BE_TESTED="${EXTENSIONS_TO_BE_TESTED# }" } # Extract the extensions to be tested from changes in the data/supported-extensions file # # Update: EXTENSIONS_TO_BE_TESTED extractExtensionsFromData() { IFS=' ' extractExtensionsFromData_foundAt= for extractExtensionsFromData_line in $(git -C "$CI_BUILD_DIR" diff --no-indent-heuristic --minimal --no-color --word-diff=none -no-renames --unified=0 "$CI_COMMIT_RANGE" -- data/supported-extensions); do if test -n "$extractExtensionsFromData_line"; then if test -z "$extractExtensionsFromData_foundAt"; then if test -z "${extractExtensionsFromData_line##@@*}"; then extractExtensionsFromData_foundAt=y fi elif test -z "${extractExtensionsFromData_line##+*}"; then extractExtensionsFromData_extension="${extractExtensionsFromData_line%% *}" extractExtensionsFromData_extension="${extractExtensionsFromData_extension#+}" if ! stringInList "$extractExtensionsFromData_extension" "$EXTENSIONS_TO_BE_TESTED"; then EXTENSIONS_TO_BE_TESTED="$EXTENSIONS_TO_BE_TESTED $extractExtensionsFromData_extension" fi fi fi done EXTENSIONS_TO_BE_TESTED="${EXTENSIONS_TO_BE_TESTED# }" } # Remove from the EXTENSIONS_TO_BE_TESTED variable the extensions that are # not supported in the distro specified in the data/special-requirements file # Update: EXTENSIONS_TO_BE_TESTED filterUnsupportedExensionsForDistro() { if test -z "$EXTENSIONS_TO_BE_TESTED"; then return fi filterUnsupportedExensionsForDistro_reqs="$CI_BUILD_DIR/data/special-requirements" if ! test -f "$filterUnsupportedExensionsForDistro_reqs"; then return fi filterUnsupportedExensionsForDistro_filtered='' IFS=' ' case "$IPETEST_DOCKER_DISTRO" in alpine*) filterUnsupportedExensionsForDistro_baseDistro="alpine" ;; *) filterUnsupportedExensionsForDistro_baseDistro="debian" ;; esac for filterUnsupportedExensionsForDistro_extension in $EXTENSIONS_TO_BE_TESTED; do if stringInList "!$IPETEST_DOCKER_DISTRO" "$(cat "$filterUnsupportedExensionsForDistro_reqs" | grep -E "^$filterUnsupportedExensionsForDistro_extension[ \t]")"; then printf 'Note: extension "%s" is not supported for distro "%s"\n' "$filterUnsupportedExensionsForDistro_extension" "$IPETEST_DOCKER_DISTRO" elif stringInList "!$filterUnsupportedExensionsForDistro_baseDistro" "$(cat "$filterUnsupportedExensionsForDistro_reqs" | grep -E "^$filterUnsupportedExensionsForDistro_extension[ \t]")"; then printf 'Note: extension "%s" is not supported for distro "%s"\n' "$filterUnsupportedExensionsForDistro_extension" "$filterUnsupportedExensionsForDistro_baseDistro" else filterUnsupportedExensionsForDistro_filtered="$filterUnsupportedExensionsForDistro_filtered $filterUnsupportedExensionsForDistro_extension" fi done resetIFS EXTENSIONS_TO_BE_TESTED="${filterUnsupportedExensionsForDistro_filtered# }" } # Get the docker image ID for a PHP extension and a PHP version # # Arguments: # $1: space-separated list with the names of the PHP extensions # $2: the PHP version # # Outputs: # the full docker image ID (if exists/is usable) getDockerImageName() { if ! shouldProcessPhpVersionForDistro "$IPETEST_DOCKER_DISTRO" "$2"; then return fi case "$2" in 8.3) getDockerImageName_version="$2-rc" ;; *) getDockerImageName_version="$2" ;; esac getDockerImageName_suffix='cli' getDockerImageName_reqs="$CI_BUILD_DIR/data/special-requirements" if test -f "$getDockerImageName_reqs"; then IFS=' ' for getDockerImageName_testExtension in $1; do if test -n "$(cat "$getDockerImageName_reqs" | grep -E "^$getDockerImageName_testExtension[ \t]+zts[ \t]*$")"; then getDockerImageName_suffix='zts' fi done fi getDockerImageName_imageName="$(printf 'php:%s-%s-%s' "$getDockerImageName_version" "$getDockerImageName_suffix" "$IPETEST_DOCKER_DISTRO")" case "$getDockerImageName_imageName" in php:5.5-cli-jessie) getDockerImageName_imageName='php:5.5-cli' ;; php:5.5-zts-jessie) getDockerImageName_imageName='php:5.5-zts' ;; esac if test -z "$(docker images -q "$getDockerImageName_imageName" 2>/dev/null)"; then getDockerImageName_log="$(docker pull "$getDockerImageName_imageName" 2>&1 || true)" if test -z "$(docker images -q "$getDockerImageName_imageName" 2>/dev/null)"; then if test "${getDockerImageName_log#*manifest unknown}" != "$getDockerImageName_log" || test "${getDockerImageName_log#*manifest for * not found}" != "$getDockerImageName_log"; then return fi printf '%s\n' "$getDockerImageName_log" exit 1 fi fi printf '%s' "$getDockerImageName_imageName" } # Get the list of all supported PHP versions # # Arguments: # $1: space-separated list with the names of the PHP extensions to be tested # # Outputs: # the space-separated list of supported PHP versions getAllPHPVersionsForExtensions() { if test -n "${PHP_VERSION_TO_TEST:-}"; then echo "$PHP_VERSION_TO_TEST" return fi getAllPHPVersionsForExtensions_result='' IFS=' ' for getAllPHPVersionsForExtensions_extension in $1; do getAllPHPVersionsForExtensions_this="$(getAllPHPVersionsForExtension "$getAllPHPVersionsForExtensions_extension")" if test -z "$getAllPHPVersionsForExtensions_this"; then return fi if test -z "$getAllPHPVersionsForExtensions_result"; then getAllPHPVersionsForExtensions_result="$getAllPHPVersionsForExtensions_this" else getAllPHPVersionsForExtensions_tmp='' for getAllPHPVersionsForExtensions_php1 in $getAllPHPVersionsForExtensions_this; do if stringInList "$getAllPHPVersionsForExtensions_php1" "$getAllPHPVersionsForExtensions_result"; then getAllPHPVersionsForExtensions_tmp="$getAllPHPVersionsForExtensions_tmp $getAllPHPVersionsForExtensions_php1" fi done getAllPHPVersionsForExtensions_result="${getAllPHPVersionsForExtensions_tmp# }" fi done printf '%s' "$getAllPHPVersionsForExtensions_result" } # Get the list of all supported PHP versions # # Arguments: # $1: the names of a PHP extension to be tested # # Outputs: # the space-separated list of supported PHP versions getAllPHPVersionsForExtension() { getAllPHPVersionsForExtension_result='' while IFS= read -r getAllPHPVersionsForExtension_line; do getAllPHPVersionsForExtension_ok= IFS=' ' for getAllPHPVersionsForExtension_chunk in $getAllPHPVersionsForExtension_line; do if test -z "$getAllPHPVersionsForExtension_ok"; then if test "$getAllPHPVersionsForExtension_chunk" = "$1"; then getAllPHPVersionsForExtension_ok=y else getAllPHPVersionsForExtension_ok=n fi else if test $getAllPHPVersionsForExtension_ok = 'y'; then if test -z "$getAllPHPVersionsForExtension_result"; then getAllPHPVersionsForExtension_result="$getAllPHPVersionsForExtension_chunk" else if ! stringInList "$getAllPHPVersionsForExtension_chunk" "$getAllPHPVersionsForExtension_result"; then getAllPHPVersionsForExtension_result="$getAllPHPVersionsForExtension_result $getAllPHPVersionsForExtension_chunk" fi fi fi fi done done <"$CI_BUILD_DIR/data/supported-extensions" getAllPHPVersionsForExtension_reqs="$CI_BUILD_DIR/data/special-requirements" if test -f "$getAllPHPVersionsForExtension_reqs"; then getAllPHPVersionsForExtension_filtered_result='' for getAllPHPVersionsForExtension_result_filter in $getAllPHPVersionsForExtension_result; do if stringInList "!$getAllPHPVersionsForExtension_result_filter-$IPETEST_DOCKER_DISTRO" "$(cat "$getAllPHPVersionsForExtension_reqs" | grep -E "^$1[ \t]")"; then printf 'Note: extension "%s" is not supported for distro "%s" using php "%s"\n' "$1" "$IPETEST_DOCKER_DISTRO" "$getAllPHPVersionsForExtension_result_filter" >/dev/stderr else getAllPHPVersionsForExtension_filtered_result="$getAllPHPVersionsForExtension_filtered_result $getAllPHPVersionsForExtension_result_filter" fi done else getAllPHPVersionsForExtension_filtered_result="$getAllPHPVersionsForExtension_result" fi printf '%s' "${getAllPHPVersionsForExtension_filtered_result# }" } # Test extensions # # Arguments: # $1: space-separated list with the names of the PHP extensions to be tested # # Return: # 0 (true): if test passes # 1 (false): if test fails testExtension() { testExtensionsFromMessage_result=0 IFS=' ' for testExtension_extension in $1; do testExtension_extension="$(printf '%s' "$testExtension_extension" | sed -E 's/\+/ /g')" printf '### TESTING EXTENSION(S) %s ###\n' "$testExtension_extension" for testExtension_phpVersion in $(getAllPHPVersionsForExtensions "$testExtension_extension"); do if test -z "${IPETEST_ONLY_PHPVERSIONS:-}" || stringInList "$testExtension_phpVersion" "$IPETEST_ONLY_PHPVERSIONS"; then if ! testExtensionFor "$testExtension_extension" "$testExtension_phpVersion"; then testExtensionsFromMessage_result=1 fi fi done done resetIFS return $testExtensionsFromMessage_result } # Test extensions with specific PHP versions # # Arguments: # $1: space-separated list with the names of the PHP extensions to be tested # $2: the PHP version # # Return: # 0 (true): if test passes # 1 (false): if test fails testExtensionFor() { printf 'PHP version: %s\n' "$2" if test -n "$(printf '%s' "$2" | sed -E 's/^[0-9]+\.[0-9]+$//')"; then printf ' INVALID PHP VERSION: %s\n' "$2" return 1 fi testExtensionFor_Image="$(getDockerImageName "$1" "$2")" if test $? -ne 0; then exit 1 fi if test -z "$testExtensionFor_Image"; then printf ' - Docker image not available\n' return 0 fi printf ' - Docker image: %s\n' "$testExtensionFor_Image" testExtensionFor_out="$(mktemp)" testExtensionFor_start=$(date +%s) if $(docker run --rm --volume "$CI_BUILD_DIR:/app" --env CI=true --env IPE_FIX_CACERTS=1 --env IPE_ASPELL_LANGUAGES='en fr' --workdir /app "$testExtensionFor_Image" sh -c "./install-php-extensions $1 && php ./scripts/check-installed-extension.php $1" >"$testExtensionFor_out" 2>&1); then testExtensionFor_end=$(date +%s) testExtensionFor_delta=$(expr $testExtensionFor_end - $testExtensionFor_start) rm -rf "$testExtensionFor_out" printf ' - Passed in %s seconds\n' $testExtensionFor_delta IPE_SUMMARY_GOOD="$(printf '%s- %s (%s)\n ' "${IPE_SUMMARY_GOOD% }" "$1" "$testExtensionFor_Image")" return 0 fi printf '\n\n###############\n## ##\n## FAILED! ##\n## ##\n###############\n' echo '::group::Error details' cat "$testExtensionFor_out" echo '::endgroup::' echo '' rm -rf "$testExtensionFor_out" IPE_SUMMARY_BAD="$(printf '%s- %s (%s)\n ' "${IPE_SUMMARY_BAD% }" "$1" "$testExtensionFor_Image")" return 1 } echo 'Checking environment' if test -z "${GITHUB_WORKSPACE:-}"; then echo 'Not in a CI environment' exit 1 fi CI_BUILD_DIR="$GITHUB_WORKSPACE" if test -z "${IPETEST_DOCKER_DISTRO:-}"; then echo 'IPETEST_DOCKER_DISTRO environment variable not set' exit 1 fi . "$CI_BUILD_DIR/scripts/common" case "${1:-}" in from-commits) if test -z "${2:-}"; then echo 'Missing commit range of the push event' exit 1 fi CI_COMMIT_RANGE="$2" STOP_EXTENSIONS_FOUND=0 EXTENSIONS_TO_BE_TESTED='' extractExtensionsFromCommits if test $STOP_EXTENSIONS_FOUND -eq 0; then extractExtensionsFromData fi ;; from-list) EXTENSIONS_TO_BE_TESTED="${2:-}" ;; all) EXTENSIONS_TO_BE_TESTED="$(cat "$CI_BUILD_DIR/data/supported-extensions" | cut -d' ' -f1 | tr '\n' ' ')" ;; *) if test -z "${1:-}"; then printf 'Missing source of extensions to be tested\n' else printf '"%s" is an unknown source of extensions to be tested\n' "$1" fi exit 1 ;; esac filterUnsupportedExensionsForDistro if test -z "$EXTENSIONS_TO_BE_TESTED"; then echo 'No extensions to be tested.' exit 0 fi printf '### EXTENSIONS TO BE TESTED: %s\n' "$EXTENSIONS_TO_BE_TESTED" SOME_TEST_FAILED=0 IFS=' ' IPE_SUMMARY_GOOD='' IPE_SUMMARY_BAD='' for EXTENSION_TO_BE_TESTED in "$EXTENSIONS_TO_BE_TESTED"; do testExtension "$EXTENSION_TO_BE_TESTED" || SOME_TEST_FAILED=1 done printf '\n### SUMMARY\n' if test -z "$IPE_SUMMARY_GOOD"; then printf 'Passed extensions:\n(none)\n' else printf 'Passed extensions:\n%s' "${IPE_SUMMARY_GOOD% }" fi if test -z "$IPE_SUMMARY_BAD"; then printf 'Failed extensions:\n(none)\n' else printf 'Failed extensions:\n%s' "${IPE_SUMMARY_BAD% }" fi if test $SOME_TEST_FAILED -ne 0; then exit 1 fi