Repository: rapidsai/cuml
Pull request #6246: Add shellcheck to pre-commit and fix warnings
Author: login: gforsyth
Labels: name: improvement
description: Improvement / enhancement to an existing function
name: non-breaking
description: Non-breaking change
name: ci
Created: 2025-01-22T18:57:31Z | Merged: 2025-03-14T14:06:56Z

## Description                                                                                                
                                                                                                              
`shellcheck` is a fast, static analysis tool for shell scripts. It's good at
flagging up unused variables, unintentional glob expansions, and other potential
execution and security headaches that arise from the wonders of `bash` (and
other shlangs).                   
                                                                                                              
This PR adds a `pre-commit` hook to run `shellcheck` on all of the `sh-lang`
files in the `ci/` directory, and the changes requested by `shellcheck` to make
the existing files pass the check.                              
                                                                                                              
xref: rapidsai/build-planning#135


Diff:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 437091b2f6..86cc700f16 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -87,6 +87,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
 
 default_language_version:
     python: python3
diff --git a/build.sh b/build.sh
index 6f1b6b9f83..8828feb042 100755
--- a/build.sh
+++ b/build.sh
@@ -16,7 +16,7 @@ ARGS=$*
 
 # NOTE: ensure all dir changes are relative to the location of this
 # script, and that this script resides in the repo dir!
-REPODIR=$(cd $(dirname $0); pwd)
+REPODIR=$(cd "$(dirname $0)"; pwd)
 
 VALIDTARGETS="clean libcuml cuml cuml-cpu cpp-mgtests prims bench prims-bench cppdocs pydocs"
 VALIDFLAGS="-v -g -n --allgpuarch --singlegpu --nolibcumltest --nvtx --show_depr_warn --codecov --ccache --configure-only -h --help "
@@ -96,11 +96,21 @@ export CMAKE_GENERATOR="${CMAKE_GENERATOR:=Ninja}"
 CUML_EXTRA_CMAKE_ARGS=${CUML_EXTRA_CMAKE_ARGS:=""}
 
 function hasArg {
-    (( ${NUMARGS} != 0 )) && (echo " ${ARGS} " | grep -q " $1 ")
+    (( NUMARGS != 0 )) && (echo " ${ARGS} " | grep -q " $1 ")
 }
 
+# Check for valid usage
+if (( NUMARGS != 0 )); then
+    for a in ${ARGS}; do
+    if ! (echo " ${VALIDARGS} " | grep -q " ${a} "); then
+        echo "Invalid option or formatting, check --help: ${a}"
+        exit 1
+    fi
+    done
+fi
+
 function completeBuild {
-    (( ${NUMARGS} == 0 )) && return
+    (( NUMARGS == 0 )) && return
     for a in ${ARGS}; do
         if (echo " ${VALIDTARGETS} " | grep -q " ${a} "); then
           false; return
diff --git a/ci/build_docs.sh b/ci/build_docs.sh
index 7d00d42a13..c93b20c40e 100755
--- a/ci/build_docs.sh
+++ b/ci/build_docs.sh
@@ -9,7 +9,8 @@ PYTHON_CHANNEL=$(rapids-download-conda-from-s3 python)
 rapids-logger "Create test conda environment"
 . /opt/conda/etc/profile.d/conda.sh
 
-export RAPIDS_VERSION_MAJOR_MINOR="$(rapids-version-major-minor)"
+RAPIDS_VERSION_MAJOR_MINOR="$(rapids-version-major-minor)"
+export RAPIDS_VERSION_MAJOR_MINOR
 
 rapids-dependency-file-generator \
   --output conda \
@@ -24,7 +25,8 @@ conda activate docs
 
 rapids-print-env
 
-export RAPIDS_DOCS_DIR="$(mktemp -d)"
+RAPIDS_DOCS_DIR="$(mktemp -d)"
+export RAPIDS_DOCS_DIR
 
 rapids-logger "Build CPP docs"
 pushd cpp
diff --git a/ci/build_wheel_cuml.sh b/ci/build_wheel_cuml.sh
index 0005a91887..5b761afa29 100755
--- a/ci/build_wheel_cuml.sh
+++ b/ci/build_wheel_cuml.sh
@@ -6,7 +6,7 @@ set -euo pipefail
 package_name="cuml"
 package_dir="python/cuml"
 
-RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
+RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen "${RAPIDS_CUDA_VERSION}")"
 
 # Download the libcuml wheel built in the previous step and make it
 # available for pip to find.
