From 96e5bea5603b78b40d6c2b59e738cc2b934d8d3c Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:06:46 +0000 Subject: [PATCH 01/12] Switch to AWS' dual-stack S3 URLs (#2035) The default AWS S3 URLs only support IPv4, whereas the dual-stack URLs support both IPv4 and IPv6: https://docs.aws.amazon.com/AmazonS3/latest/API/ipv6-access.html https://docs.aws.amazon.com/AmazonS3/latest/API/dual-stack-endpoints.html By switching to the dual-stack URLs, it means systems that support IPv6 will now use it instead of IPv4, which is helpful in IPv6 environments where IPv4 traffic has to fall back to being routed via NAT gateways (which has performance and ingress cost implications). See also: https://salesforce-internal.slack.com/archives/C01R6FJ738U/p1770827176094169 GUS-W-21335961. --- CHANGELOG.md | 1 + lib/python.sh | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c013734d3..c08d57b1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- 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 diff --git a/lib/python.sh b/lib/python.sh index ea87c2c63..0fa91862d 100644 --- a/lib/python.sh +++ b/lib/python.sh @@ -4,7 +4,7 @@ # however, it helps Shellcheck realise the options under which these functions will run. set -euo pipefail -S3_BASE_URL="https://heroku-buildpack-python.s3.us-east-1.amazonaws.com" +S3_BASE_URL="https://heroku-buildpack-python.s3.dualstack.us-east-1.amazonaws.com" function python::install() { local build_dir="${1}" @@ -28,7 +28,7 @@ function python::install() { local ubuntu_version="${stack/heroku-/}.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 + # 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 +117,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 From 361215e907719287c821d69639203fe615d7560d Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:14:39 +0000 Subject: [PATCH 02/12] Work around NLTK v3.9.3 regression (#2042) In NLTK v3.9.3, a new security check was added for the Zip Slip issue: https://github.com/nltk/nltk/pull/3468 This change unfortunately contained a bug that causes false positive security errors when packages are downloaded to a symlinked path, since the new check doesn't use `abspath` vs `realpath` consistently: https://github.com/nltk/nltk/issues/3509 This causes errors like: ``` -----> Downloading NLTK packages: punkt punkt_tab :128: RuntimeWarning: 'nltk.downloader' found in sys.modules after import of package 'nltk', but prior to execution of 'nltk.downloader'; this may result in unpredictable behaviour [nltk_data] Downloading package punkt to [nltk_data] /app/.heroku/python/nltk_data... [nltk_data] Unzipping tokenizers/punkt.zip. [nltk_data] Zip Slip blocked: punkt/ Error installing package. Retry? [n/y/e] Traceback (most recent call last): File "", line 198, in _run_module_as_main File "", line 88, in _run_code File "/app/.heroku/python/lib/python3.12/site-packages/nltk/downloader.py", line 2631, in rv = downloader.download( ^^^^^^^^^^^^^^^^^^^^ File "/app/.heroku/python/lib/python3.12/site-packages/nltk/downloader.py", line 773, in download choice = input().strip() ^^^^^^^ EOFError: EOF when reading a line ``` See also: https://github.com/heroku/heroku-buildpack-python/actions/runs/22545824206/job/65308000022#step:5:539 Until upstream fix this regression, we can work around it by always passing the raw path for Python home instead of the symlinked path. (We generally prefer using the symlinked path where possible, to ensure the paths used/displayed match between build time and run time, given the build directory is different from the run directory.) Longer term I will be deprecating and that removing support for `nltk.txt` since it's a Heroku-proprietary invention that's unnecessary given most users instead either commit the NLTK corpora or use the post_compile hook feature instead. Fixes #2037. GUS-W-21410908. --- CHANGELOG.md | 1 + bin/steps/nltk | 11 +++++++---- spec/hatchet/nltk_spec.rb | 4 ++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c08d57b1c..fc7b10b64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- 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 diff --git a/bin/steps/nltk b/bin/steps/nltk index a1317ebf7..0e72cdd23 100755 --- a/bin/steps/nltk +++ b/bin/steps/nltk @@ -25,10 +25,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/.heroku/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}/.heroku/python/nltk_data" "${nltk_packages[@]}" |& output::indent; then output::error <<-EOF Error: Unable to download NLTK data. @@ -41,7 +42,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/spec/hatchet/nltk_spec.rb b/spec/hatchet/nltk_spec.rb index 56ed95ce4..1c5a5a59d 100644 --- a/spec/hatchet/nltk_spec.rb +++ b/spec/hatchet/nltk_spec.rb @@ -13,10 +13,10 @@ remote: -----> Downloading NLTK packages: city_database stopwords remote: .*: RuntimeWarning: 'nltk.downloader' found in sys.modules after import of package 'nltk', but prior to execution of 'nltk.downloader'; this may result in unpredictable behaviour remote: \\[nltk_data\\] Downloading package city_database to - remote: \\[nltk_data\\] /app/.heroku/python/nltk_data... + remote: \\[nltk_data\\] /tmp/build_.+/.heroku/python/nltk_data... remote: \\[nltk_data\\] Unzipping corpora/city_database.zip. remote: \\[nltk_data\\] Downloading package stopwords to - remote: \\[nltk_data\\] /app/.heroku/python/nltk_data... + remote: \\[nltk_data\\] /tmp/build_.+/.heroku/python/nltk_data... remote: \\[nltk_data\\] Unzipping corpora/stopwords.zip. REGEX From 3af8b100bad4a9a5a0a6a4213ab378766e5590c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:19:37 +0000 Subject: [PATCH 03/12] Bump rubocop from 1.84.0 to 1.85.0 in the ruby-dependencies group (#2039) Bumps the ruby-dependencies group with 1 update: [rubocop](https://github.com/rubocop/rubocop). Updates `rubocop` from 1.84.0 to 1.85.0 - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.84.0...v1.85.0) --- updated-dependencies: - dependency-name: rubocop dependency-version: 1.85.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: ruby-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Gemfile.lock | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1d69d2744..9d76f3600 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,11 @@ 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) @@ -20,17 +23,22 @@ 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.7.1) + json-schema (>= 4.1) moneta (1.0.0) multi_json (1.18.0) 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) @@ -38,6 +46,7 @@ GEM moneta (~> 1.0.0) rate_throttle_client (~> 0.1.0) prism (1.9.0) + public_suffix (7.0.2) racc (1.8.1) rainbow (3.1.1) rate_throttle_client (0.1.2) @@ -51,10 +60,11 @@ GEM rspec-retry (0.6.2) rspec-core (> 3.3) rspec-support (3.13.6) - rubocop (1.84.0) + 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) From d06b98c6b84c868169993656d34a1c238cc2eb1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:20:36 +0000 Subject: [PATCH 04/12] Bump sigstore/cosign/cosign from v3.0.4 to v3.0.5 in /builds (#2040) Bumps sigstore/cosign/cosign from v3.0.4 to v3.0.5. --- updated-dependencies: - dependency-name: sigstore/cosign/cosign dependency-version: v3.0.5 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- builds/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From a6e6ac0d0a8241bbfb953f620b060eddb05c891d Mon Sep 17 00:00:00 2001 From: "heroku-linguist[bot]" <136119646+heroku-linguist[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 17:24:05 +0000 Subject: [PATCH 05/12] Prepare release v336 (#2038) Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc7b10b64..8d9953849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] + +## [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)) @@ -1481,7 +1484,8 @@ 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/v336...main +[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 From 522aeb09f57783855d881d193957c31fff79e6fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:14:59 +0000 Subject: [PATCH 06/12] Bump pip from 25.3 to 26.0.1 (#2032) * Bump pip from 25.3 to 26.0.1 Bumps [pip](https://github.com/pypa/pip) from 25.3 to 26.0.1. - [Changelog](https://github.com/pypa/pip/blob/main/NEWS.rst) - [Commits](https://github.com/pypa/pip/compare/25.3...26.0.1) --- updated-dependencies: - dependency-name: pip dependency-version: 26.0.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] * Add changelog entry * Update test assertion --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ed Morley <501702+edmorley@users.noreply.github.com> --- CHANGELOG.md | 1 + requirements/pip.txt | 2 +- spec/hatchet/pip_spec.rb | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d9953849..85fe04187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [Unreleased] +- Updated pip from 25.3 to 26.0.1. ([#2032](https://github.com/heroku/heroku-buildpack-python/pull/2032)) ## [v336] - 2026-03-02 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/spec/hatchet/pip_spec.rb b/spec/hatchet/pip_spec.rb index 5252aa9ee..0f37dba4c 100644 --- a/spec/hatchet/pip_spec.rb +++ b/spec/hatchet/pip_spec.rb @@ -349,7 +349,7 @@ app.deploy do |app| expect(clean_output(app.output)).to include(<<~OUTPUT) remote: -----> Installing dependencies using 'pip install -r requirements.txt' - remote: ERROR: Invalid requirement: 'an-invalid-requirement!': Expected end or semicolon (after name and no valid version specifier) + remote: ERROR: Invalid requirement: 'an-invalid-requirement!': Expected semicolon (after name with no version specifier) or end remote: an-invalid-requirement! remote: ^ (from line 1 of requirements.txt) remote: From 3b5d531100faea66fdd4d7169ad7304d7313bf58 Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Tue, 3 Mar 2026 15:08:57 +0000 Subject: [PATCH 07/12] Update to Ruby 4.0 (#2043) For the repo's development/testing tooling. Plus refresh the lockfile. GUS-W-21429033. --- .github/workflows/ci.yml | 4 +- .github/workflows/hatchet_app_cleaner.yml | 2 +- Gemfile | 2 +- Gemfile.lock | 63 +++++++++++++++++++---- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f32322a11..f92ac8a18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true - ruby-version: "3.4" + ruby-version: "4.0" - name: Run ShellCheck run: make lint-scripts - name: Run shfmt @@ -56,7 +56,7 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true - ruby-version: "3.4" + ruby-version: "4.0" - name: Hatchet setup run: bundle exec hatchet ci:setup - name: Run Hatchet integration tests diff --git a/.github/workflows/hatchet_app_cleaner.yml b/.github/workflows/hatchet_app_cleaner.yml index caf9388d9..5844b15ca 100644 --- a/.github/workflows/hatchet_app_cleaner.yml +++ b/.github/workflows/hatchet_app_cleaner.yml @@ -24,7 +24,7 @@ jobs: uses: ruby/setup-ruby@v1 with: bundler-cache: true - ruby-version: "3.4" + ruby-version: "4.0" - name: Run Hatchet destroy # Only apps older than 10 minutes are destroyed, to ensure that any # in progress CI runs are not interrupted. 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 9d76f3600..4c7138d8f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -8,7 +8,7 @@ GEM 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 @@ -30,10 +30,10 @@ GEM language_server-protocol (3.17.0.5) lint_roller (1.1.0) logger (1.7.0) - mcp (0.7.1) + 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) @@ -41,12 +41,12 @@ GEM 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.2) + public_suffix (7.0.5) racc (1.8.1) rainbow (3.1.1) rate_throttle_client (0.1.2) @@ -59,7 +59,7 @@ GEM rspec-support (~> 3.13.0) rspec-retry (0.6.2) rspec-core (> 3.3) - rspec-support (3.13.6) + rspec-support (3.13.7) rubocop (1.85.0) json (~> 2.3) language_server-protocol (~> 3.17.0.2) @@ -79,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) @@ -87,7 +87,9 @@ GEM webrick (1.9.2) PLATFORMS + arm64-darwin-24 ruby + x86_64-linux DEPENDENCIES heroku_hatchet @@ -98,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 From a64b2d902e84e5f1e95f46f39ab0098046b0d129 Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Tue, 3 Mar 2026 16:21:44 +0000 Subject: [PATCH 08/12] Explicitly configure uv to use hard links (#2044) Since in newer versions of uv the default link mode has changed from `hardlink` to `clone` (aka reflink), and reflinks are slower in some environments (such as Heroku's build system). See: https://github.com/astral-sh/uv/issues/18259 Even if the uv default ends up being changed back to hard links again, it seems worth us explicitly requesting that link mode, given that we know it's the best mode for our use-case. GUS-W-21429394. --- CHANGELOG.md | 1 + lib/uv.sh | 4 ++++ spec/hatchet/ci_spec.rb | 2 ++ spec/hatchet/uv_spec.rb | 2 ++ 4 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85fe04187..c9c1d2011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] - Updated pip from 25.3 to 26.0.1. ([#2032](https://github.com/heroku/heroku-buildpack-python/pull/2032)) +- 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 diff --git a/lib/uv.sh b/lib/uv.sh index 927c84821..53d66e990 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/spec/hatchet/ci_spec.rb b/spec/hatchet/ci_spec.rb index 60319499b..9b4ba8af9 100644 --- a/spec/hatchet/ci_spec.rb +++ b/spec/hatchet/ci_spec.rb @@ -319,6 +319,7 @@ PATH=/tmp/cache\\w+/.heroku/python-uv:/app/.heroku/python/bin:/usr/local/bin:/usr/bin:/bin:/app/.sprettur/bin/ PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config PYTHONUNBUFFERED=1 + UV_LINK_MODE=hardlink UV_NO_MANAGED_PYTHON=1 UV_PROJECT_ENVIRONMENT=/app/.heroku/python UV_PYTHON_DOWNLOADS=never @@ -331,6 +332,7 @@ PYTHONHOME=/app/.heroku/python PYTHONPATH=/app PYTHONUNBUFFERED=true + UV_LINK_MODE=hardlink UV_NO_MANAGED_PYTHON=1 UV_PROJECT_ENVIRONMENT=/app/.heroku/python UV_PYTHON_DOWNLOADS=never diff --git a/spec/hatchet/uv_spec.rb b/spec/hatchet/uv_spec.rb index 766df4dca..3b0d74c21 100644 --- a/spec/hatchet/uv_spec.rb +++ b/spec/hatchet/uv_spec.rb @@ -33,6 +33,7 @@ remote: PKG_CONFIG_PATH=/app/.heroku/python/lib/pkg-config remote: PYTHONUNBUFFERED=1 remote: UV_CACHE_DIR=/tmp/uv-cache + remote: UV_LINK_MODE=hardlink remote: UV_NO_MANAGED_PYTHON=1 remote: UV_PROJECT_ENVIRONMENT=/app/.heroku/python remote: UV_PYTHON_DOWNLOADS=never @@ -46,6 +47,7 @@ remote: PYTHONPATH=/app remote: PYTHONUNBUFFERED=true remote: UV_CACHE_DIR=/tmp/uv-cache + remote: UV_LINK_MODE=hardlink remote: UV_NO_MANAGED_PYTHON=1 remote: UV_PROJECT_ENVIRONMENT=/app/.heroku/python remote: UV_PYTHON_DOWNLOADS=never From 26565e47e78c6e7b204223f728da41e9de1b3f28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:24:53 +0000 Subject: [PATCH 09/12] Bump uv from 0.10.1 to 0.10.7 (#2041) * Bump uv from 0.10.1 to 0.10.7 Bumps [uv](https://github.com/astral-sh/uv) from 0.10.1 to 0.10.7. - [Release notes](https://github.com/astral-sh/uv/releases) - [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/uv/compare/0.10.1...0.10.7) --- updated-dependencies: - dependency-name: uv dependency-version: 0.10.7 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Add changelog entry --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ed Morley <501702+edmorley@users.noreply.github.com> --- CHANGELOG.md | 1 + requirements/uv.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c1d2011..ed0b8a998 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] - 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 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 From ab4ed894d417d6624e6205f5ac1323f69319d4a4 Mon Sep 17 00:00:00 2001 From: "heroku-linguist[bot]" <136119646+heroku-linguist[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 17:37:30 +0000 Subject: [PATCH 10/12] Prepare release v337 (#2045) Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed0b8a998..fbf0a7937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] + +## [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)) @@ -1487,7 +1490,8 @@ 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/v336...main +[unreleased]: https://github.com/heroku/heroku-buildpack-python/compare/v337...main +[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 From 07bf8f0a81dbbae24065a8aa5b282bd894b9a372 Mon Sep 17 00:00:00 2001 From: Ed Morley <501702+edmorley@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:11:24 +0000 Subject: [PATCH 11/12] Add support for Python 3.12.13, 3.11.15 and 3.10.20 (#2046) Release announcement: https://blog.python.org/2026/03/python-31213-31115-31020/ Changelogs: https://docs.python.org/release/3.12.13/whatsnew/changelog.html#python-3-12-13-final https://docs.python.org/release/3.11.15/whatsnew/changelog.html#python-3-11-15-final https://docs.python.org/release/3.10.20/whatsnew/changelog.html#python-3-10-20-final Binary builds: https://github.com/heroku/heroku-buildpack-python/actions/runs/22639900939 https://github.com/heroku/heroku-buildpack-python/actions/runs/22639906742 https://github.com/heroku/heroku-buildpack-python/actions/runs/22639923074 GUS-W-21433843. --- CHANGELOG.md | 3 +++ lib/python_version.sh | 6 +++--- spec/spec_helper.rb | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fbf0a7937..391fa2a46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +- 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 diff --git a/lib/python_version.sh b/lib/python_version.sh index d3e97631b..a12943e8f 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/spec/spec_helper.rb b/spec/spec_helper.rb index 5652c9ff3..1fed97721 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -10,9 +10,9 @@ FIXTURE_DIR = Pathname.new(__FILE__).parent.join('fixtures') -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' DEFAULT_PYTHON_FULL_VERSION = LATEST_PYTHON_3_14 From 855d7e66d99357f814f74a1e0920f6c0f377f64f Mon Sep 17 00:00:00 2001 From: "heroku-linguist[bot]" <136119646+heroku-linguist[bot]@users.noreply.github.com> Date: Tue, 3 Mar 2026 20:18:59 +0000 Subject: [PATCH 12/12] Prepare release v338 (#2047) Co-authored-by: heroku-linguist[bot] <136119646+heroku-linguist[bot]@users.noreply.github.com> --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 391fa2a46..5678fc956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [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)) @@ -1493,7 +1496,8 @@ 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/v337...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