diff --git a/CHANGELOG.md b/CHANGELOG.md index b608d9a90..03469c50e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,23 @@ ## [Unreleased] +## [v338] - 2026-03-03 + +- The Python 3.12 version alias now resolves to Python 3.12.13. ([#2046](https://github.com/heroku/heroku-buildpack-python/pull/2046)) +- The Python 3.11 version alias now resolves to Python 3.11.15. ([#2046](https://github.com/heroku/heroku-buildpack-python/pull/2046)) +- The Python 3.10 version alias now resolves to Python 3.10.20. ([#2046](https://github.com/heroku/heroku-buildpack-python/pull/2046)) + +## [v337] - 2026-03-03 + +- Updated pip from 25.3 to 26.0.1. ([#2032](https://github.com/heroku/heroku-buildpack-python/pull/2032)) +- Updated uv from 0.10.1 to 0.10.7. ([#2041](https://github.com/heroku/heroku-buildpack-python/pull/2041)) +- Explicitly configured uv to use hard links to maintain the behaviour of previous versions. ([#2044](https://github.com/heroku/heroku-buildpack-python/pull/2044)) + +## [v336] - 2026-03-02 + +- Added a workaround for `nltk.txt` package downloader errors caused by an upstream regression in NLTK v3.9.3. ([#2041](https://github.com/heroku/heroku-buildpack-python/pull/2041)) +- Changed the S3 URL used to download Python to use AWS' dual-stack (IPv6 compatible) endpoint. ([#2035](https://github.com/heroku/heroku-buildpack-python/pull/2035)) + ## [v335] - 2026-02-10 - Updated uv from 0.9.29 to 0.10.1. ([#2031](https://github.com/heroku/heroku-buildpack-python/pull/2031)) @@ -1478,7 +1495,10 @@ Default Python is now latest 2.7.10. Updated pip and Distribute. - Setuptools updated to v16.0 - pip updated to v7.0.1 -[unreleased]: https://github.com/heroku/heroku-buildpack-python/compare/v335...main +[unreleased]: https://github.com/heroku/heroku-buildpack-python/compare/v338...main +[v338]: https://github.com/heroku/heroku-buildpack-python/compare/v337...v338 +[v337]: https://github.com/heroku/heroku-buildpack-python/compare/v336...v337 +[v336]: https://github.com/heroku/heroku-buildpack-python/compare/v335...v336 [v335]: https://github.com/heroku/heroku-buildpack-python/compare/v334...v335 [v334]: https://github.com/heroku/heroku-buildpack-python/compare/v333...v334 [v333]: https://github.com/heroku/heroku-buildpack-python/compare/v332...v333 diff --git a/Gemfile b/Gemfile index 2abf2b7cb..7ed84f7d1 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -ruby '>= 3.2', '< 3.5' +ruby '>= 3.2', '< 4.1' group :test, :development do gem 'heroku_hatchet' diff --git a/Gemfile.lock b/Gemfile.lock index 1d69d2744..4c7138d8f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,11 +1,14 @@ GEM remote: https://rubygems.org/ specs: + addressable (2.8.9) + public_suffix (>= 2.0.2, < 8.0) ast (2.4.3) base64 (0.3.0) + bigdecimal (4.0.1) diff-lcs (1.6.2) erubis (2.7.0) - excon (1.3.1) + excon (1.4.0) logger heroics (0.1.3) base64 @@ -20,24 +23,30 @@ GEM rrrretry (~> 1) thor (~> 1) threaded (~> 0) - json (2.18.0) + json (2.18.1) + json-schema (6.1.0) + addressable (~> 2.8) + bigdecimal (>= 3.1, < 5) language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) + mcp (0.8.0) + json-schema (>= 4.1) moneta (1.0.0) - multi_json (1.18.0) + multi_json (1.19.1) parallel (1.27.0) parallel_split_test (0.10.0) parallel (>= 0.5.13) rspec-core (>= 3.9.0) - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc - platform-api (3.8.0) + platform-api (3.9.0) heroics (~> 0.1.1) moneta (~> 1.0.0) rate_throttle_client (~> 0.1.0) prism (1.9.0) + public_suffix (7.0.5) racc (1.8.1) rainbow (3.1.1) rate_throttle_client (0.1.2) @@ -50,11 +59,12 @@ GEM rspec-support (~> 3.13.0) rspec-retry (0.6.2) rspec-core (> 3.3) - rspec-support (3.13.6) - rubocop (1.84.0) + rspec-support (3.13.7) + rubocop (1.85.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) @@ -69,7 +79,7 @@ GEM lint_roller (~> 1.1) rubocop (~> 1.81) ruby-progressbar (1.13.0) - thor (1.4.0) + thor (1.5.0) threaded (0.0.4) unicode-display_width (3.2.0) unicode-emoji (~> 4.1) @@ -77,7 +87,9 @@ GEM webrick (1.9.2) PLATFORMS + arm64-darwin-24 ruby + x86_64-linux DEPENDENCIES heroku_hatchet @@ -88,8 +100,51 @@ DEPENDENCIES rubocop rubocop-rspec +CHECKSUMS + addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 + ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 + diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 + erubis (2.7.0) sha256=63653f5174a7997f6f1d6f465fbe1494dcc4bdab1fb8e635f6216989fb1148ba + excon (1.4.0) sha256=5d2bc9d2c79511a562e7fcac77cc7a40acd9cebcc55b80e537975ad8187f2924 + heroics (0.1.3) sha256=31d792e8706ecc6f78299f52d0b0f8ab55489a13b5f4f76c3050b07912563562 + heroku_hatchet (8.0.6) sha256=886a7d7d686859db9fac19df1194a35c5349285956ca4d134054c5c376accea3 + json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 + json-schema (6.1.0) sha256=6bf70a2cfb6dfd5a06da28093fa8190f324c88eabd36a7f47097f227321dc702 + language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc + lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb + moneta (1.0.0) sha256=2224e5a68156e8eceb525fb0582c8c4e0f29f67cae86507cdcfb406abbb1fc5d + multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 + parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parallel_split_test (0.10.0) sha256=4abd6bedc6a1b169ecb93a1bf982fa7fbd437b66c360594d56d107c25f53b05f + parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357 + platform-api (3.9.0) sha256=be3b919955c52649fd931a9f62d571bd6f06613de630970dc7255fc95eb7d962 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a + rate_throttle_client (0.1.2) sha256=f9de968b892fea9272154f6182b4f5cfb74292585e66763fb8a8510181ec83ee + regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 + rrrretry (1.0.0) sha256=3c60784501701a49d8ad499af7e76dbddf9a8be916beffe885bd0f443ad1c749 + rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858 + rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c + rubocop (1.85.0) sha256=317407feb681a07d54f64d2f9e1d6b6af1ce7678e51cd658e3ad8bd66da48c01 + rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd + rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 + ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 + thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 + threaded (0.0.4) sha256=8f32c11d29f7c7237aabeb36d286c15ac495137ce06ace3357de743aafd4f0e3 + unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 + unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f + webrick (1.9.2) sha256=beb4a15fc474defed24a3bda4ffd88a490d517c9e4e6118c3edce59e45864131 + RUBY VERSION - ruby 3.4.7p58 + ruby 4.0.1 BUNDLED WITH - 2.7.2 + 4.0.7 diff --git a/bin/steps/nltk b/bin/steps/nltk index 2a6d3c096..647e05dbb 100755 --- a/bin/steps/nltk +++ b/bin/steps/nltk @@ -33,10 +33,11 @@ if is_module_available 'nltk'; then readarray -t nltk_packages <"${nltk_packages_definition}" output::step "Downloading NLTK packages: ${nltk_packages[*]}" - nltk_data_dir="/app/.scalingo/python/nltk_data" - + # Note: We have to use the raw build directory path here and not the symlinked `/app` path, + # otherwise it will cause a false positive in NLTK v3.9.3's new Zip Slip security check, + # which doesn't handle symlinked paths correctly: https://github.com/nltk/nltk/issues/3509 # TODO: Does this even need user-provided env vars, or can we remove the sub_env usage here? - if ! sub_env python -m nltk.downloader -d "${nltk_data_dir}" "${nltk_packages[@]}" |& output::indent; then + if ! sub_env python -m nltk.downloader -d "${BUILD_DIR}/.scalingo/python/nltk_data" "${nltk_packages[@]}" |& output::indent; then output::error <<-EOF Error: Unable to download NLTK data. @@ -49,7 +50,9 @@ if is_module_available 'nltk'; then exit 1 fi - set_env NLTK_DATA "${nltk_data_dir}" + # Since this will be used at runtime, we must use the symlinked `/app` path and not + # the raw build directory path. + set_env NLTK_DATA "/app/.heroku/python/nltk_data" else build_data::set_string "nltk_downloader" "skipped-no-nltk-file" echo " 'nltk.txt' not found, not downloading any corpora" diff --git a/builds/Dockerfile b/builds/Dockerfile index be7742e9e..53610332e 100644 --- a/builds/Dockerfile +++ b/builds/Dockerfile @@ -1,5 +1,5 @@ ARG STACK_VERSION="24" -FROM ghcr.io/sigstore/cosign/cosign:v3.0.4@sha256:0b015a3557a64a751712da8a6395534160018eaaa2d969882a85a336de9adb70 AS cosign +FROM ghcr.io/sigstore/cosign/cosign:v3.0.5@sha256:be924970ba7438c22e18067dec5637946d6566eac711f5bedd1584e7137008fb AS cosign FROM heroku/heroku:${STACK_VERSION}-build ARG STACK_VERSION diff --git a/lib/python.sh b/lib/python.sh index 2accf87bc..06a62b8e8 100644 --- a/lib/python.sh +++ b/lib/python.sh @@ -30,9 +30,8 @@ function python::install() { ubuntu_version="${ubuntu_version: -2}.04" local arch - arch=$( dpkg --print-architecture ) - - # e.g.: https://heroku-buildpack-python.s3.us-east-1.amazonaws.com/python-3.13.0-ubuntu-24.04-amd64.tar.zst + arch=$(dpkg --print-architecture) + # e.g.: https://heroku-buildpack-python.s3.dualstack.us-east-1.amazonaws.com/python-3.14.0-ubuntu-24.04-amd64.tar.zst local python_url="${S3_BASE_URL}/python-${python_full_version}-ubuntu-${ubuntu_version}-${arch}.tar.zst" local error_log @@ -117,7 +116,7 @@ function python::install() { Then try building again to see if the error resolves itself. EOF build_data::set_string "failure_reason" "install-python" - # e.g.: 'curl: (6) Could not resolve host: heroku-buildpack-python.s3.us-east-1.amazonaws.com' + # e.g.: 'curl: (6) Could not resolve host: heroku-buildpack-python.s3.dualstack.us-east-1.amazonaws.com' build_data::set_string "failure_detail" "$(head --lines=1 "${error_log}" || true)" fi diff --git a/lib/python_version.sh b/lib/python_version.sh index c8b4c32bf..04f45c61e 100644 --- a/lib/python_version.sh +++ b/lib/python_version.sh @@ -4,9 +4,9 @@ # however, it helps Shellcheck realise the options under which these functions will run. set -euo pipefail -LATEST_PYTHON_3_10="3.10.19" -LATEST_PYTHON_3_11="3.11.14" -LATEST_PYTHON_3_12="3.12.12" +LATEST_PYTHON_3_10="3.10.20" +LATEST_PYTHON_3_11="3.11.15" +LATEST_PYTHON_3_12="3.12.13" LATEST_PYTHON_3_13="3.13.12" LATEST_PYTHON_3_14="3.14.3" diff --git a/lib/uv.sh b/lib/uv.sh index a956613d1..c50efaaae 100644 --- a/lib/uv.sh +++ b/lib/uv.sh @@ -88,6 +88,9 @@ function uv::install_uv() { # Prevent uv from downloading/using its own Python installation. export UV_NO_MANAGED_PYTHON="1" export UV_PYTHON_DOWNLOADS="never" + # Force uv to use hardlinks rather than the new default of reflinks, since the latter are + # significantly slower on Kodon: https://github.com/astral-sh/uv/issues/18259 + export UV_LINK_MODE="hardlink" # Set the same env vars in the environment used by later buildpacks. cat >>"${export_file}" <<-EOF @@ -95,6 +98,7 @@ function uv::install_uv() { export UV_PROJECT_ENVIRONMENT="${python_home}" export UV_NO_MANAGED_PYTHON="1" export UV_PYTHON_DOWNLOADS="never" + export UV_LINK_MODE="hardlink" EOF # As a performance optimisation, uv attempts to use hardlinks instead of copying files from its diff --git a/requirements/pip.txt b/requirements/pip.txt index 0b3d671e6..c1dd99e28 100644 --- a/requirements/pip.txt +++ b/requirements/pip.txt @@ -1 +1 @@ -pip==25.3 +pip==26.0.1 diff --git a/requirements/uv.txt b/requirements/uv.txt index cc5692614..971e116c0 100644 --- a/requirements/uv.txt +++ b/requirements/uv.txt @@ -1 +1 @@ -uv==0.10.1 +uv==0.10.7