Repository: bazelbuild/rules_python (language: python)

Issue:
[Bazel CI] Error no native function or rules 'sh_test'

CI: https://buildkite.com/bazel/bazel-at-head-plus-downstream/builds/4198#01928938-25f1-42af-ad77-b740b840f290

 Platform: MacOS

 Logs: 
```
 ERROR: Traceback (most recent call last):
	File "/Users/buildkite/builds/bk-macos-pln3-pwxc/bazel-org-repo-root/rules_python/tests/integration/BUILD.bazel", line 90, column 30, in <toplevel>
		rules_python_integration_test(
	File "/Users/buildkite/builds/bk-macos-pln3-pwxc/bazel-org-repo-root/rules_python/tests/integration/integration_test.bzl", line 109, column 31, in rules_python_integration_test
		bazel_integration_test(
	File "/private/var/tmp/_bazel_buildkite/023fa58f497dd9f18a2fd23677a71d45/external/rules_bazel_integration_test+/bazel_integration_test/private/bazel_integration_test.bzl", line 164, column 11, in bazel_integration_test
		native.sh_test(
Error: no native function or rule 'sh_test'
Available attributes: action_listener, alias, available_xcodes, cc_binary, cc_import, cc_libc_top_alias, cc_library, cc_proto_library, cc_shared_library, cc_static_library, cc_test, cc_toolchain, cc_toolchain_alias, cc_toolchain_suite, config_feature_flag, config_setting, constraint_setting, constraint_value, environment, existing_rule, existing_rules, exports_files, extra_action, fdo_prefetch_hints, fdo_profile, filegroup, genquery, genrule, glob, java_binary, java_import, java_library, java_lite_proto_library, java_package_configuration, java_plugin, java_plugins_flag_alias, java_proto_library, java_runtime, java_test, java_toolchain, label_flag, label_setting, legacy_globals, memprof_profile, module_name, module_version, objc_import, objc_library, package, package_group, package_name, package_relative_label, platform, propeller_optimize, proto_lang_toolchain, proto_library, repo_name, repository_name, starlark_doc_extract, subpackages, test_suite, toolchain, toolchain_type, xcode_config, xcode_config_alias, xcode_version

``` 
 Culprit: 

 Steps:
  ``` 
 git clone https://github.com/bazelbuild/rules_python
git reset 0e604b06ae4c29c9d386677d3ac57ffe4012e3d7  --hard
export USE_BAZEL_VERSION=00fdb673ba8276e83f1c821a7f93aab7038377e2
bazel build ... @rules_python//examples/wheel/... 
 ``` 
 CC Greenteam @meteorcloudy
 


Discussion / hints:

Fix patch:
diff --git a/.bazelrc b/.bazelrc
index b484751c3c..1ca469cd75 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -4,8 +4,8 @@
 # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
 # To update these lines, execute
 # `bazel run @rules_bazel_integration_test//tools:update_deleted_packages`
-build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
-query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/python/private,gazelle/pythonconfig,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
+build --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
+query --deleted_packages=examples/build_file_generation,examples/build_file_generation/random_number_generator,examples/bzlmod,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod/entry_points,examples/bzlmod/entry_points/tests,examples/bzlmod/libs/my_lib,examples/bzlmod/other_module,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/patches,examples/bzlmod/py_proto_library,examples/bzlmod/py_proto_library/example.com/another_proto,examples/bzlmod/py_proto_library/example.com/proto,examples/bzlmod/runfiles,examples/bzlmod/tests,examples/bzlmod/tests/other_module,examples/bzlmod/whl_mods,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_proto_library,examples/py_proto_library/example.com/another_proto,examples/py_proto_library/example.com/proto,gazelle,gazelle/manifest,gazelle/manifest/generate,gazelle/manifest/hasher,gazelle/manifest/test,gazelle/modules_mapping,gazelle/python,gazelle/pythonconfig,gazelle/python/private,tests/integration/compile_pip_requirements,tests/integration/compile_pip_requirements_test_from_external_repo,tests/integration/custom_commands,tests/integration/ignore_root_user_error,tests/integration/ignore_root_user_error/submodule,tests/integration/local_toolchains,tests/integration/pip_parse,tests/integration/pip_parse/empty,tests/integration/py_cc_toolchain_registered
 
 test --test_output=errors
 
diff --git a/BUILD.bazel b/BUILD.bazel
index 038b56a0c6..5a58422328 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -13,7 +13,6 @@
 # limitations under the License.
 
 load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
-load(":version.bzl", "BAZEL_VERSION")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -24,6 +23,11 @@ exports_files([
     "version.bzl",
 ])
 
