diff --git a/CHANGELOG.md b/CHANGELOG.md index a501f638e..0ba8015bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # heroku-buildpack-php CHANGELOG +## v141 (2018-08-07) + +### ADD + +- ext-redis/4.1.1 [David Zuelke] +- ext-mongodb/1.5.2 [David Zuelke] + +### CHG + +- Verbose error messasge on `bin/detect` failure [David Zuelke] +- Emit brief warnings for common regexed build failure cases [David Zuelke] +- Run most internal 'composer' invocations using '--no-plugins' [David Zuelke] +- Composer/1.7.1 [David Zuelke] +- Warn about 'minimum-stability' only if 'prefer-stable' is off [David Zuelke] + +### FIX + +- Generate Composer package repositories with empty JSON objects, not arrays, where required by Composer 1.7+ [David Zuelke] + ## v140 (2018-07-25) ### CHG diff --git a/bin/compile b/bin/compile index f479d733c..9a4a8202f 100755 --- a/bin/compile +++ b/bin/compile @@ -148,24 +148,6 @@ if [[ -s "$COMPOSER" ]]; then mcount "failures.composer_lock.lint" error "$composer_lock_parse_error" } - cat "$COMPOSER_LOCK" | python -c 'import sys, json; l = json.load(sys.stdin); sys.exit(not(l["minimum-stability"] == "stable"));' 2> /dev/null || { - mcount "warnings.composer_lock.minimum_stability" - warning <<-EOF - Non-stable 'minimum-stability'! - - Your '$COMPOSER' contains a non-'stable' value for the - for the 'minimum-stability' setting. - - This setting affects behavior globally and may result in the - installation of unstable versions of PHP and extensions in - addition to your dependencies. - - It is strongly recommended that you always use stability flags - on the specific packages which you want in a non-stable version - instead, even if you have 'prefer-stable' enabled. More info: - https://getcomposer.org/doc/04-schema.md#package-links - EOF - } fi else if [[ ! -f "$COMPOSER" ]]; then @@ -249,7 +231,7 @@ curl_retry_on_18 --fail --silent --location -o $build_dir/.heroku/php-min.tar.gz tar xzf $build_dir/.heroku/php-min.tar.gz -C $build_dir/.heroku/php-min rm $build_dir/.heroku/php-min.tar.gz -curl_retry_on_18 --fail --silent --location -o $build_dir/.heroku/composer.tar.gz "${s3_url}composer-1.6.5.tar.gz" || { +curl_retry_on_18 --fail --silent --location -o $build_dir/.heroku/composer.tar.gz "${s3_url}composer-1.7.1.tar.gz" || { mcount "failures.bootstrap.download.composer" error <<-EOF Failed to download Composer for bootstrapping! @@ -268,8 +250,9 @@ composer() { } export -f composer -composer_vendordir=$(composer config vendor-dir) -composer_bindir=$(composer config bin-dir) +# we use --no-plugins just in case the vendor dir is there, see e.g. https://github.com/Ocramius/PackageVersions/issues/64 +composer_vendordir=$(composer config --no-plugins vendor-dir) +composer_bindir=$(composer config --no-plugins bin-dir) # packages that get installed will add to this file, it's both for us and for buildpacks that follow # composer bin-dir goes last to avoid any conflicts @@ -281,7 +264,7 @@ echo "export PATH=\$HOME/.heroku/php/bin:\$PATH:\$HOME/$composer_bindir" > $buil # we perform this check early so people with stale lock files are reminded why if their lock file errors in the next step composer_lock_outdated=false -composer validate --no-check-publish --no-check-all --quiet "$COMPOSER" 2>/dev/null || { +composer validate --no-plugins --no-check-publish --no-check-all --quiet "$COMPOSER" 2>/dev/null || { mcount "warnings.composer_lock.outdated" composer_lock_outdated=true warning <<-EOF @@ -314,6 +297,47 @@ composer validate --no-check-publish --no-check-all --quiet "$COMPOSER" 2>/dev/n EOF } +# if prefer-stable is false and minimum-stability is not stable, warn about potential unstable platform installs +[[ -f "$COMPOSER_LOCK" ]] && minimum_stability=$(cat "$COMPOSER_LOCK" | python -c 'import sys, json; l = json.load(sys.stdin); print(l.get("minimum-stability")); sys.exit(l.get("minimum-stability", "stable") != "stable" and l.get("prefer-stable", False) == False);' 2> /dev/null) || { + possible_stabilities="dev, alpha, beta, or RC" + case $minimum_stability in + alpha) + possible_stabilities="alpha, beta, or RC" + ;; + beta) + possible_stabilities="beta or RC" + ;; + [rR][cC]) + possible_stabilities="release candidate" + ;; + esac + mcount "warnings.composer_lock.minimum_stability" + warning <<-EOF + Non-stable 'minimum-stability'! + + Your '$COMPOSER' contains a 'minimum-stability' setting of + '$minimum_stability', and the 'prefer-stable' setting is not enabled. + + This combination of options may negatively impact the stability + of your app and result in crashes as it permits installation of + $possible_stabilities versions of PHP runtimes and extensions. + + If possible, you should always use explicit stability flags on + only those dependencies that you want unstable versions of, and + leave 'minimum-stability' at its default 'stable' setting. + + If you really need a global 'minimum-stability' setting lower + than 'stable', it is strongly recommended that you enable the + 'prefer-stable' setting in '$COMPOSER'. + + For more information, refer to the following documentation: + https://getcomposer.org/doc/articles/versions.md + https://getcomposer.org/doc/04-schema.md#package-links + https://getcomposer.org/doc/04-schema.md#minimum-stability + https://getcomposer.org/doc/04-schema.md#prefer-stable + EOF +} + status "Installing platform packages..." if [[ $STACK == "cedar-14" || $STACK == "heroku-16" ]]; then @@ -470,7 +494,7 @@ status "Installing dependencies..." # echo composer version for info purposes # tail to get rid of outdated version warnings (Composer sends those to STDOUT instead of STDERR) -composer --version 2> /dev/null | tail -n 1 | indent +composer --no-plugins --version 2> /dev/null | tail -n 1 | indent # throw a notice if people have added their vendor dir to Git; that's bad practice and makes everything slow and cluttered if [[ -f "$composer_vendordir/autoload.php" && -d "$composer_vendordir/composer" ]]; then @@ -479,16 +503,16 @@ if [[ -f "$composer_vendordir/autoload.php" && -d "$composer_vendordir/composer" warning <<-EOF Composer vendor dir found in project! - Your Composer vendor dir is part of your Git repository. + Your Git repository contains Composer's '$composer_vendordir' directory. This directory should not be under version control; only your - '$COMPOSER' and '$COMPOSER_LOCK' files should be added, because + '$COMPOSER' and '$COMPOSER_LOCK' files need to be added, as Composer will handle installation of dependencies on deploy. To suppress this notice, first remove the folder from the index by running 'git rm -r --cached $composer_vendordir/'. Next, edit your project's '.gitignore' file and add the folder - '/$composer_vendordir/' to the list. + '/$composer_vendordir/' to the list, then commit the changes. $( [[ ! "$composer_bindir/" == "$composer_vendordir"/* && -d "$composer_bindir" ]] && cat <<-EOF2 @@ -506,7 +530,7 @@ export_env_dir "$env_dir" '^COMPOSER_GITHUB_OAUTH_TOKEN$' COMPOSER_GITHUB_OAUTH_TOKEN=${COMPOSER_GITHUB_OAUTH_TOKEN:-} if [[ -n "$COMPOSER_GITHUB_OAUTH_TOKEN" ]]; then if curl --fail --silent -H "Authorization: token $COMPOSER_GITHUB_OAUTH_TOKEN" https://api.github.com/rate_limit > /dev/null; then - composer config -g github-oauth.github.com "$COMPOSER_GITHUB_OAUTH_TOKEN" &> /dev/null # redirect outdated version warnings (Composer sends those to STDOUT instead of STDERR) + composer config --no-plugins -g github-oauth.github.com "$COMPOSER_GITHUB_OAUTH_TOKEN" &> /dev/null # redirect outdated version warnings (Composer sends those to STDOUT instead of STDERR) notice_inline 'Using $COMPOSER_GITHUB_OAUTH_TOKEN for GitHub OAuth.' else mcount "failures.dependencies.auth.COMPOSER_GITHUB_OAUTH_TOKEN" @@ -521,7 +545,7 @@ if [[ -n "$COMPOSER_GITHUB_OAUTH_TOKEN" ]]; then fi else # don't forget to remove any stored key if it's gone from the env - composer config -g --unset github-oauth.github.com &> /dev/null # redirect outdated version warnings (Composer sends those to STDOUT instead of STDERR) + composer config --no-plugins -g --unset github-oauth.github.com &> /dev/null # redirect outdated version warnings (Composer sends those to STDOUT instead of STDERR) fi # no need for the token to stay around in the env unset COMPOSER_GITHUB_OAUTH_TOKEN diff --git a/bin/detect b/bin/detect index 8eabae00b..d3ad02003 100755 --- a/bin/detect +++ b/bin/detect @@ -1,7 +1,29 @@ #!/usr/bin/env bash +# convenience functions +source "$(cd $(dirname $0); cd ..; pwd)"/bin/util/common.sh + if [[ -f "$1/composer.json" || -f "$1/index.php" ]]; then echo "PHP" && exit 0 else - exit 1 + error <<-EOF + Application not supported by this buildpack! + + The 'heroku/php' buildpack is set on this application, but was + unable to detect a PHP codebase. + + A PHP app on Heroku requires a 'composer.json' at the root of + the directory structure, or an 'index.php' for legacy behavior. + + If you are trying to deploy a PHP application, ensure that one + of these files is present at the top level directory. + + If you are trying to deploy an application written in another + language, you need to change the list of buildpacks set on your + Heroku app using the 'heroku buildpacks' command. + + For more information, refer to the following documentation: + https://devcenter.heroku.com/articles/buildpacks + https://devcenter.heroku.com/articles/php-support#activation + EOF fi diff --git a/bin/util/common.sh b/bin/util/common.sh index 8862bb896..c4e1d905c 100755 --- a/bin/util/common.sh +++ b/bin/util/common.sh @@ -14,9 +14,9 @@ error() { indent no_first_line_indent " ! " if [[ -s "$_captured_warnings_file" ]]; then echo "" | indent "" " ! " - echo "REMINDER: the following warnings were emitted during the build;" | indent "" " ! " - echo "check the details above, as they may be related to the error:" | indent "" " ! " - cat "$_captured_warnings_file" | indent "" " ! - " + echo -e "\033[1;33mREMINDER:\033[1;31m the following \033[1;33mwarnings\033[1;31m were emitted during the build;" | indent "" " ! " + echo "check the details above, as they may be related to this error:" | indent "" " ! " + cat "$_captured_warnings_file" | indent "" "$(echo -e " ! \033[1;33m-\033[1;31m ")" fi echo -e "\033[0m" # reset style exit 1 diff --git a/bin/util/failures.sh b/bin/util/failures.sh index ca585ea0e..2648d503b 100755 --- a/bin/util/failures.sh +++ b/bin/util/failures.sh @@ -2,27 +2,35 @@ detect_platform_solving_failures() { # we can't use an associative array, as those are pure hashes in Bash 4+, so they don't preserve an order, but we need that local failures=() local regexes=() + local warnings=() regexes+=("requires php \S+ -> no matching package found") failures+=("requirements.php") + warnings+=("Requested version for package 'php' not available") regexes+=("requires php-64bit \S+ -> no matching package found") failures+=("requirements.php-64bit") + warnings+=("Requested version for package 'php-64bit' not available") regexes+=("requires hhvm \S+ -> no matching package found") failures+=("requirements.hhvm") + warnings+=("Requested version for package 'hhvm' is not available") regexes+=("requires ext-\S+ \S+ -> no matching package found") failures+=("requirements.extension") + warnings+=("A requested extension is not available") regexes+=("requires ext-mcrypt \S+ -> satisfiable by php") failures+=("requirements.extension.mcrypt") + warnings+=("ext-mcrypt not available for selected PHP version") regexes+=("requires ext-mongo \S+ -> satisfiable by ext-mongo") failures+=("requirements.extension.mongo") + warnings+=("ext-mongo not available for selected PHP version") regexes+=("requires ext-mysql \S+ -> satisfiable by php") failures+=("requirements.extension.mysql") + warnings+=("ext-mysql not available for selected PHP version") # FIXME: detect multiple conflicing requirements (e.g. from require and require-dev)? # regex_failures expects variable names, not values, as it dereferences the arrays - regex_failures regexes failures || echo "unknown" + regex_failures regexes failures warnings || echo "unknown" } detect_platform_install_failures() { @@ -60,52 +68,73 @@ detect_dependencies_install_failures() { # we can't use an associative array, as those are pure hashes in Bash 4+, so they don't preserve an order, but we need that local failures=() local regexes=() + local warnings=() regexes+=("Your configuration does not allow connections to") failures+=("download.insecure") + warnings+=("A package dependency failed to download over plain HTTP") regexes+=("Failed to download \S+ from dist: Could not authenticate against") failures+=("download.authentication.failed") + warnings+=("Authentication against an external repository failed") regexes+=("No bitbucket authentication configured") failures+=("download.authentication.bitbucket.missing") + warnings+=("Bitbucket authentication details not configured") regexes+=('Failed to clone \S+ via \S+(, \S+)* protocols, aborting.') failures+=("download.clone") + warnings+=("") # FIXME: may be followed first by "remote: Invalid username or password" and then "Host key verification failed." regexes+=("Failed to execute git clone") failures+=("download.clone") + warnings+=("") # FIXME: "Host key verification failed." may show up a few lines later # FIXME: or "fatal: unable to access 'https://foo:***@bitbucket.org/foo/bar.git/': The requested URL returned error: 403" regexes+=('Failed to download \S+ from dist: The "[^"]+" file could not be downloaded \(HTTP/\S+ 404') failures+=("download.not_found") + warnings+=("") regexes+=("Parse error: syntax error") failures+=("scripts.any.parse_error") + warnings+=("There was a PHP syntax error in your code") regexes+=("(ClassNotFoundException|Class '[^']+' not found)") failures+=("scripts.any.class_not_found") + warnings+=("There was a class not found error in your code") regexes+=('Symfony\\Component\\Process\\Exception\\ProcessTimedOutException') failures+=("scripts.any.timeout") + warnings+=("A script or process has exceeded the Composer timeout") regexes+=('(SQLSTATE|PDOException|Doctrine\\DBAL\\Exception)') failures+=("scripts.any.database") + warnings+=("An error occurred during a database connection or query") regexes+=("handling the post-install-cmd event terminated with an exception") failures+=("scripts.post-install-cmd.exception") + warnings+=("A post-install-cmd script terminated with an exception") regexes+=("handling the post-install-cmd event returned with error code") failures+=("scripts.post-install-cmd.error") + warnings+=("A post-install-cmd script terminated with an error") regexes+=("handling the post-autoload-dump event terminated with an exception") failures+=("scripts.post-autoload-dump.exception") + warnings+=("A post-autoload-dump script terminated with an exception") regexes+=("handling the post-autoload-dump event returned with error code") failures+=("scripts.post-autoload-dump.error") + warnings+=("A post-autoload-dump script terminated with an error") regexes+=("handling the \S+ event terminated with an exception") failures+=("scripts.unknown.exception") + warnings+=("A Composer script terminated with an exception") regexes+=("handling the \S+ event returned with error code") failures+=("scripts.unknown.error") + warnings+=("A Composer script terminated with an error") - regexes+=("(APP_ENV environment variable is not defined|Environment variable not found)") + regexes+=("APP_ENV environment variable is not defined") + failures+=("symfony.env_var_missing") + warnings+=($'The APP_ENV environment variable is missing\nRun \'heroku config:set APP_ENV=prod\' to set it.') + regexes+=("Environment variable not found") failures+=("symfony.env_var_missing") + warnings+=($'A required environment variable is missing\nUse the \'heroku config:set\' command to set it; more details:\nhttps://devcenter.heroku.com/articles/config-vars') # regex_failures expects variable names, not values, as it dereferences the arrays - regex_failures regexes failures || echo "unknown" + regex_failures regexes failures warnings || echo "unknown" } regex_failures() { @@ -114,6 +143,12 @@ regex_failures() { local regexes=("${!name}") local name=$2[@] local failures=("${!name}") + if (( $# > 2 )); then + local name=$3[@] + local warnings=("${!name}") + else + local warnings=() + fi # buffer input, as we have to read it many times local input=$( $package["name"], "version" => $package["version"], - "require" => mkdep($preq), - "replace" => mkdep($prep), - "provide" => mkdep($ppro), - "conflict" => mkdep($pcon), + "require" => (object) mkdep($preq), + "replace" => (object) mkdep($prep), + "provide" => (object) mkdep($ppro), + "conflict" => (object) mkdep($pcon), ]; return true; } diff --git a/support/build/composer-1.6.5 b/support/build/composer-1.7.1 similarity index 100% rename from support/build/composer-1.6.5 rename to support/build/composer-1.7.1 diff --git a/support/build/extensions/no-debug-non-zts-20121212/mongodb-1.5.1 b/support/build/extensions/no-debug-non-zts-20121212/mongodb-1.5.2 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20121212/mongodb-1.5.1 rename to support/build/extensions/no-debug-non-zts-20121212/mongodb-1.5.2 diff --git a/support/build/extensions/no-debug-non-zts-20121212/redis-4.1.0 b/support/build/extensions/no-debug-non-zts-20121212/redis-4.1.1 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20121212/redis-4.1.0 rename to support/build/extensions/no-debug-non-zts-20121212/redis-4.1.1 diff --git a/support/build/extensions/no-debug-non-zts-20131226/mongodb-1.5.1 b/support/build/extensions/no-debug-non-zts-20131226/mongodb-1.5.2 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20131226/mongodb-1.5.1 rename to support/build/extensions/no-debug-non-zts-20131226/mongodb-1.5.2 diff --git a/support/build/extensions/no-debug-non-zts-20131226/redis-4.1.0 b/support/build/extensions/no-debug-non-zts-20131226/redis-4.1.1 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20131226/redis-4.1.0 rename to support/build/extensions/no-debug-non-zts-20131226/redis-4.1.1 diff --git a/support/build/extensions/no-debug-non-zts-20151012/mongodb-1.5.1 b/support/build/extensions/no-debug-non-zts-20151012/mongodb-1.5.2 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20151012/mongodb-1.5.1 rename to support/build/extensions/no-debug-non-zts-20151012/mongodb-1.5.2 diff --git a/support/build/extensions/no-debug-non-zts-20151012/redis-4.1.0 b/support/build/extensions/no-debug-non-zts-20151012/redis-4.1.1 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20151012/redis-4.1.0 rename to support/build/extensions/no-debug-non-zts-20151012/redis-4.1.1 diff --git a/support/build/extensions/no-debug-non-zts-20160303/mongodb-1.5.1 b/support/build/extensions/no-debug-non-zts-20160303/mongodb-1.5.2 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20160303/mongodb-1.5.1 rename to support/build/extensions/no-debug-non-zts-20160303/mongodb-1.5.2 diff --git a/support/build/extensions/no-debug-non-zts-20160303/redis-4.1.0 b/support/build/extensions/no-debug-non-zts-20160303/redis-4.1.1 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20160303/redis-4.1.0 rename to support/build/extensions/no-debug-non-zts-20160303/redis-4.1.1 diff --git a/support/build/extensions/no-debug-non-zts-20170718/mongodb-1.5.1 b/support/build/extensions/no-debug-non-zts-20170718/mongodb-1.5.2 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20170718/mongodb-1.5.1 rename to support/build/extensions/no-debug-non-zts-20170718/mongodb-1.5.2 diff --git a/support/build/extensions/no-debug-non-zts-20170718/redis-4.1.0 b/support/build/extensions/no-debug-non-zts-20170718/redis-4.1.1 similarity index 100% rename from support/build/extensions/no-debug-non-zts-20170718/redis-4.1.0 rename to support/build/extensions/no-debug-non-zts-20170718/redis-4.1.1