Compare commits

...

10 Commits

Author SHA1 Message Date
Ed Morley c9504ffd2e Release v184 (#1106) 2020-10-21 14:56:33 +01:00
Ed Morley 96822983ed Add Heroku-20 to deploy-runtimes default stacks list (#1104)
Now `make deploy-runtimes` will build binaries for Heroku-20 by default
too, without the need to pass it in via an explicit `STACKS=...`list.

Closes @W-8233698@.

[skip changelog]
2020-10-19 20:48:34 +01:00
Ed Morley f9d5c0010d Move changelog entries to the correct section (#1103)
The changelog entries for #1099 and #1100 were added to the previous
release section (rather than the "unreleased" section) by mistake.
2020-10-19 13:02:12 +01:00
Ed Morley 9b1a69a1b3 Vendor buildpack-stdlib instead of fetching from S3 (#1100)
Since fetching buildpack-stdlib from S3 has a number of disadvantages:
- it's not possible to grep the repo when trying to work out what
  something from the stdlib is doing
- shellcheck can't fully scan the code, since it similarly doesn't
  have the source
- another compile-time HTTP request that can fail due to transient
  network issues and so reduce reliability
- dependency on the security/release-process of an additional bucket

Since the stdlib is small, doesn't often change, and is not a binary,
it's a great fit for just vendoring in the repo.

The `BIN_DIR` calculation added to `bin/utils` is based on the approach
mentioned here:
https://www.ostricher.com/2014/10/the-right-way-to-get-the-directory-of-a-bash-script/

...rather than copying the version in `bin/compile`, since the latter uses
`$0` so doesn't work with sourced scripts (such as `bin/utils`).

Closes W-8094463.
2020-10-19 12:27:16 +01:00
dependabot[bot] 58dd638fb6 Bump heroku_hatchet from 7.3.1 to 7.3.3 (#1102)
Bumps [heroku_hatchet](https://github.com/heroku/hatchet) from 7.3.1 to 7.3.3.
- [Release notes](https://github.com/heroku/hatchet/releases)
- [Changelog](https://github.com/heroku/hatchet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/heroku/hatchet/compare/v7.3.1...v7.3.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-19 12:20:27 +01:00
Ed Morley ead59ac7ff Expose BPLOG_PREFIX to sub-shells again (#1099)
In #1011 the number of buildpack variables that are exported (and so
exposed to subprocesses) was reduced, since in general we don't want
to leak buildpack internals into end-user steps such as the pre/post
compile hooks.

However since this change, any buildpack metric emitted from within
a `sub_env` wrapper (which is a buildpack-stdlib utility function)
is missing its `buildpack.python` prefix.

This is because buildpack-stdlib:
* lazy-loads `BPLOG_PREFIX` (rather than doing so when initially
  sourced, which is the approach used for `BUILDPACK_LOG_FILE`)
* doesn't check whether `BPLOG_PREFIX` is set before emitting metrics

See:
https://github.com/heroku/buildpack-stdlib/blob/v8/stdlib.sh

As a stop-gap until we either fix this in buildpack-stdlib (W-8095466),
or remove usages of the `sub_env` wrapper (since I think they are
counter-productive in this buildpack), I've added back the export
for `BPLOG_PREFIX`.

Fixes @W-8095436@.
2020-10-15 15:31:32 +01:00
Ed Morley ac8fd555b8 Release v183 (#1097) 2020-10-12 12:31:44 +01:00
dependabot[bot] f825896c4e Bump heroku_hatchet from 7.3.0 to 7.3.1 (#1095)
Bumps [heroku_hatchet](https://github.com/heroku/hatchet) from 7.3.0 to 7.3.1.
- [Release notes](https://github.com/heroku/hatchet/releases)
- [Changelog](https://github.com/heroku/hatchet/blob/main/CHANGELOG.md)
- [Commits](https://github.com/heroku/hatchet/compare/v7.3.0...v7.3.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-12 10:01:20 +01:00
Ed Morley fcf696b835 Add support for Heroku-20 (#968)
This adds support for the upcoming Heroku-20 stack.

The Heroku-20 Dockerfile is identical to that for Heroku-18, other
than the base image, and stack-related env var changes.

The initial Python versions made available will be those in:
https://devcenter.heroku.com/articles/python-support#supported-runtimes
https://devcenter.heroku.com/articles/python-support#supported-pypy-runtimes

...minus CPython 2.7, since it's EOL.

Which are:
* `python-3.6.12`
* `python-3.7.9`
* `python-3.8.6`
* `python-3.9.0`
* `pypy2.7-7.3.2`
* `pypy3.6-7.3.2`

Note: Unlike CPython 2.7, the PyPy 2.7 branch is still supported:
https://doc.pypy.org/en/latest/faq.html#how-long-will-pypy-support-python2

In addition, I've generated binaries for each patch release immediately
prior to the latest versions (with the exception of 3.9.0, since there
isn't one), otherwise it's not possible to run the "out of date Python"
warning tests.

The binaries were generated using the process here:
https://github.com/heroku/heroku-buildpack-python/blob/main/builds/README.md

Specifically:

```
make deploy-runtimes STACKS='heroku-20' \
  RUNTIMES='python-3.6.11 python-3.6.12 python-3.7.8 python-3.7.9 python-3.8.5 python-3.8.6 python-3.9.0 pypy2.7-7.3.1 pypy2.7-7.3.2 pypy3.6-7.3.1 pypy3.6-7.3.2' \
  ENV_FILE=...
```

Binaries for the GDAL/GEOS/PROJ feature have not been generated, since
it's deprecated and due for removal shortly:
https://help.heroku.com/D5INLB1A/python-s-build_with_geo_libraries-legacy-feature-is-now-deprecated

Note: Like the Python 3.9.0 release, this uses the new S3 bucket, so
apps will need to be using a recent version of the buildpack in order
to build on Heroku-20:
https://devcenter.heroku.com/articles/python-support#checking-the-python-buildpack-version

Closes @W-7485877@.
2020-10-07 19:44:33 +01:00
Ed Morley a98ef91566 Tests: Clean up the Python version unit tests (#1092)
* Fixes the "Installing <version>" assertions so that they don't false
  positive against the "please upgrade to <version>" output.
* Removes modification of test fixtures during tests, since it can lead
  to failures depending on test order, and confusion when debugging.
* Updates the PyPy version warning tests to use a slightly newer (but
  still not latest) PyPy version, which means that the test now passes
  on Cedar-14 and can be unskipped.
* Switches to using an empty requirements file for version tests that
  duplicate the main test, to save spending time installing dependencies
  unnecessarily.
* Switches the NLTK test to using the default buildpack Python version,
  rather than an ancient Python 3.6.
* Skips the Python 3.7/3.8 tests on Cedar-14 rather than asserting
  failure, since we know they'll never pass due to Cedar-14's libssl being
  older than required.
* Removes redundant `testSqliteInstall` test since it duplicates the
  Python version install tests.

Longer term I'll be moving many of the unit tests to Hatchet, however
this at least makes the tests more dependable in the meantime.

Closes @W-8060219@.
Closes @W-8176779@.

[skip changelog]
2020-10-07 15:10:20 +01:00
33 changed files with 396 additions and 170 deletions
+4
View File
@@ -34,6 +34,10 @@ env:
- STACK=heroku-18 TEST_CMD=test/run-deps - STACK=heroku-18 TEST_CMD=test/run-deps
- STACK=heroku-18 TEST_CMD=test/run-versions - STACK=heroku-18 TEST_CMD=test/run-versions
- STACK=heroku-18 TEST_CMD=test/run-features - STACK=heroku-18 TEST_CMD=test/run-features
- STACK=heroku-20 TEST_CMD=test/run-deps
- STACK=heroku-20 TEST_CMD=test/run-versions
- STACK=heroku-20 TEST_CMD=test/run-features
global: global:
- HATCHET_RETRIES=3 - HATCHET_RETRIES=3
- IS_RUNNING_ON_CI=true - IS_RUNNING_ON_CI=true
+9
View File
@@ -3,6 +3,15 @@
## Unreleased ## Unreleased
## v184 (2020-10-21)
- Vendor buildpack-stdlib instead of fetching from S3 (#1100).
- Fix metric names for metrics emitted within `sub_env` (#1099).
## v183 (2020-10-12)
- Add support for Heroku-20 (#968).
## v182 (2020-10-06) ## v182 (2020-10-06)
- Python 3.9.0 is now available (CPython) (#1090). - Python 3.9.0 is now available (CPython) (#1090).
+4 -4
View File
@@ -3,17 +3,17 @@ GEM
specs: specs:
diff-lcs (1.4.4) diff-lcs (1.4.4)
erubis (2.7.0) erubis (2.7.0)
excon (0.76.0) excon (0.78.0)
heroics (0.1.1) heroics (0.1.1)
erubis (~> 2.0) erubis (~> 2.0)
excon excon
moneta moneta
multi_json (>= 1.9.2) multi_json (>= 1.9.2)
heroku_hatchet (7.3.0) heroku_hatchet (7.3.3)
excon (~> 0) excon (~> 0)
platform-api (~> 3) platform-api (~> 3)
rrrretry (~> 1) rrrretry (~> 1)
thor (~> 0) thor (~> 1)
threaded (~> 0) threaded (~> 0)
moneta (1.0.0) moneta (1.0.0)
multi_json (1.15.0) multi_json (1.15.0)
@@ -43,7 +43,7 @@ GEM
rspec-retry (0.6.2) rspec-retry (0.6.2)
rspec-core (> 3.3) rspec-core (> 3.3)
rspec-support (3.9.3) rspec-support (3.9.3)
thor (0.20.3) thor (1.0.1)
threaded (0.0.4) threaded (0.0.4)
PLATFORMS PLATFORMS
+1 -1
View File
@@ -2,7 +2,7 @@
.PHONY: check test builder-image buildenv deploy-runtimes tools .PHONY: check test builder-image buildenv deploy-runtimes tools
STACK ?= heroku-18 STACK ?= heroku-18
STACKS ?= cedar-14 heroku-16 heroku-18 STACKS ?= cedar-14 heroku-16 heroku-18 heroku-20
TEST_CMD ?= test/run-versions && test/run-features && test/run-deps TEST_CMD ?= test/run-versions && test/run-features && test/run-deps
ENV_FILE ?= builds/dockerenv.default ENV_FILE ?= builds/dockerenv.default
BUILDER_IMAGE_PREFIX := heroku-python-build BUILDER_IMAGE_PREFIX := heroku-python-build
+2 -4
View File
@@ -15,10 +15,8 @@
# Fail fast and fail hard. # Fail fast and fail hard.
set -eo pipefail set -eo pipefail
# Boostrap the Buildpack Standard Library. # Used by buildpack-stdlib's metrics features.
# Disable unused env var warning since shellcheck doesn't know about the stdlib. export BPLOG_PREFIX="buildpack.python"
# shellcheck disable=2034
BPLOG_PREFIX="buildpack.python"
export BUILDPACK_LOG_FILE=${BUILDPACK_LOG_FILE:-/dev/null} export BUILDPACK_LOG_FILE=${BUILDPACK_LOG_FILE:-/dev/null}
[ "$BUILDPACK_XTRACE" ] && set -o xtrace [ "$BUILDPACK_XTRACE" ] && set -o xtrace
+4 -1
View File
@@ -21,8 +21,11 @@ source "$BIN_DIR/utils"
# If GDAL exists within requirements, use vendored gdal. # If GDAL exists within requirements, use vendored gdal.
if (pip-grep -s requirements.txt GDAL gdal pygdal &> /dev/null) then if (pip-grep -s requirements.txt GDAL gdal pygdal &> /dev/null) then
if [ ! -f ".heroku/vendor/bin/gdalserver" ]; then if [[ ! -f ".heroku/vendor/bin/gdalserver" && "${STACK}" == 'heroku-20' ]]; then
puts-warn "The buildpack's built-in GDAL functonality is not supported on Heroku-20."
puts-warn "Please use this buildpack instead: https://github.com/heroku/heroku-geo-buildpack"
elif [[ ! -f ".heroku/vendor/bin/gdalserver" ]]; then
puts-warn "The vendored GDAL package in the Heroku Python Buildpack now deprecated." puts-warn "The vendored GDAL package in the Heroku Python Buildpack now deprecated."
puts-warn "To enable GDAL use an alternative buildpack is available here - https://github.com/heroku/heroku-geo-buildpack" puts-warn "To enable GDAL use an alternative buildpack is available here - https://github.com/heroku/heroku-geo-buildpack"
+5 -2
View File
@@ -20,8 +20,11 @@ PKG_CONFIG_PATH="/app/.heroku/vendor/lib/pkgconfig:$PKG_CONFIG_PATH"
# shellcheck source=bin/utils # shellcheck source=bin/utils
source "$BIN_DIR/utils" source "$BIN_DIR/utils"
# If GDAL exists within requirements, use vendored gdal. if [[ "$BUILD_WITH_GEO_LIBRARIES" && "${STACK}" == 'heroku-20' ]]; then
if [[ "$BUILD_WITH_GEO_LIBRARIES" ]]; then puts-warn "The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality is not supported on Heroku-20."
puts-warn "Please use this buildpack for GDAL, GEOS and PROJ: https://github.com/heroku/heroku-geo-buildpack"
puts-warn "To hide this message, unset the BUILD_WITH_GEO_LIBRARIES variable using: heroku config:unset BUILD_WITH_GEO_LIBRARIES"
elif [[ "$BUILD_WITH_GEO_LIBRARIES" ]]; then
mcount "buildvar.BUILD_WITH_GEO_LIBRARIES" mcount "buildvar.BUILD_WITH_GEO_LIBRARIES"
puts-warn "The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality are now deprecated." puts-warn "The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality are now deprecated."
+7 -6
View File
@@ -2,12 +2,13 @@
shopt -s extglob shopt -s extglob
shopt -s nullglob shopt -s nullglob
# The standard library. # This is necessary since this script is sometimes sourced from
if [[ ! -f /tmp/stdlib.sh ]]; then # subshells that don't have the variables from bin/compile.
curl --retry 3 -s https://lang-common.s3.amazonaws.com/buildpack-stdlib/v8/stdlib.sh > /tmp/stdlib.sh # Remove this once we no longer wrap all the things in `sub_env`.
fi BIN_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=/dev/null ROOT_DIR=$(dirname "${BIN_DIR}")
source /tmp/stdlib.sh # shellcheck source=vendor/buildpack-stdlib_v8.sh
source "${ROOT_DIR}/vendor/buildpack-stdlib_v8.sh"
if [ "$(uname)" == Darwin ]; then if [ "$(uname)" == Darwin ]; then
sed() { command sed -l "$@"; } sed() { command sed -l "$@"; }
+20
View File
@@ -0,0 +1,20 @@
FROM heroku/heroku:20-build
ENV WORKSPACE_DIR="/app/builds" \
S3_BUCKET="heroku-buildpack-python" \
S3_PREFIX="heroku-20/" \
STACK="heroku-20"
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
libsqlite3-dev \
python3-pip \
python3-setuptools \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt /app/
RUN pip3 install --disable-pip-version-check --no-cache-dir -r /app/requirements.txt
COPY . /app
-1
View File
@@ -1 +0,0 @@
python-3.6.6
+1 -1
View File
@@ -6,4 +6,4 @@ verify_ssl = true
requests = "*" requests = "*"
[requires] [requires]
python_full_version = "3.6.3" python_full_version = "3.7.8"
+2 -15
View File
@@ -1,24 +1,11 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "22a052f4d1cfe6518b2f236fe45c3208c587a9ab1323bdd390632e27278b541e" "sha256": "8a36860f0f9cb55716222098062cea5c5e0f8127cafb9d0c694de327bac9fbc0"
},
"host-environment-markers": {
"implementation_name": "cpython",
"implementation_version": "3.6.3",
"os_name": "posix",
"platform_machine": "x86_64",
"platform_python_implementation": "CPython",
"platform_release": "16.7.0",
"platform_system": "Darwin",
"platform_version": "Darwin Kernel Version 16.7.0: Thu Jun 15 17:36:27 PDT 2017; root:xnu-3789.70.16~2/RELEASE_X86_64",
"python_full_version": "3.6.3",
"python_version": "3.6",
"sys_platform": "darwin"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
"python_full_version": "3.6.3" "python_full_version": "3.7.8"
}, },
"sources": [ "sources": [
{ {
+1 -1
View File
@@ -1 +1 @@
pypy2.7-7.2.0 pypy2.7-7.3.1
+1 -1
View File
@@ -1 +1 @@
pypy3.6-7.2.0 pypy3.6-7.3.1
-1
View File
@@ -1 +0,0 @@
requests
-1
View File
@@ -1 +0,0 @@
flask
-1
View File
@@ -1 +0,0 @@
flask
+1 -1
View File
@@ -1 +1 @@
python-3.6.8 python-3.6.12
-1
View File
@@ -1 +0,0 @@
requests
+1 -1
View File
@@ -1 +1 @@
python-3.6.7 python-3.6.11
+1 -1
View File
@@ -1 +1 @@
python-3.7.2 python-3.7.9
-1
View File
@@ -1 +0,0 @@
requests
+1 -1
View File
@@ -1 +1 @@
python-3.7.1 python-3.7.8
+1 -1
View File
@@ -1 +1 @@
python-3.8.2 python-3.8.6
-1
View File
@@ -1 +0,0 @@
requests
+1 -1
View File
@@ -1 +1 @@
python-3.8.0 python-3.8.5
-1
View File
@@ -1 +0,0 @@
flask
+12 -16
View File
@@ -21,16 +21,13 @@ testGEOS() {
local env_dir="$(mktmpdir)" local env_dir="$(mktmpdir)"
echo '1' > "${env_dir}/BUILD_WITH_GEO_LIBRARIES" echo '1' > "${env_dir}/BUILD_WITH_GEO_LIBRARIES"
compile 'geos' '' "${env_dir}" compile 'geos' '' "${env_dir}"
assertCaptured "geos" if [[ $STACK == "heroku-20" ]]; then
assertCapturedSuccess assertCaptured " ! The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality is not supported on Heroku-20."
} else
assertCaptured " ! The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality are now deprecated."
testGEOSDeprecation() { fi
local env_dir="$(mktmpdir)" # This should assertCapturedError on Heroku-20, but the test doesn't actually
echo '1' > "${env_dir}/BUILD_WITH_GEO_LIBRARIES" # install anything that uses GEOS so succeeds (see W-8145375)
compile 'geos' '' "${env_dir}"
assertCaptured " ! The GDAL, GEOS and PROJ binaries and BUILD_WITH_GEO_LIBRARIES functonality are now deprecated.
! An alternative buildpack to enable GDAL, GEOS and PROJ use is available here - https://github.com/heroku/heroku-geo-buildpack"
assertCapturedSuccess assertCapturedSuccess
} }
@@ -55,17 +52,16 @@ testPsycopg2() {
} }
testPysqlite() { testPysqlite() {
# pysqlite does not support Python 3 (since the sqlite3 stdlib can be used there),
# so we have to test with Python 2, which we've not made available for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "pysqlite" compile "pysqlite"
assertCaptured "pysqlite" assertCaptured "pysqlite"
assertCapturedSuccess assertCapturedSuccess
} }
testSqliteInstall() {
compile "pythonDefault"
assertNotCaptured "Sqlite3 failed to install."
assertCapturedSuccess
}
testCffi() { testCffi() {
compile "cffi" compile "cffi"
assertCaptured "cffi" assertCaptured "cffi"
+16 -6
View File
@@ -59,24 +59,33 @@ testPipenvLock() {
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
} }
testPipenvVersion() { testPipenvPythonVersion3_6() {
compile "pipenv-version" compile "pipenv-version"
assertCaptured $DEFAULT_PYTHON_VERSION assertCaptured "Installing ${LATEST_36}"
# Can't use `assertCapturedSuccess` since stderr contains: # Can't use `assertCapturedSuccess` since stderr contains:
# "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941) # "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941)
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
} }
testPipenvVersion2() { testPipenvPythonVersion2_7() {
# Python 2.7 is EOL, so it has not been built for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "pipenv-version2" compile "pipenv-version2"
assertCaptured $LATEST_27 assertCaptured "Installing ${LATEST_27}"
# Can't use `assertCapturedSuccess` since stderr contains: # Can't use `assertCapturedSuccess` since stderr contains:
# "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941) # "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941)
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
} }
testPipenvFullVersion() {
testPipenvPythonFullVersion() {
# Python 3.7+ requires newer libssl than is present on Cedar-14.
if [[ "${STACK}" = "cedar-14" ]]; then
return
fi
compile "pipenv-full-version" compile "pipenv-full-version"
assertCaptured "3.6.3" assertCaptured "3.7.8"
# Can't use `assertCapturedSuccess` since stderr contains: # Can't use `assertCapturedSuccess` since stderr contains:
# "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941) # "cp: cannot stat '/tmp/build_*/requirements.txt': No such file or directory" (W-7924941)
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
@@ -107,6 +116,7 @@ testHooks() {
local expected_env_vars=( local expected_env_vars=(
_ _
BIN_DIR BIN_DIR
BPLOG_PREFIX
BUILD_DIR BUILD_DIR
BUILDPACK_LOG_FILE BUILDPACK_LOG_FILE
CACHE_DIR CACHE_DIR
+106 -93
View File
@@ -4,59 +4,78 @@
# shellcheck source=bin/default_pythons # shellcheck source=bin/default_pythons
source "bin/default_pythons" source "bin/default_pythons"
testPythonDefault() { testPythonVersionUnspecified() {
updateVersion "pythonDefault" $DEFAULT_PYTHON_VERSION compile "python_version_unspecified"
compile "pythonDefault" assertCaptured "Installing ${DEFAULT_PYTHON_VERSION}"
assertCaptured $DEFAULT_PYTHON_VERSION
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3" assertCaptured "Installing SQLite3"
assertCapturedSuccess assertCapturedSuccess
} }
testPython2() { testPython2_7() {
updateVersion "python2" $LATEST_27 # Python 2.7 is EOL, so it has not been built for Heroku-20.
echo $LATEST_27 > "runtime.txt" if [[ $STACK == "heroku-20" ]]; then
compile "python2" return
assertCaptured $LATEST_27 fi
assertCaptured "python-2-7-eol-faq"; compile "python2"
assertNotCaptured "security update" assertCaptured "Installing ${LATEST_27}"
assertCaptured "Installing pip 20.1.1, setuptools 44.1.1 and wheel 0.34.2" assertCaptured "python-2-7-eol-faq";
assertCaptured "Installing SQLite3" assertNotCaptured "security update"
assertCapturedSuccess assertCaptured "Installing pip 20.1.1, setuptools 44.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
} }
testPython2_warn() { testPython2_7_warn() {
compile "python2_warn" # Python 2.7 is EOL, so it has not been built for Heroku-20.
assertCaptured "python-2.7.15" if [[ $STACK == "heroku-20" ]]; then
assertCaptured "python-2-7-eol-faq"; return
assertCaptured "Only the latest version" fi
assertCaptured "Installing SQLite3" compile "python2_warn"
assertCapturedSuccess assertCaptured "Installing python-2.7.15"
assertCaptured "python-2-7-eol-faq";
assertCaptured "Only the latest version"
assertCaptured "${LATEST_27}"
assertCapturedSuccess
} }
testPython3_4() { testPython3_4() {
# Python 3.4 is EOL, so it has not been built for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "python3_4" compile "python3_4"
assertCaptured $LATEST_34 assertCaptured "Installing ${LATEST_34}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "Installing pip 19.1.1, setuptools 43.0.0 and wheel 0.33.6" assertCaptured "Installing pip 19.1.1, setuptools 43.0.0 and wheel 0.33.6"
assertCaptured "Installing SQLite3"
# Can't use `assertCapturedSuccess` since Pip outputs a Python 3.4 EOL warning to stderr, # Can't use `assertCapturedSuccess` since Pip outputs a Python 3.4 EOL warning to stderr,
# and the newest Pip that works on Python 3.4 doesn't support `PIP_NO_PYTHON_VERSION_WARNING`. # and the newest Pip that works on Python 3.4 doesn't support `PIP_NO_PYTHON_VERSION_WARNING`.
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
} }
testPython3_4_warn() { testPython3_4_warn() {
# Python 3.4 is EOL, so it has not been built for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "python3_4_warn" compile "python3_4_warn"
assertCaptured "python-3.4.9" assertCaptured "Installing python-3.4.9"
assertCaptured "security update!" assertCaptured "security update!"
assertCaptured "${LATEST_34}"
# Can't use `assertCapturedSuccess` since Pip outputs a Python 3.4 EOL warning to stderr, # Can't use `assertCapturedSuccess` since Pip outputs a Python 3.4 EOL warning to stderr,
# and the newest Pip that works on Python 3.4 doesn't support `PIP_NO_PYTHON_VERSION_WARNING`. # and the newest Pip that works on Python 3.4 doesn't support `PIP_NO_PYTHON_VERSION_WARNING`.
assertCapturedSuccessWithStdErr assertCapturedSuccessWithStdErr
} }
testPython3_5() { testPython3_5() {
# Python 3.5 is EOL, so it has not been built for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "python3_5" compile "python3_5"
assertCaptured $LATEST_35 assertCaptured "Installing ${LATEST_35}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3" assertCaptured "Installing SQLite3"
@@ -64,16 +83,20 @@ testPython3_5() {
} }
testPython3_5_warn() { testPython3_5_warn() {
# Python 3.5 is EOL, so it has not been built for Heroku-20.
if [[ $STACK == "heroku-20" ]]; then
return
fi
compile "python3_5_warn" compile "python3_5_warn"
assertCaptured "python-3.5.6" assertCaptured "Installing python-3.5.6"
assertCaptured "security update!" assertCaptured "security update!"
assertCaptured "${LATEST_35}"
assertCapturedSuccess assertCapturedSuccess
} }
testPython3_6() { testPython3_6() {
updateVersion "python3_6" $LATEST_36
compile "python3_6" compile "python3_6"
assertCaptured $LATEST_36 assertCaptured "Installing ${LATEST_36}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3" assertCaptured "Installing SQLite3"
@@ -82,62 +105,60 @@ testPython3_6() {
testPython3_6_warn() { testPython3_6_warn() {
compile "python3_6_warn" compile "python3_6_warn"
assertCaptured "python-3.6.7" assertCaptured "Installing python-3.6.11"
assertCaptured "security update!" assertCaptured "security update!"
assertCaptured "Installing SQLite3" assertCaptured "${LATEST_36}"
assertCapturedSuccess assertCapturedSuccess
} }
testPython3_7() { testPython3_7() {
updateVersion "python3_7" $LATEST_37 # Python 3.7+ requires newer libssl than is present on Cedar-14.
compile "python3_7" if [[ "${STACK}" = "cedar-14" ]]; then
if [[ $STACK = "cedar-14" ]]; then return
assertCapturedError
else
assertNotCaptured "security update"
assertCaptured $LATEST_37
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
fi fi
compile "python3_7"
assertCaptured "Installing ${LATEST_37}"
assertNotCaptured "security update"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
} }
testPython3_7_warn() { testPython3_7_warn() {
compile "python3_7_warn" # Python 3.7+ requires newer libssl than is present on Cedar-14.
if [[ $STACK = "cedar-14" ]]; then if [[ "${STACK}" = "cedar-14" ]]; then
assertCapturedError return
else
assertCaptured "python-3.7.1"
assertCaptured "security update!"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
fi fi
compile "python3_7_warn"
assertCaptured "Installing python-3.7.8"
assertCaptured "security update!"
assertCaptured "${LATEST_37}"
assertCapturedSuccess
} }
testPython3_8() { testPython3_8() {
updateVersion "python3_8" $LATEST_38 # Python 3.7+ requires newer libssl than is present on Cedar-14.
compile "python3_8" if [[ "${STACK}" = "cedar-14" ]]; then
if [[ $STACK = "cedar-14" ]]; then return
assertCapturedError
else
assertNotCaptured "security update"
assertCaptured $LATEST_38
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
fi fi
compile "python3_8"
assertCaptured "Installing ${LATEST_38}"
assertNotCaptured "security update"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
} }
testPython3_8_warn() { testPython3_8_warn() {
compile "python3_8_warn" # Python 3.7+ requires newer libssl than is present on Cedar-14.
if [[ $STACK = "cedar-14" ]]; then if [[ "${STACK}" = "cedar-14" ]]; then
assertCapturedError return
else
assertCaptured "python-3.8.0"
assertCaptured "security update!"
assertCaptured "Installing SQLite3"
assertCapturedSuccess
fi fi
compile "python3_8_warn"
assertCaptured "Installing python-3.8.5"
assertCaptured "security update!"
assertCaptured "${LATEST_38}"
assertCapturedSuccess
} }
testPython3_9() { testPython3_9() {
@@ -146,8 +167,8 @@ testPython3_9() {
return return
fi fi
compile "python3_9" compile "python3_9"
assertCaptured "Installing ${LATEST_39}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured $LATEST_39
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCaptured "Installing SQLite3" assertCaptured "Installing SQLite3"
assertCapturedSuccess assertCapturedSuccess
@@ -163,8 +184,9 @@ testPython3_9_warn() {
return return
fi fi
compile "python3_9_warn" compile "python3_9_warn"
assertCaptured "python-3.9.0" assertCaptured "Installing python-3.9.0"
assertCaptured "security update!" assertCaptured "security update!"
assertCaptured "${LATEST_39}"
assertCapturedSuccess assertCapturedSuccess
} }
@@ -177,67 +199,58 @@ testPythonVersionInvalid() {
testPypy3_6() { testPypy3_6() {
compile "pypy3_6" compile "pypy3_6"
assertCaptured "Installing pypy" assertCaptured "Installing ${LATEST_PYPY_36}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "$LATEST_PYPY_36"
assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 47.1.1 and wheel 0.34.2"
assertCapturedSuccess assertCapturedSuccess
} }
testPypy3_6_warn() { testPypy3_6_warn() {
compile "pypy3_6_warn" compile "pypy3_6_warn"
if [[ $STACK = "cedar-14" ]]; then assertCaptured "Installing pypy3.6-7.3.1"
assertCapturedError assertCaptured "security update!"
else assertCaptured "${LATEST_PYPY_36}"
assertCaptured "Installing pypy" assertCapturedSuccess
assertCaptured "security update!"
assertCaptured "$LATEST_PYPY_36"
assertCapturedSuccess
fi
} }
testPypy2_7() { testPypy2_7() {
compile "pypy2_7" compile "pypy2_7"
assertCaptured "Installing pypy" assertCaptured "Installing ${LATEST_PYPY_27}"
assertNotCaptured "security update" assertNotCaptured "security update"
assertCaptured "$LATEST_PYPY_27"
assertCaptured "Installing pip 20.1.1, setuptools 44.1.1 and wheel 0.34.2" assertCaptured "Installing pip 20.1.1, setuptools 44.1.1 and wheel 0.34.2"
assertCapturedSuccess assertCapturedSuccess
} }
testPypy2_7_warn() { testPypy2_7_warn() {
compile "pypy2_7_warn" compile "pypy2_7_warn"
if [[ $STACK = "cedar-14" ]]; then assertCaptured "Installing pypy2.7-7.3.1"
assertCapturedError assertCaptured "security update!"
else assertCaptured "${LATEST_PYPY_27}"
assertCaptured "Installing pypy" assertCapturedSuccess
assertCaptured "security update!"
assertCaptured "$LATEST_PYPY_27"
assertCapturedSuccess
fi
} }
testStickyPythonVersion() { testStickyPythonVersion() {
local cache_dir="$(mktmpdir)" local cache_dir="$(mktmpdir)"
compile "python3_6_warn" "$cache_dir" compile "python3_6_warn" "$cache_dir"
assertCaptured "Installing python-3.6.7" assertCaptured "Installing python-3.6.11"
assertCapturedSuccess assertCapturedSuccess
compile "no-runtime-txt" "$cache_dir" compile "python_version_unspecified" "$cache_dir"
assertCaptured "Installing python-3.6.7" assertNotCaptured "Installing python"
assertCaptured "security update!"
assertCapturedSuccess assertCapturedSuccess
# Whilst this file seems like an implementation detail (so something that should # Whilst this file seems like an implementation detail (so something that should
# not be tested), we must guarantee the filename remains consistent for backwards # not be tested), we must guarantee the filename remains consistent for backwards
# compatibility across buildpack versions for already-built apps. # compatibility across buildpack versions for already-built apps.
assertFile "python-3.6.7" ".heroku/python-version" assertFile "python-3.6.11" ".heroku/python-version"
} }
testPythonVersionChange() { testPythonVersionChange() {
local cache_dir="$(mktmpdir)" local cache_dir="$(mktmpdir)"
compile "python3_6_warn" "$cache_dir" compile "python3_6_warn" "$cache_dir"
assertCaptured "Installing python-3.6.7" assertCaptured "Installing python-3.6.11"
assertCapturedSuccess assertCapturedSuccess
compile "python3_6" "$cache_dir" compile "python3_6" "$cache_dir"
assertCaptured "Found python-3.6.7, removing" assertCaptured "Found python-3.6.11, removing"
assertCapturedSuccess assertCapturedSuccess
} }
-5
View File
@@ -56,11 +56,6 @@ resetCapture()
unset rtrn # deprecated unset rtrn # deprecated
} }
updateVersion()
{
echo "$2" > "test/fixtures/${1}/runtime.txt"
}
assertCapturedEquals() assertCapturedEquals()
{ {
assertEquals "$@" "$(cat ${STD_OUT})" assertEquals "$@" "$(cat ${STD_OUT})"
+195
View File
@@ -0,0 +1,195 @@
#!/usr/bin/env bash
# From:
# https://raw.githubusercontent.com/heroku/buildpack-stdlib/v8/stdlib.sh
# Buildpack defaults
# ---------------
export BUILDPACK_LOG_FILE="${BUILDPACK_LOG_FILE:-/dev/null}"
# Standard Output
# ---------------
# Buildpack Steps.
puts_step() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[36m=== $output\\e[0m"
unset output
}
# Buildpack Error.
puts_error() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[31m=!= $output\\e[0m"
}
# Buildpack Warning.
puts_warn() {
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo -e "\\e[1m\\e[33m=!= $output\\e[0m"
}
# Is verbose set?
is_verbose() {
if [[ -n $BUILDPACK_VERBOSE ]]; then
return 0
else
return 1
fi
}
# Buildpack Verbose.
puts_verbose() {
if is_verbose; then
if [[ "$*" == "-" ]]; then
read -r output
else
output=$*
fi
echo "$output"
unset output
fi
}
# Buildpack Utilities
# -------------------
# Usage: $ set-env key value
# NOTICE: Expects PROFILE_PATH & EXPORT_PATH to be set!
set_env() {
# TODO: automatically create profile path directory if it doesn't exist.
echo "export $1=$2" >> "$PROFILE_PATH"
echo "export $1=$2" >> "$EXPORT_PATH"
}
# Usage: $ set-default-env key value
# NOTICE: Expects PROFILE_PATH & EXPORT_PATH to be set!
set_default_env() {
echo "export $1=\${$1:-$2}" >> "$PROFILE_PATH"
echo "export $1=\${$1:-$2}" >> "$EXPORT_PATH"
}
# Usage: $ un-set-env key
# NOTICE: Expects PROFILE_PATH to be set!
un_set_env() {
echo "unset $1" >> "$PROFILE_PATH"
}
# Usage: $ _env-blacklist pattern
# Outputs a regex of default blacklist env vars.
_env_blacklist() {
local regex=${1:-''}
if [ -n "$regex" ]; then
regex="|$regex"
fi
echo "^(PATH|GIT_DIR|CPATH|CPPATH|LD_PRELOAD|LIBRARY_PATH$regex)$"
}
# Usage: $ export-env ENV_DIR WHITELIST BLACKLIST
# Exports the environment variables defined in the given directory.
export_env() {
local env_dir=${1:-$ENV_DIR}
local whitelist=${2:-''}
local blacklist
blacklist="$(_env_blacklist "$3")"
if [ -d "$env_dir" ]; then
# Environment variable names won't contain characters affected by:
# shellcheck disable=SC2045
for e in $(ls "$env_dir"); do
echo "$e" | grep -E "$whitelist" | grep -qvE "$blacklist" &&
export "$e=$(cat "$env_dir/$e")"
:
done
fi
}
# Usage: $ sub-env command
# Runs a subshell of specified command with user-provided config.
# NOTICE: Expects ENV_DIR to be set. WHITELIST & BLACKLIST are optional.
# Examples:
# WHITELIST=${2:-''}
# BLACKLIST=${3:-'^(GIT_DIR|PYTHONHOME|LD_LIBRARY_PATH|LIBRARY_PATH|PATH)$'}
sub_env() {
(
# TODO: Fix https://github.com/heroku/buildpack-stdlib/issues/37
# shellcheck disable=SC2153
export_env "$ENV_DIR" "$WHITELIST" "$BLACKLIST"
"$@"
)
}
# Logging
# -------
# Notice: These functions expect BPLOG_PREFIX and BUILDPACK_LOG_FILE to be defined (BUILDPACK_LOG_FILE can point to /dev/null if not provided by the buildpack).
# Example: BUILDPACK_LOG_FILE=${BUILDPACK_LOG_FILE:-/dev/null}; BPLOG_PREFIX="buildpack.go"
# Returns now, in milleseconds. Useful for logging.
# Example: $ let start=$(nowms); sleep 30; mtime "glide.install.time" "${start}"
nowms() {
date +%s%3N
}
# Log arbitrary data to the logfile (e.g. a packaging file).
# Usage: $ bplog "$(<${vendorJSON})
bplog() {
echo -n "${@}" | awk 'BEGIN {printf "msg=\""; f="%s"} {gsub(/"/, "\\\"", $0); printf f, $0} {if (NR == 1) f="\\n%s" } END { print "\"" }' >> "${BUILDPACK_LOG_FILE}"
}
# Measures time elapsed for a specific build step.
# Usage: $ let start=$(nowms); mtime "glide.install.time" "${start}"
# https://github.com/heroku/engineering-docs/blob/master/guides/logs-as-data.md#distributions-measure
mtime() {
local key="${BPLOG_PREFIX}.${1}"
local start="${2}"
local end="${3:-$(nowms)}"
echo "${key} ${start} ${end}" | awk '{ printf "measure#%s=%.3f\n", $1, ($3 - $2)/1000 }' >> "${BUILDPACK_LOG_FILE}"
}
# Logs a count for a specific built step.
# Usage: $ mcount "tool.govendor"
# https://github.com/heroku/engineering-docs/blob/master/guides/logs-as-data.md#counting-count
mcount() {
local k="${BPLOG_PREFIX}.${1}"
local v="${2:-1}"
echo "count#${k}=${v}" >> "${BUILDPACK_LOG_FILE}"
}
# Logs a measure for a specific build step.
# Usage: $ mmeasure "tool.installed_dependencies" 42
# https://github.com/heroku/engineering-docs/blob/master/guides/logs-as-data.md#distributions-measure
mmeasure() {
local k="${BPLOG_PREFIX}.${1}"
local v="${2}"
echo "measure#${k}=${v}" >> "${BUILDPACK_LOG_FILE}"
}
# Logs a unuique measurement build step.
# Usage: $ munique "versions.count" 2.7.13
# https://github.com/heroku/engineering-docs/blob/master/guides/logs-as-data.md#uniques-unique
munique() {
local k="${BPLOG_PREFIX}.${1}"
local v="${2}"
echo "unique#${k}=${v}" >> "${BUILDPACK_LOG_FILE}"
}
# Measures when an exit path to the buildpack is reached, given a name, then exits 1.
# Usage: $ mcount-exi "binExists"
mcount_exit() {
mcount "error.${1}"
exit 1
}