diff --git a/ci/build_wheel_libcuml.sh b/ci/build_wheel_libcuml.sh
index 784f530715..3eee59962b 100755
--- a/ci/build_wheel_libcuml.sh
+++ b/ci/build_wheel_libcuml.sh
@@ -6,7 +6,7 @@ set -euo pipefail
 package_name="libcuml"
 package_dir="python/libcuml"
 
-RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
+RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen "${RAPIDS_CUDA_VERSION}")"
 
 rapids-logger "Generating build requirements"
 
diff --git a/ci/checks/black_lists.sh b/ci/checks/black_lists.sh
index 6300ab359e..c98eb7a3ee 100755
--- a/ci/checks/black_lists.sh
+++ b/ci/checks/black_lists.sh
@@ -6,7 +6,7 @@
 
 # PR_TARGET_BRANCH is set by the CI environment
 
-git checkout --quiet $PR_TARGET_BRANCH
+git checkout --quiet "$PR_TARGET_BRANCH"
 
 # Switch back to tip of PR branch
 git checkout --quiet current-pr-branch
@@ -20,16 +20,16 @@ set +H
 RETVAL=0
 
 for black_listed in cudaDeviceSynchronize cudaMalloc cudaMallocManaged cudaFree cudaMallocHost cudaHostAlloc cudaFreeHost; do