+exports_files(
+    [".bazelversion"],
+    visibility = ["//tests:__subpackages__"],
+)
+
 exports_files(
     glob(["*.md"]),
     visibility = ["//docs:__subpackages__"],
@@ -69,29 +73,3 @@ filegroup(
     ],
     visibility = ["//visibility:public"],
 )
-
-genrule(
-    name = "assert_bazelversion",
-    srcs = [".bazelversion"],
-    outs = ["assert_bazelversion_test.sh"],
-    cmd = """\
-set -o errexit -o nounset -o pipefail
-current=$$(cat "$(execpath .bazelversion)")
-cat > "$@" <<EOF
-#!/usr/bin/env bash
-set -o errexit -o nounset -o pipefail
-if [[ \"$${{current}}\" != \"{expected}\" ]]; then
-    >&2 echo "ERROR: current bazel version '$${{current}}' is not the expected '{expected}'"
-    exit 1
-fi
-EOF
-""".format(
-        expected = BAZEL_VERSION,
-    ),
-    executable = True,
-)
-
-sh_test(
-    name = "assert_bazelversion_test",
-    srcs = [":assert_bazelversion_test.sh"],
-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 835d541641..a6fe297c44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -62,6 +62,9 @@ A brief description of the categories of changes:
   enables (or disables) using pyc files from targets transitively
 * (pip) Skip patching wheels not matching `pip.override`'s `file`
   ([#2294](https://github.com/bazelbuild/rules_python/pull/2294)).
+* (chore): Add a `rules_shell` dev dependency and moved a `sh_test` target
+  outside of the `//:BUILD.bazel` file.
+  Fixes [#2299](https://github.com/bazelbuild/rules_python/issues/2299).
 
 ### Added
 * (py_wheel) Now supports `compress = (True|False)` to allow disabling
diff --git a/MODULE.bazel b/MODULE.bazel
index 780d7f8666..a1a8a5e507 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -70,6 +70,7 @@ use_repo(pip, "rules_python_publish_deps")
 bazel_dep(name = "stardoc", version = "0.6.2", dev_dependency = True, repo_name = "io_bazel_stardoc")
 bazel_dep(name = "rules_bazel_integration_test", version = "0.20.0", dev_dependency = True)
 bazel_dep(name = "rules_testing", version = "0.6.0", dev_dependency = True)
+bazel_dep(name = "rules_shell", version = "0.2.0", dev_dependency = True)
 
 # Extra gazelle plugin deps so that WORKSPACE.bzlmod can continue including it for e2e tests.
 # We use `WORKSPACE.bzlmod` because it is impossible to have dev-only local overrides.
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index 5504172e1d..e9d69c5ab8 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -256,3 +256,6 @@ local_path_override(
     module_name = "other_module",
     path = "other_module",
 )
+
+# example test dependencies
+bazel_dep(name = "rules_shell", version = "0.2.0", dev_dependency = True)
diff --git a/examples/bzlmod/MODULE.bazel.lock b/examples/bzlmod/MODULE.bazel.lock
index e292528dc0..2bd67a30e6 100644
--- a/examples/bzlmod/MODULE.bazel.lock
+++ b/examples/bzlmod/MODULE.bazel.lock
@@ -10,7 +10,8 @@
     "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel": "50341a62efbc483e8a2a6aec30994a58749bd7b885e18dd96aa8c33031e558ef",
     "https://bcr.bazel.build/modules/apple_support/1.5.0/source.json": "eb98a7627c0bc486b57f598ad8da50f6625d974c8f723e9ea71bd39f709c9862",
     "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8",
-    "https://bcr.bazel.build/modules/bazel_features/1.11.0/source.json": "c9320aa53cd1c441d24bd6b716da087ad7e4ff0d9742a9884587596edfe53015",
+    "https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
+    "https://bcr.bazel.build/modules/bazel_features/1.18.0/source.json": "cde886d88c8164b50b9b97dba7c0a64ca24d257b72ca3a2fcb06bee1fdb47ee4",
     "https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
     "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
     "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
@@ -25,13 +26,14 @@
     "https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
     "https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
     "https://bcr.bazel.build/modules/googletest/1.14.0/source.json": "2478949479000fdd7de9a3d0107ba2c85bb5f961c3ecb1aa448f52549ce310b5",
+    "https://bcr.bazel.build/modules/platforms/0.0.10/MODULE.bazel": "8cb8efaf200bdeb2150d93e162c40f388529a25852b332cec879373771e48ed5",
+    "https://bcr.bazel.build/modules/platforms/0.0.10/source.json": "f22828ff4cf021a6b577f1bf6341cb9dcd7965092a439f64fc1bb3b7a5ae4bd5",
     "https://bcr.bazel.build/modules/platforms/0.0.4/MODULE.bazel": "9b328e31ee156f53f3c416a64f8491f7eb731742655a47c9eec4703a71644aee",
     "https://bcr.bazel.build/modules/platforms/0.0.5/MODULE.bazel": "5733b54ea419d5eaf7997054bb55f6a1d0b5ff8aedf0176fef9eea44f3acda37",
     "https://bcr.bazel.build/modules/platforms/0.0.6/MODULE.bazel": "ad6eeef431dc52aefd2d77ed20a4b353f8ebf0f4ecdd26a807d2da5aa8cd0615",
     "https://bcr.bazel.build/modules/platforms/0.0.7/MODULE.bazel": "72fd4a0ede9ee5c021f6a8dd92b503e089f46c227ba2813ff183b71616034814",
     "https://bcr.bazel.build/modules/platforms/0.0.8/MODULE.bazel": "9f142c03e348f6d263719f5074b21ef3adf0b139ee4c5133e2aa35664da9eb2d",
     "https://bcr.bazel.build/modules/platforms/0.0.9/MODULE.bazel": "4a87a60c927b56ddd67db50c89acaa62f4ce2a1d2149ccb63ffd871d5ce29ebc",
-    "https://bcr.bazel.build/modules/platforms/0.0.9/source.json": "cd74d854bf16a9e002fb2ca7b1a421f4403cda29f824a765acd3a8c56f8d43e6",
     "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel": "a5a29bb89544f9b97edce05642fac225a808b5b7be74038ea3640fae2f8e66a7",
     "https://bcr.bazel.build/modules/protobuf/23.1/MODULE.bazel": "88b393b3eb4101d18129e5db51847cd40a5517a53e81216144a8c32dfeeca52a",
     "https://bcr.bazel.build/modules/protobuf/24.4/MODULE.bazel": "7bc7ce5f2abf36b3b7b7c8218d3acdebb9426aeb35c2257c96445756f970eb12",
@@ -60,6 +62,8 @@
     "https://bcr.bazel.build/modules/rules_proto/5.3.0-21.7/MODULE.bazel": "e8dff86b0971688790ae75528fe1813f71809b5afd57facb44dad9e8eca631b7",
     "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/MODULE.bazel": "1e5b502e2e1a9e825eef74476a5a1ee524a92297085015a052510b09a1a09483",
     "https://bcr.bazel.build/modules/rules_proto/6.0.0-rc1/source.json": "8d8448e71706df7450ced227ca6b3812407ff5e2ccad74a43a9fbe79c84e34e0",
+    "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
+    "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95",
     "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
     "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
     "https://bcr.bazel.build/modules/stardoc/0.5.3/source.json": "cd53fe968dc8cd98197c052db3db6d82562960c87b61e7a90ee96f8e4e0dda97",
@@ -104,7 +108,7 @@
     "@@platforms//host:extension.bzl%host_platform": {
       "general": {
         "bzlTransitiveDigest": "xelQcPZH8+tmuOHVjL9vDxMnnQNMlwj0SlvgoqBkm4U=",
-        "usagesDigest": "meSzxn3DUCcYEhq4HQwExWkWtU4EjriRBQLsZN+Q0SU=",
+        "usagesDigest": "V1R2Y2oMxKNfx2WCWpSCaUV1WefW1o8HZGm3v1vHgY4=",
         "recordedFileInputs": {},
         "recordedDirentsInputs": {},
         "envVariables": {},
@@ -1231,7 +1235,7 @@
     },
     "@@rules_python~//python/extensions:pip.bzl%pip": {
       "general": {
-        "bzlTransitiveDigest": "R4UDOUIolqXHNPsTlMq16g3t0xLeGMK/p7Ou1Cziz04=",
+        "bzlTransitiveDigest": "iikkSIkMsBiM/vadkEf9xEoVbaxZqrkUg08hiHr/LKk=",
         "usagesDigest": "MChlcSw99EuW3K7OOoMcXQIdcJnEh6YmfyjJm+9mxIg=",
         "recordedFileInputs": {
           "@@other_module~//requirements_lock_3_11.txt": "a7d0061366569043d5efcf80e34a32c732679367cb3c831c4cdc606adc36d314",
@@ -1242,7 +1246,7 @@
           "@@//requirements_lock_3_9.txt": "6a4990586366467d1e7d56d9f2ec9bafdd7e17fb29dc959aa5a6b0395c22eac7",
           "@@rules_python~~internal_deps~pypi__packaging//packaging-24.0.dist-info/RECORD": "be1aea790359b4c2c9ea83d153c1a57c407742a35b95ee36d00723509f5ed5dd",
           "@@//requirements_windows_3_10.txt": "c79f04bfaca147b8330275911a3328b81fc80828b9050a6bebdb15477627dabc",
-          "@@rules_python~//BUILD.bazel": "c421a2c2f3f428d2685a16eb9cc3fb8662605aba4ef151a87a356678bb7e866d",
+          "@@rules_python~//BUILD.bazel": "140002ce7e68de2fbf064bcdc37f854d4fa5b5d611a5fece6eb6cf19b8822bc4",
           "@@rules_python~~python~python_3_9_host//BUILD.bazel": "cf97d5763b728ce5ba8fdc3243350b967658ba4e3879734504aee002cec0d2b3",
           "@@rules_python~//python/private/pypi/requirements_parser/resolve_target_platforms.py": "42bf51980528302373529bcdfddb8014e485182d6bc9d2f7d3bbe1f11d8d923d"
         },
@@ -6138,7 +6142,7 @@
     },
     "@@rules_python~//python/private/pypi:pip.bzl%pip_internal": {
       "general": {
-        "bzlTransitiveDigest": "8o/oP53WcjE/4DQ4VQ0l0IVCa/i3A1mTDmN05qwm0yM=",
+        "bzlTransitiveDigest": "WPfU9gogl29lCI8A/N2aYn7RAhsCpZikVU1Hw7nMtAc=",
         "usagesDigest": "Y8ihY+R57BAFhalrVLVdJFrpwlbsiKz9JPJ99ljF7HA=",
         "recordedFileInputs": {
           "@@rules_python~//tools/publish/requirements.txt": "031e35d03dde03ae6305fe4b3d1f58ad7bdad857379752deede0f93649991b8a",
diff --git a/examples/multi_python_versions/MODULE.bazel b/examples/multi_python_versions/MODULE.bazel
index 1e5d32ebc0..4223916d22 100644
--- a/examples/multi_python_versions/MODULE.bazel
+++ b/examples/multi_python_versions/MODULE.bazel
@@ -55,3 +55,6 @@ pip.parse(
     python_version = "3.11",
     requirements_lock = "//requirements:requirements_lock_3_11.txt",
 )
+
+# example test dependencies
+bazel_dep(name = "rules_shell", version = "0.2.0", dev_dependency = True)
diff --git a/examples/multi_python_versions/WORKSPACE b/examples/multi_python_versions/WORKSPACE
index 4f731d95a8..48d2065282 100644
--- a/examples/multi_python_versions/WORKSPACE
+++ b/examples/multi_python_versions/WORKSPACE
@@ -45,3 +45,19 @@ multi_pip_parse(
 load("@pypi//:requirements.bzl", "install_deps")
 
 install_deps()
+
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# See https://github.com/bazelbuild/rules_shell/releases/tag/v0.2.0
+http_archive(
+    name = "rules_shell",
+    sha256 = "410e8ff32e018b9efd2743507e7595c26e2628567c42224411ff533b57d27c28",
+    strip_prefix = "rules_shell-0.2.0",
+    url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.2.0/rules_shell-v0.2.0.tar.gz",
+)
+
+load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains")
+
+rules_shell_dependencies()
+
+rules_shell_toolchains()
diff --git a/internal_deps.bzl b/internal_deps.bzl
index 56962cbd19..9c2e6b2581 100644
--- a/internal_deps.bzl
+++ b/internal_deps.bzl
@@ -46,6 +46,14 @@ def rules_python_internal_deps():
         ],
     )
 
+    # See https://github.com/bazelbuild/rules_shell/releases/tag/v0.2.0
+    http_archive(
+        name = "rules_shell",
+        sha256 = "410e8ff32e018b9efd2743507e7595c26e2628567c42224411ff533b57d27c28",
+        strip_prefix = "rules_shell-0.2.0",
+        url = "https://github.com/bazelbuild/rules_shell/releases/download/v0.2.0/rules_shell-v0.2.0.tar.gz",
+    )
+
     http_archive(
         name = "rules_pkg",
         urls = [
diff --git a/internal_setup.bzl b/internal_setup.bzl
index 3029c1546f..b3dc326444 100644
--- a/internal_setup.bzl
+++ b/internal_setup.bzl
@@ -21,6 +21,7 @@ load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")
 load("@rules_bazel_integration_test//bazel_integration_test:deps.bzl", "bazel_integration_test_rules_dependencies")
 load("@rules_bazel_integration_test//bazel_integration_test:repo_defs.bzl", "bazel_binaries")
 load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
+load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains")
 load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS")
 load("//python:versions.bzl", "MINOR_MAPPING", "TOOL_VERSIONS")
 load("//python/private:internal_config_repo.bzl", "internal_config_repo")  # buildifier: disable=bzl-visibility
@@ -56,3 +57,5 @@ def rules_python_internal_setup():
     bazel_starlib_dependencies()
     bazel_binaries(versions = SUPPORTED_BAZEL_VERSIONS)
     bazel_features_deps()
+    rules_shell_dependencies()
+    rules_shell_toolchains()


Test patch:
diff --git a/examples/bzlmod/tests/BUILD.bazel b/examples/bzlmod/tests/BUILD.bazel
index a778ca1aee..7cbc8d47b7 100644
--- a/examples/bzlmod/tests/BUILD.bazel
+++ b/examples/bzlmod/tests/BUILD.bazel
@@ -4,6 +4,7 @@ load("@python_versions//3.9:defs.bzl", py_binary_3_9 = "py_binary", py_test_3_9
 load("@pythons_hub//:versions.bzl", "MINOR_MAPPING")
 load("@rules_python//python:defs.bzl", "py_binary", "py_test")
 load("@rules_python//python/config_settings:transition.bzl", py_versioned_binary = "py_binary", py_versioned_test = "py_test")
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 
 py_binary(
     name = "version_default",
diff --git a/examples/multi_python_versions/tests/BUILD.bazel b/examples/multi_python_versions/tests/BUILD.bazel
index 5df41bded7..cf14bf0a3b 100644
--- a/examples/multi_python_versions/tests/BUILD.bazel
+++ b/examples/multi_python_versions/tests/BUILD.bazel
@@ -4,6 +4,7 @@ load("@python//3.11:defs.bzl", py_binary_3_11 = "py_binary", py_test_3_11 = "py_
 load("@python//3.8:defs.bzl", py_binary_3_8 = "py_binary", py_test_3_8 = "py_test")
 load("@python//3.9:defs.bzl", py_binary_3_9 = "py_binary", py_test_3_9 = "py_test")
 load("@rules_python//python:defs.bzl", "py_binary", "py_test")
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 
 copy_file(
     name = "copy_version",
diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel
index e7dbef65d8..0fb8e88135 100644
--- a/tests/BUILD.bazel
+++ b/tests/BUILD.bazel
@@ -1,4 +1,6 @@
 load("@bazel_skylib//rules:build_test.bzl", "build_test")
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
+load("//:version.bzl", "BAZEL_VERSION")
 
 package(default_visibility = ["//visibility:public"])
 
@@ -25,3 +27,29 @@ build_test(
         "//python/entry_points:py_console_script_binary_bzl",
     ],
 )
+
+genrule(
+    name = "assert_bazelversion",
+    srcs = ["//:.bazelversion"],
+    outs = ["assert_bazelversion_test.sh"],
+    cmd = """\
+set -o errexit -o nounset -o pipefail
+current=$$(cat "$(execpath //:.bazelversion)")
+cat > "$@" <<EOF
+#!/usr/bin/env bash
+set -o errexit -o nounset -o pipefail
+if [[ \"$${{current}}\" != \"{expected}\" ]]; then
+    >&2 echo "ERROR: current bazel version '$${{current}}' is not the expected '{expected}'"
+    exit 1
+fi
+EOF
+""".format(
+        expected = BAZEL_VERSION,
+    ),
+    executable = True,
+)
+
+sh_test(
+    name = "assert_bazelversion_test",
+    srcs = [":assert_bazelversion_test.sh"],
+)
diff --git a/tests/support/sh_py_run_test.bzl b/tests/support/sh_py_run_test.bzl
index 32df5b8caf..066f091650 100644
--- a/tests/support/sh_py_run_test.bzl
+++ b/tests/support/sh_py_run_test.bzl
@@ -17,6 +17,7 @@ This facilitates verify running binaries with different configuration settings
 without the overhead of a bazel-in-bazel integration test.
 """
 
+load("@rules_shell//shell:sh_test.bzl", "sh_test")
 load("//python:py_binary.bzl", "py_binary")
 load("//python:py_test.bzl", "py_test")
 load("//python/private:toolchain_types.bzl", "TARGET_TOOLCHAIN_TYPE")  # buildifier: disable=bzl-visibility
@@ -149,7 +150,7 @@ def py_reconfig_test(*, name, **kwargs):
 
 def sh_py_run_test(*, name, sh_src, py_src, **kwargs):
     bin_name = "_{}_bin".format(name)
-    native.sh_test(
+    sh_test(
         name = name,
         srcs = [sh_src],
         data = [bin_name],