Skip to content

Commit

Permalink
Merge pull request heroku#291 from heroku/develop
Browse files Browse the repository at this point in the history
v141
  • Loading branch information
dzuelke authored Aug 7, 2018
2 parents d4336f0 + 8bbe252 commit 6f315ae
Show file tree
Hide file tree
Showing 17 changed files with 141 additions and 39 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
80 changes: 52 additions & 28 deletions bin/compile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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!
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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"
Expand All @@ -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
Expand Down
24 changes: 23 additions & 1 deletion bin/detect
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions bin/util/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
43 changes: 40 additions & 3 deletions bin/util/failures.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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() {
Expand All @@ -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=$(</dev/stdin)
Expand All @@ -122,6 +157,8 @@ regex_failures() {
for i in "${!regexes[@]}"; do
if grep -qE "${regexes[$i]}" <<< "$input"; then
echo "${failures[$i]}"
# if there is a warnings entry for this index and it is not empty...
[[ "${warnings[$i]:+isset}" ]] && warning_inline "${warnings[$i]}"
return 0;
fi
done
Expand Down
8 changes: 4 additions & 4 deletions bin/util/platform.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ function mkmetas($package, array &$metapaks, &$have_runtime_req = false) {
// we re-use the dep name and version, makes for nice error messages if dependencies cannot be fulfilled :)
"name" => $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;
}
Expand Down
File renamed without changes.

0 comments on commit 6f315ae

Please sign in to comment.