Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
75 changes: 65 additions & 10 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -69,15 +79,17 @@ 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)
unicode-emoji (4.2.0)
webrick (1.9.2)

PLATFORMS
arm64-darwin-24
ruby
x86_64-linux

DEPENDENCIES
heroku_hatchet
Expand All @@ -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
11 changes: 7 additions & 4 deletions bin/steps/nltk
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion builds/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
Expand Down
7 changes: 3 additions & 4 deletions lib/python.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions lib/python_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
4 changes: 4 additions & 0 deletions lib/uv.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,17 @@ 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
export PATH="${uv_dir}:\${PATH}"
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
Expand Down
2 changes: 1 addition & 1 deletion requirements/pip.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pip==25.3
pip==26.0.1
2 changes: 1 addition & 1 deletion requirements/uv.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
uv==0.10.1
uv==0.10.7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legal Risk

uv 0.10.7 was released under the non-standard license, a license that
is currently prohibited by your organization. Merging is blocked until this is resolved.

Recommendation

Reach out to your security team or Semgrep admin to address this issue. In special cases, exceptions may be made for dependencies with violating licenses, however, the general recommendation is to avoid using a dependency under such a license.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Custom exception added
You can proceed