From 2398a0d9037903bd0e602d82d0c075d7a7d4e926 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 12 Jan 2026 13:36:49 -0600 Subject: [PATCH 01/18] Adding sonarqube support --- .github/sonar-project.properties | 1 + .github/workflows/build.yaml | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 .github/sonar-project.properties diff --git a/.github/sonar-project.properties b/.github/sonar-project.properties new file mode 100644 index 000000000..b8af80dca --- /dev/null +++ b/.github/sonar-project.properties @@ -0,0 +1 @@ +sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cddb79556..cfd6814c2 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -182,6 +182,21 @@ jobs: artifact-name: "cuopt_docs" container_image: "rapidsai/ci-conda:26.02-latest" script: "ci/build_docs.sh" + sonarqube-analysis: + name: SonarQube Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + - uses: SonarSource/sonarqube-quality-gate-action@v1 + timeout-minutes: 5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} wheel-build-cuopt-sh-client: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-13.1.0 From 829c43129ebf73dcd447a1e440fd5ecabcc2453e Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 12 Jan 2026 13:37:31 -0600 Subject: [PATCH 02/18] fix --- .github/sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/sonar-project.properties b/.github/sonar-project.properties index b8af80dca..d8d1a2978 100644 --- a/.github/sonar-project.properties +++ b/.github/sonar-project.properties @@ -1 +1 @@ -sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt \ No newline at end of file +sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt From ff534a57f6eff7be0e6de397e1e0ce6e18ef8641 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 12 Jan 2026 16:56:23 -0600 Subject: [PATCH 03/18] update as per review --- .github/sonar-project.properties | 27 +++++++++++++++++++++++++++ .github/workflows/build.yaml | 2 ++ 2 files changed, 29 insertions(+) diff --git a/.github/sonar-project.properties b/.github/sonar-project.properties index d8d1a2978..7918d43fc 100644 --- a/.github/sonar-project.properties +++ b/.github/sonar-project.properties @@ -1 +1,28 @@ sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt +sonar.projectName=NVIDIA cuOpt +sonar.projectVersion=1.0 + +# Source code location (relative to projectBaseDir which is .github) +sonar.sources=../ + +# Exclusions - files/directories to skip +sonar.exclusions=**/node_modules/**,**/build/**,**/dist/**,**/*.min.js,\ + **/cpp/build/**,\ + **/cvxpy/**,\ + **/datasets/**,\ + **/img/**,\ + **/.venv/**,\ + **/__pycache__/**,\ + **/*.egg-info/**,\ + **/thirdparty/** + +# Test files +sonar.tests=../ +sonar.test.inclusions=**/tests/**,**/test_*.py,**/test_*.cpp,**/test_*.cu + +# Coverage exclusions +sonar.coverage.exclusions=**/tests/**,**/test_*.py,**/test_*.cpp,**/test_*.cu + +# Language-specific settings +sonar.python.version=3.10,3.11,3.12,3.13 +sonar.sourceEncoding=UTF-8 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index cfd6814c2..52298f2ca 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -190,6 +190,8 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - uses: SonarSource/sonarqube-scan-action@v4 + with: + projectBaseDir: .github env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} From f2b8381f81cf1031649da69a5d55161ce35305f8 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Mon, 12 Jan 2026 16:58:37 -0600 Subject: [PATCH 04/18] Add it to PR job to test --- .github/workflows/pr.yaml | 50 +++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 796e39338..6d2c41905 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -20,6 +20,7 @@ jobs: - compute-matrix-filters - changed-files - checks + - sonarqube-analysis - conda-cpp-build - conda-cpp-tests - conda-python-build @@ -176,6 +177,23 @@ jobs: uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@cuda-13.1.0 with: enable_check_generated_files: false + sonarqube-analysis: + name: SonarQube Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - uses: SonarSource/sonarqube-scan-action@v4 + with: + projectBaseDir: .github + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + - uses: SonarSource/sonarqube-quality-gate-action@v1 + timeout-minutes: 5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} conda-cpp-build: needs: [checks, compute-matrix-filters] secrets: inherit @@ -186,19 +204,13 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_lean_filter }} conda-cpp-tests: needs: [conda-cpp-build, changed-files, compute-matrix-filters] + secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp with: build_type: pull-request script: ci/test_cpp.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} - secrets: - script-env-secret-1-key: CUOPT_DATASET_S3_URI - script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} - script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID - script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} - script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY - script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} conda-python-build: needs: [conda-cpp-build, compute-matrix-filters] secrets: inherit @@ -209,6 +221,7 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} conda-python-tests: needs: [conda-python-build, changed-files, compute-matrix-filters] + secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: @@ -216,13 +229,6 @@ jobs: build_type: pull-request script: ci/test_python.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} - secrets: - script-env-secret-1-key: CUOPT_DATASET_S3_URI - script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} - script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID - script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} - script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY - script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} docs-build: needs: conda-python-build secrets: inherit @@ -270,19 +276,13 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} wheel-tests-cuopt: needs: [wheel-build-cuopt, wheel-build-cuopt-mps-parser, wheel-build-cuopt-sh-client, changed-files, compute-matrix-filters] + secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_cuopt with: build_type: pull-request script: ci/test_wheel_cuopt.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} - secrets: - script-env-secret-1-key: CUOPT_DATASET_S3_URI - script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} - script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID - script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} - script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY - script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} wheel-build-cuopt-server: needs: [checks, compute-matrix-filters] secrets: inherit @@ -310,19 +310,13 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.cuopt_sh_client_filter }} wheel-tests-cuopt-server: needs: [wheel-build-cuopt, wheel-build-cuopt-server, changed-files, compute-matrix-filters] + secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_cuopt_server with: build_type: pull-request script: ci/test_wheel_cuopt_server.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} - secrets: - script-env-secret-1-key: CUOPT_DATASET_S3_URI - script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} - script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID - script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} - script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY - script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} test-self-hosted-server: needs: [wheel-build-cuopt, wheel-build-cuopt-server, changed-files] secrets: inherit From 906bd890a5803fe9c7db76a1918af6b966a7a8fd Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 10:47:47 -0600 Subject: [PATCH 05/18] revert sonarqube changes --- .github/workflows/build.yaml | 17 ------------ .github/workflows/pr.yaml | 50 ++++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 52298f2ca..cddb79556 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -182,23 +182,6 @@ jobs: artifact-name: "cuopt_docs" container_image: "rapidsai/ci-conda:26.02-latest" script: "ci/build_docs.sh" - sonarqube-analysis: - name: SonarQube Analysis - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - uses: SonarSource/sonarqube-scan-action@v4 - with: - projectBaseDir: .github - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} - - uses: SonarSource/sonarqube-quality-gate-action@v1 - timeout-minutes: 5 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} wheel-build-cuopt-sh-client: secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-build.yaml@cuda-13.1.0 diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 6d2c41905..796e39338 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -20,7 +20,6 @@ jobs: - compute-matrix-filters - changed-files - checks - - sonarqube-analysis - conda-cpp-build - conda-cpp-tests - conda-python-build @@ -177,23 +176,6 @@ jobs: uses: rapidsai/shared-workflows/.github/workflows/checks.yaml@cuda-13.1.0 with: enable_check_generated_files: false - sonarqube-analysis: - name: SonarQube Analysis - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - uses: SonarSource/sonarqube-scan-action@v4 - with: - projectBaseDir: .github - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} - - uses: SonarSource/sonarqube-quality-gate-action@v1 - timeout-minutes: 5 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} conda-cpp-build: needs: [checks, compute-matrix-filters] secrets: inherit @@ -204,13 +186,19 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_lean_filter }} conda-cpp-tests: needs: [conda-cpp-build, changed-files, compute-matrix-filters] - secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/conda-cpp-tests.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_cpp with: build_type: pull-request script: ci/test_cpp.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} + secrets: + script-env-secret-1-key: CUOPT_DATASET_S3_URI + script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} + script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID + script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} + script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY + script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} conda-python-build: needs: [conda-cpp-build, compute-matrix-filters] secrets: inherit @@ -221,7 +209,6 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} conda-python-tests: needs: [conda-python-build, changed-files, compute-matrix-filters] - secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/conda-python-tests.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python with: @@ -229,6 +216,13 @@ jobs: build_type: pull-request script: ci/test_python.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.conda_test_filter }} + secrets: + script-env-secret-1-key: CUOPT_DATASET_S3_URI + script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} + script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID + script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} + script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY + script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} docs-build: needs: conda-python-build secrets: inherit @@ -276,13 +270,19 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} wheel-tests-cuopt: needs: [wheel-build-cuopt, wheel-build-cuopt-mps-parser, wheel-build-cuopt-sh-client, changed-files, compute-matrix-filters] - secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_cuopt with: build_type: pull-request script: ci/test_wheel_cuopt.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} + secrets: + script-env-secret-1-key: CUOPT_DATASET_S3_URI + script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} + script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID + script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} + script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY + script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} wheel-build-cuopt-server: needs: [checks, compute-matrix-filters] secrets: inherit @@ -310,13 +310,19 @@ jobs: matrix_filter: ${{ needs.compute-matrix-filters.outputs.cuopt_sh_client_filter }} wheel-tests-cuopt-server: needs: [wheel-build-cuopt, wheel-build-cuopt-server, changed-files, compute-matrix-filters] - secrets: inherit uses: rapidsai/shared-workflows/.github/workflows/wheels-test.yaml@cuda-13.1.0 #if: fromJSON(needs.changed-files.outputs.changed_file_groups).test_python_cuopt_server with: build_type: pull-request script: ci/test_wheel_cuopt_server.sh matrix_filter: ${{ needs.compute-matrix-filters.outputs.wheel_lean_filter }} + secrets: + script-env-secret-1-key: CUOPT_DATASET_S3_URI + script-env-secret-1-value: ${{ secrets.CUOPT_DATASET_S3_URI }} + script-env-secret-2-key: CUOPT_AWS_ACCESS_KEY_ID + script-env-secret-2-value: ${{ secrets.CUOPT_AWS_ACCESS_KEY_ID }} + script-env-secret-3-key: CUOPT_AWS_SECRET_ACCESS_KEY + script-env-secret-3-value: ${{ secrets.CUOPT_AWS_SECRET_ACCESS_KEY }} test-self-hosted-server: needs: [wheel-build-cuopt, wheel-build-cuopt-server, changed-files] secrets: inherit From 281881bbf02bc2ba056c26ec79c3c2a5c633e710 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:20:42 -0600 Subject: [PATCH 06/18] Adding sonarqube project details to cuopt and script to run --- .github/sonar-project.properties | 28 ----- CONTRIBUTING.md | 2 +- sonar-project.properties | 6 + sonarqube/run-sonar-analysis.sh | 195 +++++++++++++++++++++++++++++++ sonarqube/sonar-branches.txt | 12 ++ 5 files changed, 214 insertions(+), 29 deletions(-) delete mode 100644 .github/sonar-project.properties create mode 100644 sonar-project.properties create mode 100755 sonarqube/run-sonar-analysis.sh create mode 100644 sonarqube/sonar-branches.txt diff --git a/.github/sonar-project.properties b/.github/sonar-project.properties deleted file mode 100644 index 7918d43fc..000000000 --- a/.github/sonar-project.properties +++ /dev/null @@ -1,28 +0,0 @@ -sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt -sonar.projectName=NVIDIA cuOpt -sonar.projectVersion=1.0 - -# Source code location (relative to projectBaseDir which is .github) -sonar.sources=../ - -# Exclusions - files/directories to skip -sonar.exclusions=**/node_modules/**,**/build/**,**/dist/**,**/*.min.js,\ - **/cpp/build/**,\ - **/cvxpy/**,\ - **/datasets/**,\ - **/img/**,\ - **/.venv/**,\ - **/__pycache__/**,\ - **/*.egg-info/**,\ - **/thirdparty/** - -# Test files -sonar.tests=../ -sonar.test.inclusions=**/tests/**,**/test_*.py,**/test_*.cpp,**/test_*.cu - -# Coverage exclusions -sonar.coverage.exclusions=**/tests/**,**/test_*.py,**/test_*.cpp,**/test_*.cu - -# Language-specific settings -sonar.python.version=3.10,3.11,3.12,3.13 -sonar.sourceEncoding=UTF-8 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f9ab0875..fc5cb13d3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -136,7 +136,7 @@ Please install conda if you don't have it already. You can install [miniforge](h # create the conda environment (assuming in base `cuopt` directory) # note: cuOpt currently doesn't support `channel_priority: strict`; # use `channel_priority: flexible` instead -conda env create --name cuopt_dev --file conda/environments/all_cuda-130_arch-$(uname -m).yaml +conda env create --name cuopt_dev --file conda/environments/all_cuda-131_arch-$(uname -m).yaml # activate the environment conda activate cuopt_dev ``` diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..00638a81d --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,6 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt +sonar.projectName=NVIDIA cuOpt +sonar.projectVersion=1.0 \ No newline at end of file diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh new file mode 100755 index 000000000..b32e862d3 --- /dev/null +++ b/sonarqube/run-sonar-analysis.sh @@ -0,0 +1,195 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BRANCHES_FILE="$SCRIPT_DIR/sonar-branches.txt" +WORK_DIR="/tmp/sonar-analysis-$(date +%Y%m%d-%H%M%S)" + +# Conda environment file to use for building +# Adjust this path based on your CUDA version and architecture +# Available: all_cuda-129_arch-{x86_64,aarch64}.yaml, all_cuda-131_arch-{x86_64,aarch64}.yaml +ARCH=$(uname -m) +CONDA_ENV_FILE="conda/environments/all_cuda-131_arch-${ARCH}.yaml" + +# Get git remote URL from current directory +REPO_URL=$(git config --get remote.origin.url 2>/dev/null) +if [ -z "$REPO_URL" ]; then + echo "ERROR: Could not determine git remote URL" + echo "Make sure you run this script from within the git repository" + exit 1 +fi + +echo "Repository URL: $REPO_URL" +echo "Working directory: $WORK_DIR" + +# Create working directory +mkdir -p "$WORK_DIR" + +# Cleanup function +cleanup() { + echo "" + echo "Cleaning up working directory: $WORK_DIR" + rm -rf "$WORK_DIR" +} + +# Register cleanup on exit +trap cleanup EXIT + +# Check if branches file exists +if [ ! -f "$BRANCHES_FILE" ]; then + echo "ERROR: Branches file not found: $BRANCHES_FILE" + exit 1 +fi + +# Read and validate branches +branches=() +while IFS= read -r branch || [ -n "$branch" ]; do + # Skip comments and empty lines + [[ "$branch" =~ ^#.*$ ]] && continue + [[ -z "${branch// }" ]] && continue + + # Trim whitespace and add to array + branch=$(echo "$branch" | xargs) + branches+=("$branch") +done < "$BRANCHES_FILE" + +# Fail if no branches found +if [ ${#branches[@]} -eq 0 ]; then + echo "ERROR: No branches configured in $BRANCHES_FILE" + echo "Please add at least one branch to the file." + exit 1 +fi + +echo "Found ${#branches[@]} branch(es) to process: ${branches[*]}" +echo "Host: $(hostname)" +echo "Start time: $(date)" + +# Track success/failure +successful_branches=() +failed_branches=() + +# Process each branch +for branch in "${branches[@]}"; do + echo "==========================================" + echo "Processing branch: $branch" + echo "==========================================" + + # Create a safe directory name from branch name + safe_branch_name="${branch//\//_}" + clone_dir="$WORK_DIR/$safe_branch_name" + + # Clone the specific branch + echo "Cloning branch: $branch into $clone_dir" + if ! git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log; then + echo "ERROR: Failed to clone branch: $branch" + failed_branches+=("$branch (clone failed)") + continue + fi + + # Change to cloned directory + cd "$clone_dir" + + # Setup conda environment, build, and analyze + echo "Setting up conda environment for: $branch" + + # Create a unique conda environment name for this branch + conda_env_name="cuopt_sonar_${safe_branch_name}" + + # Create conda environment + if ! conda env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then + echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log" + failed_branches+=("$branch (conda env creation failed)") + rm -rf "$clone_dir" + continue + fi + + # Activate conda environment and run build + analysis in a subshell + echo "Building and analyzing branch: $branch in conda environment: $conda_env_name" + + if ! bash -c " + set -e + source \$(conda info --base)/etc/profile.d/conda.sh + conda activate $conda_env_name + + echo 'Conda environment activated: $conda_env_name' + echo 'Python version:' \$(python --version) + + # Build the project + echo 'Building project...' + if ! ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log; then + echo 'Build failed' + exit 1 + fi + + # Run SonarQube analysis + echo 'Running SonarQube analysis...' + if ! sonar-scanner -Dsonar.branch.name='$branch' 2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then + echo 'SonarQube analysis failed' + exit 1 + fi + + echo 'Build and analysis completed successfully' + "; then + echo "ERROR: Build or analysis failed for branch: $branch" + if grep -q "Build failed" /tmp/build_${safe_branch_name}.log 2>/dev/null; then + failed_branches+=("$branch (build failed)") + else + failed_branches+=("$branch (sonar analysis failed)") + fi + + # Clean up conda environment + conda env remove -n "$conda_env_name" -y 2>/dev/null || true + rm -rf "$clone_dir" + continue + fi + + # Clean up conda environment after successful analysis + echo "Cleaning up conda environment: $conda_env_name" + conda env remove -n "$conda_env_name" -y 2>/dev/null || true + + successful_branches+=("$branch") + echo "✓ Successfully completed analysis for: $branch" + echo "Progress: ${#successful_branches[@]} succeeded, ${#failed_branches[@]} failed out of ${#branches[@]} total" + + # Clean up clone directory after successful analysis + echo "Cleaning up clone directory for: $branch" + rm -rf "$clone_dir" +done + +# Final summary +echo "==========================================" +echo "SonarQube Analysis Complete" +echo "==========================================" +echo "Total branches: ${#branches[@]}" +echo "Successful: ${#successful_branches[@]}" +echo "Failed: ${#failed_branches[@]}" +echo "" + +if [ ${#successful_branches[@]} -gt 0 ]; then + echo "✓ Successful branches:" + for branch in "${successful_branches[@]}"; do + echo " - $branch" + done + echo "" +fi + +if [ ${#failed_branches[@]} -gt 0 ]; then + echo "✗ Failed branches:" + for branch in "${failed_branches[@]}"; do + echo " - $branch" + done + echo "" +fi + +echo "End time: $(date)" +echo "==========================================" + +# Exit with error if any branches failed +if [ ${#failed_branches[@]} -gt 0 ]; then + echo "ERROR: ${#failed_branches[@]} branch(es) failed analysis" + exit 1 +fi + +echo "All branches processed successfully!" +exit 0 diff --git a/sonarqube/sonar-branches.txt b/sonarqube/sonar-branches.txt new file mode 100644 index 000000000..a75ecac67 --- /dev/null +++ b/sonarqube/sonar-branches.txt @@ -0,0 +1,12 @@ +# SonarQube Branch List +# One branch name per line +# Lines starting with # are comments and should be ignored by the cron job +# Empty lines are also ignored + +# Main development branches +main +release/26.02 + +# Add release branches as needed +# release/v1.0 +# release/v2.0 From ac0a4dbe786c24cbc000ba5b1d32c5dd019af7d7 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:21:11 -0600 Subject: [PATCH 07/18] Add updates --- sonar-project.properties | 6 +++- sonarqube/README.md | 60 +++++++++++++++++++++++++++++++++ sonarqube/run-sonar-analysis.sh | 26 +++++++++++++- 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 sonarqube/README.md diff --git a/sonar-project.properties b/sonar-project.properties index 00638a81d..214fdb330 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,6 +1,10 @@ # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +# SonarQube Project Configuration sonar.projectKey=GPUSW_cuOpt_Nvidia-cuOpt_cuopt sonar.projectName=NVIDIA cuOpt -sonar.projectVersion=1.0 \ No newline at end of file +sonar.projectVersion=1.0 + +# Source code location +sonar.sources=. \ No newline at end of file diff --git a/sonarqube/README.md b/sonarqube/README.md new file mode 100644 index 000000000..5fc0dc1b6 --- /dev/null +++ b/sonarqube/README.md @@ -0,0 +1,60 @@ +# SonarQube Analysis + +This directory contains the configuration and scripts for running automated SonarQube analysis on cuOpt branches. + +## Files + +- `sonar-branches.txt` - List of branches to analyze (one per line) +- `run-sonar-analysis.sh` - Automated script that clones, builds, and analyzes branches + +## Quick Start + +### 1. Configure Branches + +Edit `sonar-branches.txt` to specify which branches to analyze: + +```bash +# One branch per line +main +release/26.02 + +# Lines starting with # are comments +# Empty lines are ignored +``` + +### 2. Set Required Environment Variable + +The script requires authentication: + +```bash +export SONAR_TOKEN="your_token_here" +``` + +**Note**: Contact the cuOpt team for token details. + +### 3. Run the Analysis + +```bash +cd /path/to/cuopt +./sonarqube/run-sonar-analysis.sh +``` + +## Script Behavior + +The script will automatically: + +1. ✅ Validate branch configuration file exists and has at least one branch +2. ✅ Clone each branch into a fresh temporary directory +3. ✅ Create an isolated conda environment per branch +4. ✅ Build the project using `./build.sh` +5. ✅ Run SonarQube analysis with branch-specific tagging +6. ✅ Clean up temporary files and conda environments +7. ✅ Provide a summary of successful and failed branches + +## Support + +**Contact**: cuOpt team + +For issues with: +- Build failures: See [CONTRIBUTING.md](../CONTRIBUTING.md) +- Script bugs: Report to the cuOpt team diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index b32e862d3..d5f903da7 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -12,6 +12,26 @@ WORK_DIR="/tmp/sonar-analysis-$(date +%Y%m%d-%H%M%S)" ARCH=$(uname -m) CONDA_ENV_FILE="conda/environments/all_cuda-131_arch-${ARCH}.yaml" +# SonarQube Configuration +# The token should be set via environment variable SONAR_TOKEN for security +# You can also set SONAR_HOST_URL if using a custom SonarQube server +# Default: https://sonarcloud.io (if not set) +if [ -z "$SONAR_TOKEN" ]; then + echo "ERROR: SONAR_TOKEN environment variable is not set" + echo "Please set it with: export SONAR_TOKEN=your_sonarqube_token" + echo "" + echo "To generate a token:" + echo " 1. Log in to SonarQube/SonarCloud" + echo " 2. Go to My Account > Security" + echo " 3. Generate a new token" + exit 1 +fi + +# Optional: Set SonarQube host URL (defaults to SonarCloud if not set) +SONAR_HOST_URL="${SONAR_HOST_URL:-https://sonarcloud.io}" + +echo "SonarQube Host: $SONAR_HOST_URL" + # Get git remote URL from current directory REPO_URL=$(git config --get remote.origin.url 2>/dev/null) if [ -z "$REPO_URL" ]; then @@ -124,7 +144,11 @@ for branch in "${branches[@]}"; do # Run SonarQube analysis echo 'Running SonarQube analysis...' - if ! sonar-scanner -Dsonar.branch.name='$branch' 2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then + if ! sonar-scanner \ + -Dsonar.host.url='$SONAR_HOST_URL' \ + -Dsonar.token='$SONAR_TOKEN' \ + -Dsonar.branch.name='$branch' \ + 2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then echo 'SonarQube analysis failed' exit 1 fi From f390175bb7d6ccad3f976b238605867e64bc814e Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:22:27 -0600 Subject: [PATCH 08/18] update --- sonarqube/run-sonar-analysis.sh | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index d5f903da7..d470f1278 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -14,24 +14,12 @@ CONDA_ENV_FILE="conda/environments/all_cuda-131_arch-${ARCH}.yaml" # SonarQube Configuration # The token should be set via environment variable SONAR_TOKEN for security -# You can also set SONAR_HOST_URL if using a custom SonarQube server -# Default: https://sonarcloud.io (if not set) if [ -z "$SONAR_TOKEN" ]; then echo "ERROR: SONAR_TOKEN environment variable is not set" echo "Please set it with: export SONAR_TOKEN=your_sonarqube_token" - echo "" - echo "To generate a token:" - echo " 1. Log in to SonarQube/SonarCloud" - echo " 2. Go to My Account > Security" - echo " 3. Generate a new token" exit 1 fi -# Optional: Set SonarQube host URL (defaults to SonarCloud if not set) -SONAR_HOST_URL="${SONAR_HOST_URL:-https://sonarcloud.io}" - -echo "SonarQube Host: $SONAR_HOST_URL" - # Get git remote URL from current directory REPO_URL=$(git config --get remote.origin.url 2>/dev/null) if [ -z "$REPO_URL" ]; then @@ -145,7 +133,6 @@ for branch in "${branches[@]}"; do # Run SonarQube analysis echo 'Running SonarQube analysis...' if ! sonar-scanner \ - -Dsonar.host.url='$SONAR_HOST_URL' \ -Dsonar.token='$SONAR_TOKEN' \ -Dsonar.branch.name='$branch' \ 2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then From 64e7cc563bd2a8fdf687dc99155f1e05371fe6d5 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:23:10 -0600 Subject: [PATCH 09/18] fix style --- sonar-project.properties | 2 +- sonarqube/run-sonar-analysis.sh | 34 ++++++++++++++++----------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 214fdb330..ae8d6bd25 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -7,4 +7,4 @@ sonar.projectName=NVIDIA cuOpt sonar.projectVersion=1.0 # Source code location -sonar.sources=. \ No newline at end of file +sonar.sources=. diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index d470f1278..5439ae2d1 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -56,7 +56,7 @@ while IFS= read -r branch || [ -n "$branch" ]; do # Skip comments and empty lines [[ "$branch" =~ ^#.*$ ]] && continue [[ -z "${branch// }" ]] && continue - + # Trim whitespace and add to array branch=$(echo "$branch" | xargs) branches+=("$branch") @@ -82,11 +82,11 @@ for branch in "${branches[@]}"; do echo "==========================================" echo "Processing branch: $branch" echo "==========================================" - + # Create a safe directory name from branch name safe_branch_name="${branch//\//_}" clone_dir="$WORK_DIR/$safe_branch_name" - + # Clone the specific branch echo "Cloning branch: $branch into $clone_dir" if ! git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log; then @@ -94,16 +94,16 @@ for branch in "${branches[@]}"; do failed_branches+=("$branch (clone failed)") continue fi - + # Change to cloned directory cd "$clone_dir" - + # Setup conda environment, build, and analyze echo "Setting up conda environment for: $branch" - + # Create a unique conda environment name for this branch conda_env_name="cuopt_sonar_${safe_branch_name}" - + # Create conda environment if ! conda env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log" @@ -111,25 +111,25 @@ for branch in "${branches[@]}"; do rm -rf "$clone_dir" continue fi - + # Activate conda environment and run build + analysis in a subshell echo "Building and analyzing branch: $branch in conda environment: $conda_env_name" - + if ! bash -c " set -e source \$(conda info --base)/etc/profile.d/conda.sh conda activate $conda_env_name - + echo 'Conda environment activated: $conda_env_name' echo 'Python version:' \$(python --version) - + # Build the project echo 'Building project...' if ! ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log; then echo 'Build failed' exit 1 fi - + # Run SonarQube analysis echo 'Running SonarQube analysis...' if ! sonar-scanner \ @@ -139,7 +139,7 @@ for branch in "${branches[@]}"; do echo 'SonarQube analysis failed' exit 1 fi - + echo 'Build and analysis completed successfully' "; then echo "ERROR: Build or analysis failed for branch: $branch" @@ -148,21 +148,21 @@ for branch in "${branches[@]}"; do else failed_branches+=("$branch (sonar analysis failed)") fi - + # Clean up conda environment conda env remove -n "$conda_env_name" -y 2>/dev/null || true rm -rf "$clone_dir" continue fi - + # Clean up conda environment after successful analysis echo "Cleaning up conda environment: $conda_env_name" conda env remove -n "$conda_env_name" -y 2>/dev/null || true - + successful_branches+=("$branch") echo "✓ Successfully completed analysis for: $branch" echo "Progress: ${#successful_branches[@]} succeeded, ${#failed_branches[@]} failed out of ${#branches[@]} total" - + # Clean up clone directory after successful analysis echo "Cleaning up clone directory for: $branch" rm -rf "$clone_dir" From 2718429b46fa63cb4c727bd8abeb8dab4d355328 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:24:06 -0600 Subject: [PATCH 10/18] fix style --- sonarqube/run-sonar-analysis.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 5439ae2d1..10f895c25 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -96,7 +96,11 @@ for branch in "${branches[@]}"; do fi # Change to cloned directory - cd "$clone_dir" + if ! cd "$clone_dir"; then + echo "ERROR: Failed to change directory to: $clone_dir" + failed_branches+=("$branch (cd failed)") + continue + fi # Setup conda environment, build, and analyze echo "Setting up conda environment for: $branch" From 14137bc3fb5b6ec3e20eae3d7ad5bf0b1485b1d1 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 13:51:10 -0600 Subject: [PATCH 11/18] fix repo url --- sonarqube/run-sonar-analysis.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 10f895c25..02db06e8f 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -20,13 +20,8 @@ if [ -z "$SONAR_TOKEN" ]; then exit 1 fi -# Get git remote URL from current directory -REPO_URL=$(git config --get remote.origin.url 2>/dev/null) -if [ -z "$REPO_URL" ]; then - echo "ERROR: Could not determine git remote URL" - echo "Make sure you run this script from within the git repository" - exit 1 -fi +# Repository URL +REPO_URL="git@github.com:NVIDIA/cuopt.git" echo "Repository URL: $REPO_URL" echo "Working directory: $WORK_DIR" From 1f5605fba77068c3204d7e874666e52a37030d4b Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 14:34:17 -0600 Subject: [PATCH 12/18] change from conda to mamba --- sonarqube/run-sonar-analysis.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 02db06e8f..003c1e25c 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -104,7 +104,7 @@ for branch in "${branches[@]}"; do conda_env_name="cuopt_sonar_${safe_branch_name}" # Create conda environment - if ! conda env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then + if ! mamba env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log" failed_branches+=("$branch (conda env creation failed)") rm -rf "$clone_dir" From 07aa4fa6facdd16beabe8cfb0c84ac3fa95f7f81 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 14:40:17 -0600 Subject: [PATCH 13/18] address review comment --- sonarqube/run-sonar-analysis.sh | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 003c1e25c..08c4056be 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -84,7 +84,8 @@ for branch in "${branches[@]}"; do # Clone the specific branch echo "Cloning branch: $branch into $clone_dir" - if ! git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log; then + git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log + if [ "${PIPESTATUS[0]}" -ne 0 ]; then echo "ERROR: Failed to clone branch: $branch" failed_branches+=("$branch (clone failed)") continue @@ -104,7 +105,8 @@ for branch in "${branches[@]}"; do conda_env_name="cuopt_sonar_${safe_branch_name}" # Create conda environment - if ! mamba env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log; then + mamba env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log + if [ "${PIPESTATUS[0]}" -ne 0 ]; then echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log" failed_branches+=("$branch (conda env creation failed)") rm -rf "$clone_dir" @@ -124,17 +126,19 @@ for branch in "${branches[@]}"; do # Build the project echo 'Building project...' - if ! ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log; then + ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log + if [ \${PIPESTATUS[0]} -ne 0 ]; then echo 'Build failed' exit 1 fi - + # Run SonarQube analysis + # Note: SONAR_TOKEN is read from environment automatically by sonar-scanner echo 'Running SonarQube analysis...' - if ! sonar-scanner \ - -Dsonar.token='$SONAR_TOKEN' \ + sonar-scanner \ -Dsonar.branch.name='$branch' \ - 2>&1 | tee /tmp/sonar_${safe_branch_name}.log; then + 2>&1 | tee /tmp/sonar_${safe_branch_name}.log + if [ \${PIPESTATUS[0]} -ne 0 ]; then echo 'SonarQube analysis failed' exit 1 fi From 99dd6b90353bc7e0e1c167dcaba7c6b098f093b3 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Tue, 13 Jan 2026 14:40:40 -0600 Subject: [PATCH 14/18] fix --- sonarqube/cron-wrapper.sh | 129 ++++++++++++++++++++++++++++++++ sonarqube/run-sonar-analysis.sh | 2 +- 2 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 sonarqube/cron-wrapper.sh diff --git a/sonarqube/cron-wrapper.sh b/sonarqube/cron-wrapper.sh new file mode 100644 index 000000000..c17a9e933 --- /dev/null +++ b/sonarqube/cron-wrapper.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Cron wrapper script for SonarQube analysis +# This script clones/pulls the cuopt repository and runs SonarQube analysis + +set -e + +# Configuration +REPO_URL="git@github.com:rgsl888prabhu/cuopt_public.git" +REPO_BRANCH="enable_sonar_cube_for_cuopt" +WORK_DIR="/tmp/cuopt-sonar-cron" +LOG_DIR="/var/log/sonarqube" + +# Create log directory if it doesn't exist +mkdir -p "$LOG_DIR" + +# Log file with timestamp +LOG_FILE="$LOG_DIR/sonar-cron-$(date +%Y%m%d-%H%M%S).log" + +# Function to log messages +log() { + echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" +} + +log "========================================" +log "SonarQube Cron Job Started" +log "========================================" +log "Repository: $REPO_URL" +log "Branch: $REPO_BRANCH" +log "Work Directory: $WORK_DIR" +log "Log File: $LOG_FILE" + +# Check if SONAR_TOKEN is set +if [ -z "$SONAR_TOKEN" ]; then + log "ERROR: SONAR_TOKEN environment variable is not set" + log "Please set it in crontab or source a secrets file" + exit 1 +fi + +# Clone or update repository +if [ -d "$WORK_DIR" ]; then + log "Repository directory exists, pulling latest changes..." + cd "$WORK_DIR" + + # Check if it's a git repository + if ! git rev-parse --git-dir > /dev/null 2>&1; then + log "ERROR: $WORK_DIR exists but is not a git repository" + log "Removing and will re-clone..." + cd /tmp + rm -rf "$WORK_DIR" + else + # Ensure we're on the correct branch + current_branch=$(git rev-parse --abbrev-ref HEAD) + if [ "$current_branch" != "$REPO_BRANCH" ]; then + log "Switching from branch $current_branch to $REPO_BRANCH" + if ! git fetch origin; then + log "ERROR: Failed to fetch from origin" + exit 1 + fi + if ! git checkout "$REPO_BRANCH"; then + log "ERROR: Failed to checkout branch $REPO_BRANCH" + exit 1 + fi + fi + + # Pull latest changes + log "Pulling latest changes for branch: $REPO_BRANCH" + if ! git pull origin "$REPO_BRANCH"; then + log "ERROR: Failed to pull latest changes" + exit 1 + fi + + log "Successfully updated repository" + fi +fi + +# Clone if directory doesn't exist +if [ ! -d "$WORK_DIR" ]; then + log "Cloning repository for the first time..." + if ! git clone --branch "$REPO_BRANCH" "$REPO_URL" "$WORK_DIR"; then + log "ERROR: Failed to clone repository" + exit 1 + fi + log "Successfully cloned repository" +fi + +# Change to repository directory +cd "$WORK_DIR" + +# Show current commit +CURRENT_COMMIT=$(git rev-parse --short HEAD) +COMMIT_MSG=$(git log -1 --pretty=%B) +log "Current commit: $CURRENT_COMMIT" +log "Commit message: $COMMIT_MSG" + +# Check if sonarqube directory exists +if [ ! -d "sonarqube" ]; then + log "ERROR: sonarqube directory not found in repository" + exit 1 +fi + +# Check if run-sonar-analysis.sh exists +if [ ! -f "sonarqube/run-sonar-analysis.sh" ]; then + log "ERROR: sonarqube/run-sonar-analysis.sh not found" + exit 1 +fi + +# Make script executable +chmod +x sonarqube/run-sonar-analysis.sh + +# Run SonarQube analysis +log "========================================" +log "Starting SonarQube Analysis" +log "========================================" + +if ./sonarqube/run-sonar-analysis.sh 2>&1 | tee -a "$LOG_FILE"; then + log "========================================" + log "SonarQube Analysis Completed Successfully" + log "========================================" + exit 0 +else + EXIT_CODE=$? + log "========================================" + log "SonarQube Analysis Failed (Exit Code: $EXIT_CODE)" + log "========================================" + exit $EXIT_CODE +fi diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 08c4056be..fc9471415 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -131,7 +131,7 @@ for branch in "${branches[@]}"; do echo 'Build failed' exit 1 fi - + # Run SonarQube analysis # Note: SONAR_TOKEN is read from environment automatically by sonar-scanner echo 'Running SonarQube analysis...' From 83c97ac46bf2801bc0675f6e5cea6c6ae062ccaa Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Wed, 14 Jan 2026 13:32:57 -0600 Subject: [PATCH 15/18] remove wrapper --- sonarqube/cron-wrapper.sh | 129 -------------------------------------- 1 file changed, 129 deletions(-) delete mode 100644 sonarqube/cron-wrapper.sh diff --git a/sonarqube/cron-wrapper.sh b/sonarqube/cron-wrapper.sh deleted file mode 100644 index c17a9e933..000000000 --- a/sonarqube/cron-wrapper.sh +++ /dev/null @@ -1,129 +0,0 @@ -#!/bin/bash -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Cron wrapper script for SonarQube analysis -# This script clones/pulls the cuopt repository and runs SonarQube analysis - -set -e - -# Configuration -REPO_URL="git@github.com:rgsl888prabhu/cuopt_public.git" -REPO_BRANCH="enable_sonar_cube_for_cuopt" -WORK_DIR="/tmp/cuopt-sonar-cron" -LOG_DIR="/var/log/sonarqube" - -# Create log directory if it doesn't exist -mkdir -p "$LOG_DIR" - -# Log file with timestamp -LOG_FILE="$LOG_DIR/sonar-cron-$(date +%Y%m%d-%H%M%S).log" - -# Function to log messages -log() { - echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE" -} - -log "========================================" -log "SonarQube Cron Job Started" -log "========================================" -log "Repository: $REPO_URL" -log "Branch: $REPO_BRANCH" -log "Work Directory: $WORK_DIR" -log "Log File: $LOG_FILE" - -# Check if SONAR_TOKEN is set -if [ -z "$SONAR_TOKEN" ]; then - log "ERROR: SONAR_TOKEN environment variable is not set" - log "Please set it in crontab or source a secrets file" - exit 1 -fi - -# Clone or update repository -if [ -d "$WORK_DIR" ]; then - log "Repository directory exists, pulling latest changes..." - cd "$WORK_DIR" - - # Check if it's a git repository - if ! git rev-parse --git-dir > /dev/null 2>&1; then - log "ERROR: $WORK_DIR exists but is not a git repository" - log "Removing and will re-clone..." - cd /tmp - rm -rf "$WORK_DIR" - else - # Ensure we're on the correct branch - current_branch=$(git rev-parse --abbrev-ref HEAD) - if [ "$current_branch" != "$REPO_BRANCH" ]; then - log "Switching from branch $current_branch to $REPO_BRANCH" - if ! git fetch origin; then - log "ERROR: Failed to fetch from origin" - exit 1 - fi - if ! git checkout "$REPO_BRANCH"; then - log "ERROR: Failed to checkout branch $REPO_BRANCH" - exit 1 - fi - fi - - # Pull latest changes - log "Pulling latest changes for branch: $REPO_BRANCH" - if ! git pull origin "$REPO_BRANCH"; then - log "ERROR: Failed to pull latest changes" - exit 1 - fi - - log "Successfully updated repository" - fi -fi - -# Clone if directory doesn't exist -if [ ! -d "$WORK_DIR" ]; then - log "Cloning repository for the first time..." - if ! git clone --branch "$REPO_BRANCH" "$REPO_URL" "$WORK_DIR"; then - log "ERROR: Failed to clone repository" - exit 1 - fi - log "Successfully cloned repository" -fi - -# Change to repository directory -cd "$WORK_DIR" - -# Show current commit -CURRENT_COMMIT=$(git rev-parse --short HEAD) -COMMIT_MSG=$(git log -1 --pretty=%B) -log "Current commit: $CURRENT_COMMIT" -log "Commit message: $COMMIT_MSG" - -# Check if sonarqube directory exists -if [ ! -d "sonarqube" ]; then - log "ERROR: sonarqube directory not found in repository" - exit 1 -fi - -# Check if run-sonar-analysis.sh exists -if [ ! -f "sonarqube/run-sonar-analysis.sh" ]; then - log "ERROR: sonarqube/run-sonar-analysis.sh not found" - exit 1 -fi - -# Make script executable -chmod +x sonarqube/run-sonar-analysis.sh - -# Run SonarQube analysis -log "========================================" -log "Starting SonarQube Analysis" -log "========================================" - -if ./sonarqube/run-sonar-analysis.sh 2>&1 | tee -a "$LOG_FILE"; then - log "========================================" - log "SonarQube Analysis Completed Successfully" - log "========================================" - exit 0 -else - EXIT_CODE=$? - log "========================================" - log "SonarQube Analysis Failed (Exit Code: $EXIT_CODE)" - log "========================================" - exit $EXIT_CODE -fi From 0026e9a1eadd4cf51dafbdc085f23b759ca26e0c Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 15 Jan 2026 15:44:39 -0600 Subject: [PATCH 16/18] Add update to address reviews --- sonarqube/run-sonar-analysis.sh | 59 ++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index fc9471415..11df7fcf0 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -17,6 +17,7 @@ CONDA_ENV_FILE="conda/environments/all_cuda-131_arch-${ARCH}.yaml" if [ -z "$SONAR_TOKEN" ]; then echo "ERROR: SONAR_TOKEN environment variable is not set" echo "Please set it with: export SONAR_TOKEN=your_sonarqube_token" + HAD_FAILURES=1 exit 1 fi @@ -26,14 +27,36 @@ REPO_URL="git@github.com:NVIDIA/cuopt.git" echo "Repository URL: $REPO_URL" echo "Working directory: $WORK_DIR" -# Create working directory +# Create working directory and logs directory mkdir -p "$WORK_DIR" +LOG_DIR="$WORK_DIR/logs" +mkdir -p "$LOG_DIR" + +# Persistent log directory for failures +PERSISTENT_LOG_DIR="/var/log/sonarqube/runs" +mkdir -p "$PERSISTENT_LOG_DIR" 2>/dev/null || PERSISTENT_LOG_DIR="$HOME/.sonarqube/logs/runs" +mkdir -p "$PERSISTENT_LOG_DIR" + +# Track if we had failures (for cleanup decision) +HAD_FAILURES=0 # Cleanup function cleanup() { echo "" - echo "Cleaning up working directory: $WORK_DIR" - rm -rf "$WORK_DIR" + if [ "$HAD_FAILURES" -eq 0 ] && [ ${#failed_branches[@]} -eq 0 ]; then + echo "All branches succeeded - cleaning up working directory: $WORK_DIR" + rm -rf "$WORK_DIR" + else + # Preserve logs on failure + RUN_ID=$(date +%Y%m%d-%H%M%S) + SAVED_LOG_DIR="$PERSISTENT_LOG_DIR/failed-run-$RUN_ID" + echo "Failures detected - preserving logs to: $SAVED_LOG_DIR" + mkdir -p "$SAVED_LOG_DIR" + cp -r "$LOG_DIR"/* "$SAVED_LOG_DIR/" 2>/dev/null || true + echo "Logs saved. Check: $SAVED_LOG_DIR" + echo "Cleaning up working directory: $WORK_DIR" + rm -rf "$WORK_DIR" + fi } # Register cleanup on exit @@ -42,6 +65,7 @@ trap cleanup EXIT # Check if branches file exists if [ ! -f "$BRANCHES_FILE" ]; then echo "ERROR: Branches file not found: $BRANCHES_FILE" + HAD_FAILURES=1 exit 1 fi @@ -61,6 +85,7 @@ done < "$BRANCHES_FILE" if [ ${#branches[@]} -eq 0 ]; then echo "ERROR: No branches configured in $BRANCHES_FILE" echo "Please add at least one branch to the file." + HAD_FAILURES=1 exit 1 fi @@ -84,7 +109,7 @@ for branch in "${branches[@]}"; do # Clone the specific branch echo "Cloning branch: $branch into $clone_dir" - git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee /tmp/clone_${safe_branch_name}.log + git clone --single-branch --branch "$branch" --depth 1 "$REPO_URL" "$clone_dir" 2>&1 | tee "$LOG_DIR/clone_${safe_branch_name}.log" if [ "${PIPESTATUS[0]}" -ne 0 ]; then echo "ERROR: Failed to clone branch: $branch" failed_branches+=("$branch (clone failed)") @@ -105,48 +130,49 @@ for branch in "${branches[@]}"; do conda_env_name="cuopt_sonar_${safe_branch_name}" # Create conda environment - mamba env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee /tmp/conda_create_${safe_branch_name}.log + mamba env create -n "$conda_env_name" -f "$CONDA_ENV_FILE" 2>&1 | tee "$LOG_DIR/conda_create_${safe_branch_name}.log" if [ "${PIPESTATUS[0]}" -ne 0 ]; then - echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at /tmp/conda_create_${safe_branch_name}.log" + echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at $LOG_DIR/conda_create_${safe_branch_name}.log" failed_branches+=("$branch (conda env creation failed)") + cd "$WORK_DIR" rm -rf "$clone_dir" continue fi # Activate conda environment and run build + analysis in a subshell echo "Building and analyzing branch: $branch in conda environment: $conda_env_name" - + if ! bash -c " set -e source \$(conda info --base)/etc/profile.d/conda.sh conda activate $conda_env_name - + echo 'Conda environment activated: $conda_env_name' echo 'Python version:' \$(python --version) - + # Build the project echo 'Building project...' - ./build.sh 2>&1 | tee /tmp/build_${safe_branch_name}.log + ./build.sh 2>&1 | tee '$LOG_DIR/build_${safe_branch_name}.log' if [ \${PIPESTATUS[0]} -ne 0 ]; then echo 'Build failed' exit 1 fi - + # Run SonarQube analysis # Note: SONAR_TOKEN is read from environment automatically by sonar-scanner echo 'Running SonarQube analysis...' sonar-scanner \ -Dsonar.branch.name='$branch' \ - 2>&1 | tee /tmp/sonar_${safe_branch_name}.log + 2>&1 | tee '$LOG_DIR/sonar_${safe_branch_name}.log' if [ \${PIPESTATUS[0]} -ne 0 ]; then echo 'SonarQube analysis failed' exit 1 fi - + echo 'Build and analysis completed successfully' "; then echo "ERROR: Build or analysis failed for branch: $branch" - if grep -q "Build failed" /tmp/build_${safe_branch_name}.log 2>/dev/null; then + if grep -q "Build failed" "$LOG_DIR/build_${safe_branch_name}.log" 2>/dev/null; then failed_branches+=("$branch (build failed)") else failed_branches+=("$branch (sonar analysis failed)") @@ -154,6 +180,7 @@ for branch in "${branches[@]}"; do # Clean up conda environment conda env remove -n "$conda_env_name" -y 2>/dev/null || true + cd "$WORK_DIR" rm -rf "$clone_dir" continue fi @@ -165,9 +192,10 @@ for branch in "${branches[@]}"; do successful_branches+=("$branch") echo "✓ Successfully completed analysis for: $branch" echo "Progress: ${#successful_branches[@]} succeeded, ${#failed_branches[@]} failed out of ${#branches[@]} total" - + # Clean up clone directory after successful analysis echo "Cleaning up clone directory for: $branch" + cd "$WORK_DIR" rm -rf "$clone_dir" done @@ -202,6 +230,7 @@ echo "==========================================" # Exit with error if any branches failed if [ ${#failed_branches[@]} -gt 0 ]; then echo "ERROR: ${#failed_branches[@]} branch(es) failed analysis" + HAD_FAILURES=1 exit 1 fi From 975cf58acb86c0379c43aeaa25ac90325b00ad75 Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 15 Jan 2026 15:45:29 -0600 Subject: [PATCH 17/18] fix style --- sonarqube/run-sonar-analysis.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index 11df7fcf0..ec064efee 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -141,15 +141,15 @@ for branch in "${branches[@]}"; do # Activate conda environment and run build + analysis in a subshell echo "Building and analyzing branch: $branch in conda environment: $conda_env_name" - + if ! bash -c " set -e source \$(conda info --base)/etc/profile.d/conda.sh conda activate $conda_env_name - + echo 'Conda environment activated: $conda_env_name' echo 'Python version:' \$(python --version) - + # Build the project echo 'Building project...' ./build.sh 2>&1 | tee '$LOG_DIR/build_${safe_branch_name}.log' @@ -157,7 +157,7 @@ for branch in "${branches[@]}"; do echo 'Build failed' exit 1 fi - + # Run SonarQube analysis # Note: SONAR_TOKEN is read from environment automatically by sonar-scanner echo 'Running SonarQube analysis...' @@ -168,7 +168,7 @@ for branch in "${branches[@]}"; do echo 'SonarQube analysis failed' exit 1 fi - + echo 'Build and analysis completed successfully' "; then echo "ERROR: Build or analysis failed for branch: $branch" @@ -192,7 +192,7 @@ for branch in "${branches[@]}"; do successful_branches+=("$branch") echo "✓ Successfully completed analysis for: $branch" echo "Progress: ${#successful_branches[@]} succeeded, ${#failed_branches[@]} failed out of ${#branches[@]} total" - + # Clean up clone directory after successful analysis echo "Cleaning up clone directory for: $branch" cd "$WORK_DIR" From 6d505423745ebcfd6f214c559e77a6a3e2a6dbfa Mon Sep 17 00:00:00 2001 From: Ramakrishna Prabhu Date: Thu, 15 Jan 2026 15:48:42 -0600 Subject: [PATCH 18/18] fix style --- sonarqube/run-sonar-analysis.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sonarqube/run-sonar-analysis.sh b/sonarqube/run-sonar-analysis.sh index ec064efee..ce2cab79b 100755 --- a/sonarqube/run-sonar-analysis.sh +++ b/sonarqube/run-sonar-analysis.sh @@ -134,7 +134,7 @@ for branch in "${branches[@]}"; do if [ "${PIPESTATUS[0]}" -ne 0 ]; then echo "ERROR: Conda environment creation failed for branch: $branch. Check logs at $LOG_DIR/conda_create_${safe_branch_name}.log" failed_branches+=("$branch (conda env creation failed)") - cd "$WORK_DIR" + cd "$WORK_DIR" || echo "WARNING: Failed to cd to $WORK_DIR" rm -rf "$clone_dir" continue fi @@ -180,7 +180,7 @@ for branch in "${branches[@]}"; do # Clean up conda environment conda env remove -n "$conda_env_name" -y 2>/dev/null || true - cd "$WORK_DIR" + cd "$WORK_DIR" || echo "WARNING: Failed to cd to $WORK_DIR" rm -rf "$clone_dir" continue fi @@ -195,7 +195,7 @@ for branch in "${branches[@]}"; do # Clean up clone directory after successful analysis echo "Cleaning up clone directory for: $branch" - cd "$WORK_DIR" + cd "$WORK_DIR" || echo "WARNING: Failed to cd to $WORK_DIR" rm -rf "$clone_dir" done