From 2ece844519a83504ee31435b8260c113ec347b22 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 11:01:43 -0500 Subject: [PATCH 1/7] docs: add release notes for 26.08 Draft release notes for 26.08 based on commits since v26.06.00a, covering new features (multi-GPU PDLP, routing improvements, gRPC enhancements, MIP cuts and heuristics), improvements, and bug fixes. Intended for team review. --- RELEASE-NOTES.md | 75 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index df14b8a0e9..c9c91bc698 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,5 +1,80 @@ # Release Notes +## Release Notes 26.08 + +### New Features (26.08) + +- Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning and NCCL communication; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs +- Fast free-format MPS parser with significantly reduced parse time on large models +- Recursive RINS heuristic for MIP to find better feasible solutions earlier in the search +- Zero-half (odd-cycle) cuts for MIP +- Implied bound cuts, extended knapsack cuts, and lifted knapsack cuts +- Conflict graph improvements: incorporate non-binary rows and probing implications +- MIP row and objective scaling +- Papilo-based primal/dual crush for MIP presolve +- `UnboundedOrInfeasible` termination status for LP/MIP +- Expose GPU heuristics tuning parameters via configuration files +- Race batch PDLP against dual simplex in strong branching and reliability branching +- Dump presolved problem to file for debugging +- UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`) +- Routing: `cuopt.routing` is now importable without a GPU present (CPU-only environments and gRPC clients) +- Routing: accept NumPy and pandas inputs in the routing Python `DataModel` in addition to cuDF +- Routing: export the recorded routing problem to a host representation (Phase 3) +- Routing: defer device construction to solve time (`DataModel` store-then-build) +- gRPC: Python interface to the C++ gRPC async client +- gRPC: allow TLS arguments in the Python gRPC async API +- gRPC: assign gRPC workers to distinct GPUs via `cudaSetDevice` +- C API: extend getters with additional query functions +- C API: automatic CPU/GPU memory selection for problem construction + +### Breaking Changes (26.08) + +None. + +### Improvements (26.08) + +- Reduce latency in LP concurrent mode +- Improve crossover dual simplex performance and accuracy +- Reliability branching candidates are now ranked using a dual simplex single-pivot estimate +- Build CUDA 13 wheels with CTK 13.3.0 +- Remove `cuda-python` as an explicit dependency +- Clamp crushed solutions to dual-reduced bounds for better feasibility +- `objective_scaling_factor` is now optional in gRPC requests +- MIP log output cleanup and improved readability +- Print diagnostic logs when exceptions are caught during solves + +### Bug Fixes (26.08) + +- Fix routing YAML best-results export writing incorrect values +- Allow zero-valued coefficient updates in the LP Python model +- Fix row-major layout not preserved when resizing capacity routes in routing +- Fix libomp ABI incompatibility causing crashes in some environments +- Fix variable fixing for initial solutions in MIP +- Fix cuts ignoring the solver time limit +- Fix clique size computation and associated numerical issues in MIP +- Fix cuDSS descriptors being freed before their backing buffers in the barrier solver +- Fix SOC index collision and incorrect QC-to-SOC conversion for rotated SOC constraints +- Fix root-cut CPU feasibility-jump solutions being dropped by GPU heuristics +- Fix incorrect GF2 presolve constraint addressing +- Validate MPS row type byte before enum cast to avoid undefined behavior +- Fix nonconvex quadratic constraint detection bug +- Fix Ruiz equilibration skip heuristic to also check column imbalance +- Fix race condition in CUDA graph capture when `set_simplex_solution()` is called +- Fix infinite lower bounds handling in barrier when bounding free variables +- Fix lower bound being incorrect in MIP single-thread mode +- Add exception handling for PDLP failures in concurrent mode +- Fix double `va_start` undefined behavior in C error handling +- gRPC: terminate worker threads cleanly on server shutdown +- gRPC: cancel active jobs when a job is deleted +- gRPC: drain all remaining log lines at job completion in `StreamLogs` +- gRPC: constrain server data file paths to prevent path traversal +- Fix papilo probing cache synchronization bug +- Fix Gomory cut generation when `b_bar` test is active + +### Documentation (26.08) + +- Align cuOpt documentation branding and update Doxygen configuration + ## Release Notes 26.06 ### New Features (26.06) From f73053d61caee7916d2ae8ab63967ec9a6997a9a Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 29 Jul 2026 11:32:06 -0500 Subject: [PATCH 2/7] docs: regenerate 26.08 release notes from script output Use AyodeAwe's filtered release notes script against .github/release.yml labels; removes 75 forward-merged PRs and duplicates from prior releases. --- RELEASE-NOTES.md | 100 +++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index c9c91bc698..dd0d2a8811 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,76 +4,90 @@ ### New Features (26.08) -- Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning and NCCL communication; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs -- Fast free-format MPS parser with significantly reduced parse time on large models -- Recursive RINS heuristic for MIP to find better feasible solutions earlier in the search +- Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning and NCCL; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs +- Add sparsity exploitation for large-dimensional SOC constraints in the barrier solver +- Fast free-format MPS parser with reduced parse time on large models +- Recursive RINS heuristic for MIP - Zero-half (odd-cycle) cuts for MIP -- Implied bound cuts, extended knapsack cuts, and lifted knapsack cuts - Conflict graph improvements: incorporate non-binary rows and probing implications -- MIP row and objective scaling -- Papilo-based primal/dual crush for MIP presolve -- `UnboundedOrInfeasible` termination status for LP/MIP -- Expose GPU heuristics tuning parameters via configuration files -- Race batch PDLP against dual simplex in strong branching and reliability branching -- Dump presolved problem to file for debugging +- Papilo-based primal/dual crush in MIP presolve +- Vector length diving and Farkas diving heuristics for MIP - UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`) -- Routing: `cuopt.routing` is now importable without a GPU present (CPU-only environments and gRPC clients) +- Routing: `cuopt.routing` is now importable without a GPU (CPU-only environments and gRPC clients) - Routing: accept NumPy and pandas inputs in the routing Python `DataModel` in addition to cuDF -- Routing: export the recorded routing problem to a host representation (Phase 3) -- Routing: defer device construction to solve time (`DataModel` store-then-build) +- Routing: export the recorded routing problem to a host representation - gRPC: Python interface to the C++ gRPC async client - gRPC: allow TLS arguments in the Python gRPC async API -- gRPC: assign gRPC workers to distinct GPUs via `cudaSetDevice` - C API: extend getters with additional query functions - C API: automatic CPU/GPU memory selection for problem construction ### Breaking Changes (26.08) -None. +- Routing `DataModel` now defers GPU device construction to solve time; applications that accessed device-side data between `DataModel` construction and the solve call must be updated ### Improvements (26.08) - Reduce latency in LP concurrent mode -- Improve crossover dual simplex performance and accuracy -- Reliability branching candidates are now ranked using a dual simplex single-pivot estimate -- Build CUDA 13 wheels with CTK 13.3.0 +- Reduce memory footprint of PDLP +- Reduce QP solver overhead +- Run feasibility-jump (CPU-FJ) heuristics at the root node +- Expose diving hyperparameters for MIP solver configuration +- Unify threading model in the MIP solver using OpenMP tasks +- MIP log cleanup and improved readability +- Add short `cu12`/`cu13` Docker tag aliases +- Build and test with CUDA 13.3.0 - Remove `cuda-python` as an explicit dependency -- Clamp crushed solutions to dual-reduced bounds for better feasibility -- `objective_scaling_factor` is now optional in gRPC requests -- MIP log output cleanup and improved readability -- Print diagnostic logs when exceptions are caught during solves +- gRPC: `objective_scaling_factor` is now optional in requests +- gRPC: assign workers to distinct GPUs via `cudaSetDevice` +- Routing: cost matrix validation check is now optional ### Bug Fixes (26.08) -- Fix routing YAML best-results export writing incorrect values -- Allow zero-valued coefficient updates in the LP Python model -- Fix row-major layout not preserved when resizing capacity routes in routing -- Fix libomp ABI incompatibility causing crashes in some environments -- Fix variable fixing for initial solutions in MIP -- Fix cuts ignoring the solver time limit -- Fix clique size computation and associated numerical issues in MIP -- Fix cuDSS descriptors being freed before their backing buffers in the barrier solver -- Fix SOC index collision and incorrect QC-to-SOC conversion for rotated SOC constraints +- Fix lower bound being incorrect in MIP single-thread mode +- Fix concurrent LP exception cleanup +- Fix destruction order and by-reference capture bugs in solve.cu +- Fix route priority sort indexing in routing +- Fix lost nodes in branch and bound +- Fix vehicle fixed cost accounting in fragment-vs-route deltas +- Fix PDLP cublas error capture and hang on infeasible solutions +- Add guard for huge bounds in bounds propagation +- gRPC: fix race condition with log streaming +- Fix routing min-vehicles bug +- Fix nonconvex quadratic constraint detection bug +- Fix rotated SOC detection: canonicalize QC Q COO +- Validate MPS row type byte before enum cast to avoid undefined behavior - Fix root-cut CPU feasibility-jump solutions being dropped by GPU heuristics - Fix incorrect GF2 presolve constraint addressing -- Validate MPS row type byte before enum cast to avoid undefined behavior -- Fix nonconvex quadratic constraint detection bug +- Remove wrong unsupported-QCQP exception +- Fix cuDSS descriptors being freed before their backing buffers in the barrier solver +- Fix cuts not obeying the time limit +- Fix variable fixing for initial solutions in MIP +- Fix clique size computation and numerical issues in MIP +- Fix libomp ABI incompatibility +- Fix SOC index collision and incorrect QC-to-SOC conversion for rotated SOC constraints +- gRPC: cancel active jobs on delete +- gRPC: terminate workers cleanly on server shutdown +- gRPC: drain all remaining log lines at job completion in `StreamLogs` - Fix Ruiz equilibration skip heuristic to also check column imbalance -- Fix race condition in CUDA graph capture when `set_simplex_solution()` is called -- Fix infinite lower bounds handling in barrier when bounding free variables -- Fix lower bound being incorrect in MIP single-thread mode -- Add exception handling for PDLP failures in concurrent mode +- Fix row-major layout not preserved when resizing routing capacity routes +- Allow zero-valued coefficient updates in the LP Python model +- Fix routing YAML best-results export - Fix double `va_start` undefined behavior in C error handling -- gRPC: terminate worker threads cleanly on server shutdown -- gRPC: cancel active jobs when a job is deleted -- gRPC: drain all remaining log lines at job completion in `StreamLogs` -- gRPC: constrain server data file paths to prevent path traversal -- Fix papilo probing cache synchronization bug -- Fix Gomory cut generation when `b_bar` test is active ### Documentation (26.08) - Align cuOpt documentation branding and update Doxygen configuration +- Update cuOpt MIP positioning documentation + +### New Contributors (26.08) + +- @jolorunyomi +- @cafzal +- @Sylendran95 +- @jackthepunished +- @fallintoplace +- @arhag23 +- @divyegala ## Release Notes 26.06 From 33a2324e730acc72866ea38fb2df2d8d0318b68b Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu <42624703+ramakrishnap-nv@users.noreply.github.com> Date: Thu, 30 Jul 2026 08:20:49 -0500 Subject: [PATCH 3/7] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index dd0d2a8811..d786c00970 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -13,9 +13,7 @@ - Papilo-based primal/dual crush in MIP presolve - Vector length diving and Farkas diving heuristics for MIP - UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`) -- Routing: `cuopt.routing` is now importable without a GPU (CPU-only environments and gRPC clients) - Routing: accept NumPy and pandas inputs in the routing Python `DataModel` in addition to cuDF -- Routing: export the recorded routing problem to a host representation - gRPC: Python interface to the C++ gRPC async client - gRPC: allow TLS arguments in the Python gRPC async API - C API: extend getters with additional query functions @@ -35,12 +33,10 @@ - Unify threading model in the MIP solver using OpenMP tasks - MIP log cleanup and improved readability - Add short `cu12`/`cu13` Docker tag aliases -- Build and test with CUDA 13.3.0 - Remove `cuda-python` as an explicit dependency - gRPC: `objective_scaling_factor` is now optional in requests - gRPC: assign workers to distinct GPUs via `cudaSetDevice` -- Routing: cost matrix validation check is now optional - + ### Bug Fixes (26.08) - Fix lower bound being incorrect in MIP single-thread mode @@ -51,7 +47,7 @@ - Fix vehicle fixed cost accounting in fragment-vs-route deltas - Fix PDLP cublas error capture and hang on infeasible solutions - Add guard for huge bounds in bounds propagation -- gRPC: fix race condition with log streaming +- gRPC: fix race condition in gRPC with log streaming - Fix routing min-vehicles bug - Fix nonconvex quadratic constraint detection bug - Fix rotated SOC detection: canonicalize QC Q COO From 9aba79de6d0236511ea118538c7ffbf2f4883783 Mon Sep 17 00:00:00 2001 From: Chris Maes Date: Fri, 31 Jul 2026 10:55:27 -0700 Subject: [PATCH 4/7] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 65 ++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index d786c00970..312f2c95a4 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,20 +4,18 @@ ### New Features (26.08) -- Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning and NCCL; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs -- Add sparsity exploitation for large-dimensional SOC constraints in the barrier solver -- Fast free-format MPS parser with reduced parse time on large models -- Recursive RINS heuristic for MIP -- Zero-half (odd-cycle) cuts for MIP -- Conflict graph improvements: incorporate non-binary rows and probing implications -- Papilo-based primal/dual crush in MIP presolve -- Vector length diving and Farkas diving heuristics for MIP -- UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`) +- New Multi-GPU PDLP: distribute LP solves across multiple GPUs using METIS partitioning; 2.5x–8.8x speedup on 8 NVLink-connected B200 GPUs +- Exploit sparsity in Barrier's augmented system on problems with large second order cone constraints +- New free-format MPS parser with faster parse time on large models +- New recursive RINS heuristic for MIP +- New zero-half (odd-cycle) cuts for MIP +- New vector length diving and Farkas diving heuristics for MIP - Routing: accept NumPy and pandas inputs in the routing Python `DataModel` in addition to cuDF -- gRPC: Python interface to the C++ gRPC async client -- gRPC: allow TLS arguments in the Python gRPC async API - C API: extend getters with additional query functions - C API: automatic CPU/GPU memory selection for problem construction +- gRPC: Python interface to the C++ gRPC async client +- gRPC: allow TLS arguments in the Python gRPC async API +- UBI10 (Red Hat Universal Base Image) container variant for FIPS 140-3 compliant environments; image tags use `-ubi10` suffix (e.g. `latest-cu13-ubi10`) ### Breaking Changes (26.08) @@ -25,10 +23,13 @@ ### Improvements (26.08) +- Conflict graph improvements: incorporate non-binary rows and probing implications - Reduce latency in LP concurrent mode - Reduce memory footprint of PDLP - Reduce QP solver overhead +- Improve Ruiz equilibration heuristic to also check column imbalance on QPs - Run feasibility-jump (CPU-FJ) heuristics at the root node +- Papilo-based primal/dual crush in MIP presolve - Expose diving hyperparameters for MIP solver configuration - Unify threading model in the MIP solver using OpenMP tasks - MIP log cleanup and improved readability @@ -39,36 +40,36 @@ ### Bug Fixes (26.08) -- Fix lower bound being incorrect in MIP single-thread mode -- Fix concurrent LP exception cleanup +- Allow zero-valued coefficient updates in the LP Python model +- Fix an issue in PDLP with cublas error capture and hang on infeasible solutions +- Fix an exception in concurrent LP cleanup +- Fix bug in barrier solver where cuDSS descriptors were being freed before their backing buffers +- Fix a bug in MIP where the lower bound was incorrect when using a singlethread +- Fix a bug in MIP where nodes were lost in branch and bound +- Fix a bug in MIP where root cut pass CPU feasibility-jump solutions were dropped by GPU heuristics +- Add guard in MIP for huge bounds in bounds propagation +- Fix a bug in MIP with incorrect GF2 presolve constraint addressing +- Fix a bug in MIP in variable fixing for initial solutions +- Fix issue in MIP in clique size computation and numerical issues +- Fix an issue in MIP where cut generation did not obey the time limit +- Fix an issue on QPs where maximization was not supported +- Fix rotated second-order cone detection: canonicalize quadratic constraint Q matrix in triplet formt +- Fix nonconvex quadratic constraint detection bug +- Fix a bug on QCQP models that incorrectly threw an unsupported exception +- Fix second-order cone index collision and incorrect quadratic constraint to cone conversion for rotated cone constraints +- Validate MPS row type byte before enum cast to avoid undefined behavior - Fix destruction order and by-reference capture bugs in solve.cu - Fix route priority sort indexing in routing -- Fix lost nodes in branch and bound -- Fix vehicle fixed cost accounting in fragment-vs-route deltas -- Fix PDLP cublas error capture and hang on infeasible solutions -- Add guard for huge bounds in bounds propagation -- gRPC: fix race condition in gRPC with log streaming +- Fix vehicle fixed cost accounting in fragment-vs-route deltas in routing - Fix routing min-vehicles bug -- Fix nonconvex quadratic constraint detection bug -- Fix rotated SOC detection: canonicalize QC Q COO -- Validate MPS row type byte before enum cast to avoid undefined behavior -- Fix root-cut CPU feasibility-jump solutions being dropped by GPU heuristics -- Fix incorrect GF2 presolve constraint addressing -- Remove wrong unsupported-QCQP exception -- Fix cuDSS descriptors being freed before their backing buffers in the barrier solver -- Fix cuts not obeying the time limit -- Fix variable fixing for initial solutions in MIP -- Fix clique size computation and numerical issues in MIP - Fix libomp ABI incompatibility -- Fix SOC index collision and incorrect QC-to-SOC conversion for rotated SOC constraints +- gRPC: fix race condition in gRPC with log streaming - gRPC: cancel active jobs on delete - gRPC: terminate workers cleanly on server shutdown - gRPC: drain all remaining log lines at job completion in `StreamLogs` -- Fix Ruiz equilibration skip heuristic to also check column imbalance - Fix row-major layout not preserved when resizing routing capacity routes -- Allow zero-valued coefficient updates in the LP Python model - Fix routing YAML best-results export -- Fix double `va_start` undefined behavior in C error handling +- Fix double `va_start` undefined behavior in C API error handling ### Documentation (26.08) From 2071bcf282d2b96964e8a14b39ffd01d907ffa2a Mon Sep 17 00:00:00 2001 From: Chris Maes Date: Mon, 3 Aug 2026 09:21:59 -0700 Subject: [PATCH 5/7] Update RELEASE-NOTES.md --- RELEASE-NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 312f2c95a4..5dbb55f008 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -44,7 +44,7 @@ - Fix an issue in PDLP with cublas error capture and hang on infeasible solutions - Fix an exception in concurrent LP cleanup - Fix bug in barrier solver where cuDSS descriptors were being freed before their backing buffers -- Fix a bug in MIP where the lower bound was incorrect when using a singlethread +- Fix a bug in MIP where the lower bound was incorrect when using a single thread - Fix a bug in MIP where nodes were lost in branch and bound - Fix a bug in MIP where root cut pass CPU feasibility-jump solutions were dropped by GPU heuristics - Add guard in MIP for huge bounds in bounds propagation @@ -53,7 +53,7 @@ - Fix issue in MIP in clique size computation and numerical issues - Fix an issue in MIP where cut generation did not obey the time limit - Fix an issue on QPs where maximization was not supported -- Fix rotated second-order cone detection: canonicalize quadratic constraint Q matrix in triplet formt +- Fix rotated second-order cone detection: make canonical quadratic constraint Q matrix in triplet form - Fix nonconvex quadratic constraint detection bug - Fix a bug on QCQP models that incorrectly threw an unsupported exception - Fix second-order cone index collision and incorrect quadratic constraint to cone conversion for rotated cone constraints From 448c83d32b562aaf373e2ecc6e36ec961565ba0f Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 4 Aug 2026 10:14:29 -0500 Subject: [PATCH 6/7] fix trailing whitespace in RELEASE-NOTES.md --- RELEASE-NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 5dbb55f008..f1dd11daaf 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -37,14 +37,14 @@ - Remove `cuda-python` as an explicit dependency - gRPC: `objective_scaling_factor` is now optional in requests - gRPC: assign workers to distinct GPUs via `cudaSetDevice` - + ### Bug Fixes (26.08) - Allow zero-valued coefficient updates in the LP Python model - Fix an issue in PDLP with cublas error capture and hang on infeasible solutions - Fix an exception in concurrent LP cleanup - Fix bug in barrier solver where cuDSS descriptors were being freed before their backing buffers -- Fix a bug in MIP where the lower bound was incorrect when using a single thread +- Fix a bug in MIP where the lower bound was incorrect when using a single thread - Fix a bug in MIP where nodes were lost in branch and bound - Fix a bug in MIP where root cut pass CPU feasibility-jump solutions were dropped by GPU heuristics - Add guard in MIP for huge bounds in bounds propagation From 999a96eb278dcd4a16cf1d29297e98feddcbe589 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 4 Aug 2026 11:05:58 -0500 Subject: [PATCH 7/7] ci: add workflow to promote nightly docker images to release tags on NGC staging --- .../workflows/promote_nightly_to_release.yaml | 73 ++++++++++++ ci/docker/promote_nightly_to_release.sh | 107 ++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 .github/workflows/promote_nightly_to_release.yaml create mode 100755 ci/docker/promote_nightly_to_release.sh diff --git a/.github/workflows/promote_nightly_to_release.yaml b/.github/workflows/promote_nightly_to_release.yaml new file mode 100644 index 0000000000..71c048b2be --- /dev/null +++ b/.github/workflows/promote_nightly_to_release.yaml @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Promote nightly images to release on NGC staging + +on: + workflow_dispatch: + inputs: + nightly_tag_prefix: + description: "Nightly IMAGE_TAG_PREFIX to promote from (e.g. 26.8.0a)" + required: true + type: string + release_version: + description: "Release version to tag as (e.g. 26.8.0)" + required: true + type: string + cuda_ver: + description: 'JSON array of CUDA versions to promote (e.g. ["12.9.0", "13.3.0"])' + required: false + type: string + default: '["12.9.0", "13.3.0"]' + python_ver: + description: 'JSON array of Python versions to promote (e.g. ["3.14.4"])' + required: false + type: string + default: '["3.14.4"]' + +defaults: + run: + shell: bash + +permissions: {} + +jobs: + promote-images: + name: Promote cuda${{ matrix.cuda_ver }}-py${{ matrix.python_ver }} + runs-on: ubuntu-latest + permissions: + contents: read + strategy: + fail-fast: false + matrix: + cuda_ver: ${{ fromJson(inputs.cuda_ver) }} + python_ver: ${{ fromJson(inputs.python_ver) }} + steps: + - name: Checkout + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + persist-credentials: false + + - name: Login to NGC + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0 + with: + registry: "nvcr.io" + username: "$oauthtoken" + password: ${{ secrets.CUOPT_NGC_DOCKER_KEY }} + + - name: Trim CUDA and Python versions + id: trim + env: + CUDA_VER: ${{ matrix.cuda_ver }} + PYTHON_VER: ${{ matrix.python_ver }} + run: | + echo "CUDA_SHORT=$(echo "$CUDA_VER" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + echo "PYTHON_SHORT=$(echo "$PYTHON_VER" | sed -E 's/([0-9]+\.[0-9]+)\.[0-9]+/\1/')" >> $GITHUB_OUTPUT + + - name: Promote nightly to release + env: + NIGHTLY_TAG_PREFIX: ${{ inputs.nightly_tag_prefix }} + RELEASE_VERSION: ${{ inputs.release_version }} + CUDA_SHORT: ${{ steps.trim.outputs.CUDA_SHORT }} + PYTHON_SHORT: ${{ steps.trim.outputs.PYTHON_SHORT }} + run: bash ci/docker/promote_nightly_to_release.sh diff --git a/ci/docker/promote_nightly_to_release.sh b/ci/docker/promote_nightly_to_release.sh new file mode 100755 index 0000000000..45ad51705d --- /dev/null +++ b/ci/docker/promote_nightly_to_release.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# Promote nightly per-arch images to release tags on NGC staging and create +# multi-arch manifests. No rebuild is performed — images are pulled, retagged, +# pushed, and stitched into manifests. +# +# Required environment variables: +# NIGHTLY_TAG_PREFIX — nightly IMAGE_TAG_PREFIX, e.g. 26.8.0a +# RELEASE_VERSION — release version to tag as, e.g. 26.8.0 +# CUDA_SHORT — trimmed CUDA version, e.g. 12.9 +# PYTHON_SHORT — trimmed Python version, e.g. 3.14 + +set -euo pipefail + +REGISTRY="nvcr.io/nvstaging/nvaie/cuopt" +CUDA_MAJOR="${CUDA_SHORT%%.*}" + +promote_image() { + local src=$1 + local dst=$2 + echo "Promoting: $src -> $dst" + docker pull "$src" + docker tag "$src" "$dst" + docker push "$dst" + echo "✓ Promoted: $dst" +} + +create_manifest() { + local manifest_name=$1 + local amd64_image=$2 + local arm64_image=$3 + + echo "Creating manifest: $manifest_name" + docker manifest create --amend "$manifest_name" "$amd64_image" "$arm64_image" + docker manifest annotate "$manifest_name" "$arm64_image" --arch arm64 + docker manifest annotate "$manifest_name" "$amd64_image" --arch amd64 + docker manifest push "$manifest_name" + echo "✓ Pushed manifest: $manifest_name" +} + +# ── cuda+py per-arch images ──────────────────────────────────────────────────── + +NIGHTLY_AMD64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" +NIGHTLY_ARM64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" +RELEASE_AMD64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-amd64" +RELEASE_ARM64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}-arm64" + +echo "=== Promoting cuda+py per-arch images ===" +promote_image "$NIGHTLY_AMD64" "$RELEASE_AMD64" +promote_image "$NIGHTLY_ARM64" "$RELEASE_ARM64" + +# ── cuda+py and cu manifests ─────────────────────────────────────────── + +echo "=== Creating release manifests ===" +create_manifest \ + "${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "$RELEASE_AMD64" "$RELEASE_ARM64" + +create_manifest \ + "${REGISTRY}:${RELEASE_VERSION}-cu${CUDA_MAJOR}" \ + "$RELEASE_AMD64" "$RELEASE_ARM64" + +echo "=== Creating latest manifests ===" +create_manifest \ + "${REGISTRY}:latest-cuda${CUDA_SHORT}-py${PYTHON_SHORT}" \ + "$RELEASE_AMD64" "$RELEASE_ARM64" + +create_manifest \ + "${REGISTRY}:latest-cu${CUDA_MAJOR}" \ + "$RELEASE_AMD64" "$RELEASE_ARM64" + +# ── UBI10 (CUDA 13+ only) ───────────────────────────────────────────────────── + +if [[ "${CUDA_MAJOR}" == "13" ]]; then + NIGHTLY_UBI10_AMD64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-ubi10-amd64" + NIGHTLY_UBI10_ARM64="${REGISTRY}:${NIGHTLY_TAG_PREFIX}-cuda${CUDA_SHORT}-ubi10-arm64" + RELEASE_UBI10_AMD64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10-amd64" + RELEASE_UBI10_ARM64="${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10-arm64" + + echo "=== Promoting UBI10 per-arch images ===" + promote_image "$NIGHTLY_UBI10_AMD64" "$RELEASE_UBI10_AMD64" + promote_image "$NIGHTLY_UBI10_ARM64" "$RELEASE_UBI10_ARM64" + + echo "=== Creating UBI10 release manifests ===" + create_manifest \ + "${REGISTRY}:${RELEASE_VERSION}-cuda${CUDA_SHORT}-ubi10" \ + "$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64" + + create_manifest \ + "${REGISTRY}:${RELEASE_VERSION}-cu${CUDA_MAJOR}-ubi10" \ + "$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64" + + create_manifest \ + "${REGISTRY}:latest-cuda${CUDA_SHORT}-ubi10" \ + "$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64" + + create_manifest \ + "${REGISTRY}:latest-cu${CUDA_MAJOR}-ubi10" \ + "$RELEASE_UBI10_AMD64" "$RELEASE_UBI10_ARM64" +else + echo "Skipping UBI10 (CUDA_MAJOR='${CUDA_MAJOR}' — UBI10 requires CUDA 13+)" +fi + +echo "=== Promotion to release complete ==="