From 00a0a387942ebe57faea6423395d936857f4c6d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20KUBLER?= Date: Tue, 25 Nov 2025 16:26:22 +0100 Subject: [PATCH 1/5] chore(ext): update code handling extensions --- bin/compile | 67 +++++--- lib/composer | 362 ++++++++++++++++++++++++++++++-------------- lib/extensions/oci8 | 134 ++++++++++++++++ 3 files changed, 434 insertions(+), 129 deletions(-) create mode 100644 lib/extensions/oci8 diff --git a/bin/compile b/bin/compile index 86da8c52..63b6c39f 100755 --- a/bin/compile +++ b/bin/compile @@ -1,11 +1,26 @@ #!/bin/bash -set -e +export BUILDPACK_DEBUG="true" +export DEBUG="yes" -set -o pipefail +SCALINGO_CMNLIB_VERSION="${SCALINGO_CMNLIB_VERSION:-"1.0.0"}" +# cmnlib_url="https://github.com/Scalingo/buildpack-cmnlib/raw/refs/tags/v${SCALINGO_CMNLIB_VERSION}/cmnlib.sh" +cmnlib_url="https://raw.githubusercontent.com/Scalingo/buildpack-cmnlib/refs/heads/main/cmnlib.sh" + +if ! declare -F cmn::output::info >/dev/null; then + source /dev/stdin <<< \ + "$( curl --silent --location --retry 3 "${cmnlib_url}" \ + || printf "echo ' 🗙 Unable to load cmnlib, aborting.' >&2 && exit 1"; )" +fi + + +cmn::main::start "${0}" "${1}" "${2}" "${3}" + +# is this required? What for? shopt -s dotglob -basedir="$( cd -P "$( dirname "$0" )" && pwd )" +basedir="${base_dir}" + source "$basedir/../conf/versions.sh" source $basedir/common.sh source $basedir/../lib/package @@ -21,10 +36,6 @@ if [ "$PHP_BUILDPACK_NO_NODE" != "true" ] ; then source $basedir/../lib/nodejs fi -if [ -n "$BUILDPACK_DEBUG" ]; then - set -x -fi - BUILD_DIR="$1" CACHE_DIR="$2" ENV_DIR="$3" @@ -401,21 +412,26 @@ source ".profile.d/php.sh" fetch_package "$PHP_BASE_URL" "libonig-${libonig_version}" /app/vendor/libonig > /dev/null export LD_LIBRARY_PATH="/app/vendor/libonig:$LD_LIBRARY_PATH" + +## REMOVEME OR ONLY_FOR_PHP8.4 AND BELOW? + # Fetch additional extensions -status "Bundling platform default extensions" -echo " apcu" -fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-apcu" "/app/vendor/php" | indent +#status "Bundling platform default extensions" +#echo " apcu" +#fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-apcu" "/app/vendor/php" | indent -echo " phpredis" -fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-redis" "/app/vendor/php" | indent +#echo " phpredis" +#fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-redis" "/app/vendor/php" | indent -echo " mongodb" -fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-mongodb" "/app/vendor/php" | indent +#echo " mongodb" +#fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-mongodb" "/app/vendor/php" | indent -if [ "$(php_api_version)" = "20170718" -o "$(php_api_version)" = "20180731" ] ; then - echo " mcrypt" - fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-mcrypt" "/app/vendor/php" | indent -fi +#if [ "$(php_api_version)" = "20170718" -o "$(php_api_version)" = "20180731" ] ; then +# echo " mcrypt" +# fetch_package "$PHP_BASE_URL" "ext/$(php_api_version)/php-mcrypt" "/app/vendor/php" | indent +#fi + +## ENDREMOVEME if [ -f "$BUILD_DIR/composer.json" ] && package_datadog_enabled; then install_datadog "${DATADOG_TRACER_VERSION}" "${DATADOG_APPSEC_VERSION}" @@ -483,7 +499,18 @@ if [ "$PHP_BUILDPACK_NO_NODE" != "true" ] ; then install_node_deps "$BUILD_DIR" fi -install_composer_deps "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" +# install_composer_deps "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}" + +## Install dependencies using Composer: +if [ -f "${build_dir}/composer.json" ]; then + # Install Composer + php::composer::install "${build_dir}" "${cache_dir}" "${PHP_BASE_URL}" + + # Install dependencies using Composer + php::composer::install_dependencies \ + "${build_dir}" "${cache_dir}" "${PHP_BASE_URL}" +fi + # Detect PHP framework # Set FRAMEWORK if not set in environment by user @@ -631,3 +658,5 @@ exit 1 SH chmod +x "bin/run" + +cmn::main::finish diff --git a/lib/composer b/lib/composer index b3b9faf5..86d51da3 100644 --- a/lib/composer +++ b/lib/composer @@ -1,4 +1,5 @@ -# vim:set ft=sh: +#!/usr/bin/env bash +# function get_php_version() { # Prints out the PHP version (major.minor) @@ -52,104 +53,213 @@ function check_composer_json_and_lock() { fi } -function install_composer_deps() { - local cwd=$(pwd) - local target="$1" +php::extension::is_embedded() { - # If there is no composer.json, we install nothing - if [ ! -f "$target/composer.json" ] ; then - return 0 - fi - - export COMPOSER_CACHE_DIR=$CACHE_DIR/composer - mkdir -p $COMPOSER_CACHE_DIR - mkdir -p "$target/vendor/composer/bin" - - local checksum=$(curl --silent "${PHP_BASE_URL}/composer/composer-${COMPOSER_VERSION}.phar.md5") - - status "Vendoring Composer ${COMPOSER_VERSION}" - if [ ! -f "$CACHE_DIR/composer.phar.md5" ] || [ "$(cat $CACHE_DIR/composer.phar.md5)" != "$checksum" ]; then - echo "Updating Composer" | indent - curl --silent "${PHP_BASE_URL}/composer/composer-${COMPOSER_VERSION}.phar" > "$CACHE_DIR/composer.phar" | indent - chmod a+x "$CACHE_DIR/composer.phar" - echo "$checksum" > $CACHE_DIR/composer.phar.md5 - else - echo "Checksums match. Fetching Composer from cache." |indent - fi + local extension_name="${1}" - cp "$CACHE_DIR/composer.phar" "$target/vendor/composer/bin/" + local rc=1 + local result="false" + local php_modules - # Add composer to the path - cp "$target/vendor/composer/bin/composer.phar" "$target/bin/composer" - cp "$target/vendor/composer/bin/composer.phar" "$target/bin/composer.phar" + if php_modules="$( php --modules 2>&1 )"; then + rc=0 - local required_extensions=$(jq --raw-output '.require // {} | keys | .[]' < "$BUILD_DIR/composer.json" | grep '^ext-' | sed 's/^ext-//') - - if [ -n "$required_extensions" ]; then - status "Bundling additional extensions" - - for ext in $required_extensions; do - local ext_lower - local apt_deps="" - local is_embedded="" - local ext_version=$(jq --raw-output ".require | .[\"ext-${ext}\"]" < "${BUILD_DIR}/composer.json") - - # The Zend OPCache extension is a bit special: - # It's listed by `php --modules` as `Zend OPCache`, but we must - # refer to it as `zend-opcache` in `composer.json`. - # Consequently, we have to detect it and rename it for the - # buildpack logic to work. - - # First convert extension name to lowercase: - ext_lower="$( tr "[:upper:]" "[:lower:]" <<< "${ext}" )" - - # Then compare: - if [ "${ext_lower}" = "zend-opcache" ]; then - ext="zend opcache" - fi + if grep --quiet --extended-regexp --ignore-case "^${extension_name}$" \ + <<< "${php_modules}" + then + result="true" + fi + fi + echo "${result}" + exit "${rc}" +} - # The oci8 extension is only available for PHP 8.2 and above: - - if [ "$ext" = "oci8" ] ; then - local php_version="$( get_php_version )" - - if ! version_is_ge "${php_version}" "8.2"; then - echo "\ -! The oci8 PHP extension requires PHP 8.2 or above to run. -! Please make sure to specify this requirement in your composer.json file. -! For further help, please see https://doc.scalingo.com/languages/php/start#select-a-version -! Aborting. -" >&2 - exit 1 - fi - fi - - is_embedded="$( is_embedded_extension "${ext}" )" - rc=$? - [ ${rc} -ne 0 ] && error "error while trying to identify if ${ext} is embedded in runtime." - - if [ "${is_embedded}" = "true" ] ; then - echo "PHP extension ${ext} is embedded in runtime" | indent - continue - fi +php::composer::install() { + + local build_dir="${1}" + local cache_dir="${2}" + local php_url="${3}" + + # FIXME: provide a default version + local version="${COMPOSER_VERSION:-"${COMPOSER_DEFAULT_VERSION}"}" + + local archive="composer-${version}.phar" + + local file_url="${php_url}/composer/${archive}" + local hash_url="${file_url}.md5" + + local cache_file="${cache_dir:?}/${archive}" + local hash_file="${cache_file}.md5" + + local target_dir="${build_dir}/vendor/composer/bin" + + + cmn::step::start "Installing Composer ${version}" + + cmn::task::start "Downloading checksum" + # Remove previous hash file: + rm --recursive --force "${cache_dir:?}"/*.phar.md5 + + if ! cmn::file::download "${hash_url}" "${hash_file}"; then + cmn::task::fail + cmn::step::fail + exit 200 + fi + cmn::task::finish + + if [ ! -f "${cache_file}" ]; then + # Remove archive from cache, since they are probably for another version: + cmn::task::start "Cleaning cache" + rm --recursive --force "${cache_dir:?}"/*.phar + cmn::task::finish + + # Download appropriate version: + cmn::task::start "Downloading archive" + if ! cmn::file::download "${file_url}" "${cache_file}"; then + cmn::task::fail + cmn::step::fail + exit 201 + fi + cmn::task::finish + else + cmn::output::info "Found matching file in cache." + fi + + cmn::task::start "Checking archive validity" + if ! cmn::file::check_checksum "${cache_file}" "${hash_file}"; then + cmn::task::fail + cmn::output::err <<- EOM + Unable to check archive validity. + I'm now removing the file from the cache to force + a new download during the next deployment attempt. + EOM + + # File in cache does not seem valid, + # remove it so a future run can be successful + rm --force "${cache_file}" + rm --force "${hash_file}" + cmn::step::fail + exit 202 + fi + cmn::task::finish + + # From this point we have a valid file in cache. + + cmn::task::start "Vendoring Composer" + if ! mkdir --parents "${target_dir}" 2>/dev/null + then + cmn::task::fail + cmn::step::fail + exit 203 + fi + + if ! cp "${cache_file}" "${target_dir}/composer.phar" 2>/dev/null + then + cmn::task::fail + cmn::step::fail + cmn::output::debug <<- EOM + Failed to copy + from: ${cache_file} + to: ${target_dir} + EOM + exit 204 + fi + cmn::task::finish + + cmn::task::start "Setting permissions" + if ! chmod a+x "${target_dir}/composer.phar" 2>/dev/null + then + cmn::task::fail + cmn::step::fail + exit 205 + fi + cmn::task::finish + + cmn::task::start "Creating symlinks" + for link in "${target}/bin/composer" "${target}/bin/composer.phar" + do + if ln --symbolic --force --no-dereference \ + "${target_dir}/composer.phar" "${link}" 2>/dev/null + then + cmn::task::fail + cmn::step::fail + exit 206 + fi + done + cmn::task::finish + + cmn::step::finish +} - if [ "$ext" = "oci8" ] ; then - apt_deps="libaio-dev" - fi - - if [ -n "$apt_deps" ] ; then - echo "Installing dependencies for ${ext}: ${apt_deps}" | indent - install_env_file="$(apt_install "${apt_deps}" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}")" - # Source environment to export LD_LIBRARY_PATH - source "${install_env_file}" - rm "${install_env_file}" - fi +php::composer::install_dependency() { + local build_dir="${1}" + local cache_dir="${2}" + local ext_name="${3}" + + local ext + local version + + + # First convert extension name to lowercase: + ext="$( tr "[:upper:]" "[:lower:]" <<< "${ext_name}" )" + + cmn::task::start "Installing extension ${ext}" + if ! version="$( jq --raw-output ".require | .[\"ext-${ext}\"]" \ + < "${build_dir}/composer.json" \ + )" + then + cmn::task::fail + cmn::output::error <<- EOM + Unable to retrieve the desired version of extension ${ext}. + Is your composer.json file valid? + EOM + exit 320 + fi + + cmn::output::debug "Installing ${ext} ${version}" + + if ! is_embbeded="$( php::extension::is_embedded "${ext}" )"; then + cmn::task::fail + cmn::output::err <<- EOM + Unable to check whether ${ext} is already embedded or not. + Aborting. + EOM + exit 321 + fi + + if [[ "${is_embedded}" == "true" ]]; then + cmn::output::info <<- EOM + PHP extension ${ext} is already embedded in runtime. Skipping. + EOM + exit 0 + fi + + # Install pre-requisites, if any: + if [ -f "${buildpack_dir}/lib/extensions/${ext}" ]; then + source "${buildpack_dir}/lib/extensions/${ext}" + fi + + # The Zend OPCache extension is a bit special: + # It's listed by `php --modules` as `Zend OPCache`, but we must + # refer to it as `zend-opcache` in `composer.json`. + # Consequently, we have to detect it and rename it for the + # buildpack logic to work. + + if [ "${ext}" = "zend-opcache" ]; then + ext="zend opcache" + fi + + #if [ -n "$apt_deps" ] ; then + # echo "Installing dependencies for ${ext}: ${apt_deps}" | indent + # install_env_file="$(apt_install "${apt_deps}" "${BUILD_DIR}" "${CACHE_DIR}" "${ENV_DIR}")" + # Source environment to export LD_LIBRARY_PATH + # source "${install_env_file}" + # rm "${install_env_file}" + #fi # Install third-party dependencies after ubuntu packages have been added - if [ "$ext" = "oci8" ] ; then - install_oracle_client_extension "${BUILD_DIR}" "${CACHE_DIR}" - elif [ "$ext" = "memcached" ] ; then + if [ "$ext" = "memcached" ] ; then fetch_package "$PHP_BASE_URL" "libmemcached-${libmemcached_version}" "/app/vendor/libmemcached" | indent elif [ "$ext" = "gmp" ] ; then fetch_package "$PHP_BASE_URL" "gmp-${gmp_version}" "/app/vendor/gmp" | indent @@ -170,8 +280,58 @@ function install_composer_deps() { else install_pecl_extension "${ext}" "${ext_version}" "${CACHE_DIR}" fi - done - fi + + +} + +php::composer::install_dependencies() { + + local build_dir="${1}" + local cache_dir="${2}" + local php_base_url="${3}" + + local cwd="$( pwd )" + + COMPOSER_CACHE_DIR="${cache_dir}/composer" + export COMPOSER_CACHE_DIR + + mkdir --parents "${COMPOSER_CACHE_DIR}" + + + cmn::step::start "Installing dependencies" + + cmn::task::start "Checking dependencies" + local required_extensions + + if ! required_extensions="$( jq --raw-output '.require // {} | keys | .[]' \ + < "${build_dir}/composer.json" \ + | grep "^ext-" \ + | sed "s/^ext-//" \ + )" + then + cmn::task::fail + cmn::output::err <<- EOM + Unable to retrieve the list of required extensions. + Please make sur your composer.json file is well-formatted. + Aborting. + EOM + cmn::step::fail + exit 210 + fi + cmn::task::finish + cmn::step::finish + + if [ -n "${required_extensions}" ]; then + cmn::step::start "Bundling dependencies" + + # /!\ Not quoting required_extensions on purpose + for ext in ${required_extensions}; do + php::composer::install_dependency \ + "${build_dir}" "${cache_dir}" "${ext}" + done + fi + +### FIXME: if [ -n "$COMPOSER_GITHUB_TOKEN" ]; then status "Configuring the GitHub authentication for Composer" @@ -193,21 +353,3 @@ function install_composer_deps() { cd "$cwd" } | indent } - -function is_embedded_extension() { - local extension_name="${1}" - local php_modules - - php_modules=$(php --modules 2>&1) - rc=$? - # error function exits with a return code 1 - [ ${rc} -ne 0 ] && \ - error "error while trying to identify if ${extension_name} is embedded in runtime: ${php_modules} (${rc})." - - if echo "${php_modules}" | grep --quiet --extended-regexp --ignore-case "^${extension_name}$" ; then - echo "true" - else - echo "false" - fi - exit 0 -} diff --git a/lib/extensions/oci8 b/lib/extensions/oci8 new file mode 100644 index 00000000..9ec65be9 --- /dev/null +++ b/lib/extensions/oci8 @@ -0,0 +1,134 @@ +#!/usr/bin/env bash + +# +# OCI8 extension requires the following: +# - PHP 8.2 or above +# - libaio-dev must be installed +# +# OCI8 extensions exit codes for errors are [1010-1019] +# + +apt_deps=( "libaio-dev" ) +php_api_min="${PHP_MODULE_API_VERSIONS["8.2"]}" + + +# Check we have the minimum PHP version: + +php_version="$( php -v | head -n 1 | cut -d " " -f2 | cut -f 1-2 -d "." )" +php_api="${PHP_MODULE_API_VERSIONS["${php_version}"]}" + +if [ ${php_api} -lt ${php_api_min} ]; then + cmn::task::fail + cmn::output::err <<- EOM + The oci8 PHP extension requires PHP 8.2 or above to run. + Please make sure to specify this requirement in your composer.json file. + For further help, please see https://doc.scalingo.com/languages/php/start#select-a-version + Unable to fulfill the prerequisites, aborting. + EOM + exit 1010 +fi + +cmn::output::debug "PHP version is OK for oci8." + + +# Install apt dependencies: + +# We need a temporary "Aptfile" so we don't overwrite one that could be +# provided by the user. + +orig_aptfile="${APT_FILE_MANIFEST:-"Aptfile"}" +APT_FILE_MANIFEST="$( cat /dev/urandom | tr -cd 'a-f0-9' | head -c 32 )" +export APT_FILE_MANIFEST + +for d in ${apt_deps}; do + echo "${d}" >> "${build_dir}/${APT_FILE_MANIFEST}" +done + +if ! cmn::bp::run "https://github.com/Scalingo/apt-buildpack" \ + "${build_dir}" "${cache_dir}" "${env_dir}" +then + cmn::task::fail + cmn::output::err <<- EOM + An error occured while installing dependencies using the apt buildpack. + Unable to install prerequisites, aborting. + EOM + exit 1011 +fi + +# Once the packages have been installed, reset APT_FILE: +APT_FILE_MANIFEST="${orig_aptfile}" +export APT_FILE_MANIFEST + + +cmn::output::debug "Dependencies installed via apt." + + +# Install Oracle client: + +instantclient_basic_url="https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip" +instantclient_sdk_url="https://download.oracle.com/otn_software/linux/instantclient/instantclient-sdk-linuxx64.zip" +oracle_install_dir="${build_dir}/vendor/oracle-client/" + +mkdir --parents "${oracle_install_dir}" + +if ! cmn::file::download "${instantclient_basic_url}" \ + "${cache_dir}/oracleclient.zip" +then + cmn::task::fail + cmn::output::err <<- EOM + Unable to download Oracle InstantClient, aborting. + EOM + exit 1012 +fi + +if ! cmn::file::download "${instantclient_sdk_url}" \ + "${cache_dir}/oraclesdk.zip" +then + cmn::task::fail + cmn::output::err <<- EOM + Unable to download Oracle InstantClient SDK, aborting. + EOM + exit 1013 +fi + +# We are only interested in the "instantclient_..." directory: +# -o = overwrite existing files without prompting +# -d = destination directory +# -qq = very quiet output +if ! unzip -o -qq "${cache_dir}/oracleclient.zip" "instantclient*" \ + -d "${oracle_install_dir}" +then + cmn::task::fail + cmn::output::err <<- EOM + Unable to uncompress InstantClient archive, aborting. + EOM + exit 1014 +fi + +if ! unzip -o -qq "${cache_dir}/oraclesdk.zip" "instantclient*" \ + -d "${oracle_install_dir}" +then + cmn::task::fail + cmn::output::err <<- EOM + Unable to uncompress InstantClient SDK archive, aborting. + EOM + exit 1015 +fi + +export ORACLE_HOME="${oracle_install_dir}$(ls ${oracle_install_dir})" +export LD_LIBRARY_PATH="${ORACLE_HOME}/lib:${ORACLE_HOME}:${LD_LIBRARY_PATH}" + +profile_script="${build_dir}/.profile.d/oracle-client.sh" + +# Replace build_dir by HOME, which is the final location of the image data: +runtime_oracle_home="${ORACLE_HOME//${build_dir}/\${HOME}}" + +cat << EOF > "${profile_script}" +export ORACLE_HOME="${runtime_oracle_home}" +export LD_LIBRARY_PATH="${runtime_oracle_home}/lib:${runtime_oracle_home}:\${LD_LIBRARY_PATH} +EOF + +#runtime_oracle_home="$(echo "${ORACLE_HOME}" | sed -e "s+${build_dir}+/app+")" +#echo "export ORACLE_HOME=${runtime_oracle_home}" >> "${startup_script}" +#echo "export LD_LIBRARY_PATH=${runtime_oracle_home}/lib:${runtime_oracle_home}:\${LD_LIBRARY_PATH}" >> "${startup_script}" + From a519f46d478a691044f9cc15cfb9e784e2eeb477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20KUBLER?= Date: Mon, 29 Dec 2025 10:17:20 +0100 Subject: [PATCH 2/5] chore(imagick): release of Imagick PHP ext 3.8.1 --- support/ext/imagick | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/ext/imagick b/support/ext/imagick index 99f69ecd..35fc3d6e 100755 --- a/support/ext/imagick +++ b/support/ext/imagick @@ -6,7 +6,7 @@ if [ -n "$DEBUG" ]; then set -x fi -imagick_version="3.7.0" +imagick_version="3.8.1" curl -L "https://pecl.php.net/get/imagick-${imagick_version}.tgz" \ | tar xzv From bb35fa60e9426cde68412b6a3b6070e2a76d7088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20KUBLER?= Date: Mon, 29 Dec 2025 10:18:39 +0100 Subject: [PATCH 3/5] chore: remove now useless comment --- support/package_all | 3 --- 1 file changed, 3 deletions(-) diff --git a/support/package_all b/support/package_all index b6083a5d..3f830bed 100755 --- a/support/package_all +++ b/support/package_all @@ -30,9 +30,6 @@ source "../conf/versions.sh" # SKIP igbinary extension since it doesn't compile with PHP 8.5 # https://github.com/igbinary/igbinary/issues/400 # -# SKIP imagick extension since it doesn't compile with PHP 8.5 -# https://github.com/Imagick/imagick/issues/755 -# skip=( "imap" "amqp" "igbinary" "imagick" ) for e in ./ext-internal/*; do From 707d9f30a0fa1e8c5ccf8d0f51d221f9cd63e264 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20KUBLER?= Date: Mon, 29 Dec 2025 11:24:19 +0100 Subject: [PATCH 4/5] fix(tests): fix tests following release of Composer 2.9.2 --- test/defaults | 8 ++++---- .../composer-2.9.2/composer/composer.json | 9 +++++++++ .../composer-2.9.2/composer/composer.lock | 18 ++++++++++++++++++ test/fixtures/composer-latest | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/composer-2.9.2/composer/composer.json create mode 100644 test/fixtures/composer-2.9.2/composer/composer.lock diff --git a/test/defaults b/test/defaults index 14cd6742..fdb75af2 100644 --- a/test/defaults +++ b/test/defaults @@ -8,14 +8,14 @@ declare -gA latest_composer # Defaults for scalingo-22: default_php["scalingo-22"]="8.1." # This one depends on the semver stable rule configuration default_nginx["scalingo-22"]="1.28." -default_composer["scalingo-22"]="2.8." -latest_composer["scalingo-22"]="2.8.12" +default_composer["scalingo-22"]="2.9." +latest_composer["scalingo-22"]="2.9.2" # Defaults for scalingo-24: default_php["scalingo-24"]="8.4." default_nginx["scalingo-24"]="1.28." -default_composer["scalingo-24"]="2.8." -latest_composer["scalingo-24"]="2.8.12" +default_composer["scalingo-24"]="2.9." +latest_composer["scalingo-24"]="2.9.2" test::defaults::classic() { diff --git a/test/fixtures/composer-2.9.2/composer/composer.json b/test/fixtures/composer-2.9.2/composer/composer.json new file mode 100644 index 00000000..6cdc38dd --- /dev/null +++ b/test/fixtures/composer-2.9.2/composer/composer.json @@ -0,0 +1,9 @@ +{ + "extra": { + "paas": { + "engines": { + "composer": "2.9.2" + } + } + } +} diff --git a/test/fixtures/composer-2.9.2/composer/composer.lock b/test/fixtures/composer-2.9.2/composer/composer.lock new file mode 100644 index 00000000..b3a75590 --- /dev/null +++ b/test/fixtures/composer-2.9.2/composer/composer.lock @@ -0,0 +1,18 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "5aac55146c9cabad521141fa4188bbda", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": {}, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/test/fixtures/composer-latest b/test/fixtures/composer-latest index 328db78b..86eeaefd 120000 --- a/test/fixtures/composer-latest +++ b/test/fixtures/composer-latest @@ -1 +1 @@ -composer-2.8.12 \ No newline at end of file +composer-2.9.2 \ No newline at end of file From 8c4e1e5436bf63bea779bfc121c5fd118f1cee0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20KUBLER?= Date: Mon, 29 Dec 2025 12:09:56 +0100 Subject: [PATCH 5/5] feat(tests): add tests for PHP 8.5 --- test/fixtures/php-8.5/composer/composer.json | 5 ++ test/fixtures/php-8.5/composer/composer.lock | 20 +++++ .../php-8.5/optional_builtin/composer.json | 11 +++ .../php-8.5/optional_builtin/composer.lock | 26 +++++++ test/php85 | 77 +++++++++++++++++++ test/tests | 7 ++ 6 files changed, 146 insertions(+) create mode 100644 test/fixtures/php-8.5/composer/composer.json create mode 100644 test/fixtures/php-8.5/composer/composer.lock create mode 100644 test/fixtures/php-8.5/optional_builtin/composer.json create mode 100644 test/fixtures/php-8.5/optional_builtin/composer.lock create mode 100644 test/php85 diff --git a/test/fixtures/php-8.5/composer/composer.json b/test/fixtures/php-8.5/composer/composer.json new file mode 100644 index 00000000..1659532c --- /dev/null +++ b/test/fixtures/php-8.5/composer/composer.json @@ -0,0 +1,5 @@ +{ + "require": { + "php": "~8.5" + } +} diff --git a/test/fixtures/php-8.5/composer/composer.lock b/test/fixtures/php-8.5/composer/composer.lock new file mode 100644 index 00000000..6b0f8281 --- /dev/null +++ b/test/fixtures/php-8.5/composer/composer.lock @@ -0,0 +1,20 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "376fc9f81f2d9ad80d18e90efda04653", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "~8.5" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/test/fixtures/php-8.5/optional_builtin/composer.json b/test/fixtures/php-8.5/optional_builtin/composer.json new file mode 100644 index 00000000..90cf9165 --- /dev/null +++ b/test/fixtures/php-8.5/optional_builtin/composer.json @@ -0,0 +1,11 @@ +{ + "require": { + "php": "~8.5", + "ext-calendar": "*", + "ext-ftp": "*", + "ext-gettext": "*", + "ext-gmp": "*", + "ext-sodium": "*", + "ext-tidy": "*" + } +} diff --git a/test/fixtures/php-8.5/optional_builtin/composer.lock b/test/fixtures/php-8.5/optional_builtin/composer.lock new file mode 100644 index 00000000..1e13b8ba --- /dev/null +++ b/test/fixtures/php-8.5/optional_builtin/composer.lock @@ -0,0 +1,26 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "117f5b234daeb22fb27fa5cbd5b95676", + "packages": [], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": "~8.5", + "ext-calendar": "*", + "ext-ftp": "*", + "ext-gettext": "*", + "ext-gmp": "*", + "ext-sodium": "*", + "ext-tidy": "*" + }, + "platform-dev": {}, + "plugin-api-version": "2.6.0" +} diff --git a/test/php85 b/test/php85 new file mode 100644 index 00000000..44606ade --- /dev/null +++ b/test/php85 @@ -0,0 +1,77 @@ +#!/usr/bin/env bash + +readonly default_builtins_extensions_85=( + "bcmath" "bz2" "Core" "ctype" "curl" "date" "dom" "exif" "fileinfo" + "filter" "gd" "hash" "iconv" "intl" "json" "libxml" "mbstring" "mysqli" + "mysqlnd" "openssl" "pcntl" "pcre" "PDO" "pdo_mysql" "pdo_pgsql" + "pdo_sqlite" "pgsql" "Phar" "posix" "random" "readline" "Reflection" + "session" "shmop" "SimpleXML" "soap" "sockets" "SPL" "sqlite3" "standard" + "tokenizer" "xml" "xmlreader" "xmlwriter" "xsl" "Zend OPcache" "zip" "zlib" +) + +readonly default_pecl_extensions_85=( + "apcu" "mongodb" "redis" +) + +readonly optional_builtin_extensions_85=( + "calendar" "ftp" "gettext" "gmp" "sodium" "tidy" +) + +test::php85::classic() { +# +# Test a deployment of a classic app (not using Composer) +# Specifying we want PHP 8.4.x via environment +# + + PHP_VERSION="8.5" + export PHP_VERSION + + test::utils::setupFixture "defaults/classic" + test::helpers::classic_deploy \ + "8.5." \ + "${default_nginx["${STACK}"]}" +} + +test::php85::composer() { +# +# Test a deployment of a PHP app using Composer +# Specifying we want PHP 8.4.x via composer.json +# + + test::utils::setupFixture "php-8.5/composer" + test::helpers::composer_deploy \ + "8.5." \ + "${default_nginx["${STACK}"]}" \ + "${default_composer["${STACK}"]}" +} + +test::php85::extensions::default() { +# +# Test the presence of default enabled built-in extensions. +# + + test::utils::setupFixture "php-8.5/composer" + + test::utils::compile + test::helpers::enter_prod + + local extensions=("${default_builtin_extensions_85[@]}") + extensions+=("${default_pecl_extensions_85[@]}") + + test::helpers::enabled_extensions "${extensions[@]}" +} + +test::php85::extensions::optional_builtin() { +# +# Test the presence of available built-in extensions that are not +# enabled by default. +# + + test::utils::setupFixture "php-8.5/optional_builtin" + + test::utils::compile + test::helpers::enter_prod + + test::helpers::enabled_extensions "${optional_builtin_extensions_85[@]}" +} + diff --git a/test/tests b/test/tests index 4b5dada7..02da4d63 100755 --- a/test/tests +++ b/test/tests @@ -8,6 +8,7 @@ source "$( pwd )/test/php81" source "$( pwd )/test/php82" source "$( pwd )/test/php83" source "$( pwd )/test/php84" +source "$( pwd )/test/php85" source "$( pwd )/test/php" source "$( pwd )/test/composer" source "$( pwd )/test/nginx" @@ -42,6 +43,12 @@ suite_addTest test::php84::composer suite_addTest test::php84::extensions::default suite_addTest test::php84::extensions::optional_builtin +# PHP 8.5 +suite_addTest test::php85::classic +suite_addTest test::php85::composer +suite_addTest test::php85::extensions::default +suite_addTest test::php85::extensions::optional_builtin + # PHP suite_addTest test::php::composer_over_lower_classic suite_addTest test::php::composer_over_higher_classic