From b49e5929d155675dc11bbb535806c69c33bfe3c2 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:11:23 +0200 Subject: [PATCH 01/20] Add local release validation status reporting --- release-check.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 release-check.sh diff --git a/release-check.sh b/release-check.sh new file mode 100644 index 000000000..3ba6f55f7 --- /dev/null +++ b/release-check.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Usage: bash release-check.sh cuda|rocm PR_NUMBER_OR_URL COMMAND [ARG...] +set -euo pipefail +if (( $# < 3 )) || [[ $1 != cuda && $1 != rocm ]]; then + echo "Usage: bash release-check.sh cuda|rocm PR_NUMBER_OR_URL COMMAND [ARG...]" >&2 + exit 2 +fi +backend=$1 +pr=$2 +shift 2 +cd "$(git rev-parse --show-toplevel)" + +if ! git diff HEAD --quiet; then + echo "Commit or stash changes to tracked files before validation." >&2 + exit 1 +fi +IFS=$'\t' read -r sha url < <( + gh pr view "$pr" --json headRefOid,url --jq '[.headRefOid, .url] | @tsv' +) +if [[ $(git rev-parse HEAD) != "$sha" ]]; then + echo "Local HEAD must match the PR's latest commit." >&2 + exit 1 +fi +repo=${url#https://github.com/} +repo=${repo%/pull/*} +publish() { + gh api "repos/$repo/statuses/$sha" \ + -f "state=$1" -f "context=release/$backend" >/dev/null +} +publish pending +state=failure +trap 'result=$?; publish "$state" || exit 1; exit "$result"' EXIT +"$@" +state=success From 489020f4ec3fac45984b04d1a728e5efee9478da Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:11:42 +0200 Subject: [PATCH 02/20] Document local GPU release validation --- contributors.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 contributors.md diff --git a/contributors.md b/contributors.md new file mode 100644 index 000000000..2ea91a70f --- /dev/null +++ b/contributors.md @@ -0,0 +1,22 @@ +# Release validation + +1. Open a release PR and wait for CI to pass. +2. On each GPU machine, check out the PR head and build/install its candidate + wheel and native tests. Authenticate with `gh auth login` using an account + permitted to write commit statuses. +3. Run your validation command, including GPU tests, `../ct2-eval`, and benchmarks: + + ```bash + bash release-check.sh cuda PR_URL COMMAND [ARG...] + bash release-check.sh rocm PR_URL COMMAND [ARG...] + ``` + +The script checks that tracked files are clean and HEAD matches the PR. It +publishes `release/cuda` or `release/rocm` as pending, then success or failure. +The backend argument only labels the status; configure GPU execution yourself. +Your command must fail on regressions or missing GPU coverage. Compare against +the previous release on the same hardware. Detailed output stays in the terminal. + +Configure both statuses as required checks for release PRs. Re-run validation +for every new commit. Publish the validated artifacts after CI and both GPU +checks pass; validate again if the final release changes the tested code. From 2ee56f163bad4154d1be036636c72988dfc44b86 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:13:01 +0200 Subject: [PATCH 03/20] Focus release validation on native and Python tests --- contributors.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/contributors.md b/contributors.md index 2ea91a70f..f988677ae 100644 --- a/contributors.md +++ b/contributors.md @@ -4,18 +4,22 @@ 2. On each GPU machine, check out the PR head and build/install its candidate wheel and native tests. Authenticate with `gh auth login` using an account permitted to write commit statuses. -3. Run your validation command, including GPU tests, `../ct2-eval`, and benchmarks: +3. Run the native and Python tests, including GPU tests: ```bash - bash release-check.sh cuda PR_URL COMMAND [ARG...] - bash release-check.sh rocm PR_URL COMMAND [ARG...] + bash release-check.sh cuda PR_URL bash -ec ' + tests/ctranslate2_test tests/data + python -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py + ' ``` + Replace `cuda` with `rocm` on the AMD machine. + The script checks that tracked files are clean and HEAD matches the PR. It publishes `release/cuda` or `release/rocm` as pending, then success or failure. The backend argument only labels the status; configure GPU execution yourself. -Your command must fail on regressions or missing GPU coverage. Compare against -the previous release on the same hardware. Detailed output stays in the terminal. +The status succeeds only if both test commands pass. Ensure GPU tests run; +skipped GPU tests do not validate the backend. Detailed output stays in the terminal. Configure both statuses as required checks for release PRs. Re-run validation for every new commit. Publish the validated artifacts after CI and both GPU From 6f0294b9d5182791355aea232cdbe3e613cfbc74 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:14:19 +0200 Subject: [PATCH 04/20] Run release tests with a single backend argument --- contributors.md | 9 ++++----- release-check.sh | 13 ++++++------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/contributors.md b/contributors.md index f988677ae..3b201c9a8 100644 --- a/contributors.md +++ b/contributors.md @@ -7,15 +7,14 @@ 3. Run the native and Python tests, including GPU tests: ```bash - bash release-check.sh cuda PR_URL bash -ec ' - tests/ctranslate2_test tests/data - python -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py - ' + bash release-check.sh cuda ``` Replace `cuda` with `rocm` on the AMD machine. -The script checks that tracked files are clean and HEAD matches the PR. It +The script finds the PR for the current branch using `gh pr view`. If needed, +select its repository first with `gh repo set-default OWNER/CTranslate2`. +It checks that tracked files are clean and HEAD matches the PR, then publishes `release/cuda` or `release/rocm` as pending, then success or failure. The backend argument only labels the status; configure GPU execution yourself. The status succeeds only if both test commands pass. Ensure GPU tests run; diff --git a/release-check.sh b/release-check.sh index 3ba6f55f7..77123fade 100644 --- a/release-check.sh +++ b/release-check.sh @@ -1,13 +1,11 @@ #!/usr/bin/env bash -# Usage: bash release-check.sh cuda|rocm PR_NUMBER_OR_URL COMMAND [ARG...] +# Usage: bash release-check.sh cuda|rocm set -euo pipefail -if (( $# < 3 )) || [[ $1 != cuda && $1 != rocm ]]; then - echo "Usage: bash release-check.sh cuda|rocm PR_NUMBER_OR_URL COMMAND [ARG...]" >&2 +if (( $# != 1 )) || [[ $1 != cuda && $1 != rocm ]]; then + echo "Usage: bash release-check.sh cuda|rocm" >&2 exit 2 fi backend=$1 -pr=$2 -shift 2 cd "$(git rev-parse --show-toplevel)" if ! git diff HEAD --quiet; then @@ -15,7 +13,7 @@ if ! git diff HEAD --quiet; then exit 1 fi IFS=$'\t' read -r sha url < <( - gh pr view "$pr" --json headRefOid,url --jq '[.headRefOid, .url] | @tsv' + gh pr view --json headRefOid,url --jq '[.headRefOid, .url] | @tsv' ) if [[ $(git rev-parse HEAD) != "$sha" ]]; then echo "Local HEAD must match the PR's latest commit." >&2 @@ -30,5 +28,6 @@ publish() { publish pending state=failure trap 'result=$?; publish "$state" || exit 1; exit "$result"' EXIT -"$@" +tests/ctranslate2_test tests/data +python -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py state=success From 7f7f0cdc48f09d3b5fd10a22ccf75adfb622c3de Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:16:31 +0200 Subject: [PATCH 05/20] Bump version to 4.8.3 --- python/ctranslate2/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ctranslate2/version.py b/python/ctranslate2/version.py index ebb987b0d..eec04197c 100644 --- a/python/ctranslate2/version.py +++ b/python/ctranslate2/version.py @@ -1,3 +1,3 @@ """Version information.""" -__version__ = "4.8.2" +__version__ = "4.8.3" From 88d3598557083518f5bb354dd45af085a21fabbd Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:17:15 +0200 Subject: [PATCH 06/20] Update changelog for 4.8.3 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10f86b02c..e474862e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## [Unreleased] +## [v4.8.3](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.3) (2026-10-12) + ### New features * Support converting UMT5 models from Hugging Face Transformers @@ -7,6 +9,12 @@ ### Fixes and improvements * Support Transformer models where each layer has its own relative attention bias +* Fix Windows ROCm Translator hangs and DLL loading (#2106) +* Fix crashes when loading model files from Python file-like objects (#2110) +* Upgrade pybind11 to 2.13.6 for CPython 3.14 allocator safety (#2108) +* Guard exhausted candidates in CUDA TopK stage 2 (#2104) +* Fix Windows shutdown deadlocks and memory leaks with the Ruy backend (#2076) +* Add Clang ThreadSanitizer testing (#1986) ## [v4.8.2](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.2) (2026-08-31) From e0a6a4c57199aa9a1f7d3d277b488704c3fb73fe Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:18:58 +0200 Subject: [PATCH 07/20] Find release PR in origin repository --- contributors.md | 3 +-- release-check.sh | 9 ++++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/contributors.md b/contributors.md index 3b201c9a8..3692f01ee 100644 --- a/contributors.md +++ b/contributors.md @@ -12,8 +12,7 @@ Replace `cuda` with `rocm` on the AMD machine. -The script finds the PR for the current branch using `gh pr view`. If needed, -select its repository first with `gh repo set-default OWNER/CTranslate2`. +The script finds the current branch's PR in the GitHub repository named by `origin`. It checks that tracked files are clean and HEAD matches the PR, then publishes `release/cuda` or `release/rocm` as pending, then success or failure. The backend argument only labels the status; configure GPU execution yourself. diff --git a/release-check.sh b/release-check.sh index 77123fade..eb2ca2efa 100644 --- a/release-check.sh +++ b/release-check.sh @@ -12,15 +12,14 @@ if ! git diff HEAD --quiet; then echo "Commit or stash changes to tracked files before validation." >&2 exit 1 fi -IFS=$'\t' read -r sha url < <( - gh pr view --json headRefOid,url --jq '[.headRefOid, .url] | @tsv' -) +repo=$(git remote get-url origin) +repo=${repo#*github.com[:/]} +repo=${repo%.git} +sha=$(gh pr view "$(git branch --show-current)" --repo "$repo" --json headRefOid --jq .headRefOid) if [[ $(git rev-parse HEAD) != "$sha" ]]; then echo "Local HEAD must match the PR's latest commit." >&2 exit 1 fi -repo=${url#https://github.com/} -repo=${repo%/pull/*} publish() { gh api "repos/$repo/statuses/$sha" \ -f "state=$1" -f "context=release/$backend" >/dev/null From 664e79b574871ee4c0fa0cae7ae6d7f82a65018d Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:19:41 +0200 Subject: [PATCH 08/20] Require a visible GPU before release tests --- contributors.md | 4 ++-- release-check.sh | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/contributors.md b/contributors.md index 3692f01ee..9e4b02bcb 100644 --- a/contributors.md +++ b/contributors.md @@ -16,8 +16,8 @@ The script finds the current branch's PR in the GitHub repository named by `orig It checks that tracked files are clean and HEAD matches the PR, then publishes `release/cuda` or `release/rocm` as pending, then success or failure. The backend argument only labels the status; configure GPU execution yourself. -The status succeeds only if both test commands pass. Ensure GPU tests run; -skipped GPU tests do not validate the backend. Detailed output stays in the terminal. +The script requires a visible GPU and both test commands to pass. +Detailed output stays in the terminal. Configure both statuses as required checks for release PRs. Re-run validation for every new commit. Publish the validated artifacts after CI and both GPU diff --git a/release-check.sh b/release-check.sh index eb2ca2efa..6d18ff4c6 100644 --- a/release-check.sh +++ b/release-check.sh @@ -27,6 +27,7 @@ publish() { publish pending state=failure trap 'result=$?; publish "$state" || exit 1; exit "$result"' EXIT +python -c "import ctranslate2; assert ctranslate2.get_cuda_device_count() > 0, 'No GPU available'" tests/ctranslate2_test tests/data python -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py state=success From 855f7b7405b4a8fb2582b9dbb2b24394a68a906d Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:20:22 +0200 Subject: [PATCH 09/20] Move release validation script into tools --- contributors.md | 2 +- release-check.sh => tools/release-check.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename release-check.sh => tools/release-check.sh (91%) diff --git a/contributors.md b/contributors.md index 9e4b02bcb..9a60e407d 100644 --- a/contributors.md +++ b/contributors.md @@ -7,7 +7,7 @@ 3. Run the native and Python tests, including GPU tests: ```bash - bash release-check.sh cuda + bash tools/release-check.sh cuda ``` Replace `cuda` with `rocm` on the AMD machine. diff --git a/release-check.sh b/tools/release-check.sh similarity index 91% rename from release-check.sh rename to tools/release-check.sh index 6d18ff4c6..fc41309f3 100644 --- a/release-check.sh +++ b/tools/release-check.sh @@ -1,8 +1,8 @@ #!/usr/bin/env bash -# Usage: bash release-check.sh cuda|rocm +# Usage: bash tools/release-check.sh cuda|rocm set -euo pipefail if (( $# != 1 )) || [[ $1 != cuda && $1 != rocm ]]; then - echo "Usage: bash release-check.sh cuda|rocm" >&2 + echo "Usage: bash tools/release-check.sh cuda|rocm" >&2 exit 2 fi backend=$1 From 8ae2cbb7d17edd71848b68c712ee53f3bbdcb665 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:22:03 +0200 Subject: [PATCH 10/20] Make release check script executable --- tools/release-check.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/release-check.sh diff --git a/tools/release-check.sh b/tools/release-check.sh old mode 100644 new mode 100755 From da6e0a3a1e0fbfc397d06b7b37c0d9e909bd0c47 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:23:46 +0200 Subject: [PATCH 11/20] Validate release tests against the PR wheel artifact --- contributors.md | 13 +++++++++---- tools/release-check.sh | 19 ++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/contributors.md b/contributors.md index 9a60e407d..a88e50772 100644 --- a/contributors.md +++ b/contributors.md @@ -1,9 +1,10 @@ # Release validation 1. Open a release PR and wait for CI to pass. -2. On each GPU machine, check out the PR head and build/install its candidate - wheel and native tests. Authenticate with `gh auth login` using an account - permitted to write commit statuses. +2. On each Linux x86-64 GPU machine, check out the PR head and build its native + tests locally. Install test dependencies, model data, GPU drivers, and runtime + libraries, including a GPU-compatible PyTorch. Authenticate with `gh auth login` + using an account permitted to download artifacts and write commit statuses. 3. Run the native and Python tests, including GPU tests: ```bash @@ -15,7 +16,11 @@ The script finds the current branch's PR in the GitHub repository named by `origin`. It checks that tracked files are clean and HEAD matches the PR, then publishes `release/cuda` or `release/rocm` as pending, then success or failure. -The backend argument only labels the status; configure GPU execution yourself. +It downloads the CUDA or ROCm wheel from the latest push CI run for that exact +commit. Wait for the wheel artifact to be uploaded before running validation. +The wheel is installed into a temporary environment that reuses your Python +dependencies; the environment is removed afterwards. Your installed CTranslate2 +is left untouched. Native tests use your local build, since CI does not upload it. The script requires a visible GPU and both test commands to pass. Detailed output stays in the terminal. diff --git a/tools/release-check.sh b/tools/release-check.sh index fc41309f3..68e36a8f9 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -26,8 +26,21 @@ publish() { } publish pending state=failure -trap 'result=$?; publish "$state" || exit 1; exit "$result"' EXIT -python -c "import ctranslate2; assert ctranslate2.get_cuda_device_count() > 0, 'No GPU available'" +workdir=$(mktemp -d) +trap 'result=$?; rm -rf "$workdir"; publish "$state" || exit 1; exit "$result"' EXIT +run=$(gh run list --repo "$repo" --workflow ci.yml --event push --commit "$sha" --limit 1 --json databaseId --jq '.[0].databaseId // empty') +if [[ -z $run ]]; then + echo "No CI build found for the PR commit. Run CI first." >&2 + exit 1 +fi +artifact=python-wheels-Linux-auto64 +[[ $backend == rocm ]] && artifact=rocm-python-wheels-Linux +gh run download "$run" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" +python -m venv --system-site-packages "$workdir/venv" +python="$workdir/venv/bin/python" +"$python" -m pip install --no-index --no-deps --force-reinstall --find-links "$workdir/wheels" ctranslate2 +"$python" -I -c "import ctranslate2; print(ctranslate2.__file__); assert ctranslate2.get_cuda_device_count() > 0, 'No GPU available in the PR wheel; check drivers and runtime libraries'" +test -d tests/data/models/transliteration-aren-all tests/ctranslate2_test tests/data -python -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py +"$python" -I -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py state=success From 35a027656acb4fa10f7a743055b68a8eb1bbb980 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 15:24:01 +0200 Subject: [PATCH 12/20] Simplify missing CI build validation --- tools/release-check.sh | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index 68e36a8f9..51376f6bb 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -29,13 +29,9 @@ state=failure workdir=$(mktemp -d) trap 'result=$?; rm -rf "$workdir"; publish "$state" || exit 1; exit "$result"' EXIT run=$(gh run list --repo "$repo" --workflow ci.yml --event push --commit "$sha" --limit 1 --json databaseId --jq '.[0].databaseId // empty') -if [[ -z $run ]]; then - echo "No CI build found for the PR commit. Run CI first." >&2 - exit 1 -fi artifact=python-wheels-Linux-auto64 [[ $backend == rocm ]] && artifact=rocm-python-wheels-Linux -gh run download "$run" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" +gh run download "${run:?No CI build found for the PR commit}" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" python -m venv --system-site-packages "$workdir/venv" python="$workdir/venv/bin/python" "$python" -m pip install --no-index --no-deps --force-reinstall --find-links "$workdir/wheels" ctranslate2 From 6696993a66b23ce5168a1dab78bd13e93dfe20ac Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 18:22:38 +0200 Subject: [PATCH 13/20] Fix release check artifact selection across CI events --- tools/release-check.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index 51376f6bb..b8b58e6c2 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -28,10 +28,23 @@ publish pending state=failure workdir=$(mktemp -d) trap 'result=$?; rm -rf "$workdir"; publish "$state" || exit 1; exit "$result"' EXIT -run=$(gh run list --repo "$repo" --workflow ci.yml --event push --commit "$sha" --limit 1 --json databaseId --jq '.[0].databaseId // empty') artifact=python-wheels-Linux-auto64 [[ $backend == rocm ]] && artifact=rocm-python-wheels-Linux -gh run download "${run:?No CI build found for the PR commit}" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" +runs=$(gh run list --repo "$repo" --workflow ci.yml --commit "$sha" --limit 100 --json databaseId --jq '.[].databaseId') +run= +for candidate in $runs; do + artifacts=$(gh api --paginate "repos/$repo/actions/runs/$candidate/artifacts" --jq '.artifacts[] | select(.expired == false) | .name') + if [[ $'\n'"$artifacts"$'\n' == *$'\n'"$artifact"$'\n'* ]]; then + run=$candidate + break + fi +done +if [[ -z $run ]]; then + echo "No CI run for commit $sha contains an available $artifact artifact." >&2 + exit 1 +fi +echo "Downloading $artifact from https://github.com/$repo/actions/runs/$run" +gh run download "$run" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" python -m venv --system-site-packages "$workdir/venv" python="$workdir/venv/bin/python" "$python" -m pip install --no-index --no-deps --force-reinstall --find-links "$workdir/wheels" ctranslate2 From b2752538b152ed563eceeb8be0ba17e3067ca6d2 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 18:32:34 +0200 Subject: [PATCH 14/20] Update 4.8.3 changelog with recent changes and contributors --- CHANGELOG.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e474862e4..c416c0e55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,17 +4,19 @@ ### New features -* Support converting UMT5 models from Hugging Face Transformers +* Support converting UMT5 models from Hugging Face Transformers (#2103) by [@the-cross-art](https://github.com/the-cross-art) ### Fixes and improvements -* Support Transformer models where each layer has its own relative attention bias -* Fix Windows ROCm Translator hangs and DLL loading (#2106) -* Fix crashes when loading model files from Python file-like objects (#2110) -* Upgrade pybind11 to 2.13.6 for CPython 3.14 allocator safety (#2108) -* Guard exhausted candidates in CUDA TopK stage 2 (#2104) -* Fix Windows shutdown deadlocks and memory leaks with the Ruy backend (#2076) -* Add Clang ThreadSanitizer testing (#1986) +* Support Transformer models where each layer has its own relative attention bias by [@the-cross-art](https://github.com/the-cross-art) +* Fix Windows ROCm Translator hangs and DLL loading (#2106) by [@0xDELUXA](https://github.com/0xDELUXA) +* Fix crashes when loading model files from Python file-like objects (#2110) by [@Purfview](https://github.com/Purfview) +* Upgrade pybind11 to 2.13.6 for CPython 3.14 allocator safety (#2108) by [@cepeter](https://github.com/cepeter) +* Guard exhausted candidates in CUDA TopK stage 2 (#2104) by [@mzggr0914](https://github.com/mzggr0914) +* Fix Windows shutdown deadlocks and memory leaks with the Ruy backend (#2076) by [@timo9378](https://github.com/timo9378) +* Add Clang ThreadSanitizer testing (#1986) by [@3manifold](https://github.com/3manifold) +* Test Linux and Windows ROCm wheels, require tests and style checks before publishing, and upgrade wheel build actions to Node.js 24 (#2112) by [@jordimas](https://github.com/jordimas) +* Add local CUDA and ROCm release validation using PR CI wheel artifacts, native and Python tests, and GitHub commit status reporting by [@jordimas](https://github.com/jordimas) ## [v4.8.2](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.2) (2026-08-31) From ac603a331de5ea7d678f9a52e6ca7cc37f1fc450 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 19:06:15 +0200 Subject: [PATCH 15/20] Shorten 4.8.3 release validation changelog entries --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c416c0e55..76e3d66a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,8 @@ * Guard exhausted candidates in CUDA TopK stage 2 (#2104) by [@mzggr0914](https://github.com/mzggr0914) * Fix Windows shutdown deadlocks and memory leaks with the Ruy backend (#2076) by [@timo9378](https://github.com/timo9378) * Add Clang ThreadSanitizer testing (#1986) by [@3manifold](https://github.com/3manifold) -* Test Linux and Windows ROCm wheels, require tests and style checks before publishing, and upgrade wheel build actions to Node.js 24 (#2112) by [@jordimas](https://github.com/jordimas) -* Add local CUDA and ROCm release validation using PR CI wheel artifacts, native and Python tests, and GitHub commit status reporting by [@jordimas](https://github.com/jordimas) +* Improve ROCm wheel testing and release checks; upgrade build actions to Node.js 24 (#2112) by [@jordimas](https://github.com/jordimas) +* Add local CUDA and ROCm release validation with CI wheels and commit status reporting by [@jordimas](https://github.com/jordimas) ## [v4.8.2](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.2) (2026-08-31) From 895b92804253820c0b79d2ff6d2efc07d39cb1aa Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 20:51:50 +0200 Subject: [PATCH 16/20] Get proper PRs --- tools/release-check.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index b8b58e6c2..5d62203bc 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -15,9 +15,10 @@ fi repo=$(git remote get-url origin) repo=${repo#*github.com[:/]} repo=${repo%.git} -sha=$(gh pr view "$(git branch --show-current)" --repo "$repo" --json headRefOid --jq .headRefOid) -if [[ $(git rev-parse HEAD) != "$sha" ]]; then - echo "Local HEAD must match the PR's latest commit." >&2 +sha=$(git rev-parse HEAD) +pr=$(gh api --paginate "repos/$repo/commits/$sha/pulls" --jq ".[] | select(.state == \"open\" and .head.sha == \"$sha\") | .number" | head -n 1) +if [[ -z $pr ]]; then + echo "Local HEAD is not the head commit of an open PR in $repo." >&2 exit 1 fi publish() { From 5d7a6444627d5f6273057f6b8769ac80b9662c9d Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 19:54:41 +0000 Subject: [PATCH 17/20] Run Python release tests without native build --- tools/release-check.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index 5d62203bc..29b9d3f66 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -12,7 +12,11 @@ if ! git diff HEAD --quiet; then echo "Commit or stash changes to tracked files before validation." >&2 exit 1 fi -repo=$(git remote get-url origin) +if git remote get-url upstream >/dev/null 2>&1; then + repo=$(git remote get-url upstream) +else + repo=$(git remote get-url origin) +fi repo=${repo#*github.com[:/]} repo=${repo%.git} sha=$(git rev-parse HEAD) @@ -50,7 +54,10 @@ python -m venv --system-site-packages "$workdir/venv" python="$workdir/venv/bin/python" "$python" -m pip install --no-index --no-deps --force-reinstall --find-links "$workdir/wheels" ctranslate2 "$python" -I -c "import ctranslate2; print(ctranslate2.__file__); assert ctranslate2.get_cuda_device_count() > 0, 'No GPU available in the PR wheel; check drivers and runtime libraries'" -test -d tests/data/models/transliteration-aren-all -tests/ctranslate2_test tests/data +if [[ -x tests/ctranslate2_test ]]; then + tests/ctranslate2_test tests/data +else + echo "Skipping native tests: tests/ctranslate2_test is not built." >&2 +fi "$python" -I -m pytest -v python/tests/ --ignore=python/tests/test_fairseq.py --ignore=python/tests/test_opennmt_py.py --ignore=python/tests/test_opennmt_tf.py state=success From a370b034fbd65c0ad8409d91dc333843f09afaa5 Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 19:57:22 +0000 Subject: [PATCH 18/20] Allow selecting a release workflow run --- tools/release-check.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index 29b9d3f66..aeb575d4a 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -35,15 +35,23 @@ workdir=$(mktemp -d) trap 'result=$?; rm -rf "$workdir"; publish "$state" || exit 1; exit "$result"' EXIT artifact=python-wheels-Linux-auto64 [[ $backend == rocm ]] && artifact=rocm-python-wheels-Linux -runs=$(gh run list --repo "$repo" --workflow ci.yml --commit "$sha" --limit 100 --json databaseId --jq '.[].databaseId') -run= -for candidate in $runs; do - artifacts=$(gh api --paginate "repos/$repo/actions/runs/$candidate/artifacts" --jq '.artifacts[] | select(.expired == false) | .name') - if [[ $'\n'"$artifacts"$'\n' == *$'\n'"$artifact"$'\n'* ]]; then - run=$candidate - break +run=${RELEASE_CHECK_RUN_ID:-} +if [[ -n $run ]]; then + artifacts=$(gh api --paginate "repos/$repo/actions/runs/$run/artifacts" --jq '.artifacts[] | select(.expired == false) | .name') + if [[ $'\n'"$artifacts"$'\n' != *$'\n'"$artifact"$'\n'* ]]; then + echo "CI run $run does not contain an available $artifact artifact." >&2 + exit 1 fi -done +else + runs=$(gh run list --repo "$repo" --workflow ci.yml --commit "$sha" --limit 100 --json databaseId --jq '.[].databaseId') + for candidate in $runs; do + artifacts=$(gh api --paginate "repos/$repo/actions/runs/$candidate/artifacts" --jq '.artifacts[] | select(.expired == false) | .name') + if [[ $'\n'"$artifacts"$'\n' == *$'\n'"$artifact"$'\n'* ]]; then + run=$candidate + break + fi + done +fi if [[ -z $run ]]; then echo "No CI run for commit $sha contains an available $artifact artifact." >&2 exit 1 From 9b49f9d9164fdef6bfa3ade79e30a7ac3cf247fb Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 19:58:47 +0000 Subject: [PATCH 19/20] Find fork PRs by branch name --- tools/release-check.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/release-check.sh b/tools/release-check.sh index aeb575d4a..4bf384a81 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -20,9 +20,10 @@ fi repo=${repo#*github.com[:/]} repo=${repo%.git} sha=$(git rev-parse HEAD) -pr=$(gh api --paginate "repos/$repo/commits/$sha/pulls" --jq ".[] | select(.state == \"open\" and .head.sha == \"$sha\") | .number" | head -n 1) +branch=$(git branch --show-current) +pr=$(gh pr list --repo "$repo" --state open --head "$branch" --json number,headRefOid --jq ".[] | select(.headRefOid == \"$sha\") | .number" | head -n 1) if [[ -z $pr ]]; then - echo "Local HEAD is not the head commit of an open PR in $repo." >&2 + echo "Local HEAD is not the head commit of an open PR for $branch in $repo." >&2 exit 1 fi publish() { From b52be5b4e72085124b55138fee8eed952fd39a4d Mon Sep 17 00:00:00 2001 From: Jordi Mas Date: Mon, 12 Oct 2026 21:05:42 +0000 Subject: [PATCH 20/20] install depnds --- CHANGELOG.md | 2 +- tools/release-check.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e3d66a5..f91773617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## [Unreleased] -## [v4.8.3](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.3) (2026-10-12) +## [v4.8.3](https://github.com/OpenNMT/CTranslate2/releases/tag/v4.8.3) (2026-10-13) ### New features diff --git a/tools/release-check.sh b/tools/release-check.sh index 4bf384a81..f65367d14 100755 --- a/tools/release-check.sh +++ b/tools/release-check.sh @@ -59,9 +59,10 @@ if [[ -z $run ]]; then fi echo "Downloading $artifact from https://github.com/$repo/actions/runs/$run" gh run download "$run" --repo "$repo" --name "$artifact" --dir "$workdir/wheels" -python -m venv --system-site-packages "$workdir/venv" +python -m venv "$workdir/venv" python="$workdir/venv/bin/python" -"$python" -m pip install --no-index --no-deps --force-reinstall --find-links "$workdir/wheels" ctranslate2 +"$python" -m pip install -r python/tests/requirements.txt +"$python" -m pip install --force-reinstall --find-links "$workdir/wheels" ctranslate2 "$python" -I -c "import ctranslate2; print(ctranslate2.__file__); assert ctranslate2.get_cuda_device_count() > 0, 'No GPU available in the PR wheel; check drivers and runtime libraries'" if [[ -x tests/ctranslate2_test ]]; then tests/ctranslate2_test tests/data