-    TMP=`git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$black_listed" $PR_TARGET_BRANCH | grep '^+' | grep -v '^+++' | grep "$black_listed"`
+    TMP=$(git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$black_listed" "$PR_TARGET_BRANCH" | grep '^+' | grep -v '^+++' | grep "$black_listed")
     if [ "$TMP" != "" ]; then
-        for filename in `git --no-pager diff --ignore-submodules -w --minimal --name-only -S"$black_listed" $PR_TARGET_BRANCH`; do
+        for filename in $(git --no-pager diff --ignore-submodules -w --minimal --name-only -S"$black_listed" "$PR_TARGET_BRANCH"); do
             basefilename=$(basename -- "$filename")
             filext="${basefilename##*.}"
             if [ "$filext" != "md" ] && [ "$filext" != "sh" ]; then
-                TMP2=`git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$black_listed" $PR_TARGET_BRANCH -- $filename | grep '^+' | grep -v '^+++' | grep "$black_listed" | grep -vE "^\+[[:space:]]*/{2,}.*$black_listed"`
+                TMP2=$(git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$black_listed" "$PR_TARGET_BRANCH" -- "$filename" | grep '^+' | grep -v '^+++' | grep "$black_listed" | grep -vE "^\+[[:space:]]*/{2,}.*$black_listed")
                 if [ "$TMP2" != "" ]; then
                     echo "=== ERROR: black listed function call $black_listed added to $filename ==="
-                    git --no-pager diff --ignore-submodules -w --minimal -S"$black_listed" $PR_TARGET_BRANCH -- $filename
+                    git --no-pager diff --ignore-submodules -w --minimal -S"$black_listed" "$PR_TARGET_BRANCH" -- "$filename"
                     echo "=== END ERROR ==="
                     RETVAL=1
                 fi
@@ -39,17 +39,16 @@ for black_listed in cudaDeviceSynchronize cudaMalloc cudaMallocManaged cudaFree
 done
 
 for cond_black_listed in cudaMemcpy cudaMemset; do
-    TMP=`git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$cond_black_listed" $PR_TARGET_BRANCH | grep '^+' | grep -v '^+++' | grep -P "$cond_black_listed(?!Async)"`
-
+    TMP=$(git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$cond_black_listed" "$PR_TARGET_BRANCH" | grep '^+' | grep -v '^+++' | grep -P "$cond_black_listed(?!Async)")
     if [ "$TMP" != "" ]; then
-        for filename in `git --no-pager diff --ignore-submodules -w --minimal --name-only -S"$cond_black_listed" $PR_TARGET_BRANCH`; do
+        for filename in $(git --no-pager diff --ignore-submodules -w --minimal --name-only -S"$cond_black_listed" "$PR_TARGET_BRANCH"); do
             basefilename=$(basename -- "$filename")
             filext="${basefilename##*.}"
             if [ "$filext" != "md" ] && [ "$filext" != "sh" ]; then
-                TMP2=`git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$cond_black_listed" $PR_TARGET_BRANCH -- $filename | grep '^+' | grep -v '^+++' | grep -P "$cond_black_listed(?!Async)" | grep -vE "^\+[[:space:]]*/{2,}.*$cond_black_listed"`
+                TMP2=$(git --no-pager diff --ignore-submodules -w --minimal -U0 -S"$cond_black_listed" "$PR_TARGET_BRANCH" -- "$filename" | grep '^+' | grep -v '^+++' | grep -P "$cond_black_listed(?!Async)" | grep -vE "^\+[[:space:]]*/{2,}.*$cond_black_listed")
                 if [ "$TMP2" != "" ]; then
                     echo "=== ERROR: black listed function call $cond_black_listed added to $filename ==="
-                    git --no-pager diff --ignore-submodules -w --minimal -S"$cond_black_listed" $PR_TARGET_BRANCH -- $filename
+                    git --no-pager diff --ignore-submodules -w --minimal -S"$cond_black_listed" "$PR_TARGET_BRANCH" -- "$filename"
                     echo "=== END ERROR ==="
                     RETVAL=1
                 fi
diff --git a/ci/release/update-version.sh b/ci/release/update-version.sh
index ad7b7fd94d..80daa2bf15 100755
--- a/ci/release/update-version.sh
+++ b/ci/release/update-version.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (c) 2019-2024, NVIDIA CORPORATION.
+# Copyright (c) 2019-2025, NVIDIA CORPORATION.
 ########################
 # cuML Version Updater #
 ########################
@@ -13,14 +13,13 @@ NEXT_FULL_TAG=$1
 
 # Get current version
 CURRENT_TAG=$(git tag --merged HEAD | grep -xE '^v.*' | sort --version-sort | tail -n 1 | tr -d 'v')
-CURRENT_MAJOR=$(echo $CURRENT_TAG | awk '{split($0, a, "."); print a[1]}')
-CURRENT_MINOR=$(echo $CURRENT_TAG | awk '{split($0, a, "."); print a[2]}')
-CURRENT_PATCH=$(echo $CURRENT_TAG | awk '{split($0, a, "."); print a[3]}')
+CURRENT_MAJOR=$(echo "$CURRENT_TAG" | awk '{split($0, a, "."); print a[1]}')
+CURRENT_MINOR=$(echo "$CURRENT_TAG" | awk '{split($0, a, "."); print a[2]}')
 CURRENT_SHORT_TAG=${CURRENT_MAJOR}.${CURRENT_MINOR}
 
 # Get <major>.<minor> for next version
-NEXT_MAJOR=$(echo $NEXT_FULL_TAG | awk '{split($0, a, "."); print a[1]}')
-NEXT_MINOR=$(echo $NEXT_FULL_TAG | awk '{split($0, a, "."); print a[2]}')
+NEXT_MAJOR=$(echo "$NEXT_FULL_TAG" | awk '{split($0, a, "."); print a[1]}')
+NEXT_MINOR=$(echo "$NEXT_FULL_TAG" | awk '{split($0, a, "."); print a[2]}')
 NEXT_SHORT_TAG=${NEXT_MAJOR}.${NEXT_MINOR}
 
 # Need to distutils-normalize the original version
@@ -30,7 +29,7 @@ echo "Preparing release $CURRENT_TAG => $NEXT_FULL_TAG"
 
 # Inplace sed replace; workaround for Linux and Mac
 function sed_runner() {
-    sed -i.bak ''"$1"'' $2 && rm -f ${2}.bak
+    sed -i.bak ''"$1"'' "$2" && rm -f "${2}".bak
 }
 
 
diff --git a/ci/run_cuml_dask_pytests.sh b/ci/run_cuml_dask_pytests.sh
index 9a28c6bbcc..3063e68414 100755
--- a/ci/run_cuml_dask_pytests.sh
+++ b/ci/run_cuml_dask_pytests.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_dask_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests/dask
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests/dask || exit 1
 
 python -m pytest --cache-clear "$@" .
diff --git a/ci/run_cuml_integration_pytests.sh b/ci/run_cuml_integration_pytests.sh
index bd74db7895..38572fb061 100755
--- a/ci/run_cuml_integration_pytests.sh
+++ b/ci/run_cuml_integration_pytests.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit 1
 
 python -m pytest -p cudf.pandas --cache-clear --ignore=dask -m "not memleak" "$@" --quick_run .
diff --git a/ci/run_cuml_singlegpu_accel_pytests.sh b/ci/run_cuml_singlegpu_accel_pytests.sh
index 6023131ac2..0a9463c2c0 100755
--- a/ci/run_cuml_singlegpu_accel_pytests.sh
+++ b/ci/run_cuml_singlegpu_accel_pytests.sh
@@ -2,6 +2,6 @@
 # Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests/accel
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests/accel || exit 1
 
 python -m pytest -p cuml.accel --cache-clear "$@" .
diff --git a/ci/run_cuml_singlegpu_memleak_pytests.sh b/ci/run_cuml_singlegpu_memleak_pytests.sh
index 89b56e2cb2..dfe2113a89 100755
--- a/ci/run_cuml_singlegpu_memleak_pytests.sh
+++ b/ci/run_cuml_singlegpu_memleak_pytests.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit 1
 
 python -m pytest --cache-clear --ignore=dask -m "memleak" "$@" .
diff --git a/ci/run_cuml_singlegpu_pytests.sh b/ci/run_cuml_singlegpu_pytests.sh
index a395baab1b..336e081d49 100755
--- a/ci/run_cuml_singlegpu_pytests.sh
+++ b/ci/run_cuml_singlegpu_pytests.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit 1
 
 python -m pytest --cache-clear --ignore=dask -m "not memleak" "$@" .
diff --git a/ci/test_cpp.sh b/ci/test_cpp.sh
index ea6d1cdc11..2aa92819f6 100755
--- a/ci/test_cpp.sh
+++ b/ci/test_cpp.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (c) 2022-2024, NVIDIA CORPORATION.
+# Copyright (c) 2022-2025, NVIDIA CORPORATION.
 
 set -euo pipefail
 
@@ -39,4 +39,4 @@ export GTEST_OUTPUT=xml:${RAPIDS_TESTS_DIR}/
 ./ci/run_ctests.sh -j9 && EXITCODE=$? || EXITCODE=$?;
 
 rapids-logger "Test script exiting with value: $EXITCODE"
-exit ${EXITCODE}
+exit "${EXITCODE}"
diff --git a/ci/test_notebooks.sh b/ci/test_notebooks.sh
index d76a754b8b..e010aad666 100755
--- a/ci/test_notebooks.sh
+++ b/ci/test_notebooks.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (c) 2020-2024, NVIDIA CORPORATION.
+# Copyright (c) 2020-2025, NVIDIA CORPORATION.
 set -euo pipefail
 
 . /opt/conda/etc/profile.d/conda.sh
@@ -40,17 +40,19 @@ SKIPNBS="cuml_benchmarks.ipynb"
 NBTEST="$(realpath "$(dirname "$0")/utils/nbtest.sh")"
 
 cd notebooks
+
+# shellcheck disable=SC2044
 for nb in $(find . -name "*.ipynb"); do
     nbBasename=$(basename "${nb}")
     # Skip all NBs that use dask (in the code or even in their name)
-    if ((echo "${nb}" | grep -qi dask) || \
-        (grep -q dask "${nb}")); then
+    if (echo "${nb}" | grep -qi dask) || \
+        (grep -q dask "${nb}"); then
         echo "--------------------------------------------------------------------------------"
         echo "SKIPPING: ${nb} (suspected Dask usage, not currently automatable)"
         echo "--------------------------------------------------------------------------------"
     elif (echo " ${SKIPNBS} " | grep -q " ${nbBasename} "); then
         echo "--------------------------------------------------------------------------------"
-        echo "SKIPPING: "${nb}" (listed in skip list)"
+        echo "SKIPPING: ${nb} (listed in skip list)"
         echo "--------------------------------------------------------------------------------"
     else
         nvidia-smi
diff --git a/ci/test_wheel.sh b/ci/test_wheel.sh
index 7e1feff80a..67d1fee7f0 100755
--- a/ci/test_wheel.sh
+++ b/ci/test_wheel.sh
@@ -4,7 +4,7 @@
 set -euo pipefail
 
 mkdir -p ./dist
-RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen ${RAPIDS_CUDA_VERSION})"
+RAPIDS_PY_CUDA_SUFFIX="$(rapids-wheel-ctk-name-gen "${RAPIDS_CUDA_VERSION}")"
 RAPIDS_PY_WHEEL_NAME="cuml_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 python ./dist
 RAPIDS_PY_WHEEL_NAME="libcuml_${RAPIDS_PY_CUDA_SUFFIX}" rapids-download-wheels-from-s3 cpp ./dist
 
diff --git a/ci/utils/nbtest.sh b/ci/utils/nbtest.sh
index 28877273bf..9fc37d2569 100755
--- a/ci/utils/nbtest.sh
+++ b/ci/utils/nbtest.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright (c) 2020-2021, NVIDIA CORPORATION.
+# Copyright (c) 2020-2025, NVIDIA CORPORATION.
 
 MAGIC_OVERRIDE_CODE="
 def my_run_line_magic(*args, **kwargs):
@@ -24,22 +24,22 @@ get_ipython().run_cell_magic=my_run_cell_magic
 NO_COLORS=--colors=NoColor
 EXITCODE=0
 NBTMPDIR="$WORKSPACE/tmp"
-mkdir -p ${NBTMPDIR}
+mkdir -p "${NBTMPDIR}"
 
-for nb in $*; do
-    NBFILENAME=$1
+for nb in "$@"; do
+    NBFILENAME=$nb
     NBNAME=${NBFILENAME%.*}
     NBNAME=${NBNAME##*/}
     NBTESTSCRIPT=${NBTMPDIR}/${NBNAME}-test.py
     shift
 
     echo --------------------------------------------------------------------------------
-    echo STARTING: ${NBNAME}
+    echo STARTING: "${NBNAME}"
     echo --------------------------------------------------------------------------------
-    jupyter nbconvert --to script ${NBFILENAME} --output ${NBTMPDIR}/${NBNAME}-test
-    echo "${MAGIC_OVERRIDE_CODE}" > ${NBTMPDIR}/tmpfile
-    cat ${NBTESTSCRIPT} >> ${NBTMPDIR}/tmpfile
-    mv ${NBTMPDIR}/tmpfile ${NBTESTSCRIPT}
+    jupyter nbconvert --to script "${NBFILENAME}" --output "${NBTMPDIR}"/"${NBNAME}"-test
+    echo "${MAGIC_OVERRIDE_CODE}" > "${NBTMPDIR}"/tmpfile
+    cat "${NBTESTSCRIPT}" >> "${NBTMPDIR}"/tmpfile
+    mv "${NBTMPDIR}"/tmpfile "${NBTESTSCRIPT}"
 
     echo "Running \"ipython ${NO_COLORS} ${NBTESTSCRIPT}\" on $(date)"
     echo
@@ -47,7 +47,7 @@ for nb in $*; do
     NBEXITCODE=$?
     echo EXIT CODE: ${NBEXITCODE}
     echo
-    EXITCODE=$((EXITCODE | ${NBEXITCODE}))
+    EXITCODE=$((EXITCODE | "${NBEXITCODE}"))
 done
 
 exit ${EXITCODE}
diff --git a/ci/validate_wheel.sh b/ci/validate_wheel.sh
index c4bd01faab..40bb27feeb 100755
--- a/ci/validate_wheel.sh
+++ b/ci/validate_wheel.sh
@@ -12,10 +12,10 @@ rapids-logger "validate packages with 'pydistcheck'"
 
 pydistcheck \
     --inspect \
-    "$(echo ${wheel_dir_relative_path}/*.whl)"
+    "$(echo "${wheel_dir_relative_path}"/*.whl)"
 
 rapids-logger "validate packages with 'twine'"
 
 twine check \
     --strict \
-    "$(echo ${wheel_dir_relative_path}/*.whl)"
+    "$(echo "${wheel_dir_relative_path}"/*.whl)"


Reviews:
user: login: dantegd
body: lgtm, nice improvement!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-01-23T19:21:24Z
user: login: jameslamb
body: Overall I support this! But left one question and one suggestion.
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-01-23T19:40:27Z
user: login: gforsyth
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-23T20:08:14Z
user: login: gforsyth
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-23T20:08:55Z
user: login: jameslamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-23T20:32:53Z
user: login: jameslamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-01-23T20:34:20Z
user: login: gforsyth
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-01-23T20:39:35Z
user: login: jameslamb
body: Looks great, thanks!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-01-29T14:45:46Z
user: login: jameslamb
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T20:41:36Z
user: login: jameslamb
body: Re-approving. Just left one small question.

Please also re-test `update-version.sh` if you didn't already. Something like this:

```shell
./ci/release/update-version.sh '25.04.00'
git grep -E '25\.2|25\.4|0\.42|0\.43'
```
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T20:47:02Z
user: login: gforsyth
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T14:18:14Z

Review comments:
diff_hunk: @@ -80,6 +80,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
path: .pre-commit-config.yaml
user: login: jameslamb
body: Why not run this on all shell scripts in the repo?

I especially would love to have this coverage on `build.sh` (at the repo root), as that's a public interface into building the components of this project. It's be nice to have `shellcheck` catch issues in changes there.
created_at: 2025-01-23T19:36:19Z
author_association: MEMBER
diff_hunk: @@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit
path: ci/run_cuml_integration_pytests.sh
user: login: jameslamb
body: ```suggestion
cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit 1
```

The `||` half of this will only be reached if the path doesn't exist. In that case, I think we want a non-0 exit code, so this fails loudly. Otherwise, we risk silently not running any tests in CI until someone notices.

A bare `exit` returns exit code 0.

```shell
cat > ./out.sh <<EOF
exit
EOF

bash ./out.sh
echo $?
# 0
```
created_at: 2025-01-23T19:39:37Z
author_association: MEMBER
diff_hunk: @@ -80,6 +80,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
path: .pre-commit-config.yaml
user: login: gforsyth
body: Yeah, `build.sh` coverage is definitely on my list -- I wanted to start with `ci` scripts to get `shellcheck` in place, then we can start to expand coverage to other shell scripts in the repository.
created_at: 2025-01-23T20:08:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit
path: ci/run_cuml_integration_pytests.sh
user: login: gforsyth
body: yeah, that's a very good point and suggestion
created_at: 2025-01-23T20:08:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,6 +80,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
path: .pre-commit-config.yaml
user: login: jameslamb
body: ok just wanted to be sure I understood the reasoning. As long as we'll come back again and do the rest, that's fine.
created_at: 2025-01-23T20:32:53Z
author_association: MEMBER
diff_hunk: @@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit
path: ci/run_cuml_integration_pytests.sh
user: login: jameslamb
body: I put that suggestion on just this line to show the point, but there are other `|| exit` in the diff that I think should be `|| exit 1`, can you update those as well?
created_at: 2025-01-23T20:34:20Z
author_association: MEMBER
diff_hunk: @@ -1,7 +1,7 @@
 #!/bin/bash
-# Copyright (c) 2024, NVIDIA CORPORATION.
+# Copyright (c) 2024-2025, NVIDIA CORPORATION.
 
 # Support invoking run_cuml_singlegpu_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/cuml/cuml/tests || exit
path: ci/run_cuml_integration_pytests.sh
user: login: gforsyth
body: Updated
created_at: 2025-01-23T20:39:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -80,6 +80,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
path: .pre-commit-config.yaml
user: login: jameslamb
body: I noticed that in the new state of this PR, there are now changes in other files outside of `ci/`... did you manually run `shellcheck` on those? If so, does that mean this filter could now be removed?
created_at: 2025-03-13T20:41:36Z
author_association: MEMBER
diff_hunk: @@ -80,6 +80,12 @@ repos:
       hooks:
           - id: rapids-dependency-file-generator
             args: ["--clean"]
+    - repo: https://github.com/shellcheck-py/shellcheck-py
+      rev: v0.10.0.1
+      hooks:
+        - id: shellcheck
+          args: ["--severity=warning"]
+          files: ^ci/
path: .pre-commit-config.yaml
user: login: gforsyth
body: It wouldn't pass yet as I haven't cleaned up all of `build.sh`, but that's on my list.

I mostly wanted to make sure we actually had the `VALIDARGS` check in `build.sh`, since otherwise there's no point in defining `VALIDARGS`
created_at: 2025-03-14T14:18:14Z
author_association: CONTRIBUTOR

PR comments:
user: login: gforsyth
created_at: 2025-01-29T15:20:34Z
body: ~/merge~
author_association: CONTRIBUTOR
user: login: gforsyth
created_at: 2025-03-14T14:06:44Z
body: > Please also re-test `update-version.sh` if you didn't already. Something like this:

Yep, confirmed with `./ci/release/update-version.sh '25.06.00'` that no version markers were left behind.
author_association: CONTRIBUTOR
user: login: gforsyth
created_at: 2025-03-14T14:06:51Z
body: /merge
author_association: CONTRIBUTOR