diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e9e1abedc8..d5ac1c0ac7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -97,6 +97,13 @@ repos: (?x)^( ^cpp/tests/utilities/cxxopts.hpp ) + - repo: local + hooks: + - id: update-versions + name: Update versions1.json + entry: python ci/utils/update_doc_versions.py + language: system + files: docs/cuopt/source/versions1.json default_language_version: diff --git a/ci/build_docs.sh b/ci/build_docs.sh index 3089358c97..127e390034 100755 --- a/ci/build_docs.sh +++ b/ci/build_docs.sh @@ -22,6 +22,9 @@ rapids-logger "Create test conda environment" ENV_YAML_DIR="$(mktemp -d)" +CUOPT_VERSION_MAJOR_MINOR="$(rapids-version-major-minor)" +export CUOPT_VERSION_MAJOR_MINOR + rapids-logger "Downloading artifacts from previous jobs" CPP_CHANNEL=$(rapids-download-conda-from-github cpp) PYTHON_CHANNEL=$(rapids-download-conda-from-github python) diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh index 9c5f21a8ee..ea37fe83f7 100755 --- a/ci/release/update-version.sh +++ b/ci/release/update-version.sh @@ -57,57 +57,23 @@ DEPENDENCIES=( ) for DEP in "${DEPENDENCIES[@]}"; do - for FILE in dependencies.yaml conda/environments/*.yaml python/*/pyproject.toml; do - sed_runner "s/\(${DEP}==\)[0-9]\+\.[0-9]\+/\1${NEXT_SHORT_TAG_PEP440}/" "${FILE}" + for FILE in dependencies.yaml conda/environments/*.yaml; do + sed_runner "/-.* ${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}\(\[.*\]\)\{0,1\}==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0/g" "${FILE}" done for FILE in python/*/pyproject.toml; do - sed_runner "/-.* ${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}\(\[.*\]\)\{0,1\}==/ s/==.*/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0/g" "${FILE}" + sed_runner "/\"${DEP}==/ s/==.*\"/==${NEXT_SHORT_TAG_PEP440}.*,>=0.0.0a0\"/g" "${FILE}" done for FILE in docs/cuopt/source/*/quick-start.rst README.md; do - sed_runner "s/\(${DEP}==\)[0-9]\+\.[0-9]\+\.\\*/\1${NEXT_SHORT_TAG_PEP440}.\*/g" "${FILE}" - sed_runner "s/\(${DEP}=\)[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?[^ ]*/\1${NEXT_SHORT_TAG}.*/g" "${FILE}" - sed_runner "s/\(${DEP}:\)[0-9]\{2\}\.[0-9]\{1,2\}\.[0-9]\+\(-cuda[0-9]\+\.[0-9]\+-\)\(py[0-9]\+\)/\1${DOCKER_TAG}\2\3/g" "${FILE}" + sed_runner "/${DEP}\(-cu[[:digit:]]\{2\}\)\{0,1\}==/ s/==[0-9]\+\.[0-9]\+\.\*/==${NEXT_SHORT_TAG_PEP440}.\*/g" "${FILE}" + sed_runner "/${DEP}=/ s/=[0-9]\+\.[0-9]\+\(\.[0-9]\+\)\?[^ ]*/=${NEXT_SHORT_TAG}.*/g" "${FILE}" + sed_runner "/${DEP}:/ s/:[0-9]\{2\}\.[0-9]\{1,2\}\.[0-9]\+\(-cuda[0-9]\+\.[0-9]\+-\)\(py[0-9]\+\)/:${DOCKER_TAG}\1\2/g" "${FILE}" done done -# CMakeLists update -sed_runner 's/'"VERSION [0-9][0-9].[0-9][0-9].[0-9][0-9]"'/'"VERSION ${NEXT_FULL_TAG}"'/g' cpp/CMakeLists.txt -sed_runner 's/'"VERSION [0-9][0-9].[0-9][0-9].[0-9][0-9]"'/'"VERSION ${NEXT_FULL_TAG}"'/g' cpp/libmps_parser/CMakeLists.txt -sed_runner 's/'"DEPENDENT_LIB_MAJOR_VERSION \"[0-9][0-9]\""'/'"DEPENDENT_LIB_MAJOR_VERSION \"${NEXT_MAJOR}\""'/g' cpp/CMakeLists.txt -sed_runner 's/'"DEPENDENT_LIB_MINOR_VERSION \"[0-9][0-9]\""'/'"DEPENDENT_LIB_MINOR_VERSION \"${NEXT_MINOR}\""'/g' cpp/CMakeLists.txt - -# Server version update -sed_runner 's/'"\"version\": \"[0-9][0-9].[0-9][0-9]\""'/'"\"version\": \"${NEXT_SHORT_TAG}\""'/g' python/cuopt_server/cuopt_server/utils/data_definition.py -sed_runner 's/'"\"client_version\": \"[0-9][0-9].[0-9][0-9]\""'/'"\"client_version\": \"${NEXT_SHORT_TAG}\""'/g' python/cuopt_server/cuopt_server/utils/routing/data_definition.py -sed_runner 's/'"\"client_version\": \"[0-9][0-9].[0-9][0-9]\""'/'"\"client_version\": \"${NEXT_SHORT_TAG}\""'/g' python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py - -# Doc update -sed_runner 's/'"version = \"[0-9][0-9].[0-9][0-9]\""'/'"version = \"${NEXT_SHORT_TAG}\""'/g' docs/cuopt/source/conf.py -sed_runner 's/'"PROJECT_NUMBER = [0-9][0-9].[0-9][0-9]"'/'"PROJECT_NUMBER = ${NEXT_SHORT_TAG}"'/g' cpp/doxygen/Doxyfile - # Update project.json PROJECT_FILE="docs/cuopt/source/project.json" sed_runner 's/\("version": "\)[0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]"/\1'${NEXT_FULL_TAG}'"/g' "${PROJECT_FILE}" -# Update VERSIONS.json -VERSIONS_FILE="docs/cuopt/source/versions1.json" -# Only update if NEXT_FULL_TAG is not already present -if ! grep -q "\"version\": \"${NEXT_FULL_TAG}\"" "${VERSIONS_FILE}"; then - # Remove preferred and latest flags, but keep the version entry and URL - sed_runner '/"name": "latest",/d' "${VERSIONS_FILE}" - sed_runner '/"preferred": true,\?/d' "${VERSIONS_FILE}" - # Remove trailing comma after "url": ... in all version entries - sed_runner 's/\("url": "[^"]*"\),/\1/' "${VERSIONS_FILE}" - # Add new version entry with both preferred and latest flags - NEW_VERSION_ENTRY=' {\n "version": "'${NEXT_FULL_TAG}'",\n "url": "../'${NEXT_FULL_TAG}'/",\n "name": "latest",\n "preferred": true\n },' - sed_runner "/\[/a\\${NEW_VERSION_ENTRY}" "${VERSIONS_FILE}" -fi - -# RTD update -sed_runner "/^set(cuopt_version/ s/[0-9][0-9].[0-9][0-9].[0-9][0-9]/${NEXT_FULL_TAG}/g" python/cuopt/CMakeLists.txt -sed_runner "/^set(cuopt_version/ s/[0-9][0-9].[0-9][0-9].[0-9][0-9]/${NEXT_FULL_TAG}/g" python/cuopt/cuopt/linear_programming/CMakeLists.txt -sed_runner "/^set(cuopt_version/ s/[0-9][0-9].[0-9][0-9].[0-9][0-9]/${NEXT_FULL_TAG}/g" python/libcuopt/CMakeLists.txt - # Update nightly sed_runner 's/'"cuopt_version: \"[0-9][0-9].[0-9][0-9]\""'/'"cuopt_version: \"${NEXT_SHORT_TAG}\""'/g' .github/workflows/nightly.yaml @@ -121,6 +87,3 @@ for FILE in .github/workflows/*.yaml; do # CI image tags of the form {rapids_version}-{something} sed_runner "s/:[0-9]*\\.[0-9]*-/:${NEXT_SHORT_TAG}-/g" "${FILE}" done - -# PYTHON for RAPIDS -sed_runner "/DOWNLOAD.*rapids-cmake/ s/branch-[0-9][0-9].[0-9][0-9]/branch-${NEXT_SHORT_TAG}/g" python/cuopt/CMakeLists.txt diff --git a/ci/utils/update_doc_versions.py b/ci/utils/update_doc_versions.py new file mode 100644 index 0000000000..8c2a1e19a3 --- /dev/null +++ b/ci/utils/update_doc_versions.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Script to update versions1.json with new version from VERSION file. +This script reads the current version from the VERSION file and adds it to versions1.json +if it doesn't already exist, setting it as the latest and preferred version. +""" + +import json +import os +import sys +from pathlib import Path + + +def update_versions(version_file_path, versions_file_path): + """Update the versions list with the new version.""" + # Read VERSION file + with open(version_file_path, "r") as f: + version = f.read().strip() + if not version: + raise ValueError("VERSION file is empty") + + # Read versions1.json file + with open(versions_file_path, "r") as f: + versions = json.load(f) + + # Check if version already exists + for version_entry in versions: + if version_entry.get("version") == version: + print(f"Version {version} already exists in versions1.json") + return False + + # Remove "latest" and "preferred" from existing entries + for version_entry in versions: + if version_entry.get("name") == "latest": + version_entry.pop("name", None) + if version_entry.get("preferred"): + version_entry.pop("preferred", None) + + # Create new entry for the current version + new_entry = { + "version": version, + "url": f"../{version}/", + "name": "latest", + "preferred": True, + } + + # Add new entry at the beginning (most recent first) + versions.insert(0, new_entry) + + # Write updated versions back to file + with open(versions_file_path, "w") as f: + json.dump(versions, f, indent=2) + + return True + + +def main(): + """Main function to update versions1.json.""" + # Get the repository root directory (assuming script is run from repo root) + repo_root = Path.cwd() + + # Hard-coded file paths + version_file_path = repo_root / "VERSION" + versions_file_path = ( + repo_root / "docs" / "cuopt" / "source" / "versions1.json" + ) + + # Update versions + update_versions(version_file_path, versions_file_path) + + +if __name__ == "__main__": + main() diff --git a/cmake/rapids_config.cmake b/cmake/rapids_config.cmake index 9b361d59fc..898680fe26 100644 --- a/cmake/rapids_config.cmake +++ b/cmake/rapids_config.cmake @@ -28,3 +28,18 @@ endif() set(rapids-cmake-version "${RAPIDS_VERSION_MAJOR_MINOR}") include("${CMAKE_CURRENT_LIST_DIR}/RAPIDS.cmake") + +file(READ "${CMAKE_CURRENT_LIST_DIR}/../VERSION" _cuopt_version) +if(_cuopt_version MATCHES [[^([0-9][0-9])\.([0-9][0-9])\.([0-9][0-9])]]) + set(CUOPT_VERSION_MAJOR "${CMAKE_MATCH_1}") + set(CUOPT_VERSION_MINOR "${CMAKE_MATCH_2}") + set(CUOPT_VERSION_PATCH "${CMAKE_MATCH_3}") + set(CUOPT_VERSION_MAJOR_MINOR "${CUOPT_VERSION_MAJOR}.${CUOPT_VERSION_MINOR}") + set(CUOPT_VERSION "${CUOPT_VERSION_MAJOR}.${CUOPT_VERSION_MINOR}.${CUOPT_VERSION_PATCH}") +else() + string(REPLACE "\n" "\n " _cuopt_version_formatted " ${_cuopt_version}") + message( + FATAL_ERROR + "Could not determine cuOpt version. Contents of VERSION file:\n${_cuopt_version_formatted}" + ) +endif() diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 290016f902..76c856e0e5 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -32,12 +32,12 @@ rapids_cuda_init_architectures(CUOPT) project( CUOPT - VERSION 25.10.00 + VERSION "${CUOPT_VERSION}" LANGUAGES CXX CUDA C ) -set(DEPENDENT_LIB_MAJOR_VERSION "25") -set(DEPENDENT_LIB_MINOR_VERSION "10") +set(DEPENDENT_LIB_MAJOR_VERSION "${RAPIDS_VERSION_MAJOR}") +set(DEPENDENT_LIB_MINOR_VERSION "${RAPIDS_VERSION_MINOR}") rapids_cmake_write_version_file(include/cuopt/version_config.hpp) # ################################################################################################## diff --git a/cpp/doxygen/Doxyfile b/cpp/doxygen/Doxyfile index 12ae21c19a..cf86936a9c 100644 --- a/cpp/doxygen/Doxyfile +++ b/cpp/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "libcuopt" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 25.10 +PROJECT_NUMBER = "$(CUOPT_VERSION_MAJOR_MINOR)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/cpp/libmps_parser/CMakeLists.txt b/cpp/libmps_parser/CMakeLists.txt index d6b87f03fa..33f0a7b581 100644 --- a/cpp/libmps_parser/CMakeLists.txt +++ b/cpp/libmps_parser/CMakeLists.txt @@ -22,7 +22,7 @@ include(rapids-find) project( MPS_PARSER - VERSION 25.10.00 + VERSION "${CUOPT_VERSION}" LANGUAGES CXX ) diff --git a/python/cuopt/CMakeLists.txt b/python/cuopt/CMakeLists.txt index 097082d709..a6ae668e7d 100644 --- a/python/cuopt/CMakeLists.txt +++ b/python/cuopt/CMakeLists.txt @@ -15,15 +15,13 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(cuopt_version 25.10.00) - include(../../cmake/rapids_config.cmake) include(rapids-cuda) rapids_cuda_init_architectures(CUOPT) project( cuopt-python - VERSION ${cuopt_version} + VERSION "${CUOPT_VERSION}" LANGUAGES # TODO: Building Python extension modules via the python_extension_module requires the C # language to be enabled here. The test project that is built in scikit-build to verify # various linking options for the python library is hardcoded to build with C, so until @@ -31,8 +29,8 @@ project( C CXX CUDA) -find_package(cuopt ${cuopt_version}) -find_package(mps_parser ${cuopt_version}) +find_package(cuopt "${CUOPT_VERSION}") +find_package(mps_parser "${CUOPT_VERSION}") include(rapids-cython-core) rapids_cython_init() diff --git a/python/cuopt/cuopt/__init__.py b/python/cuopt/cuopt/__init__.py index c12a888281..ab52891e3c 100644 --- a/python/cuopt/cuopt/__init__.py +++ b/python/cuopt/cuopt/__init__.py @@ -22,4 +22,4 @@ del libcuopt from cuopt import linear_programming, routing -from cuopt._version import __git_commit__, __version__ +from cuopt._version import __git_commit__, __version__, __version_major_minor__ diff --git a/python/cuopt/cuopt/_version.py b/python/cuopt/cuopt/_version.py index 6d6f1fa097..a89da8a08d 100644 --- a/python/cuopt/cuopt/_version.py +++ b/python/cuopt/cuopt/_version.py @@ -19,3 +19,5 @@ importlib.resources.files("cuopt").joinpath("VERSION").read_text().strip() ) __git_commit__ = "" + +__version_major_minor__ = ".".join(__version__.split(".")[:2]) diff --git a/python/cuopt/cuopt/linear_programming/CMakeLists.txt b/python/cuopt/cuopt/linear_programming/CMakeLists.txt index c31ed89bef..1f46f31826 100644 --- a/python/cuopt/cuopt/linear_programming/CMakeLists.txt +++ b/python/cuopt/cuopt/linear_programming/CMakeLists.txt @@ -15,13 +15,11 @@ cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR) -set(cuopt_version 25.10.00) - include(../../../../cmake/rapids_config.cmake) project( mpsparser-python - VERSION ${cuopt_version} + VERSION "${CUOPT_VERSION}" LANGUAGES # TODO: Building Python extension modules via the python_extension_module requires the C # language to be enabled here. The test project that is built in scikit-build to verify # various linking options for the python library is hardcoded to build with C, so until @@ -34,7 +32,7 @@ option(CUOPT_BUILD_WHEELS "Whether this build is generating a Python wheel." ON) # If the user requested it we attempt to find MPS Parser. if(FIND_MPS_PARSER_CPP) - find_package(mps_parser ${cuopt_version}) + find_package(mps_parser "${CUOPT_VERSION}") else() set(mps_parser_FOUND OFF) endif() diff --git a/python/cuopt_server/cuopt_server/__init__.py b/python/cuopt_server/cuopt_server/__init__.py index c8477dae4f..55a82de16f 100644 --- a/python/cuopt_server/cuopt_server/__init__.py +++ b/python/cuopt_server/cuopt_server/__init__.py @@ -14,4 +14,8 @@ # limitations under the License. from cuopt_server import cuopt_service -from cuopt_server._version import __git_commit__, __version__ +from cuopt_server._version import ( + __git_commit__, + __version__, + __version_major_minor__, +) diff --git a/python/cuopt_server/cuopt_server/_version.py b/python/cuopt_server/cuopt_server/_version.py index ba27ac7b05..d0c1ac4afc 100644 --- a/python/cuopt_server/cuopt_server/_version.py +++ b/python/cuopt_server/cuopt_server/_version.py @@ -22,3 +22,5 @@ .strip() ) __git_commit__ = "" + +__version_major_minor__ = ".".join(__version__.split(".")[:2]) diff --git a/python/cuopt_server/cuopt_server/utils/data_definition.py b/python/cuopt_server/cuopt_server/utils/data_definition.py index 4d4df04346..4fb5d8d2ce 100644 --- a/python/cuopt_server/cuopt_server/utils/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/data_definition.py @@ -23,6 +23,7 @@ import jsonref from pydantic import BaseModel, Extra, Field +from .._version import __version_major_minor__ from .linear_programming.data_definition import ( # noqa IncumbentSolution, LPData, @@ -343,7 +344,10 @@ class LogResponseModel(StrictModel): "application/json": { "examples": { "Healthy response": { - "value": {"status": "RUNNING", "version": "25.10"} + "value": { + "status": "RUNNING", + "version": __version_major_minor__, + } } } } diff --git a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py index 5c5ee7e6ee..30dcf8695f 100644 --- a/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/linear_programming/data_definition.py @@ -22,6 +22,8 @@ from pydantic import BaseModel, Extra, Field, PlainValidator from typing_extensions import Annotated +from ..._version import __version_major_minor__ + # INPUT DATA DEFINITIONS @@ -853,7 +855,7 @@ class IncumbentSolution(StrictModel): managed_lp_example_data = { "action": "cuOpt_LP", "data": lp_example_data, - "client_version": "25.10", + "client_version": __version_major_minor__, } # cut and pasted from actual run of LP example data. diff --git a/python/cuopt_server/cuopt_server/utils/routing/data_definition.py b/python/cuopt_server/cuopt_server/utils/routing/data_definition.py index 03bc63f1ef..530af4712d 100644 --- a/python/cuopt_server/cuopt_server/utils/routing/data_definition.py +++ b/python/cuopt_server/cuopt_server/utils/routing/data_definition.py @@ -21,6 +21,8 @@ import jsonref from pydantic import BaseModel, Extra, Field, RootModel, root_validator +from ..._version import __version_major_minor__ + class LocationTypeEnum(str, Enum): Depot = "Depot" @@ -1026,7 +1028,7 @@ class InFeasibleSolve(StrictModel): managed_vrp_example_data = { "action": "cuOpt_OptimizedRouting", "data": vrp_example_data, - "client_version": "25.10", + "client_version": __version_major_minor__, } # cut and pasted from actual run of VRP example data. diff --git a/python/libcuopt/CMakeLists.txt b/python/libcuopt/CMakeLists.txt index b997708876..85b7c98513 100644 --- a/python/libcuopt/CMakeLists.txt +++ b/python/libcuopt/CMakeLists.txt @@ -15,21 +15,19 @@ cmake_minimum_required(VERSION 3.30.4 FATAL_ERROR) -set(cuopt_version 25.10.00) - include(../../cmake/rapids_config.cmake) include(rapids-cuda) rapids_cuda_init_architectures(libcuopt-python) project( libcuopt-python - VERSION "${cuopt_version}" + VERSION "${CUOPT_VERSION}" LANGUAGES CXX CUDA ) # Check if cuopt is already available. If so, it is the user's responsibility to ensure that the # CMake package is also available at build time of the Python cuopt package. -find_package(cuopt "${cuopt_version}") +find_package(cuopt "${CUOPT_VERSION}") if(cuopt_FOUND) return()